summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2017-10-17 23:39:11 +0100
committerCarl Hetherington <cth@carlh.net>2017-10-17 23:39:11 +0100
commitb93328f113f06a4fa4eca344410f81420876b673 (patch)
tree38e0ad590b684a8bbc8cd19d0c62a9d543c8908b
Initial commit.
-rw-r--r--Makefile2
-rw-r--r--ffcmp.c170
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, &copy_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;
+}