Merge branch 'master' into medusa
@@ -553,8 +553,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()
@@ -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
@@ -0,0 +1,219 @@
+/* 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" + +#include <libswscale/swscale.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 = -1; + decoder->height = -1; +#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) { +#ifdef FFMPEG_USE_CODECPAR + avcodec_free_context(&decoder->audio); +#else + avcodec_close(decoder->audio); + decoder->audio = NULL; +#endif + } + + if (decoder->scaleContext) { + sws_freeContext(decoder->scaleContext); + decoder->scaleContext = NULL; + } + + if (decoder->videoFrame) { +#if LIBAVCODEC_VERSION_MAJOR >= 55 + av_frame_free(&decoder->videoFrame); +#else + avcodec_free_frame(&decoder->videoFrame); +#endif + } + + if (decoder->pixels) { + free(decoder->pixels); + decoder->pixels = NULL; + } + + if (decoder->video) { +#ifdef FFMPEG_USE_CODECPAR + avcodec_free_context(&decoder->video); +#else + avcodec_close(decoder->video); + decoder->video = NULL; +#endif + } + + if (decoder->context) { + avformat_close_input(&decoder->context); + } +} + +bool FFmpegDecoderIsOpen(struct FFmpegDecoder* decoder) { + return !!decoder->context; +} + +bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) { + bool readPacket = false; + while (!readPacket) { + AVPacket packet = { + .stream_index = -2 + }; + if (av_read_frame(decoder->context, &packet) < 0) { + break; + } + + readPacket = true; + if (packet.stream_index == decoder->audioStream) { + // TODO + } else if (packet.stream_index == decoder->videoStream) { +#ifdef FFMPEG_USE_CODECPAR + if (avcodec_send_packet(decoder->video, &packet) < 0) { + // TODO + } + if (avcodec_receive_frame(decoder->video, decoder->videoFrame) < 0) { + readPacket = false; + } +#else + int gotData; + if (avcodec_decode_video2(decoder->video, decoder->videoFrame, &gotData, &packet) < 0 || !gotData) { + readPacket = false; + } +#endif + if (readPacket) { + if (decoder->width != decoder->videoFrame->width || decoder->height != decoder->videoFrame->height) { + decoder->width = decoder->videoFrame->width; + decoder->height = decoder->videoFrame->height; + if (decoder->out->videoDimensionsChanged) { + decoder->out->videoDimensionsChanged(decoder->out, decoder->width, decoder->height); + } + if (decoder->pixels) { + free(decoder->pixels); + } + decoder->pixels = calloc(decoder->width * decoder->height, BYTES_PER_PIXEL); + if (decoder->scaleContext) { + sws_freeContext(decoder->scaleContext); + decoder->scaleContext = NULL; + } + } + if (decoder->out->postVideoFrame) { + if (!decoder->scaleContext) { + decoder->scaleContext = sws_getContext(decoder->width, decoder->height, decoder->videoFrame->format, + decoder->width, decoder->height, AV_PIX_FMT_BGR32, + SWS_POINT, 0, 0, 0); + } + int stride = decoder->width * BYTES_PER_PIXEL; + sws_scale(decoder->scaleContext, (const uint8_t* const*) decoder->videoFrame->data, decoder->videoFrame->linesize, 0, decoder->videoFrame->height, &decoder->pixels, &stride); + decoder->out->postVideoFrame(decoder->out, (const color_t*) decoder->pixels, decoder->width); + } + } + } +#ifdef FFMPEG_USE_PACKET_UNREF + av_packet_unref(&packet); +#else + av_free_packet(&packet); +#endif + } + return readPacket; +}
@@ -0,0 +1,45 @@
+/* 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* out; + struct AVFormatContext* context; + + int audioStream; + AVFrame* audioFrame; + struct AVCodecContext* audio; + + int videoStream; + AVFrame* videoFrame; + struct AVCodecContext* video; + struct SwsContext* scaleContext; + + int width; + int height; + uint8_t* pixels; +}; + +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
@@ -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>@@ -393,12 +394,24 @@ strcasecmp(encoder->containerFormat, "mov"))) {
// QuickTime and a few other things require YUV420 encoder->video->pix_fmt = AV_PIX_FMT_YUV420P; } -#if LIBAVCODEC_VERSION_MAJOR >= 57 if (encoder->video->codec->id == AV_CODEC_ID_FFV1) { +#if LIBAVCODEC_VERSION_MAJOR >= 57 av_opt_set(encoder->video->priv_data, "coder", "range_tab", 0); - } + av_opt_set_int(encoder->video->priv_data, "context", 1, 0); #endif + encoder->video->gop_size = 128; + encoder->video->level = 3; + } + if (encoder->video->codec->id == AV_CODEC_ID_PNG) { + encoder->video->compression_level = 8; + } +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 48, 100) + if (encoder->video->codec->id == AV_CODEC_ID_ZMBV) { + encoder->video->compression_level = 5; + encoder->video->pix_fmt = AV_PIX_FMT_BGR0; + } +#endif if (strcmp(vcodec->name, "libx264") == 0) { // Try to adaptively figure out when you can use a slower encoder if (encoder->width * encoder->height > 1000000) {@@ -409,13 +422,15 @@ } else {
av_opt_set(encoder->video->priv_data, "preset", "faster", 0); } if (encoder->videoBitrate == 0) { - av_opt_set(encoder->video->priv_data, "crf", "0", 0); + av_opt_set(encoder->video->priv_data, "qp", "0", 0); encoder->video->pix_fmt = AV_PIX_FMT_YUV444P; } } if (strcmp(vcodec->name, "libvpx-vp9") == 0 && encoder->videoBitrate == 0) { - av_opt_set(encoder->video->priv_data, "lossless", "1", 0); - encoder->video->pix_fmt = AV_PIX_FMT_YUV444P; + av_opt_set_int(encoder->video->priv_data, "lossless", 1, 0); + av_opt_set_int(encoder->video->priv_data, "crf", 0, 0); + encoder->video->gop_size = 120; + encoder->video->pix_fmt = AV_PIX_FMT_GBRP; } if (strcmp(vcodec->name, "libwebp_anim") == 0 && encoder->videoBitrate == 0) { av_opt_set(encoder->video->priv_data, "lossless", "1", 0);
@@ -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;
@@ -128,7 +128,7 @@ }
break; case DIRTY_OAM: if (item->address < GB_SIZE_OAM) { - logger->oam[item->address] = item->value; + ((uint8_t*) logger->oam)[item->address] = item->value; proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); } break;
@@ -11,9 +11,15 @@ #include <mgba/feature/commandline.h>
#include <mgba/feature/video-logger.h> #include <mgba-util/png-io.h> +#include <mgba-util/string.h> #include <mgba-util/table.h> #include <mgba-util/vector.h> #include <mgba-util/vfs.h> + +#ifdef USE_FFMPEG +#include "feature/ffmpeg/ffmpeg-decoder.h" +#include "feature/ffmpeg/ffmpeg-encoder.h" +#endif #ifdef _MSC_VER #include <mgba-util/platform/windows/getopt.h>@@ -127,7 +133,7 @@ return false;
} break; case 'b': - strncpy(base, optarg, sizeof(base)); + strlcpy(base, optarg, sizeof(base)); // TODO: Verify path exists break; case 'd':@@ -140,7 +146,7 @@ case 'n':
dryRun = true; break; case 'o': - strncpy(outdir, optarg, sizeof(outdir)); + strlcpy(outdir, optarg, sizeof(outdir)); // TODO: Make directory break; case 'q':@@ -244,7 +250,7 @@ }
} static void testToPath(const char* testName, char* path) { - strncpy(path, base, PATH_MAX); + strlcpy(path, base, PATH_MAX); bool dotSeen = true; size_t i;@@ -253,7 +259,7 @@ if (testName[0] == '.') {
dotSeen = true; } else { if (dotSeen) { - strncpy(&path[i], PATH_SEP, PATH_MAX - i); + strlcpy(&path[i], PATH_SEP, PATH_MAX - i); i += strlen(PATH_SEP); dotSeen = false; if (!i) {@@ -268,7 +274,7 @@ }
static void _loadConfigTree(struct Table* configTree, const char* testName) { char key[MAX_TEST]; - strncpy(key, testName, sizeof(key) - 1); + strlcpy(key, testName, sizeof(key)); struct mCoreConfig* config; while (!(config = HashTableLookup(configTree, key))) {@@ -301,7 +307,7 @@ static const char* _lookupValue(struct Table* configTree, const char* testName, const char* key) {
_loadConfigTree(configTree, testName); char testKey[MAX_TEST]; - strncpy(testKey, testName, sizeof(testKey) - 1); + strlcpy(testKey, testName, sizeof(testKey)); struct mCoreConfig* config; while (true) {@@ -378,10 +384,10 @@ if (strncmp(base, directory, strlen(base)) != 0) {
return false; } memset(test, 0, sizeof(*test)); - strncpy(test->directory, directory, sizeof(test->directory) - 1); - strncpy(test->filename, filename, sizeof(test->filename) - 1); + strlcpy(test->directory, directory, sizeof(test->directory)); + strlcpy(test->filename, filename, sizeof(test->filename)); directory += strlen(base) + 1; - strncpy(test->name, directory, sizeof(test->name) - 1); + strlcpy(test->name, directory, sizeof(test->name)); char* str = strstr(test->name, PATH_SEP); while (str) { str[0] = '.';@@ -390,7 +396,7 @@ }
return true; } -static bool _loadBaseline(struct VDir* dir, struct CInemaImage* image, size_t frame, enum CInemaStatus* status) { +static bool _loadBaselinePNG(struct VDir* dir, struct CInemaImage* image, size_t frame, enum CInemaStatus* status) { char baselineName[32]; snprintf(baselineName, sizeof(baselineName), "baseline_%04" PRIz "u.png", frame); struct VFile* baselineVF = dir->openFile(dir, baselineName, O_RDONLY);@@ -444,9 +450,35 @@ image->stride = pwidth;
return true; } +#ifdef USE_FFMPEG +struct CInemaStream { + struct mAVStream d; + struct CInemaImage* image; + enum CInemaStatus* status; +}; + +static void _cinemaDimensionsChanged(struct mAVStream* stream, unsigned width, unsigned height) { + struct CInemaStream* cistream = (struct CInemaStream*) stream; + if (height != cistream->image->height || width != cistream->image->width) { + CIlog(1, "Size mismatch for video, expected %ux%u, got %ux%u\n", width, height, cistream->image->width, cistream->image->height); + if (*cistream->status == CI_PASS) { + *cistream->status = CI_FAIL; + } + } +} + +static void _cinemaVideoFrame(struct mAVStream* stream, const color_t* pixels, size_t stride) { + struct CInemaStream* cistream = (struct CInemaStream*) stream; + cistream->image->stride = stride; + size_t bufferSize = cistream->image->stride * cistream->image->height * BYTES_PER_PIXEL; + cistream->image->data = malloc(bufferSize); + memcpy(cistream->image->data, pixels, bufferSize); +} +#endif + static struct VDir* _makeOutDir(const char* testName) { char path[PATH_MAX] = {0}; - strncpy(path, outdir, sizeof(path) - 1); + strlcpy(path, outdir, sizeof(path)); char* pathEnd = path + strlen(path); const char* pos; while (true) {@@ -565,13 +597,19 @@
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); + struct VFile* save = VFileMemChunk(NULL, 0); core->loadROM(core, rom); + if (!core->loadSave(core, save)) { + save->close(save); + } core->rtc.override = RTC_FAKE_EPOCH; core->rtc.value = 1200000000; core->reset(core);@@ -583,6 +621,45 @@ size_t frame;
for (frame = 0; frame < skip; ++frame) { core->runFrame(core); } + core->desiredVideoDimensions(core, &image.width, &image.height); + +#ifdef USE_FFMPEG + struct FFmpegDecoder decoder; + struct FFmpegEncoder encoder; + struct CInemaStream stream = {0}; + if (video) { + char fname[PATH_MAX]; + snprintf(fname, sizeof(fname), "%s" PATH_SEP "baseline.mkv", test->directory); + if (rebaseline) { + FFmpegEncoderInit(&encoder); + FFmpegEncoderSetAudio(&encoder, NULL, 0); + FFmpegEncoderSetVideo(&encoder, "png", 0, 0); + FFmpegEncoderSetContainer(&encoder, "mkv"); + FFmpegEncoderSetDimensions(&encoder, image.width, image.height); + if (!FFmpegEncoderOpen(&encoder, fname)) { + CIerr(1, "Failed to save baseline video\n"); + } else { + core->setAVStream(core, &encoder.d); + } + } else { + FFmpegDecoderInit(&decoder); + stream.d.postVideoFrame = _cinemaVideoFrame; + stream.d.videoDimensionsChanged = _cinemaDimensionsChanged; + stream.status = &test->status; + decoder.out = &stream.d; + + if (!FFmpegDecoderOpen(&decoder, fname)) { + CIerr(1, "Failed to load baseline video\n"); + } + } + } +#else + if (video) { + CIerr(0, "Failed to run video test without ffmpeg linked in\n"); + test->status = CI_ERROR; + } +#endif + for (frame = 0; limit; ++frame, --limit) { core->runFrame(core); ++test->totalFrames;@@ -599,7 +676,26 @@ .width = image.width,
.height = image.height, .stride = image.width, }; - if (_loadBaseline(dir, &expected, frame, &test->status)) { + bool baselineFound; + if (video) { + baselineFound = false; +#ifdef USE_FFMPEG + if (!rebaseline && FFmpegDecoderIsOpen(&decoder)) { + stream.image = &expected; + while (!expected.data) { + if (!FFmpegDecoderRead(&decoder)) { + CIlog(1, "Failed to read more frames. EOF?\n"); + test->status = CI_FAIL; + break; + } + } + baselineFound = expected.data; + } +#endif + } else { + baselineFound = _loadBaselinePNG(dir, &expected, frame, &test->status); + } + if (baselineFound) { uint8_t* testPixels = image.data; uint8_t* expectPixels = expected.data; size_t x;@@ -622,7 +718,7 @@ int b = expectB - testB;
if (r | g | b) { failed = true; if (diffs && !diff) { - diff = calloc(expected.width * expected.height, BYTES_PER_PIXEL); + diff = calloc(expected.stride * expected.height, BYTES_PER_PIXEL); } CIlog(3, "Frame %u failed at pixel %" PRIz "ux%" PRIz "u with diff %i,%i,%i (expected %02x%02x%02x, got %02x%02x%02x)\n", frameCounter, x, y, r, g, b,@@ -670,9 +766,9 @@ if (diff) {
if (failed) { struct CInemaImage outdiff = { .data = diff, - .width = image.width, - .height = image.height, - .stride = image.width, + .width = expected.width, + .height = expected.height, + .stride = expected.stride, }; _writeDiff(test->name, &image, frame, "result");@@ -694,8 +790,10 @@ }
free(expected.data); } else if (test->status == CI_ERROR) { break; - } else if (rebaseline) { + } else if (rebaseline && !video) { _writeBaseline(dir, &image, frame); + } else if (!rebaseline) { + test->status = CI_FAIL; } }@@ -706,6 +804,16 @@ } else if (test->status == CI_PASS) {
test->status = CI_XPASS; } } + +#ifdef USE_FFMPEG + if (video) { + if (rebaseline) { + FFmpegEncoderClose(&encoder); + } else { + FFmpegDecoderClose(&decoder); + } + } +#endif free(image.data); mCoreConfigDeinit(&core->config);@@ -768,7 +876,7 @@ }
#ifndef _WIN32 char* rbase = realpath(base, NULL); if (rbase) { - strncpy(base, rbase, PATH_MAX); + strlcpy(base, rbase, sizeof(base)); free(rbase); } #endif@@ -778,6 +886,11 @@ CInemaTestListInit(&tests, 0);
struct mLogger logger = { .log = _log }; mLogSetDefaultLogger(&logger); +#ifdef USE_FFMPEG + if (verbosity < 2) { + av_log_set_level(AV_LOG_ERROR); + } +#endif if (argc > 0) { size_t i;