введение
В предыдущей статье мы использовали FFmpeg для мягкого декодирования и рендеринга локального видео в формате mp4.
Android FFmpeg Series 03 — декодирование и рендеринг видео
На основе предыдущей демонстрации в этой статье добавлена возможность для FFmpeg использовать MediaCodec для жесткого декодирования, включая декодирование буфера, а затем использование OpenGL для рендеринга на экране и непосредственное декодирование в Surface, а затем на экране.
FFmpeg может использовать MediaCodec для получения AVPacket после декапсуляции, затем использовать jni для обратного вызова буфера на уровень Java, а затем вызывать MediaCodec на уровне Java. Вы также можете использовать AMediaCodec непосредственно на собственном уровне;
MP4, используемый для тестирования, закодирован в H264.
Поэтому при использовании двух вышеуказанных способов вызова MediaCodec необходимо сначала передать
“h264_mp4toannexb” filter
Конвертируйте AVPacket один раз,Соответствующий фон можетссылкаКодовый поток H264 Приложение B и AVCC
Однако в версии FFmepg5.0.1, используемой в этой серии руководств, соответствующие интерфейсы фильтра битового потока были удалены.
Итак, далее мы используем интерфейс C, который напрямую вызывает MediaCodec, предоставленный FFmpeg после версии 3.1, для реализации жесткого декодирования.
(https://trac.ffmpeg.org/wiki/HWAccelIntro)
Вы можете видеть, что в настоящее время он поддерживает только декодирование, но не кодирование.
компилировать
в предыдущемкомпилировать Просто откройте следующие три конфигурации в скрипте(ПодробностиссылкаAndroid FFmpeg Series 01 — компиляция и интеграция)
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
Нет необходимости настраивать аппаратное ускорение h264_mediacodec (его больше нет в списке)
--enable-hwaccel=h264_mediacodec
Декодировать буфер
Процесс декодирования аналогичен процессу мягкого декодирования, поэтому я не буду вдаваться в подробности.
Установите экземпляр JVM в FFmpeg
// libavcodec/jni.h
// int av_jni_set_java_vm(void *vm, void *log_ctx);
// Метод 1, в методе JNI_OnLoad, загруженном вызовом so.
// Второй метод: просто используйте вызов модуля ffmpeg. Этот метод можно выполнять несколько раз, если экземпляр jvm один и тот же.
JavaVM *javaVm = nullptr;
env->GetJavaVM(&javaVm);
if (javaVm != nullptr) {
av_jni_set_java_vm(javaVm, nullptr);
}
Найдите декодер через «h264_mediacodec»
Поскольку декодер h264_mediacodec и идентификатор декодера h264 одинаковы, поэтому
// Мягкое решение
avcodec_find_decoder(id);
// При использовании жесткого декодирования медиакодека
avcodec_find_decoder_by_name(“h264_mediacodec”);
Следующие шаги точно такие же, как и шаги мягкого решения, никаких изменений не требуется.
Локальное тестирование использует мягкое решениеAVFrameФормат:AV_PIX_FMT_YUV420P,
использоватьh264_mediacodecЕдва решеноAVFrameФормат:AV_PIX_FMT_NV12
Декодировать на поверхность
Процесс декодирования и процесс мягкого декодирования также схожи. Здесь мы сосредоточимся на описании различий.
Вы также можете обратиться к ffmpeg5.0.1/doc/examples/hw_decode.c.
Необходимо жестко запрограммировать на Surface,Основное внимание уделяется конфигурацииget_formatиhw_device_ctx
Найти тип
AVHWDeviceType type = av_hwdevice_find_type_by_name("mediacodec");
Найдите hw_pix_fmt. Если вы его найдете, он поддерживает декодирование медиакодеком.
for (int i = 0; ; ++i) {
const AVCodecHWConfig *config = avcodec_get_hw_config(h264Mediacodec, i);
if (!config) {
LOGE("Decoder: %s does not support device type: %s", h264Mediacodec->name,av_hwdevice_get_type_name(type))
break;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
// AV_PIX_FMT_MEDIACODEC(165)
hw_pix_fmt = config->pix_fmt;
LOGE("Decoder: %s support device type: %s, hw_pix_fmt: %d, AV_PIX_FMT_MEDIACODEC: %d", h264Mediacodec->name,
av_hwdevice_get_type_name(type), hw_pix_fmt, AV_PIX_FMT_MEDIACODEC);
break;
}
}
метод get_hw_format
static enum AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts) {
const enum AVPixelFormat *p;
for (p = pix_fmts; *p != -1; p++) {
if (*p == hw_pix_fmt) {
LOGI("get HW surface format: %d", *p);
return *p;
}
}
LOGE("Failed to get HW surface format");
return AV_PIX_FMT_NONE;
}
Настройки контекста декодера get_format
mVideoCodecContext->get_format = get_hw_format;
Настройте hw_device_ctx
av_hwdevice_ctx_create(&mHwDeviceCtx, type, nullptr, nullptr, 0);
mVideoCodecContext->hw_device_ctx = av_buffer_ref(mHwDeviceCtx);
Чтобы использовать Декодировать на поверхности, вам также необходимо настроить AVMediaCodecContext.
if (surface != nullptr) {
mMediaCodecContext = av_mediacodec_alloc_context();
av_mediacodec_default_init(mVideoCodecContext, mMediaCodecContext, surface);
}
Из исходного кода вы можете видеть, что для сгенерированного медиакодека ctx установлено значение hwaccel_context декодера ctx.
Все вышеперечисленные шаги выполняются перед открытием декодера.,Остальные шаги соответствуют мягкому решению.,Просто часть декодированного AVFrame — это hw_pix_fmt, который мы нашли выше.,То естьAV_PIX_FMT_MEDIACODE
Рендеринг на поверхность
(http://mplayerhq.hu/pipermail/ffmpeg-devel/2016-March/191700.html)
Если вам нужно получить буфер с поверхности hw, вы можете преобразовать его через av_hwframe_transfer_data
(Демо по-прежнему является адресом предыдущей статьи, просто переключитесь на ветку разработки~)
ссылка
1. [Предварительное исследование жесткого решения медиакодека ffmpeg]
http://www.4k8k.xyz/article/tifentan/80605472#google_vignette
2. [FFmpeg вызывает MediaCodec для жесткого декодирования на Surface]
https://cloud.tencent.com/developer/article/1901883?from=article.detail.1928893
~~END~~