From patchwork Thu Mar 9 08:20:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: gabriel.dalimonte@gmail.com X-Patchwork-Id: 2860 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.50.79 with SMTP id y76csp455879vsy; Thu, 9 Mar 2017 10:35:02 -0800 (PST) X-Received: by 10.28.156.195 with SMTP id f186mr11300831wme.40.1489084502188; Thu, 09 Mar 2017 10:35:02 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h19si9721844wrc.138.2017.03.09.10.35.01; Thu, 09 Mar 2017 10:35:02 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 144F168826A; Thu, 9 Mar 2017 20:34:45 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-it0-f65.google.com (mail-it0-f65.google.com [209.85.214.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5EB8F688300 for ; Thu, 9 Mar 2017 10:28:06 +0200 (EET) Received: by mail-it0-f65.google.com with SMTP id w185so9407813ita.3 for ; Thu, 09 Mar 2017 00:28:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=VciO/Pi5tJcOZ+W3DefPC3/W4FbkWBJhayEec1FT7x4=; b=LPJzEmk3dwyAUqI81Lcfu5c7xV06hWHE1TJeX2i5vIw3dnX7RTXVSWkf31uBuAf0pl wQdGHfCc+0ake8JleDVTbr9sHsAezYqWHVnHjyZRXp/Cj6KXq7q5ReCnK6CrMWeKNQNp P1RhTZOyOXuwbx3K3e6sgrDVHpu+utrQhwbNXe4HX1KBPfbEnWabIqwjSQZyYG1o5ngJ M0UNagKz49WAzA03MjJSW6UND70jegW2bIMvfPSjct0E87RgGaEm1I7+h245UEcAZCJe a4/xoalc44mv9grGgUrdrs9q1q3sPqSi06EM/Qe9lHLOzRiZP0BX9MYbegVPrUiMmyX0 4XUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=VciO/Pi5tJcOZ+W3DefPC3/W4FbkWBJhayEec1FT7x4=; b=Fsfn5yB5ZStE2wvIo0FBcZ0OYXkFHb/cg91ioU4RU0nQCEIE1Ez2PPAHszD71SK5sV YSIKlFPWQAlD+SKwsUlLg8+Z9NfJRRM9Hxy4W/6wX17jQMaEVwXflUGyU0jzLFpFFoIF XsHOAYEWKiJyjb+SK2eDdjrIMpBpKqiRPloDQn9OBq9IYni69FSE5l9nuYaji2/Z3PnA v1wtHeoFKT36FbExs4aV6D9/4BB+NVy3idIQBSzTNFyDJV7TjmfTJ1IxO+CxWzyU5oJj LXb+l1FsnnRx91xZE1p/QwwiwCrKfnGlVT6IqcsyRjMsJuZaNj34s3Hoq4lyigmJPEgw M7vQ== X-Gm-Message-State: AMke39mB4tEdfxJisPo5bMzsDXrSo3TLGnrQ/6hmafnpmyOREVWqu33jA5BQRZkeCmOopw== X-Received: by 10.36.51.68 with SMTP id k65mr29694608itk.64.1489047663233; Thu, 09 Mar 2017 00:21:03 -0800 (PST) Received: from localhost.localdomain (135-23-49-249.cpe.pppoe.ca. [135.23.49.249]) by smtp.gmail.com with ESMTPSA id n18sm2706788ion.42.2017.03.09.00.21.02 (version=TLS1 cipher=AES128-SHA bits=128/128); Thu, 09 Mar 2017 00:21:02 -0800 (PST) From: gabriel.dalimonte@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Thu, 9 Mar 2017 03:20:37 -0500 Message-Id: <20170309082037.54566-1-gabriel.dalimonte@gmail.com> X-Mailer: git-send-email 2.10.1 (Apple Git-78) X-Mailman-Approved-At: Thu, 09 Mar 2017 20:34:43 +0200 Subject: [FFmpeg-devel] [PATCH] libavfilter/vf_detelecine: Added documentation to clarify the workings of the filter. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Gabriel D'Alimonte MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Gabriel D'Alimonte Hi! I hope this patch adding code documentation to the detelecine filter will help with maintenance. --- libavfilter/vf_detelecine.c | 115 +++++++++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/libavfilter/vf_detelecine.c b/libavfilter/vf_detelecine.c index 0d5f88d..f6f9776 100644 --- a/libavfilter/vf_detelecine.c +++ b/libavfilter/vf_detelecine.c @@ -34,24 +34,31 @@ typedef struct { const AVClass *class; - int first_field; - char *pattern; - int start_frame; - int init_len; - unsigned int pattern_pos; - unsigned int nskip_fields; - int64_t start_time; - - AVRational pts; - AVRational ts_unit; - int occupied; - - int nb_planes; - int planeheight[4]; - int stride[4]; - - AVFrame *frame[2]; - AVFrame *temp; + int first_field; ///< 0 = top first field, 1 = bottom first field. + char *pattern; /**< The telecine pattern representing the original + frames -> fields mapping. */ + int start_frame; /**< Describes the input frame position offset + into pattern. */ + int init_len; /**< Number of stray fields at the beginning + resulting from a cut. */ + unsigned int pattern_pos; ///< The current position within pattern. + unsigned int nskip_fields; /**< Number of fields from input to skip writing + to the output. */ + int64_t start_time; ///< The PTS value of the first frame. + + AVRational pts; ///< PTS multiplier, telecined frames/detelecined frames. + AVRational ts_unit; ///< Timestamp interval of one output frame. + int occupied; ///< Boolean, indicates whether temp has data in it. + + int nb_planes; ///< The number of planes in the video format. + int planeheight[4]; /**< The height of each plane in nb_planes from the + video context. */ + int stride[4]; /**< The number of bytes in one row of data for each + plane in nb_planes. */ + + AVFrame *frame[2]; ///< Detelecined output frames. + AVFrame *temp; /**< Buffered frame needed to contribute fields to the + next output frame. */ } DetelecineContext; #define OFFSET(x) offsetof(DetelecineContext, x) @@ -73,9 +80,9 @@ AVFILTER_DEFINE_CLASS(detelecine); static av_cold int init(AVFilterContext *ctx) { DetelecineContext *s = ctx->priv; - const char *p; - int max = 0; - int sum = 0; + const char *p; ///< Induction variable for loops. + int max = 0; ///< Max of the field values in pattern. + int sum = 0; ///< Sum of the field values in pattern. if (!strlen(s->pattern)) { av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n"); @@ -105,6 +112,11 @@ static av_cold int init(AVFilterContext *ctx) s->init_len = 0; if (s->start_frame != 0) { + /** + * Calculates the number of residual frames as the result of a cut so the + * pattern will begin at the proper position while accounting for any + * residual frames from the previous number in the pattern. + */ int nfields = 0; for (p = s->pattern; *p; p++) { nfields += *p - '0'; @@ -129,6 +141,7 @@ static int query_formats(AVFilterContext *ctx) for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + // Skip over formats which pack data unconventionally. if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL || desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) && @@ -199,7 +212,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; DetelecineContext *s = ctx->priv; - int i, len = 0, ret = 0, out = 0; + int i; //< For loop inductive variable. + int len = 0; ///< Number of fields the current output frame spans. + int ret = 0; ///< The result of the next filter in the filtergraph. + int out = 0; ///< Number of output frames. if (s->start_time == AV_NOPTS_VALUE) s->start_time = inpicref->pts; @@ -208,6 +224,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->nskip_fields -= 2; return 0; } else if (s->nskip_fields >= 1) { + // One of the fields from the current frame will be needed. for (i = 0; i < s->nb_planes; i++) { av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], inpicref->data[i], inpicref->linesize[i], @@ -220,6 +237,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) } if (s->nskip_fields == 0) { + /** + * init_len may only not be zero during the first time filter_frame + * is called. + */ len = s->init_len; s->init_len = 0; while(!len && s->pattern[s->pattern_pos]) { @@ -230,20 +251,27 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) if (!s->pattern[s->pattern_pos]) s->pattern_pos = 0; - if(!len) { // do not output any field as the entire pattern is zero + if(!len) { // Do not output any field as the entire pattern is zero. av_frame_free(&inpicref); return 0; } if (len == 1 && s->occupied) { + /** + * The pattern describes an output frame of one field and a frame is + * buffered. Copy the frame entirely to the output despite the + * possibility it may be interlaced to maintain a consistent frame + * size. + */ s->occupied = 0; - // output THIS image as-is + // Output THIS input frame as-is. for (i = 0; i < s->nb_planes; i++) av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], s->temp->data[i], s->temp->linesize[i], s->stride[i], s->planeheight[i]); len = 0; + // Two fields in input frame, only handled the first. while(!len && s->pattern[s->pattern_pos]) { len = s->pattern[s->pattern_pos] - '0'; s->pattern_pos++; @@ -258,14 +286,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) if (s->occupied) { for (i = 0; i < s->nb_planes; i++) { - // fill in the EARLIER field from the new pic + // Fill in the EARLIER field from the new input frame. av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * s->first_field, s->frame[out]->linesize[i] * 2, inpicref->data[i] + inpicref->linesize[i] * s->first_field, inpicref->linesize[i] * 2, s->stride[i], (s->planeheight[i] - s->first_field + 1) / 2); - // fill in the LATER field from the buffered pic + // Fill in the LATER field from the buffered frame. av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * !s->first_field, s->frame[out]->linesize[i] * 2, s->temp->data[i] + s->temp->linesize[i] * !s->first_field, @@ -276,6 +304,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->occupied = 0; if (len <= 2) { + /** + * The output frame was two fields and the second field came from + * this input frame. This input frame has one field which belongs + * to the next output frame. + */ for (i = 0; i < s->nb_planes; i++) { av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], inpicref->data[i], inpicref->linesize[i], @@ -285,25 +318,43 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->occupied = 1; } ++out; + /** + * This input frame completed an output frame, a len longer than + * three signifies there are additional input frames which contain + * the same fields as this completed output frame. Subtract three + * to get the number of fields to skip (nskip_fields) in order to + * avoid treating those input frames as new data. + */ len = (len >= 3) ? len - 3 : 0; } else { if (len >= 2) { - // output THIS image as-is + // Output THIS input frame as-is. for (i = 0; i < s->nb_planes; i++) av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], inpicref->data[i], inpicref->linesize[i], s->stride[i], s->planeheight[i]); + /** + * Any number of fields representing this output frame which + * is greater than two results in duplicate input frames of + * the same fields. Skip len - 2 to avoid outputting frames + * from the same fields. + */ len -= 2; ++out; } else if (len == 1) { - // output THIS image as-is + /** + * Copy the frame entirely to the output despite the + * possibility it may be interlaced to maintain a consistent + * frame size. Buffer the frame because the second field is + * part of the next output frame. + */ + // Output THIS input frame as-is. for (i = 0; i < s->nb_planes; i++) av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], inpicref->data[i], inpicref->linesize[i], s->stride[i], s->planeheight[i]); - for (i = 0; i < s->nb_planes; i++) { av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], inpicref->data[i], inpicref->linesize[i], @@ -323,6 +374,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->occupied = 0; } } + + /** + * len > 0 indicates additional fields with duplicate data, skip that + * number of fields. + */ s->nskip_fields = len; for (i = 0; i < out; ++i) { @@ -349,6 +405,7 @@ static av_cold void uninit(AVFilterContext *ctx) { DetelecineContext *s = ctx->priv; + // Cleanup frames allocated in config_input. av_frame_free(&s->temp); av_frame_free(&s->frame[0]); av_frame_free(&s->frame[1]);