all repos — mgba @ 390ed6c83ccf761e86a97c656ac6dfad649e5439

mGBA Game Boy Advance Emulator

FFmpeg: Decoder skeleton
Vicki Pfau vi@endrift.com
Sun, 28 Jun 2020 19:03:21 -0700
commit

390ed6c83ccf761e86a97c656ac6dfad649e5439

parent

3795a64b775107bac77e03bbd2601402de57e043

M CMakeLists.txtCMakeLists.txt

@@ -535,8 +535,8 @@ list(APPEND FEATURES LIBAV)

endif() include_directories(AFTER ${FFMPEG_INCLUDE_DIRS} ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFILTER_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS}) link_directories(${FFMPEG_LIBRARY_DIRS} ${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFILTER_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS}) - list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-encoder.c") - list(APPEND DEPENDENCY_LIB ${FFMPEG_LIBRARIES} ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES}) + list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-encoder.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-decoder.c") + list(APPEND DEPENDENCY_LIB ${FFMPEG_LIBRARIES} ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES}) if(WIN32 AND NOT DEFINED VCPKG_TARGET_TRIPLET) list(APPEND DEPENDENCY_LIB bcrypt) endif()
A src/feature/ffmpeg/ffmpeg-common.h

@@ -0,0 +1,37 @@

