[FFmpeg-devel] [PATCH 2/4] lavc: share more code between h.264 and mpeg4 decoder

Thomas Volkert silvo at gmx.net
Sun Jul 24 16:06:15 EEST 2016


From: Thomas Volkert <thomas at netzeal.de>

---
 libavcodec/mediacodecdec.c       | 140 ++++++++++++++++++++++++++++++-
 libavcodec/mediacodecdec.h       |  12 +++
 libavcodec/mediacodecdec_h264.c  | 175 +++------------------------------------
 libavcodec/mediacodecdec_mpeg4.c | 154 +++-------------------------------
 4 files changed, 170 insertions(+), 311 deletions(-)

diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c
index df60104..d780ca4 100644
--- a/libavcodec/mediacodecdec.c
+++ b/libavcodec/mediacodecdec.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "libavutil/avassert.h"
 #include "libavutil/atomic.h"
 #include "libavutil/common.h"
 #include "libavutil/mem.h"
@@ -152,6 +153,8 @@ static void ff_mediacodec_dec_unref(MediaCodecDecContext *s)
         return;
 
     if (!avpriv_atomic_int_add_and_fetch(&s->refcount, -1)) {
+        av_freep(&s->codec_name);
+
         if (s->codec) {
             ff_AMediaCodec_delete(s->codec);
             s->codec = NULL;
@@ -167,8 +170,17 @@ static void ff_mediacodec_dec_unref(MediaCodecDecContext *s)
             s->surface = NULL;
         }
 
-        av_freep(&s->codec_name);
-        av_freep(&s);
+        if (s->bsf) {
+            av_bsf_free(&s->bsf);
+        	s->bsf = NULL;
+        }
+
+        if (s->fifo) {
+        	av_fifo_free(s->fifo);
+        	s->fifo = NULL;
+        }
+
+        av_packet_unref(&s->output_pkt);
     }
 }
 
@@ -752,6 +764,130 @@ int ff_mediacodec_dec_is_flushing(AVCodecContext *avctx, MediaCodecDecContext *s
     return s->flushing;
 }
 
