mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-02-09 14:14:39 +02:00
This refactor splits up the main mediacodec decode loop into two send/receive helpers, which are then used to rewrite the receive_frame callback and take full advantage of the new decoding api. Since we can now request packets on demand with ff_decode_get_packet(), the fifo buffer is no longer necessary and has been removed. This change was motivated by behavior observed on certain Android TV devices, featuring hardware mpeg2/h264 decoders which also deinterlace content (to produce multiple frames per field). Previously, this code caused buffering issues because queueInputBuffer() was always invoked before each dequeueOutputBuffer(), even though twice as many output buffers were being generated. With this patch, the decoder will always attempt to drain new frames first before sending more data into the underlying codec. Signed-off-by: Matthieu Bouron <matthieu.bouron@gmail.com>
619 lines
18 KiB
C
619 lines
18 KiB
C
/*
|
|
* Android MediaCodec MPEG-2 / H.264 / H.265 / MPEG-4 / VP8 / VP9 decoders
|
|
*
|
|
* Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "libavutil/avassert.h"
|
|
#include "libavutil/common.h"
|
|
#include "libavutil/opt.h"
|
|
#include "libavutil/intreadwrite.h"
|
|
#include "libavutil/pixfmt.h"
|
|
|
|
#include "avcodec.h"
|
|
#include "decode.h"
|
|
#include "h264_parse.h"
|
|
#include "hevc_parse.h"
|
|
#include "hwaccel.h"
|
|
#include "internal.h"
|
|
#include "mediacodec_wrapper.h"
|
|
#include "mediacodecdec_common.h"
|
|
|
|
typedef struct MediaCodecH264DecContext {
|
|
|
|
MediaCodecDecContext *ctx;
|
|
|
|
AVPacket buffered_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_packet_unref(&s->buffered_pkt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_H264_MEDIACODEC_DECODER || CONFIG_HEVC_MEDIACODEC_DECODER
|
|
static int h2645_ps_to_nalu(const uint8_t *src, int src_size, uint8_t **out, int *out_size)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
uint8_t *p = NULL;
|
|
static const uint8_t nalu_header[] = { 0x00, 0x00, 0x00, 0x01 };
|
|
|
|
if (!out || !out_size) {
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
p = av_malloc(sizeof(nalu_header) + src_size);
|
|
if (!p) {
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
*out = p;
|
|
*out_size = sizeof(nalu_header) + src_size;
|
|
|
|
memcpy(p, nalu_header, sizeof(nalu_header));
|
|
memcpy(p + sizeof(nalu_header), src, src_size);
|
|
|
|
/* Escape 0x00, 0x00, 0x0{0-3} pattern */
|
|
for (i = 4; i < *out_size; i++) {
|
|
if (i < *out_size - 3 &&
|
|
p[i + 0] == 0 &&
|
|
p[i + 1] == 0 &&
|
|
p[i + 2] <= 3) {
|
|
uint8_t *new;
|
|
|
|
*out_size += 1;
|
|
new = av_realloc(*out, *out_size);
|
|
if (!new) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto done;
|
|
}
|
|
*out = p = new;
|
|
|
|
i = i + 2;
|
|
memmove(p + i + 1, p + i, *out_size - (i + 1));
|
|
p[i] = 0x03;
|
|
}
|
|
}
|
|
done:
|
|
if (ret < 0) {
|
|
av_freep(out);
|
|
*out_size = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_H264_MEDIACODEC_DECODER
|
|
static int h264_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
H264ParamSets ps;
|
|
const PPS *pps = NULL;
|
|
const SPS *sps = NULL;
|
|
int is_avc = 0;
|
|
int nal_length_size = 0;
|
|
|
|
memset(&ps, 0, sizeof(ps));
|
|
|
|
ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size,
|
|
&ps, &is_avc, &nal_length_size, 0, avctx);
|
|
if (ret < 0) {
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < MAX_PPS_COUNT; i++) {
|
|
if (ps.pps_list[i]) {
|
|
pps = (const PPS*)ps.pps_list[i]->data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pps) {
|
|
if (ps.sps_list[pps->sps_id]) {
|
|
sps = (const SPS*)ps.sps_list[pps->sps_id]->data;
|
|
}
|
|
}
|
|
|
|
if (pps && sps) {
|
|
uint8_t *data = NULL;
|
|
int data_size = 0;
|
|
|
|
if ((ret = h2645_ps_to_nalu(sps->data, sps->data_size, &data, &data_size)) < 0) {
|
|
goto done;
|
|
}
|
|
ff_AMediaFormat_setBuffer(format, "csd-0", (void*)data, data_size);
|
|
av_freep(&data);
|
|
|
|
if ((ret = h2645_ps_to_nalu(pps->data, pps->data_size, &data, &data_size)) < 0) {
|
|
goto done;
|
|
}
|
|
ff_AMediaFormat_setBuffer(format, "csd-1", (void*)data, data_size);
|
|
av_freep(&data);
|
|
} else {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not extract PPS/SPS from extradata");
|
|
ret = AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
done:
|
|
ff_h264_ps_uninit(&ps);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_HEVC_MEDIACODEC_DECODER
|
|
static int hevc_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
HEVCParamSets ps;
|
|
HEVCSEI sei;
|
|
|
|
const HEVCVPS *vps = NULL;
|
|
const HEVCPPS *pps = NULL;
|
|
const HEVCSPS *sps = NULL;
|
|
int is_nalff = 0;
|
|
int nal_length_size = 0;
|
|
|
|
uint8_t *vps_data = NULL;
|
|
uint8_t *sps_data = NULL;
|
|
uint8_t *pps_data = NULL;
|
|
int vps_data_size = 0;
|
|
int sps_data_size = 0;
|
|
int pps_data_size = 0;
|
|
|
|
memset(&ps, 0, sizeof(ps));
|
|
memset(&sei, 0, sizeof(sei));
|
|
|
|
ret = ff_hevc_decode_extradata(avctx->extradata, avctx->extradata_size,
|
|
&ps, &sei, &is_nalff, &nal_length_size, 0, 1, avctx);
|
|
if (ret < 0) {
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < HEVC_MAX_VPS_COUNT; i++) {
|
|
if (ps.vps_list[i]) {
|
|
vps = (const HEVCVPS*)ps.vps_list[i]->data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < HEVC_MAX_PPS_COUNT; i++) {
|
|
if (ps.pps_list[i]) {
|
|
pps = (const HEVCPPS*)ps.pps_list[i]->data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pps) {
|
|
if (ps.sps_list[pps->sps_id]) {
|
|
sps = (const HEVCSPS*)ps.sps_list[pps->sps_id]->data;
|
|
}
|
|
}
|
|
|
|
if (vps && pps && sps) {
|
|
uint8_t *data;
|
|
int data_size;
|
|
|
|
if ((ret = h2645_ps_to_nalu(vps->data, vps->data_size, &vps_data, &vps_data_size)) < 0 ||
|
|
(ret = h2645_ps_to_nalu(sps->data, sps->data_size, &sps_data, &sps_data_size)) < 0 ||
|
|
(ret = h2645_ps_to_nalu(pps->data, pps->data_size, &pps_data, &pps_data_size)) < 0) {
|
|
goto done;
|
|
}
|
|
|
|
data_size = vps_data_size + sps_data_size + pps_data_size;
|
|
data = av_mallocz(data_size);
|
|
if (!data) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto done;
|
|
}
|
|
|
|
memcpy(data , vps_data, vps_data_size);
|
|
memcpy(data + vps_data_size , sps_data, sps_data_size);
|
|
memcpy(data + vps_data_size + sps_data_size, pps_data, pps_data_size);
|
|
|
|
ff_AMediaFormat_setBuffer(format, "csd-0", data, data_size);
|
|
|
|
av_freep(&data);
|
|
} else {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not extract VPS/PPS/SPS from extradata");
|
|
ret = AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
done:
|
|
ff_hevc_ps_uninit(&ps);
|
|
|
|
av_freep(&vps_data);
|
|
av_freep(&sps_data);
|
|
av_freep(&pps_data);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_MPEG2_MEDIACODEC_DECODER
|
|
static int mpeg2_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (avctx->extradata) {
|
|
ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_MPEG4_MEDIACODEC_DECODER
|
|
static int mpeg4_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (avctx->extradata) {
|
|
ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_VP8_MEDIACODEC_DECODER || CONFIG_VP9_MEDIACODEC_DECODER
|
|
static int vpx_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (avctx->extradata) {
|
|
ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static av_cold int mediacodec_decode_init(AVCodecContext *avctx)
|
|
{
|
|
int ret;
|
|
|
|
const char *codec_mime = NULL;
|
|
|
|
FFAMediaFormat *format = NULL;
|
|
MediaCodecH264DecContext *s = avctx->priv_data;
|
|
|
|
format = ff_AMediaFormat_new();
|
|
if (!format) {
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to create media format\n");
|
|
ret = AVERROR_EXTERNAL;
|
|
goto done;
|
|
}
|
|
|
|
switch (avctx->codec_id) {
|
|
#if CONFIG_H264_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_H264:
|
|
codec_mime = "video/avc";
|
|
|
|
ret = h264_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
#if CONFIG_HEVC_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_HEVC:
|
|
codec_mime = "video/hevc";
|
|
|
|
ret = hevc_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
#if CONFIG_MPEG2_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_MPEG2VIDEO:
|
|
codec_mime = "video/mpeg2";
|
|
|
|
ret = mpeg2_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
#if CONFIG_MPEG4_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_MPEG4:
|
|
codec_mime = "video/mp4v-es",
|
|
|
|
ret = mpeg4_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
#if CONFIG_VP8_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_VP8:
|
|
codec_mime = "video/x-vnd.on2.vp8";
|
|
|
|
ret = vpx_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
#if CONFIG_VP9_MEDIACODEC_DECODER
|
|
case AV_CODEC_ID_VP9:
|
|
codec_mime = "video/x-vnd.on2.vp9";
|
|
|
|
ret = vpx_set_extradata(avctx, format);
|
|
if (ret < 0)
|
|
goto done;
|
|
break;
|
|
#endif
|
|
default:
|
|
av_assert0(0);
|
|
}
|
|
|
|
ff_AMediaFormat_setString(format, "mime", codec_mime);
|
|
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;
|
|
goto done;
|
|
}
|
|
|
|
av_log(avctx, AV_LOG_INFO, "MediaCodec started successfully, ret = %d\n", ret);
|
|
|
|
done:
|
|
if (format) {
|
|
ff_AMediaFormat_delete(format);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
mediacodec_decode_close(avctx);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mediacodec_send_receive(AVCodecContext *avctx,
|
|
MediaCodecH264DecContext *s,
|
|
AVFrame *frame, bool wait)
|
|
{
|
|
int ret;
|
|
|
|
/* send any pending data from buffered packet */
|
|
while (s->buffered_pkt.size) {
|
|
ret = ff_mediacodec_dec_send(avctx, s->ctx, &s->buffered_pkt);
|
|
if (ret == AVERROR(EAGAIN))
|
|
break;
|
|
else if (ret < 0)
|
|
return ret;
|
|
s->buffered_pkt.size -= ret;
|
|
s->buffered_pkt.data += ret;
|
|
if (s->buffered_pkt.size <= 0)
|
|
av_packet_unref(&s->buffered_pkt);
|
|
}
|
|
|
|
/* check for new frame */
|
|
return ff_mediacodec_dec_receive(avctx, s->ctx, frame, wait);
|
|
}
|
|
|
|
static int mediacodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
|
{
|
|
MediaCodecH264DecContext *s = avctx->priv_data;
|
|
int ret;
|
|
|
|
/*
|
|
* 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 AVERROR(EAGAIN);
|
|
}
|
|
}
|
|
|
|
/* flush buffered packet and check for new frame */
|
|
ret = mediacodec_send_receive(avctx, s, frame, false);
|
|
if (ret != AVERROR(EAGAIN))
|
|
return ret;
|
|
|
|
/* skip fetching new packet if we still have one buffered */
|
|
if (s->buffered_pkt.size > 0)
|
|
return AVERROR(EAGAIN);
|
|
|
|
/* fetch new packet or eof */
|
|
ret = ff_decode_get_packet(avctx, &s->buffered_pkt);
|
|
if (ret == AVERROR_EOF) {
|
|
AVPacket null_pkt = { 0 };
|
|
ret = ff_mediacodec_dec_send(avctx, s->ctx, &null_pkt);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
else if (ret < 0)
|
|
return ret;
|
|
|
|
/* crank decoder with new packet */
|
|
return mediacodec_send_receive(avctx, s, frame, true);
|
|
}
|
|
|
|
static void mediacodec_decode_flush(AVCodecContext *avctx)
|
|
{
|
|
MediaCodecH264DecContext *s = avctx->priv_data;
|
|
|
|
av_packet_unref(&s->buffered_pkt);
|
|
|
|
ff_mediacodec_dec_flush(avctx, s->ctx);
|
|
}
|
|
|
|
static const AVCodecHWConfigInternal *mediacodec_hw_configs[] = {
|
|
&(const AVCodecHWConfigInternal) {
|
|
.public = {
|
|
.pix_fmt = AV_PIX_FMT_MEDIACODEC,
|
|
.methods = AV_CODEC_HW_CONFIG_METHOD_AD_HOC |
|
|
AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
|
|
.device_type = AV_HWDEVICE_TYPE_MEDIACODEC,
|
|
},
|
|
.hwaccel = NULL,
|
|
},
|
|
NULL
|
|
};
|
|
|
|
#if CONFIG_H264_MEDIACODEC_DECODER
|
|
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,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.bsfs = "h264_mp4toannexb",
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_HEVC_MEDIACODEC_DECODER
|
|
AVCodec ff_hevc_mediacodec_decoder = {
|
|
.name = "hevc_mediacodec",
|
|
.long_name = NULL_IF_CONFIG_SMALL("H.265 Android MediaCodec decoder"),
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.id = AV_CODEC_ID_HEVC,
|
|
.priv_data_size = sizeof(MediaCodecH264DecContext),
|
|
.init = mediacodec_decode_init,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.bsfs = "hevc_mp4toannexb",
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_MPEG2_MEDIACODEC_DECODER
|
|
AVCodec ff_mpeg2_mediacodec_decoder = {
|
|
.name = "mpeg2_mediacodec",
|
|
.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 Android MediaCodec decoder"),
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.id = AV_CODEC_ID_MPEG2VIDEO,
|
|
.priv_data_size = sizeof(MediaCodecH264DecContext),
|
|
.init = mediacodec_decode_init,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_MPEG4_MEDIACODEC_DECODER
|
|
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(MediaCodecH264DecContext),
|
|
.init = mediacodec_decode_init,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_VP8_MEDIACODEC_DECODER
|
|
AVCodec ff_vp8_mediacodec_decoder = {
|
|
.name = "vp8_mediacodec",
|
|
.long_name = NULL_IF_CONFIG_SMALL("VP8 Android MediaCodec decoder"),
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.id = AV_CODEC_ID_VP8,
|
|
.priv_data_size = sizeof(MediaCodecH264DecContext),
|
|
.init = mediacodec_decode_init,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_VP9_MEDIACODEC_DECODER
|
|
AVCodec ff_vp9_mediacodec_decoder = {
|
|
.name = "vp9_mediacodec",
|
|
.long_name = NULL_IF_CONFIG_SMALL("VP9 Android MediaCodec decoder"),
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.id = AV_CODEC_ID_VP9,
|
|
.priv_data_size = sizeof(MediaCodecH264DecContext),
|
|
.init = mediacodec_decode_init,
|
|
.receive_frame = mediacodec_receive_frame,
|
|
.flush = mediacodec_decode_flush,
|
|
.close = mediacodec_decode_close,
|
|
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
|
|
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS,
|
|
.hw_configs = mediacodec_hw_configs,
|
|
.wrapper_name = "mediacodec",
|
|
};
|
|
#endif
|