]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/crystalhd.c
Merge commit 'f0a41afd8a37ebe972436fabfa3d289178bbd83b'
[ffmpeg] / libavcodec / crystalhd.c
index 452edeff43365805763310025f9a3eee77ae29e6..9bbb6e8bbac147b27d636c5a8055593524d96760 100644 (file)
@@ -237,16 +237,17 @@ static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque,
  * The OpaqueList is built in decode order, while elements will be removed
  * in presentation order. If frames are reordered, this means we must be
  * able to remove elements that are not the first element.
+ *
+ * Returned node must be freed by caller.
  */
-static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp,
-                               uint64_t *reordered_opaque, uint8_t *pic_type)
+static OpaqueList *opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp)
 {
     OpaqueList *node = priv->head;
 
     if (!priv->head) {
         av_log(priv->avctx, AV_LOG_ERROR,
                "CrystalHD: Attempted to query non-existent timestamps.\n");
-        return FALSE;
+        return NULL;
     }
 
     /*
@@ -254,15 +255,13 @@ static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp,
      * the head pointer rather than the previous element in the list.
      */
     if (priv->head->fake_timestamp == fake_timestamp) {
-        *reordered_opaque = node->reordered_opaque;
-        *pic_type = node->pic_type;
         priv->head = node->next;
-        av_free(node);
 
         if (!priv->head->next)
             priv->tail = priv->head;
 
-        return TRUE;
+        node->next = NULL;
+        return node;
     }
 
     /*
@@ -270,25 +269,23 @@ static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp,
      * previous element available to rewrite its next pointer.
      */
     while (node->next) {
-        OpaqueList *next = node->next;
-        if (next->fake_timestamp == fake_timestamp) {
-            *reordered_opaque = node->reordered_opaque;
-            *pic_type = node->pic_type;
-            node->next = next->next;
-            av_free(next);
+        OpaqueList *current = node->next;
+        if (current->fake_timestamp == fake_timestamp) {
+            node->next = current->next;
 
             if (!node->next)
                priv->tail = node;
 
-            return TRUE;
+            current->next = NULL;
+            return current;
         } else {
-            node = next;
+            node = current;
         }
     }
 
     av_log(priv->avctx, AV_LOG_VERBOSE,
            "CrystalHD: Couldn't match fake_timestamp.\n");
-    return FALSE;
+    return NULL;
 }
 
 
@@ -506,24 +503,13 @@ static av_cold int init(AVCodecContext *avctx)
 }
 
 