+void ff_mediacodec_decoder_flush(AVCodecContext *avctx)
+{
+	MediaCodecDecContext *s = avctx->priv_data;
+
+    while (av_fifo_size(s->fifo)) {
+        AVPacket pkt;
+        av_fifo_generic_read(s->fifo, &pkt, sizeof(pkt), NULL);
+        av_packet_unref(&pkt);
+    }
+    av_fifo_reset(s->fifo);
+
+    av_packet_unref(&s->output_pkt);
+
+    ff_mediacodec_dec_flush(avctx, s);
+}
+
+int ff_mediacodec_decoder_decode(AVCodecContext *avctx, void *data,
+                                 int *got_frame, AVPacket *avpkt)
+{
+	MediaCodecDecContext *s = avctx->priv_data;
+    AVFrame *frame    = data;
+    int ret;
+
+    /* buffer the input packet */
+    if (avpkt->size) {
+        AVPacket input_pkt = { 0 };
+
+        if (av_fifo_space(s->fifo) < sizeof(input_pkt)) {
+            ret = av_fifo_realloc2(s->fifo,
+                                   av_fifo_size(s->fifo) + sizeof(input_pkt));
+            if (ret < 0)
+                return ret;
+        }
+
+        ret = av_packet_ref(&input_pkt, avpkt);
+        if (ret < 0)
+            return ret;
+        av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
+    }
+
+    /*
+     * MediaCodec.flush() discards both input and output buffers, thus we
+     * need to delay the call to this function until the user has released or
+     * renderered the frames he retains.
+     *
+     * After we have buffered an input packet, check if the codec is in the
+     * flushing state. If it is, we need to call ff_mediacodec_dec_flush.
+     *
+     * ff_mediacodec_dec_flush returns 0 if the flush cannot be performed on
+     * the codec (because the user retains frames). The codec stays in the
+     * flushing state.
+     *
+     * ff_mediacodec_dec_flush returns 1 if the flush can actually be
+     * performed on the codec. The codec leaves the flushing state and can
+     * process again packets.
+     *
+     * ff_mediacodec_dec_flush returns a negative value if an error has
+     * occurred.
+     *
+     */
+    if (ff_mediacodec_dec_is_flushing(avctx, s)) {
+        if (!ff_mediacodec_dec_flush(avctx, s)) {
+            return avpkt->size;
+        }
+    }
+
+    /* process buffered data */
+    while (!*got_frame) {
+        /* prepare the input data -- execute BSF if needed */
+        if (s->output_pkt.size <= 0) {
+            av_packet_unref(&s->output_pkt);
+
+            /* no more data */
+            if (av_fifo_size(s->fifo) < sizeof(AVPacket)) {
+                return avpkt->size ? avpkt->size :
+                    ff_mediacodec_dec_decode(avctx, s, frame, got_frame, avpkt);
+            }
+
+            if (s->bsf) {
+                AVPacket unfiltered_pkt = { 0 };
+
+                av_fifo_generic_read(s->fifo, &unfiltered_pkt, sizeof(unfiltered_pkt), NULL);
+
+                ret = av_bsf_send_packet(s->bsf, &unfiltered_pkt);
+				if (ret < 0) {
+					return ret;
+				}
+
+				ret = av_bsf_receive_packet(s->bsf, &s->output_pkt);
+				if (ret == AVERROR(EAGAIN)) {
+					goto done;
+				}
+
+				/* h264_mp4toannexb is used here and does not requires flushing */
+				av_assert0(ret != AVERROR_EOF);
+
+				if (ret < 0) {
+					return ret;
+				}
+            } else {
+                av_fifo_generic_read(s->fifo, &s->output_pkt, sizeof(s->output_pkt), NULL);
+            }
+        }
+
+        ret = ff_mediacodec_dec_decode(avctx, s, frame, got_frame, &s->output_pkt);
+        if (ret < 0)
+            return ret;
+
+        s->output_pkt.size -= ret;
+        s->output_pkt.data += ret;
+    }
+done:
+    return avpkt->size;
+}
+
+av_cold int ff_mediacodec_decoder_close(AVCodecContext *avctx)
+{
+	MediaCodecDecContext *s = avctx->priv_data;
+
+    ff_mediacodec_dec_close(avctx, s);
+
+    return 0;
+}
+
 AVHWAccel ff_h264_mediacodec_hwaccel = {
     .name    = "mediacodec",
     .type    = AVMEDIA_TYPE_VIDEO,
diff --git a/libavcodec/mediacodecdec.h b/libavcodec/mediacodecdec.h
index 8613352..f2fd3f8 100644
--- a/libavcodec/mediacodecdec.h
+++ b/libavcodec/mediacodecdec.h
@@ -26,6 +26,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include "libavutil/fifo.h"
 #include "libavutil/frame.h"
 #include "libavutil/pixfmt.h"
 
@@ -64,6 +65,10 @@ typedef struct MediaCodecDecContext {
     int first_buffer;
     double first_buffer_at;
 
+    AVBSFContext *bsf;
+    AVFifoBuffer *fifo;
+    AVPacket output_pkt;
+
 } MediaCodecDecContext;
 
 int ff_mediacodec_dec_init(AVCodecContext *avctx,
@@ -86,6 +91,13 @@ int ff_mediacodec_dec_close(AVCodecContext *avctx,
 int ff_mediacodec_dec_is_flushing(AVCodecContext *avctx,
                                   MediaCodecDecContext *s);
 
+int ff_mediacodec_decoder_decode(AVCodecContext *avctx, void *data,
+                                 int *got_frame, AVPacket *avpkt);
+
+void ff_mediacodec_decoder_flush(AVCodecContext *avctx);
+
+int ff_mediacodec_decoder_close(AVCodecContext *avctx);
+
 typedef struct MediaCodecBuffer {
 
     MediaCodecDecContext *ctx;
diff --git a/libavcodec/mediacodecdec_h264.c b/libavcodec/mediacodecdec_h264.c
index caeb64b..2124186 100644
--- a/libavcodec/mediacodecdec_h264.c
+++ b/libavcodec/mediacodecdec_h264.c
@@ -39,33 +39,6 @@
 
 #define CODEC_MIME "video/avc"
 
-typedef struct MediaCodecH264DecContext {
-
-    MediaCodecDecContext *ctx;
-
-    AVBSFContext *bsf;
-
-    AVFifoBuffer *fifo;
-
-    AVPacket filtered_pkt;
-
-} MediaCodecH264DecContext;
-
-static av_cold int mediacodec_decode_close(AVCodecContext *avctx)
-{
-    MediaCodecH264DecContext *s = avctx->priv_data;
-
-    ff_mediacodec_dec_close(avctx, s->ctx);
-    s->ctx = NULL;
-
-    av_fifo_free(s->fifo);
-
-    av_bsf_free(&s->bsf);
-    av_packet_unref(&s->filtered_pkt);
-
-    return 0;
-}
-
 static int h264_ps_to_nalu(const uint8_t *src, int src_size, uint8_t **out, int *out_size)
 {
     int i;
@@ -118,7 +91,7 @@ done:
     return ret;
 }
 
-static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
+static av_cold int mediacodec_decoder_init_h264(AVCodecContext *avctx)
 {
     int i;
     int ret;
@@ -130,7 +103,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
     int nal_length_size = 0;
 
     FFAMediaFormat *format = NULL;
-    MediaCodecH264DecContext *s = avctx->priv_data;
+    MediaCodecDecContext *s = avctx->priv_data;
 
     memset(&ps, 0, sizeof(ps));
 
@@ -185,15 +158,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
         goto done;
     }
 
-    s->ctx = av_mallocz(sizeof(*s->ctx));
-    if (!s->ctx) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to allocate MediaCodecDecContext\n");
-        ret = AVERROR(ENOMEM);
-        goto done;
-    }
-
-    if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, CODEC_MIME, format)) < 0) {
-        s->ctx = NULL;
+    if ((ret = ff_mediacodec_dec_init(avctx, s, CODEC_MIME, format)) < 0) {
         goto done;
     }
 
@@ -220,7 +185,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
           goto done;
     }
 
