[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