Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions enctests/playback/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 10 additions & 0 deletions enctests/playback/README.md
Original file line number Diff line number Diff line change
@@ -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.


226 changes: 226 additions & 0 deletions enctests/playback/codec_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/timestamp.h>
#include <libswscale/swscale.h>

#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 <input_file> <decoder_library> <seek_frame>\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;
}
Loading