-    av_init_packet(&s->filtered_pkt);
+    av_init_packet(&s->output_pkt);
 
 done:
     if (format) {
@@ -228,7 +193,7 @@ done:
     }
 
     if (ret < 0) {
-        mediacodec_decode_close(avctx);
+    	ff_mediacodec_decoder_close(avctx);
     }
 
     ff_h264_ps_uninit(&ps);
@@ -236,136 +201,16 @@ done:
     return ret;
 }
 
-
-static int mediacodec_process_data(AVCodecContext *avctx, AVFrame *frame,
-                                   int *got_frame, AVPacket *pkt)
-{
-    MediaCodecH264DecContext *s = avctx->priv_data;
-
-    return ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, pkt);
-}
-
-static int mediacodec_decode_frame(AVCodecContext *avctx, void *data,
-                                   int *got_frame, AVPacket *avpkt)
-{
-    MediaCodecH264DecContext *s = avctx->priv_data;
-    AVFrame *frame    = data;
-    int ret;
-
-    /* buffer the input packet */
-    if (avpkt->size) {
-        AVPacket input_pkt = { 0 };
-
-        if (av_fifo_space(s->fifo) < sizeof(input_pkt)) {
-            ret = av_fifo_realloc2(s->fifo,
-                                   av_fifo_size(s->fifo) + sizeof(input_pkt));
-            if (ret < 0)
-                return ret;
-        }
-
-        ret = av_packet_ref(&input_pkt, avpkt);
-        if (ret < 0)
-            return ret;
-        av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
-    }
-
-    /*
-     * MediaCodec.flush() discards both input and output buffers, thus we
-     * need to delay the call to this function until the user has released or
-     * renderered the frames he retains.
-     *
-     * After we have buffered an input packet, check if the codec is in the
-     * flushing state. If it is, we need to call ff_mediacodec_dec_flush.
-     *
-     * ff_mediacodec_dec_flush returns 0 if the flush cannot be performed on
-     * the codec (because the user retains frames). The codec stays in the
-     * flushing state.
-     *
-     * ff_mediacodec_dec_flush returns 1 if the flush can actually be
-     * performed on the codec. The codec leaves the flushing state and can
-     * process again packets.
-     *
-     * ff_mediacodec_dec_flush returns a negative value if an error has
-     * occurred.
-     *
-     */
-    if (ff_mediacodec_dec_is_flushing(avctx, s->ctx)) {
-        if (!ff_mediacodec_dec_flush(avctx, s->ctx)) {
-            return avpkt->size;
-        }
-    }
-
-    /* process buffered data */
-    while (!*got_frame) {
-        /* prepare the input data -- convert to Annex B if needed */
-        if (s->filtered_pkt.size <= 0) {
-            AVPacket input_pkt = { 0 };
-
-            av_packet_unref(&s->filtered_pkt);
-
-            /* no more data */
-            if (av_fifo_size(s->fifo) < sizeof(AVPacket)) {
-                return avpkt->size ? avpkt->size :
-                    ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, avpkt);
-            }
-
-            av_fifo_generic_read(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
-
-            ret = av_bsf_send_packet(s->bsf, &input_pkt);
-            if (ret < 0) {
-                return ret;
-            }
-
-            ret = av_bsf_receive_packet(s->bsf, &s->filtered_pkt);
-            if (ret == AVERROR(EAGAIN)) {
-                goto done;
-            }
-
-            /* h264_mp4toannexb is used here and does not requires flushing */
-            av_assert0(ret != AVERROR_EOF);
-
-            if (ret < 0) {
-                return ret;
-            }
-        }
-
-        ret = mediacodec_process_data(avctx, frame, got_frame, &s->filtered_pkt);
-        if (ret < 0)
-            return ret;
-
-        s->filtered_pkt.size -= ret;
-        s->filtered_pkt.data += ret;
-    }
-done:
-    return avpkt->size;
-}
-
-static void mediacodec_decode_flush(AVCodecContext *avctx)
-{
-    MediaCodecH264DecContext *s = avctx->priv_data;
-
-    while (av_fifo_size(s->fifo)) {
-        AVPacket pkt;
-        av_fifo_generic_read(s->fifo, &pkt, sizeof(pkt), NULL);
-        av_packet_unref(&pkt);
-    }
-    av_fifo_reset(s->fifo);
-
-    av_packet_unref(&s->filtered_pkt);
-
-    ff_mediacodec_dec_flush(avctx, s->ctx);
-}
-
 AVCodec ff_h264_mediacodec_decoder = {
     .name           = "h264_mediacodec",
     .long_name      = NULL_IF_CONFIG_SMALL("H.264 Android MediaCodec decoder"),
     .type           = AVMEDIA_TYPE_VIDEO,
     .id             = AV_CODEC_ID_H264,
-    .priv_data_size = sizeof(MediaCodecH264DecContext),
-    .init           = mediacodec_decode_init,
-    .decode         = mediacodec_decode_frame,
-    .flush          = mediacodec_decode_flush,
-    .close          = mediacodec_decode_close,
+    .priv_data_size = sizeof(MediaCodecDecContext),
+    .init           = mediacodec_decoder_init_h264,
+    .decode         = ff_mediacodec_decoder_decode,
+    .flush          = ff_mediacodec_decoder_flush,
+    .close          = ff_mediacodec_decoder_close,
     .capabilities   = CODEC_CAP_DELAY,
     .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS,
 };