-/*
- * The CrystalHD doesn't report interlaced H.264 content in a way that allows
- * us to distinguish between specific cases that require different handling.
- * So, for now, we have to hard-code the behaviour we want.
- *
- * Specifically, there are PAFF samples where input is always separate fields
- * but the hardware returns separate fields on one occasion and a field-pair
- * on another. The code assumes the first case and define
- * ASSUME_TWO_INPUTS_ONE_OUTPUT to assume the second case.
- */
-#define ASSUME_TWO_INPUTS_ONE_OUTPUT 0
 static inline CopyRet copy_frame(AVCodecContext *avctx,
                                  BC_DTS_PROC_OUT *output,
                                  void *data, int *data_size)
 {
     BC_STATUS ret;
     BC_DTS_STATUS decoder_status;
-    uint8_t ignore_interlaced;
+    uint8_t trust_interlaced;
     uint8_t interlaced;
 
     CHDContext *priv = avctx->priv_data;
@@ -543,19 +529,21 @@ static inline CopyRet copy_frame(AVCodecContext *avctx,
     int dStride;
 
     if (output->PicInfo.timeStamp != 0) {
-        uint8_t pop_ret;
-        pop_ret = opaque_list_pop(priv, output->PicInfo.timeStamp,
-                                  &pkt_pts, &pic_type);
-        if (!pop_ret) {
-           /*
-            * We will encounter a situation where a timestamp cannot be
-            * popped if a second field is being returned. In this case,
-            * each field has the same timestamp and the first one will
-            * cause it to be popped. To keep subsequent calculations
-            * simple, pic_type should be set a FIELD value - doesn't
-            * matter which, but I chose BOTTOM.
-            */
-           pic_type = PICT_BOTTOM_FIELD;
+        OpaqueList *node = opaque_list_pop(priv, output->PicInfo.timeStamp);
+        if (node) {
+            pkt_pts = node->reordered_opaque;
+            pic_type = node->pic_type;
+            av_free(node);
+        } else {
+            /*
+             * We will encounter a situation where a timestamp cannot be
+             * popped if a second field is being returned. In this case,
+             * each field has the same timestamp and the first one will
+             * cause it to be popped. To keep subsequent calculations
+             * simple, pic_type should be set a FIELD value - doesn't
+             * matter which, but I chose BOTTOM.
+             */
+            pic_type = PICT_BOTTOM_FIELD;
         }
         av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n",
                output->PicInfo.timeStamp);
@@ -571,18 +559,50 @@ static inline CopyRet copy_frame(AVCodecContext *avctx,
     }
 
     /*
-     * Testing has, so far, shown that we can't trust the interlaced flag for
-     * H.264 content when VDEC_FLAG_UNKNOWN_SRC is set.
+     * For most content, we can trust the interlaced flag returned
+     * by the hardware, but sometimes we can't. These are the
+     * conditions under which we can trust the flag:
+     *
+     * 1) It's not h.264 content
+     * 2) The UNKNOWN_SRC flag is not set
+     * 3) We know we're expecting a second field
+     * 4) The hardware reports this picture and the next picture
+     *    have the same picture number.
+     *
+     * Note that there can still be interlaced content that will
+     * fail this check, if the hardware hasn't decoded the next
+     * picture or if there is a corruption in the stream. (In either
+     * case a 0 will be returned for the next picture number)
      */
-    ignore_interlaced = avctx->codec->id == CODEC_ID_H264 &&
-                        (output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) &&
-                        (pic_type == 0 || pic_type == PICT_FRAME ||
-                         ASSUME_TWO_INPUTS_ONE_OUTPUT);
-    interlaced        = (output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC) &&
-                        !ignore_interlaced;
+    trust_interlaced = avctx->codec->id != CODEC_ID_H264 ||
+                       !(output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) ||
+                       priv->need_second_field ||
+                       (decoder_status.picNumFlags & ~0x40000000) ==
+                       output->PicInfo.picture_number;
 
-    av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d | ignore_interlaced %d\n",
-           interlaced, ignore_interlaced);
+    /*
+     * If we got a false negative for trust_interlaced on the first field,
+     * we will realise our mistake here when we see that the picture number is that
+     * of the previous picture. We cannot recover the frame and should discard the
+     * second field to keep the correct number of output frames.
+     */
+    if (output->PicInfo.picture_number == priv->last_picture && !priv->need_second_field) {
+        av_log(avctx, AV_LOG_WARNING,
+               "Incorrectly guessed progressive frame. Discarding second field\n");
+        /* Returning without providing a picture. */
+        return RET_OK;
+    }
+
+    interlaced = (output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC) &&
+                 trust_interlaced;
+
+    if (!trust_interlaced && (decoder_status.picNumFlags & ~0x40000000) == 0) {
+        av_log(avctx, AV_LOG_VERBOSE,
+               "Next picture number unknown. Assuming progressive frame.\n");
+    }
+
+    av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d | trust_interlaced %d\n",
+           interlaced, trust_interlaced);
 
     if (priv->pic.data[0] && !priv->need_second_field)
         avctx->release_buffer(avctx, &priv->pic);
@@ -650,8 +670,15 @@ static inline CopyRet copy_frame(AVCodecContext *avctx,
         *(AVFrame *)data = priv->pic;
     }
 
-    if (ASSUME_TWO_INPUTS_ONE_OUTPUT &&
-        output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) {
+    /*
+     * Two types of PAFF content have been observed. One form causes the
+     * hardware to return a field pair and the other individual fields,
+     * even though the input is always individual fields. We must skip
+     * copying on the next decode() call to maintain pipeline length in
+     * the first case.
+     */
+    if (!interlaced && (output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) &&
+        (pic_type == PICT_TOP_FIELD || pic_type == PICT_BOTTOM_FIELD)) {
         av_log(priv->avctx, AV_LOG_VERBOSE, "Fieldpair from two packets.\n");
         return RET_SKIP_NEXT_COPY;
     }
@@ -768,14 +795,19 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *a
         int32_t tx_free = (int32_t)DtsTxFreeSize(dev);
 
         if (priv->parser) {
-            uint8_t *pout = NULL;
-            int psize = len;
+            uint8_t *pout;
+            int psize;
+            const uint8_t *in_data = avpkt->data;
+            int in_len = len;
             H264Context *h = priv->parser->priv_data;
 
-            while (psize) {
-                ret = av_parser_parse2(priv->parser, avctx, &pout, &psize,
-                                       avpkt->data, len, avctx->pkt->pts,
-                                       avctx->pkt->dts, len - psize);
+            while (in_len) {
+                int index;
+                index = av_parser_parse2(priv->parser, avctx, &pout, &psize,
+                                         in_data, in_len, avctx->pkt->pts,
+                                         avctx->pkt->dts, 0);
+                in_data += index;
+                in_len -= index;
             }
             av_log(avctx, AV_LOG_VERBOSE,
                    "CrystalHD: parser picture type %d\n",