diff mbox

[FFmpeg-devel,v2,2/3] avcodec/bitpacked: add interlace support

Message ID 1524250961-16337-2-git-send-email-patrick.keroulas@savoirfairelinux.com
State Superseded
Headers show

Commit Message

Patrick Keroulas April 20, 2018, 7:02 p.m. UTC
From: Damien Riegel <damien.riegel@savoirfairelinux.com>

This codec is already capable of depacking some combinations of pixel
formats and depth as defined in the RFC4175. The only difference between
progressive and interlace is that either a packet will contain the whole
frame, or only a field of the frame.

There is no mechanism for interlaced frames reconstruction at the rtp
demux level, so it has to be handled by the codec which needs to
partially recompose an AVFrame with every incoming field AVPacket.
A frame is ouput only when the frame is completed with the 2nd field
(bottom).

Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
Signed-off-by: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
---

Change v1 -> v2:
 Replaced field packets cloning with partial frame decoding.

 @ Rostislav Pehlivanov: Regarding your comment on v1 (thank you for
 that), I think we can avoid using the bottom field flag because the
 interlaced/progressive format is determined once, at the decoder
 initialization, more precisely when the sdp is parsed. RFC4175
 doesn't define any dynamic flag to switch from one to another.
 So there is no doubt that 'not top field' means 'bottom field'.

---
 libavcodec/avcodec.h   |   4 ++
 libavcodec/bitpacked.c | 115 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 103 insertions(+), 16 deletions(-)

Comments

Rostislav Pehlivanov April 20, 2018, 7:40 p.m. UTC | #1
On 20 April 2018 at 20:02, Patrick Keroulas <
patrick.keroulas@savoirfairelinux.com> wrote:

> From: Damien Riegel <damien.riegel@savoirfairelinux.com>
>
> This codec is already capable of depacking some combinations of pixel
> formats and depth as defined in the RFC4175. The only difference between
> progressive and interlace is that either a packet will contain the whole
> frame, or only a field of the frame.
>
> There is no mechanism for interlaced frames reconstruction at the rtp
> demux level, so it has to be handled by the codec which needs to
> partially recompose an AVFrame with every incoming field AVPacket.
> A frame is ouput only when the frame is completed with the 2nd field
> (bottom).
>
> Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
> Signed-off-by: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
> ---
>
> Change v1 -> v2:
>  Replaced field packets cloning with partial frame decoding.
>
>  @ Rostislav Pehlivanov: Regarding your comment on v1 (thank you for
>  that), I think we can avoid using the bottom field flag because the
>  interlaced/progressive format is determined once, at the decoder
>  initialization, more precisely when the sdp is parsed. RFC4175
>  doesn't define any dynamic flag to switch from one to another.
>  So there is no doubt that 'not top field' means 'bottom field'.
>
>
Doesn't matter what RFC4175 says, the decoder needs to be able to handle
any field order sent at any time, including switching to progressive. This
isn't a theoretical situation. What if there's packet loss or the user
switches streams? What if the user doesn't use the decoder for RFC4175 but
rather uses it as a generic unpacker to decode something packed.


  */
>  #define AV_PKT_FLAG_DISPOSABLE 0x0010
>
> +/**
> + * The packet contains a top field.
> + */
> +#define AV_PKT_FLAG_TOP_FIELD  0x0010
>

 This is wrong, AV_PKT_FLAG_DISPOSABLE == AV_PKT_FLAG_TOP_FIELD.


I think there must be a BOTTOM_FIELD flag along with a top field flag, with
neither of them flag meaning packet carries progressive.
diff mbox