diff --git a/libavcodec/mediacodecdec_mpeg4.c b/libavcodec/mediacodecdec_mpeg4.c
index 7c4559b..8dfa67e 100644
--- a/libavcodec/mediacodecdec_mpeg4.c
+++ b/libavcodec/mediacodecdec_mpeg4.c
@@ -31,39 +31,12 @@
 
 #define CODEC_MIME "video/mp4v-es"
 
-typedef struct MediaCodecMPEG4DecContext {
-
-    MediaCodecDecContext *ctx;
-
-    AVBSFContext *bsf;
-
-    AVFifoBuffer *fifo;
-
-    AVPacket filtered_pkt;
-
-} MediaCodecMPEG4DecContext;
-
-static av_cold int mediacodec_decode_close(AVCodecContext *avctx)
-{
-    MediaCodecMPEG4DecContext *s = avctx->priv_data;
-
-    ff_mediacodec_dec_close(avctx, s->ctx);
-    s->ctx = NULL;
-
-    av_fifo_free(s->fifo);
-
-    av_bsf_free(&s->bsf);
-    av_packet_unref(&s->filtered_pkt);
-
-    return 0;
-}
-
-static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
+static av_cold int mediacodec_decoder_init_mpeg4(AVCodecContext *avctx)
 {
     int ret;
 
     FFAMediaFormat *format = NULL;
-    MediaCodecMPEG4DecContext *s = avctx->priv_data;
+    MediaCodecDecContext *s = avctx->priv_data;
 
     format = ff_AMediaFormat_new();
     if (!format) {
@@ -76,15 +49,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
     ff_AMediaFormat_setInt32(format, "width", avctx->width);
     ff_AMediaFormat_setInt32(format, "height", avctx->height);
 
-    s->ctx = av_mallocz(sizeof(*s->ctx));
-    if (!s->ctx) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to allocate MediaCodecDecContext\n");
-        ret = AVERROR(ENOMEM);
-        goto done;
-    }
-
-    if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, CODEC_MIME, format)) < 0) {
-        s->ctx = NULL;
+    if ((ret = ff_mediacodec_dec_init(avctx, s, CODEC_MIME, format)) < 0) {
         goto done;
     }
 
@@ -111,7 +76,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
           goto done;
     }
 