+/* Copyright (c) 2013-2020 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef FFMPEG_COMMON +#define FFMPEG_COMMON + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <libavformat/avformat.h> +#include <libavcodec/version.h> + +// Version 57.16 in FFmpeg +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100) +#define FFMPEG_USE_PACKETS +#endif + +// Version 57.15 in libav +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 35, 0) +#define FFMPEG_USE_NEW_BSF +#endif + +// Version 57.14 in libav +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 0) +#define FFMPEG_USE_CODECPAR +#endif + +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 8, 0) +#define FFMPEG_USE_PACKET_UNREF +#endif + +CXX_GUARD_END + +#endif
A src/feature/ffmpeg/ffmpeg-decoder.c

@@ -0,0 +1,170 @@

+/* Copyright (c) 2013-2020 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "ffmpeg-decoder.h" + +void FFmpegDecoderInit(struct FFmpegDecoder* decoder) { +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + + memset(decoder, 0, sizeof(*decoder)); + decoder->audioStream = -1; + decoder->videoStream = -1; +} + +bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) { + if (FFmpegDecoderIsOpen(decoder)) { + return false; + } + + if (avformat_open_input(&decoder->context, infile, NULL, NULL) < 0) { + return false; + } + + if (avformat_find_stream_info(decoder->context, NULL) < 0) { + FFmpegDecoderClose(decoder); + return false; + } + + unsigned i; + for (i = 0; i < decoder->context->nb_streams; ++i) { +#ifdef FFMPEG_USE_CODECPAR + enum AVMediaType type = decoder->context->streams[i]->codecpar->codec_type; +#else + enum AVMediaType type = decoder->context->streams[i]->codec->codec_type; +#endif + struct AVCodec* codec; + struct AVCodecContext* context = NULL; + if (type == AVMEDIA_TYPE_VIDEO && decoder->videoStream < 0) { + decoder->video = avcodec_alloc_context3(NULL); + if (!decoder->video) { + FFmpegDecoderClose(decoder); + return false; + } + context = decoder->video; + } + + if (type == AVMEDIA_TYPE_AUDIO && decoder->audioStream < 0) { + decoder->audio = avcodec_alloc_context3(NULL); + if (!decoder->audio) { + FFmpegDecoderClose(decoder); + return false; + } + context = decoder->audio; + } + if (!context) { + continue; + } + +#ifdef FFMPEG_USE_CODECPAR + if (avcodec_parameters_to_context(context, decoder->context->streams[i]->codecpar) < 0) { + FFmpegDecoderClose(decoder); + return false; + } +#endif + codec = avcodec_find_decoder(context->codec_id); + if (!codec) { + FFmpegDecoderClose(decoder); + return false; + } + if (avcodec_open2(context, codec, NULL) < 0) { + FFmpegDecoderClose(decoder); + return false; + } + + if (type == AVMEDIA_TYPE_VIDEO) { + decoder->videoStream = i; + decoder->width = context->coded_width; + decoder->height = context->coded_height; + if (decoder->d.videoDimensionsChanged) { + decoder->d.videoDimensionsChanged(&decoder->d, decoder->width, decoder->height); + } +#if LIBAVCODEC_VERSION_MAJOR >= 55 + decoder->videoFrame = av_frame_alloc(); +#else + decoder->videoFrame = avcodec_alloc_frame(); +#endif + } + + if (type == AVMEDIA_TYPE_AUDIO) { + decoder->audioStream = i; +#if LIBAVCODEC_VERSION_MAJOR >= 55 + decoder->audioFrame = av_frame_alloc(); +#else + decoder->audioFrame = avcodec_alloc_frame(); +#endif + } + } + return true; +} + +void FFmpegDecoderClose(struct FFmpegDecoder* decoder) { + if (decoder->audioFrame) { +#if LIBAVCODEC_VERSION_MAJOR >= 55 + av_frame_free(&decoder->audioFrame); +#else + avcodec_free_frame(&decoder->audioFrame); +#endif + } + + if (decoder->audio) { + avcodec_close(decoder->audio); + decoder->audio = NULL; + } + + if (decoder->videoFrame) { +#if LIBAVCODEC_VERSION_MAJOR >= 55 + av_frame_free(&decoder->videoFrame); +#else + avcodec_free_frame(&decoder->videoFrame); +#endif + } + + if (decoder->video) { + avcodec_close(decoder->video); + decoder->video = NULL; + } + + if (decoder->context) { + avformat_close_input(&decoder->context); + } +} + +bool FFmpegDecoderIsOpen(struct FFmpegDecoder* decoder) { + return !!decoder->context; +} + +bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) { + while (true) { + AVPacket packet; + if (av_read_frame(decoder->context, &packet) < 0) { + return false; + } + + if (packet.stream_index == decoder->audioStream) { + + } else if (packet.stream_index == decoder->videoStream) { +#ifdef FFMPEG_USE_CODECPAR + if (avcodec_send_packet(decoder->video, &packet) < 0) { + + } + if (avcodec_receive_frame(decoder->video, decoder->videoFrame) < 0) { + + } +#else + int gotData; + if (avcodec_decode_video2(decoder->video, decoder->videoFrame, &gotData, &packet) < 0 || !gotData) { + + } +#endif + } +#ifdef FFMPEG_USE_PACKET_UNREF + av_packet_unref(&packet); +#else + av_free_packet(&packet); +#endif + } +}
A src/feature/ffmpeg/ffmpeg-decoder.h

@@ -0,0 +1,43 @@

+/* Copyright (c) 2013-2020 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef FFMPEG_DECODER +#define FFMPEG_DECODER + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba/core/interface.h> + +#include "feature/ffmpeg/ffmpeg-common.h" + +#define FFMPEG_DECODER_BUFSIZE 4096 + +struct FFmpegDecoder { + struct mAVStream d; + struct AVFormatContext* context; + + int audioStream; + AVFrame* audioFrame; + struct AVCodecContext* audio; + + int videoStream; + AVFrame* videoFrame; + struct AVCodecContext* video; + + int width; + int height; +}; + +void FFmpegDecoderInit(struct FFmpegDecoder*); +bool FFmpegDecoderOpen(struct FFmpegDecoder*, const char* infile); +void FFmpegDecoderClose(struct FFmpegDecoder*); +bool FFmpegDecoderIsOpen(struct FFmpegDecoder*); +bool FFmpegDecoderRead(struct FFmpegDecoder*); + +CXX_GUARD_END + +#endif
M src/feature/ffmpeg/ffmpeg-encoder.csrc/feature/ffmpeg/ffmpeg-encoder.c

@@ -7,6 +7,7 @@ #include "ffmpeg-encoder.h"

#include <mgba/core/core.h> #include <mgba/gba/interface.h> +#include <mgba/internal/gba/gba.h> #include <mgba-util/math.h> #include <libavcodec/version.h>
M src/feature/ffmpeg/ffmpeg-encoder.hsrc/feature/ffmpeg/ffmpeg-encoder.h

@@ -10,29 +10,9 @@ #include <mgba-util/common.h>

CXX_GUARD_START -#include <mgba/internal/gba/gba.h> - -#include <libavformat/avformat.h> -#include <libavcodec/version.h> - -// Version 57.16 in FFmpeg -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100) -#define FFMPEG_USE_PACKETS -#endif - -// Version 57.15 in libav -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 35, 0) -#define FFMPEG_USE_NEW_BSF -#endif - -// Version 57.14 in libav -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 0) -#define FFMPEG_USE_CODECPAR -#endif +#include <mgba/core/interface.h> -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 8, 0) -#define FFMPEG_USE_PACKET_UNREF -#endif +#include "feature/ffmpeg/ffmpeg-common.h" #define FFMPEG_FILTERS_MAX 4

@@ -73,7 +53,7 @@

struct AVCodecContext* video; enum AVPixelFormat pixFormat; enum AVPixelFormat ipixFormat; - struct AVFrame* videoFrame; + AVFrame* videoFrame; int width; int height; int iwidth;
M src/platform/test/cinema-main.csrc/platform/test/cinema-main.c

@@ -16,6 +16,10 @@ #include <mgba-util/table.h>

#include <mgba-util/vector.h> #include <mgba-util/vfs.h> +#ifdef USE_FFMPEG +#include "feature/ffmpeg/ffmpeg-decoder.h" +#endif + #ifdef _MSC_VER #include <mgba-util/platform/windows/getopt.h> #else

@@ -566,10 +570,12 @@

unsigned limit = 9999; unsigned skip = 0; unsigned fail = 0; + unsigned video = 0; CInemaConfigGetUInt(configTree, test->name, "frames", &limit); CInemaConfigGetUInt(configTree, test->name, "skip", &skip); CInemaConfigGetUInt(configTree, test->name, "fail", &fail); + CInemaConfigGetUInt(configTree, test->name, "video", &video); CInemaConfigLoad(configTree, test->name, core); core->loadROM(core, rom);

@@ -600,7 +606,13 @@ .width = image.width,

.height = image.height, .stride = image.width, }; - if (_loadBaseline(dir, &expected, frame, &test->status)) { + bool baselineFound = false; + if (video) { + baselineFound = false; + } else { + baselineFound = _loadBaseline(dir, &expected, frame, &test->status); + } + if (baselineFound) { uint8_t* testPixels = image.data; uint8_t* expectPixels = expected.data; size_t x;