diff --git a/src/platform/test/cinema-main.c b/src/platform/test/cinema-main.c index 501014ff6..340723986 100644 --- a/src/platform/test/cinema-main.c +++ b/src/platform/test/cinema-main.c @@ -18,6 +18,7 @@ #ifdef USE_FFMPEG #include "feature/ffmpeg/ffmpeg-decoder.h" +#include "feature/ffmpeg/ffmpeg-encoder.h" #endif #ifdef _MSC_VER @@ -395,7 +396,7 @@ bool CInemaTestInit(struct CInemaTest* test, const char* directory, const char* 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); @@ -449,6 +450,32 @@ static bool _loadBaseline(struct VDir* dir, struct CInemaImage* image, size_t fr 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) { + CIerr(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}; strlcpy(path, outdir, sizeof(path)); @@ -590,6 +617,45 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { for (frame = 0; frame < skip; ++frame) { core->runFrame(core); } + +#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; + stream.image = ℑ + + 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; @@ -606,11 +672,24 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { .height = image.height, .stride = image.width, }; - bool baselineFound = false; + bool baselineFound; if (video) { baselineFound = false; +#ifdef USE_FFMPEG + if (!rebaseline && FFmpegDecoderIsOpen(&decoder)) { + stream.image = &expected; + while (!expected.data) { + if (!FFmpegDecoderRead(&decoder)) { + CIerr(1, "Failed to read more frames. EOF?\n"); + test->status = CI_FAIL; + break; + } + } + baselineFound = expected.data; + } +#endif } else { - baselineFound = _loadBaseline(dir, &expected, frame, &test->status); + baselineFound = _loadBaselinePNG(dir, &expected, frame, &test->status); } if (baselineFound) { uint8_t* testPixels = image.data; @@ -707,8 +786,10 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { 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; } } @@ -720,6 +801,16 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { } } +#ifdef USE_FFMPEG + if (video) { + if (rebaseline) { + FFmpegEncoderClose(&encoder); + } else { + FFmpegDecoderClose(&decoder); + } + } +#endif + free(image.data); mCoreConfigDeinit(&core->config); core->deinit(core); @@ -791,6 +882,11 @@ int main(int argc, char** argv) { 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;