-    av_init_packet(&s->filtered_pkt);
+    av_init_packet(&s->output_pkt);
 
 done:
     if (format) {
@@ -119,121 +84,22 @@ done:
     }
 
     if (ret < 0) {
-        mediacodec_decode_close(avctx);
+    	ff_mediacodec_decoder_close(avctx);
     }
 
     return ret;
 }
 
-static int mediacodec_process_data(AVCodecContext *avctx, AVFrame *frame,
-                                   int *got_frame, AVPacket *pkt)
-{
-    MediaCodecMPEG4DecContext *s = avctx->priv_data;
-
-    return ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, pkt);
-}
-
-static int mediacodec_decode_frame(AVCodecContext *avctx, void *data,
-                                   int *got_frame, AVPacket *avpkt)
-{
-    MediaCodecMPEG4DecContext *s = avctx->priv_data;
-    AVFrame *frame    = data;
-    int ret;
-
-    /* buffer the input packet */
-    if (avpkt->size) {
-        AVPacket input_pkt = { 0 };
-
-        if (av_fifo_space(s->fifo) < sizeof(input_pkt)) {
-            ret = av_fifo_realloc2(s->fifo,
-                                   av_fifo_size(s->fifo) + sizeof(input_pkt));
-            if (ret < 0)
-                return ret;
-        }
-
-        ret = av_packet_ref(&input_pkt, avpkt);
-        if (ret < 0)
-            return ret;
-        av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
-    }
-
-    if (ff_mediacodec_dec_is_flushing(avctx, s->ctx)) {
-        if (!ff_mediacodec_dec_flush(avctx, s->ctx)) {
-            return avpkt->size;
-        }
-    }
-
-    /* process buffered data */
-    while (!*got_frame) {
-        /* prepare the input data -- unpack DivX-style packed b frame if needed */
-        if (s->filtered_pkt.size <= 0) {
-            AVPacket input_pkt = { 0 };
-
-            av_packet_unref(&s->filtered_pkt);
-
-            /* no more data */
-            if (av_fifo_size(s->fifo) < sizeof(AVPacket)) {
-                return avpkt->size ? avpkt->size :
-                    ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, avpkt);
-            }
-
-            av_fifo_generic_read(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
-
-            ret = av_bsf_send_packet(s->bsf, &input_pkt);
-            if (ret < 0) {
-                return ret;
-            }
-
-            ret = av_bsf_receive_packet(s->bsf, &s->filtered_pkt);
-            if (ret == AVERROR(EAGAIN)) {
-                goto done;
-            }
-
-            /* no explicit flushing needed */
-            av_assert0(ret != AVERROR_EOF);
-
-            if (ret < 0) {
-                return ret;
-            }
-        }
-
-        ret = mediacodec_process_data(avctx, frame, got_frame, &s->filtered_pkt);
-        if (ret < 0)
-            return ret;
-
-        s->filtered_pkt.size -= ret;
-        s->filtered_pkt.data += ret;
-    }
-done:
-    return avpkt->size;
-}
-
-static void mediacodec_decode_flush(AVCodecContext *avctx)
-{
-    MediaCodecMPEG4DecContext *s = avctx->priv_data;
-
-    while (av_fifo_size(s->fifo)) {
-        AVPacket pkt;
-        av_fifo_generic_read(s->fifo, &pkt, sizeof(pkt), NULL);
-        av_packet_unref(&pkt);
-    }
-    av_fifo_reset(s->fifo);
-
-    av_packet_unref(&s->filtered_pkt);
-
-    ff_mediacodec_dec_flush(avctx, s->ctx);
-}
-
 AVCodec ff_mpeg4_mediacodec_decoder = {
     .name           = "mpeg4_mediacodec",
     .long_name      = NULL_IF_CONFIG_SMALL("MPEG-4 Android MediaCodec decoder"),
     .type           = AVMEDIA_TYPE_VIDEO,
     .id             = AV_CODEC_ID_MPEG4,
-    .priv_data_size = sizeof(MediaCodecMPEG4DecContext),
-    .init           = mediacodec_decode_init,
-    .decode         = mediacodec_decode_frame,
-    .flush          = mediacodec_decode_flush,
-    .close          = mediacodec_decode_close,
+    .priv_data_size = sizeof(MediaCodecDecContext),
+    .init           = mediacodec_decoder_init_mpeg4,
+    .decode         = ff_mediacodec_decoder_decode,
+    .flush          = ff_mediacodec_decoder_flush,
+    .close          = ff_mediacodec_decoder_close,
     .capabilities   = CODEC_CAP_DELAY,
     .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS,
 };
-- 
2.7.4



More information about the ffmpeg-devel mailing list