-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added some tests of playback, using some code from ffmpeg to test seeking to a frame and extracting some individual frames. Signed-off-by: [email protected] <[email protected]> * Comparing different intraframe encoding options. Signed-off-by: [email protected] <[email protected]> * Adding PC tests. Signed-off-by: Sam Richards <[email protected]> * Adding lots of timing information. Signed-off-by: [email protected] <[email protected]> * Adding optimization for build. Signed-off-by: [email protected] <[email protected]> * Adding more timing breakdown. Signed-off-by: [email protected] <[email protected]> --------- Signed-off-by: [email protected] <[email protected]> Signed-off-by: Sam Richards <[email protected]> Co-authored-by: [email protected] <[email protected]>
- Loading branch information
1 parent
4ca7a2d
commit b13487e
Showing
6 changed files
with
754 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.