From b13487ef36b719a7c20130d754d79f9483f08b1f Mon Sep 17 00:00:00 2001 From: Sam Richards Date: Sun, 20 Oct 2024 10:23:22 +0100 Subject: [PATCH] Playbacktest (#100) * Added some tests of playback, using some code from ffmpeg to test seeking to a frame and extracting some individual frames. Signed-off-by: Sam.Richards@taurich.org * Comparing different intraframe encoding options. Signed-off-by: Sam.Richards@taurich.org * Adding PC tests. Signed-off-by: Sam Richards * Adding lots of timing information. Signed-off-by: Sam.Richards@taurich.org * Adding optimization for build. Signed-off-by: Sam.Richards@taurich.org * Adding more timing breakdown. Signed-off-by: Sam.Richards@taurich.org --------- Signed-off-by: Sam.Richards@taurich.org Signed-off-by: Sam Richards Co-authored-by: Sam.Richards@taurich.org --- enctests/playback/CMakeLists.txt | 42 ++++ enctests/playback/README.md | 10 + enctests/playback/codec_test.c | 226 ++++++++++++++++++ enctests/playback/playbacktest.py | 167 +++++++++++++ enctests/playback/testplayback.py | 153 ++++++++++++ .../test_wedge_configs/intraframe_tests.yml | 156 ++++++++++++ 6 files changed, 754 insertions(+) create mode 100644 enctests/playback/CMakeLists.txt create mode 100644 enctests/playback/README.md create mode 100644 enctests/playback/codec_test.c create mode 100644 enctests/playback/playbacktest.py create mode 100644 enctests/playback/testplayback.py create mode 100644 enctests/test_wedge_configs/intraframe_tests.yml diff --git a/enctests/playback/CMakeLists.txt b/enctests/playback/CMakeLists.txt new file mode 100644 index 0000000..8c6faa0 --- /dev/null +++ b/enctests/playback/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.10) +project(codec_test C) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +set(CMAKE_C_STANDARD 11) + +if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") +endif() + +# Find FFmpeg packages +find_package(PkgConfig REQUIRED) +pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET + libavcodec + libavformat + libavutil + libswscale +) + +# Add the executable +add_executable(codec_test codec_test.c) + +# Link against FFmpeg libraries +target_link_libraries(codec_test PRIVATE PkgConfig::FFMPEG) + +# Include FFmpeg headers +target_include_directories(codec_test PRIVATE ${FFMPEG_INCLUDE_DIRS}) + +# If you're on macOS and using Homebrew, you might need to add this: +if(APPLE) + include_directories(/opt/homebrew/include) + link_directories(/opt/homebrew/lib) +endif() + +# Add compiler flags +target_compile_options(codec_test PRIVATE -Wall -Wextra -pedantic) diff --git a/enctests/playback/README.md b/enctests/playback/README.md new file mode 100644 index 0000000..354a34d --- /dev/null +++ b/enctests/playback/README.md @@ -0,0 +1,10 @@ + + * https://trac.ffmpeg.org/wiki/HWAccelIntro + +TODO: + * Test NVDEC + * Rework to be able to run on multiple platforms and aggregate the results. + * Add HAPQ. + * 4K testing. + + diff --git a/enctests/playback/codec_test.c b/enctests/playback/codec_test.c new file mode 100644 index 0000000..6cf1a48 --- /dev/null +++ b/enctests/playback/codec_test.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ADDITIONAL_FRAMES 30 + +double get_time() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +int decode_frames_with_library(const char* filename, const char* decoder_library, int64_t seek_frame) { + AVFormatContext* format_ctx = NULL; + AVCodecContext* codec_ctx = NULL; + const AVCodec* codec = NULL; + AVFrame* frame = NULL; + AVPacket* packet = NULL; + int video_stream_index = -1; + int ret; + + if (avformat_open_input(&format_ctx, filename, NULL, NULL) < 0) { + fprintf(stderr, "Could not open file %s\n", filename); + return -1; + } + + if (avformat_find_stream_info(format_ctx, NULL) < 0) { + fprintf(stderr, "Could not find stream information\n"); + avformat_close_input(&format_ctx); + return -1; + } + + for (int i = 0; i < format_ctx->nb_streams; i++) { + if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + video_stream_index = i; + break; + } + } + + if (video_stream_index == -1) { + fprintf(stderr, "Could not find video stream\n"); + avformat_close_input(&format_ctx); + return -1; + } + + AVCodecParameters* codecpar = format_ctx->streams[video_stream_index]->codecpar; + + codec = avcodec_find_decoder_by_name(decoder_library); + if (!codec) { + fprintf(stderr, "Decoder library %s not found\n", decoder_library); + avformat_close_input(&format_ctx); + return -1; + } + + if (codec->id != codecpar->codec_id) { + fprintf(stderr, "Specified decoder library %s does not match the video codec\n", decoder_library); + avformat_close_input(&format_ctx); + return -1; + } + + codec_ctx = avcodec_alloc_context3(codec); + if (!codec_ctx) { + fprintf(stderr, "Could not allocate codec context\n"); + avformat_close_input(&format_ctx); + return -1; + } + + + + + if (avcodec_parameters_to_context(codec_ctx, codecpar) < 0) { + fprintf(stderr, "Could not copy codec parameters to context\n"); + avcodec_free_context(&codec_ctx); + avformat_close_input(&format_ctx); + return -1; + } + // Set threading options + codec_ctx->thread_count = 0; // Let FFmpeg decide the number of threads + codec_ctx->thread_type = FF_THREAD_FRAME | FF_THREAD_SLICE; // Use both frame and slice threading + codec_ctx->flags2 |= AV_CODEC_FLAG2_FAST; + codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; + + if (avcodec_open2(codec_ctx, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + avcodec_free_context(&codec_ctx); + avformat_close_input(&format_ctx); + return -1; + } + + frame = av_frame_alloc(); + packet = av_packet_alloc(); + if (!frame || !packet) { + fprintf(stderr, "Could not allocate frame or packet\n"); + av_frame_free(&frame); + av_packet_free(&packet); + avcodec_free_context(&codec_ctx); + avformat_close_input(&format_ctx); + return -1; + } + + // Convert frame number to timestamp + AVStream* video_stream = format_ctx->streams[video_stream_index]; + int64_t seek_timestamp = av_rescale_q(seek_frame, + av_inv_q(video_stream->avg_frame_rate), + video_stream->time_base); + + + + int frames_decoded = 0; + double total_decoding_time = 0.0; + double first_frame_time = 0.0; + clock_t start_time = clock(); + + if (av_seek_frame(format_ctx, video_stream_index, seek_timestamp, AVSEEK_FLAG_BACKWARD) < 0) { + fprintf(stderr, "Error while seeking\n"); + av_frame_free(&frame); + av_packet_free(&packet); + avcodec_free_context(&codec_ctx); + avformat_close_input(&format_ctx); + return -1; + } + + avcodec_flush_buffers(codec_ctx); + + clock_t end_time = clock(); + double frame_time = ((double) (end_time - start_time)) / CLOCKS_PER_SEC; + fprintf(stderr, "Decoder seek: %s\nFirstFrame: %.6f\n", decoder_library, frame_time); + start_time = clock(); + struct timespec start_time2; + clock_gettime(CLOCK_MONOTONIC, &start_time2); + + + while (av_read_frame(format_ctx, packet) >= 0 && frames_decoded <= ADDITIONAL_FRAMES) { + clock_t end_time = clock(); + double frame_time = ((double) (end_time - start_time)) / CLOCKS_PER_SEC; + fprintf(stderr, "avread_frame: %.6f\n", frame_time); + if (packet->stream_index == video_stream_index) { + + clock_t send_start_time = clock(); + ret = avcodec_send_packet(codec_ctx, packet); + if (ret < 0) { + fprintf(stderr, "Error sending packet for decoding\n"); + break; + } + + clock_t end_time = clock(); + double frame_time = ((double) (end_time - send_start_time)) / CLOCKS_PER_SEC; + fprintf(stderr, "send_packet: %.6f\n", frame_time); + + while (ret >= 0) { + clock_t receive_time_start = clock(); + ret = avcodec_receive_frame(codec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + //fprintf(stderr, "ERROR: %d\n", ret); + break; + } else if (ret < 0) { + fprintf(stderr, "Error during decoding\n"); + goto end; + } + + clock_t end_time = clock(); + double frame_time = ((double) (end_time - start_time)) / CLOCKS_PER_SEC; + double receive_frame_time = ((double) (end_time - receive_time_start)) / CLOCKS_PER_SEC; + struct timespec end_time2; + clock_gettime(CLOCK_MONOTONIC, &end_time2); + double elapsed_seconds = (end_time2.tv_sec - start_time2.tv_sec) + + (end_time2.tv_nsec - start_time2.tv_nsec) / 1e9; + fprintf(stderr, "Decoder: %s Frame: %02d Duration:%.6f ReceiveDuration:%.6f Duration2:%.6f\n", + decoder_library, frames_decoded, frame_time, receive_frame_time, elapsed_seconds); + total_decoding_time += frame_time; + + if (frames_decoded == 0) { + first_frame_time = frame_time; + printf("Decoder: %s\nFirstFrame: %.6f\n", + decoder_library, frame_time); + total_decoding_time = 0; + start_time = clock(); // We reset, since we dont want the following times to include the first time. + } + + frames_decoded++; + + if (frames_decoded > ADDITIONAL_FRAMES) { + break; + } + + start_time = end_time; // For timing the next frame + } + } + av_packet_unref(packet); + } + +end: + if (frames_decoded > 1) { + double avg_subsequent_frame_time = (total_decoding_time - first_frame_time) / (frames_decoded - 1); + printf("Average: %.6f\n", + avg_subsequent_frame_time); + } + + av_frame_free(&frame); + av_packet_free(&packet); + avcodec_free_context(&codec_ctx); + avformat_close_input(&format_ctx); + + return 0; +} + +int main(int argc, char* argv[]) { + if (argc != 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char* input_file = argv[1]; + const char* decoder_library = argv[2]; + int64_t seek_frame = atoll(argv[3]); + + decode_frames_with_library(input_file, decoder_library, seek_frame); + + return 0; +} \ No newline at end of file diff --git a/enctests/playback/playbacktest.py b/enctests/playback/playbacktest.py new file mode 100644 index 0000000..3c93dd2 --- /dev/null +++ b/enctests/playback/playbacktest.py @@ -0,0 +1,167 @@ +import os +import subprocess +import sys +import json +import yaml + +testdirs = ["../wedge_results/ffmpeg_version_7.0/darwin-arm64/codec_tests-encode"] +testdirs = ["../wedge_results/ffmpeg_version_7.0.1/darwin-arm64/intraframe_tests-encode"] + +testdirs = ["../wedge_results/ffmpeg_version_7.0.1/darwin-arm64/intraframe_tests-encode", + "../wedge_results/ffmpeg_version_7.0/darwin-arm64/codec_tests-encode"] +testdirs = ["../wedge_results/ffmpeg_version_7.0/linux-x86_64/intraframe_tests-encode", + "../wedge_results/ffmpeg_version_7.0/linux-x86_64/codec_tests-encode"] + + +def get_video_codec(filename): + """ + Extracts the video codec from a QuickTime file using ffprobe and Python. + + Args: + filename (str): Path to the QuickTime file. + + Returns: + str: The video codec of the file, or None if an error occurs. + """ + command = ['ffprobe', '-v', 'quiet', '-show_format', '-show_streams', '-print_format', 'json', filename] + try: + # Run ffprobe with capture output + output = subprocess.run(command, capture_output=True, text=True, check=True).stdout + # Parse the JSON output + data = json.loads(output) + # Get the video stream + streams = data.get('streams', []) + for stream in streams: + if stream.get('codec_type') == 'video': + return stream.get('codec_name') + return None + except subprocess.CalledProcessError as e: + print(f"Error running ffprobe for {filename} error: {e}") + return None + +def estimate_gop_size(filename): + """ + Estimates the GOP size of a QuickTime file by analyzing frame types using ffprobe. + + Args: + filename (str): Path to the QuickTime file. + + Returns: + int: Estimated GOP size (distance between I-frames), or None if an error occurs. + """ + command = ['ffprobe', '-v', 'quiet', '-show_entries', 'frame=pict_type', filename] + try: + output = subprocess.run(command, capture_output=True, text=True, check=True).stdout + # Look for lines with pict_type=I + i_frame_count = 0 + previous_i_frame = False + for line in output.splitlines(): + if "pict_type" not in line: + continue + if "pict_type=I" in line: + if previous_i_frame: + return i_frame_count + else: + previous_i_frame = True + i_frame_count = 1 + else: + i_frame_count += 1 + if previous_i_frame: + return i_frame_count + return None # No I-frames found + except subprocess.CalledProcessError as e: + print(f"Error running ffprobe for {filename}, error {e}") + return None + + +files = [] +for testdir in testdirs: + files.extend([os.path.join(testdir, f) for f in os.listdir(testdir) if f.endswith(".mp4") or f.endswith(".mov")]) + + +f = open("playback_results2.html", "w") + +print(""" + + + + Codecs + + + + + + +""", file=f) + +codecmap = { + 'dnxhd': ['dnxhd'], + 'vp9': ['libvpx-vp9'], + 'av1': ['libdav1d', 'libaom-av1'], + 'h264': ['h264'], + 'hevc': ['hevc'], + 'cfhd': ['cfhd'], + 'prores': ['prores'], + 'mjpeg': ['mjpeg'], +} + +fields=['basename', 'Decoder', 'filesize', 'gopsize', 'FirstFrame', 'FirstFrame%30', 'FirstFrame%60', 'Average','Average%30', 'Average%60'] + +files = sorted(files, key=lambda f:os.path.basename(f).split("-")[0]) +print(files) +lastfile = None + +for file in files: + print(file) + info={} + info['basename'] = os.path.basename(file) + basefile = info['basename'].split("-")[0] + if basefile != lastfile: + if lastfile is not None: + print("", file=f) + lastfile = basefile + print(f"

{basefile}

", file=f) + print("", file=f) + for field in fields: + print(f"", file=f) + print("", file=f) + codec = get_video_codec(file) + gopsize = estimate_gop_size(file) + info['codec'] = codec + info['gopsize'] = gopsize + info['filesize'] = os.path.getsize(file) + #print(file, codec, gopsize) + if codec not in codecmap: + codecmap['codec'] = [codec] + print(f"WARNING: codec {codec} not defined, assuming default.") + + for codeclib in codecmap[codec]: + command = ['./codec_test', file, codeclib, '80'] + output = subprocess.run(command, capture_output=True, text=True, check=True).stdout + # Parse the JSON output + print("Got output:", output, " from ", codeclib) + + data = yaml.safe_load(output) + if data is None: + print(f"ERROR, Failed to run {' '.join(command)}") + continue + print(output, data) + info['FirstFrame'] = data['FirstFrame'] + info['Average'] = data['Average'] + info['Decoder'] = codeclib + print("info:", info['Decoder']) + info['FirstFrame%30'] = 100.0 * data['FirstFrame'] / (1/30.0) + + info['FirstFrame%60'] = 100.0 * data['FirstFrame'] / (1/60.0) + info['Average%30'] = 100.0 * data['Average'] / (1/30.0) + + info['Average%60'] = 100.0 * data['Average'] / (1/60.0) + print("", file=f) + for field in fields: + if "%" in field: + print(f"", file=f) + else: + print(f"", file=f) + print("", file=f) +print("
{field}
{info.get(field, 'Undef'):.2f}{info.get(field, 'Undef')}
", file=f) +f.close() diff --git a/enctests/playback/testplayback.py b/enctests/playback/testplayback.py new file mode 100644 index 0000000..941134c --- /dev/null +++ b/enctests/playback/testplayback.py @@ -0,0 +1,153 @@ +import os +import sys +from copy import deepcopy +import time +import yaml +import json +import pyseq +import shlex +import argparse +import subprocess +import platform +from pathlib import Path +import jinja2 + +extensions = ['mxf', 'mp4', 'mov'] + +decompress = {'h264': [{'name': 'h264_software', + 'flags': ' '}, + {'name': 'h264_videotoolbox', + 'platform': ['darwin'], + 'flags': '-hwaccel videotoolbox '}, + ], + 'av1': [{'name': 'av1_software', + 'flags': ' '}, + {'name': 'av1_libdav1d', + 'flags': '-c:v libdav1d '}, + {'name': 'av1_libaom-av1', + 'flags': '-c:v libaom-av1 '}, + ], + 'hevc': [{'name': 'hevc_software', + 'flags': ' '}, + {'name': 'hevc_videotoolbox', + 'platform': ['darwin'], + 'flags': '-hwaccel videotoolbox '}, + ], + 'prores': [{'name': 'prores_software', + 'flags': ' '}, + {'name': 'prores_videotoolbox', + 'platform': ['darwin'], + 'flags': '-hwaccel videotoolbox '}, + ], +} + +def scanFiles(location): + allfiles = [] + for root, dirs, files in os.walk(location): + allfiles.extend([os.path.join(root, file) for file in files if file[-3:] in extensions]) + data = [] + for file in allfiles: + filep = Path(file) + #if "sparks" not in str(filep): + # continue + if filep.stat().st_size == 0: + print(f"Skipping {file}, since its empty.") + continue + cmd = f"ffprobe -v quiet -print_format json -show_format -show_streams {file}" + _raw = subprocess.check_output(shlex.split(cmd)) + jsondata = json.loads(_raw) + print(f"{file} {jsondata['streams'][0]['codec_name']}") + data.append({'file': filep, 'codec': jsondata['streams'][0]['codec_name']}) + #benchmark([data[-1]]) + return data + +def benchmark(allfileinfo): + results = [] + for fileinfo in allfileinfo: + if fileinfo['codec'] not in decompress: + decompress[fileinfo['codec']] = [{'name': f"{fileinfo['codec']}_default", 'flags': ''}] + print(f"Adding codec: {fileinfo['codec']}") + for decode in decompress[fileinfo['codec']]: + cmd = f"ffmpeg {decode['flags']} -i {fileinfo['file']} -benchmark -f null -" + print(cmd) + try: + t1 = time.perf_counter() + _raw = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT, universal_newlines=True) + duration = time.perf_counter() - t1 + except: + print(f"Failed to run: {cmd}") + continue + bench = str(_raw).split("bench:")[1] + utime = bench.split("utime=")[1].split("s")[0] + stime = bench.split("stime=")[1].split("s")[0] + rtime = bench.split("rtime=")[1].split("s")[0] + + maxrss = str(_raw).split("bench: maxrss=")[1] + results.append({"fileinfo": fileinfo, + 'duration': duration, + 'utime': utime, + 'stime': stime, + 'rtime': rtime, + 'maxrss': maxrss, + 'cmd': cmd, + 'decode': decode, + "raw": str(_raw)}) + return results + #print(f"{fileinfo['file']} {fileinfo['codec']} {decode['flags']} utime:{utime} stime:{stime} rtime:{rtime} maxess:{maxrss}") +allfiles = scanFiles('../codec-encode') +results = benchmark(allfiles) + +resultsbyname = {} +for result in results: + path = str(result['fileinfo']['file']).split(".-")[0] + if path not in resultsbyname: + resultsbyname[path] = [result] + else: + resultsbyname[path].append(result) + +htmltemplate = """ + + + + + {{config.title}} + + + + + + +{% for media, results in resultsbyname.items() %} +

{{ media }}

+ + + + + + + + + + +{% for result in results %} + + + + + + + + + +{% endfor %} +
NameDurationUtimeStimeRtimeMax-RSSCmd
{{result.decode.name}}{{"%.3f"|format(result.duration)}}{{result.utime}}{{result.stime}}{{result.rtime}}{{result.maxrss}}{{result.cmd}}
+{% endfor %} + + +""" + +template = jinja2.Template(htmltemplate) +f = open("results.html", "w") +f.write(template.render(resultsbyname=resultsbyname, config={'title': "Codecs"})) +f.close() + diff --git a/enctests/test_wedge_configs/intraframe_tests.yml b/enctests/test_wedge_configs/intraframe_tests.yml new file mode 100644 index 0000000..5e3502e --- /dev/null +++ b/enctests/test_wedge_configs/intraframe_tests.yml @@ -0,0 +1,156 @@ +test_intra_mov: + name: test_intra_mov + description: Different encoders with intraframe encoding only. + app: ffmpeg + suffix: .mov + encoding_template: '{ffmpeg_bin} {input_args} -i "{source}" -vframes {duration} {encoding_args} -y "{outfile}"' + sources: + - sources/enc_sources/chimera_cars_srgb/chimera_cars_srgb.%05d.png.yml + - sources/enc_sources/chimera_coaster_srgb/chimera_coaster_srgb.%06d.png.yml + - sources/enc_sources/chimera_fountains_srgb/chimera_fountains_srgb.%05d.png.yml + wedges: + prores_4444: + -c:v: prores_ks + -profile:v: 4444 + -pix_fmt: yuv444p10le + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + dnxhr_444: + -c:v: dnxhd + -profile:v: dnxhr_444 + -pix_fmt: yuv444p10le + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + h264_444: + -c:v: libx264 + -pix_fmt: yuv444p10le + -crf: 12 + -g: 1 + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + h265_444: + -c:v: libx265 + -pix_fmt: yuv444p10le + -x265-params: keyint=1:min-keyint=1 + -crf: 12 + -preset: slow + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + cineform_444_12bit: + -c:v: cfhd + -pix_fmt: gbrp12le + -quality: 5 + -color_range: pc + -colorspace: rgb + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + cineform_422_10bit: + -c:v: cfhd + -pix_fmt: yuv422p10le + -quality: 5 + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + + +test_intra_mp4: + name: test_intra_mp4 + description: Different encoders with intraframe encoding only. + app: ffmpeg + suffix: .mp4 + encoding_template: '{ffmpeg_bin} {input_args} -i "{source}" -vframes {duration} {encoding_args} -y "{outfile}"' + sources: + - sources/enc_sources/chimera_cars_srgb/chimera_cars_srgb.%05d.png.yml + - sources/enc_sources/chimera_coaster_srgb/chimera_coaster_srgb.%06d.png.yml + - sources/enc_sources/chimera_fountains_srgb/chimera_fountains_srgb.%05d.png.yml + wedges: + vp9_444: + -pix_fmt: yuv444p10le + -c:v: libvpx-vp9 + -quality: good + -b:v: 0 + -crf: 10 + -g: 1 + -row-mt: 1 + -speed: 2 + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 + + aomav1_444: + -pix_fmt: yuv444p10le + -c:v: libaom-av1 + -crf: 12 + -usage: allintra + -row-mt: 1 + -cpu-used: 6 + -sws_flags: spline+accurate_rnd+full_chroma_int + -vf: '"scale=in_range=full:in_color_matrix=bt709:out_range=tv:out_color_matrix=bt709"' + -color_range: tv + -colorspace: bt709 + -color_primaries: bt709 + -color_trc: iec61966-2-1 +--- + +reports: + graphs: + - args: + color: wedge + height: 400 + barmode: group + x: media + y: vmaf_harmonic_mean + range_y: + - 90 + - 100 + name: vmaf_harmonic_mean.png + type: bar + - args: + color: wedge + height: 400 + x: media + barmode: group + y: encode_time + name: encode_time.png + type: bar + - args: + color: wedge + height: 400 + x: media + barmode: group + y: filesize + name: filesize.png + type: bar + name: intra-test + title: Comparing different codecs for intra frame encoding only. + description: Comparing different codecs for intra frame encoding only, so no compression from frame to frame, ideal if you need to quickly seek to a frame for fast playback. + directory: intra-encode + templatefile: basic.html.jinja