diff options
| author | Carl Hetherington <cth@carlh.net> | 2017-10-17 23:39:11 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2017-10-17 23:39:11 +0100 |
| commit | b93328f113f06a4fa4eca344410f81420876b673 (patch) | |
| tree | 38e0ad590b684a8bbc8cd19d0c62a9d543c8908b | |
Initial commit.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | ffcmp.c | 170 |
2 files changed, 172 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a95ccfd4 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +ffcmp: ffcmp.c + gcc -o $@ $< `pkg-config --cflags --libs libavformat libavcodec libavutil` -Wno-deprecated-declarations -Wall -g -std=c99 diff --git a/ffcmp.c b/ffcmp.c new file mode 100644 index 00000000..28713d62 --- /dev/null +++ b/ffcmp.c @@ -0,0 +1,170 @@ +#include <libavformat/avformat.h> +#include <libavutil/frame.h> +#include <stdbool.h> + +#define MAX_COMPLETE_FRAMES 64 + +typedef struct +{ + AVFrame* frame; + int stream_index; +} Frame; + +typedef struct +{ + AVFormatContext* format_context; + AVCodec* codec; + AVPacket packet; + AVFrame* current_frame; + Frame complete_frames[MAX_COMPLETE_FRAMES]; + int n_complete_frames; +} File; + +static File +open_file(char* filename) +{ + File file; + + file.format_context = avformat_alloc_context(); + if (!file.format_context) { + fprintf(stderr, "Could not create format context.\n"); + exit(EXIT_FAILURE); + } + int e = avformat_open_input(&file.format_context, filename, 0, 0); + if (e < 0) { + fprintf(stderr, "Failed to open %s\n", filename); + exit(EXIT_FAILURE); + } + for (int i = 0; i < file.format_context->nb_streams; ++i) { + file.codec = avcodec_find_decoder(file.format_context->streams[i]->codec->codec_id); + if (!file.codec) { + fprintf(stderr, "Could not find codec.\n"); + exit(EXIT_FAILURE); + } + if (avcodec_open2(file.format_context->streams[i]->codec, file.codec, 0) < 0) { + fprintf(stderr, "Could not open codec.\n"); + exit(EXIT_FAILURE); + } + } + + file.current_frame = av_frame_alloc(); + if (!file.current_frame) { + fprintf(stderr, "Could not allocate frame.\n"); + exit(EXIT_FAILURE); + } + + file.n_complete_frames = 0; + + return file; +} + +bool +read_frame(File* file) +{ + int r = av_read_frame(file->format_context, &file->packet); + if (r == AVERROR_EOF) { + return true; + } + + if (r < 0) { + fprintf(stderr, "Failed to read frame.\n"); + exit(EXIT_FAILURE); + } + + switch (file->format_context->streams[file->packet.stream_index]->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + fprintf(stderr, "Warning: ignoring video frame.\n"); + break; + case AVMEDIA_TYPE_AUDIO: + { + AVPacket copy_packet = file->packet; + while (copy_packet.size > 0) { + int frame_finished; + int decode_result = avcodec_decode_audio4(file->format_context->streams[file->packet.stream_index]->codec, file->current_frame, &frame_finished, ©_packet); + if (decode_result < 0) { + fprintf(stderr, "Failed to decode audio.\n"); + exit(EXIT_FAILURE); + } + + if (frame_finished) { + file->complete_frames[file->n_complete_frames].frame = file->current_frame; + file->complete_frames[file->n_complete_frames].stream_index = file->packet.stream_index; + ++file->n_complete_frames; + file->current_frame = av_frame_alloc(); + if (!file->current_frame) { + fprintf(stderr, "Could not allocate frame.\n"); + exit(EXIT_FAILURE); + } + } + + copy_packet.data -= decode_result; + copy_packet.size -= decode_result; + } + break; + default: + fprintf(stderr, "Warning: ignoring other frame.\n"); + break; + } + } + + return false; +} + +int main(int argc, char** argv) +{ + av_register_all(); + + File file[2] = { + open_file(argv[1]), + open_file(argv[2]) + }; + + if (file[0].format_context->nb_streams != file[1].format_context->nb_streams) { + fprintf(stderr, "Files have different stream counts.\n"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < file[0].format_context->nb_streams; ++i) { + if (file[0].format_context->streams[i]->codec->codec_type != file[1].format_context->streams[i]->codec->codec_type) { + fprintf(stderr, "Stream %d has different code type.\n", i); + exit(EXIT_FAILURE); + } + } + + while (true) { + bool done[2] = { + read_frame(&file[0]), + read_frame(&file[1]) + }; + + if (done[0] != done[1]) { + fprintf(stderr, "Files are different lengths.\n"); + exit(EXIT_FAILURE); + } + + while (file[0].n_complete_frames > 0 && file[1].n_complete_frames > 0) { + Frame frame = file[0].complete_frames[0]; + AVStream* stream = file[0].format_context->streams[frame.stream_index]; + + int const size = av_samples_get_buffer_size(0, stream->codec->channels, frame.frame->nb_samples, stream->codec->sample_fmt, 1); + int const check = av_sample_fmt_is_planar(stream->codec->sample_fmt) ? stream->codec->channels : 1; + for (int i = 0; i < check; ++i) { + if (memcmp(file[0].complete_frames[0].frame->data[i], file[1].complete_frames[0].frame->data[i], size) != 0) { + fprintf(stderr, "Audio frames differ.\n"); + exit(EXIT_FAILURE); + } + } + + memmove(file[0].complete_frames, file[0].complete_frames + 1, (MAX_COMPLETE_FRAMES - 1) * sizeof(Frame)); + memmove(file[1].complete_frames, file[1].complete_frames + 1, (MAX_COMPLETE_FRAMES - 1) * sizeof(Frame)); + --file[0].n_complete_frames; + --file[1].n_complete_frames; + } + + if (done[0]) { + break; + } + } + + return 0; +} |