Patch

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index fb0c6fa..350e8d9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1480,6 +1480,10 @@  typedef struct AVPacket {
  */
 #define AV_PKT_FLAG_DISPOSABLE 0x0010
 
+/**
+ * The packet contains a top field.
+ */
+#define AV_PKT_FLAG_TOP_FIELD  0x0010
 
 enum AVSideDataParamChangeFlags {
     AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT  = 0x0001,
diff --git a/libavcodec/bitpacked.c b/libavcodec/bitpacked.c
index 85d4bdd..84381bd 100644
--- a/libavcodec/bitpacked.c
+++ b/libavcodec/bitpacked.c
@@ -33,15 +33,20 @@ 
 
 struct BitpackedContext {
     int (*decode)(AVCodecContext *avctx, AVFrame *frame,
-                  AVPacket *pkt);
+                  AVPacket *pkt, int top_field);
+    AVFrame *cur_interlaced_frame;
+    int prev_top_field;
 };
 
 /* For this format, it's a simple passthrough */
 static int bitpacked_decode_uyvy422(AVCodecContext *avctx, AVFrame *frame,
-                                    AVPacket *avpkt)
+                                    AVPacket *avpkt, int top_field)
 {
     int ret;
 
+    if (frame->interlaced_frame)
+        return AVERROR_PATCHWELCOME;
+
     /* there is no need to copy as the data already match
      * a known pixel format */
     frame->buf[0] = av_buffer_ref(avpkt->buf);
@@ -56,17 +61,22 @@  static int bitpacked_decode_uyvy422(AVCodecContext *avctx, AVFrame *frame,
 }
 
 static int bitpacked_decode_yuv422p10(AVCodecContext *avctx, AVFrame *frame,
-                                      AVPacket *avpkt)
+                                      AVPacket *avpkt, int top_field)
 {
     uint64_t frame_size = (uint64_t)avctx->width * (uint64_t)avctx->height * 20;
     uint64_t packet_size = (uint64_t)avpkt->size * 8;
+    int interlaced = frame->interlaced_frame;
     GetBitContext bc;
     uint16_t *y, *u, *v;
     int ret, i, j;
 
-
-    if (frame_size > packet_size)
+    /* check packet size depending on the interlaced/progressive format */
+    if (interlaced) {
+        if ((frame_size >> 1) > packet_size)
+            return AVERROR_INVALIDDATA;
+    } else if (frame_size > packet_size) {
         return AVERROR_INVALIDDATA;
+    }
 
     if (avctx->width % 2)
         return AVERROR_PATCHWELCOME;
@@ -75,7 +85,18 @@  static int bitpacked_decode_yuv422p10(AVCodecContext *avctx, AVFrame *frame,
     if (ret)
         return ret;
 
-    for (i = 0; i < avctx->height; i++) {
+    /*
+     * if the frame is interlaced, the avpkt we are getting is either the top
+     * or the bottom field. If it's the bottom field, it contains all the odd
+     * lines of the recomposed frame, so we start at offset 1.
+     */
+    i = (interlaced && !top_field) ? 1 : 0;
+
+    /*
+     * Packets from interlaced frames contain either even lines, or odd
+     * lines, so increment by two in that case.
+     */
+    for (; i < avctx->height; interlaced ? i += 2 : i++) {
         y = (uint16_t*)(frame->data[0] + i * frame->linesize[0]);
         u = (uint16_t*)(frame->data[1] + i * frame->linesize[1]);
         v = (uint16_t*)(frame->data[2] + i * frame->linesize[2]);
@@ -100,17 +121,35 @@  static av_cold int bitpacked_init_decoder(AVCodecContext *avctx)
 
     if (avctx->codec_tag == MKTAG('U', 'Y', 'V', 'Y')) {
         if (avctx->bits_per_coded_sample == 16 &&
-            avctx->pix_fmt == AV_PIX_FMT_UYVY422)
+            avctx->pix_fmt == AV_PIX_FMT_UYVY422) {
+
+            if (avctx->field_order > AV_FIELD_PROGRESSIVE) {
+                av_log(avctx, AV_LOG_ERROR, "interlaced not yet supported for 8-bit\n");
+                return AVERROR_PATCHWELCOME;
+            }
+
             bc->decode = bitpacked_decode_uyvy422;
-        else if (avctx->bits_per_coded_sample == 20 &&
-                 avctx->pix_fmt == AV_PIX_FMT_YUV422P10)
+        } else if (avctx->bits_per_coded_sample == 20 &&
+                 avctx->pix_fmt == AV_PIX_FMT_YUV422P10) {
             bc->decode = bitpacked_decode_yuv422p10;
-        else
+        } else {
             return AVERROR_INVALIDDATA;
+        }
     } else {
         return AVERROR_INVALIDDATA;
     }
 
+    bc->cur_interlaced_frame = av_frame_alloc();
+
+    return 0;
+}
+
+static av_cold int bitpacked_end_decoder(AVCodecContext *avctx)
+{
+    struct BitpackedContext *bc = avctx->priv_data;
+
+    av_frame_free(&bc->cur_interlaced_frame);
+
     return 0;
 }
 
@@ -120,24 +159,67 @@  static int bitpacked_decode(AVCodecContext *avctx, void *data, int *got_frame,
     struct BitpackedContext *bc = avctx->priv_data;
     int buf_size = avpkt->size;
     AVFrame *frame = data;
+    int top_field = 0;
     int res;
 
     frame->pict_type = AV_PICTURE_TYPE_I;
     frame->key_frame = 1;
 
+    if (avctx->field_order != AV_FIELD_PROGRESSIVE) {
+        top_field = avpkt->flags & AV_PKT_FLAG_TOP_FIELD;
+        frame->interlaced_frame = 1;
+        frame->top_field_first = 1;
+    }
+
     if (avctx->pix_fmt == AV_PIX_FMT_YUV422P10) {
         res = ff_get_buffer(avctx, frame, 0);
         if (res < 0)
             return res;
     }
 
-    res = bc->decode(avctx, frame, avpkt);
-    if (res)
-        return res;
-
-    *got_frame = 1;
-    return buf_size;
+    if (frame->interlaced_frame) {
+        if (top_field) {
+            if (bc->prev_top_field)
+                av_log(avctx, AV_LOG_WARNING, "Consecutive top field detected.\n");
+
+            /* always decode the top (1st) field and ref the result frame
+             * but don't output anything */
+            if ((res = bc->decode(avctx, frame, avpkt, 1)) < 0)
+                return res;
+
+            av_frame_unref(bc->cur_interlaced_frame);
+            if ((res = av_frame_ref(bc->cur_interlaced_frame, frame)) < 0)
+                return res;
+
+            bc->prev_top_field = 1;
+
+            return 0;
+        } else {
+            if (!bc->prev_top_field) {
+                av_log(avctx, AV_LOG_WARNING, "Drop consecutive bottom field.\n");
+                return 0;
+            }
+
+            /* complete the ref'd frame with bottom field and output the
+             * result */
+            if ((res = bc->decode(avctx, bc->cur_interlaced_frame, avpkt, 0)) < 0)
+                return res;
+
+            av_frame_unref(frame);
+            if ((res = av_frame_ref(frame, bc->cur_interlaced_frame)) < 0)
+                return res;
+
+            bc->prev_top_field = 0;
+            *got_frame = 1;
+            return buf_size;
+        }
+    } else {
+        if ((res = bc->decode(avctx, frame, avpkt, 0)) < 0)
+            return res;
 
+        *got_frame = 1;
+        return buf_size;
+    }
 }
 
 AVCodec ff_bitpacked_decoder = {
@@ -147,6 +229,7 @@  AVCodec ff_bitpacked_decoder = {
     .id = AV_CODEC_ID_BITPACKED,
     .priv_data_size        = sizeof(struct BitpackedContext),
     .init = bitpacked_init_decoder,
+    .close = bitpacked_end_decoder,
     .decode = bitpacked_decode,
     .capabilities = AV_CODEC_CAP_EXPERIMENTAL,
 };