--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+extern "C" {
+#include "libavfilter/buffersrc.h"
+#include "libavfilter/buffersink.h"
+#include "libavformat/avformat.h"
+#include "libavformat/avio.h"
+#include "libavcodec/avcodec.h"
+#include "libavfilter/avfilter.h"
+#include "libavutil/avutil.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libswresample/swresample.h"
+#include "libswscale/swscale.h"
+}
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+int done = 0;
+
+void sigint(int n)
+{
+ done = 1;
+}
+
+
+void dst_exit(GtkWidget *widget, gpointer data)
+{
+ exit(0);
+}
+
+class gtk_window {
+public:
+ gtk_window(int width, int height);
+ ~gtk_window();
+
+ GdkVisual *visual;
+ GtkWidget *window;
+ GtkWidget *image;
+ GtkWidget *panel_hbox;
+ GdkImage *img0, *img1;
+ GdkPixbuf *pbuf0, *pbuf1;
+ unsigned char *bfr, *bfrs, *bfr0, *bfr1;
+ unsigned char **rows, **row0, **row1;
+ uint64_t flip_bfrs, flip_rows;
+ int width, height, linesize;
+ int done;
+
+ pthread_t tid;
+ static void *entry(void *t);
+ void start();
+ void *run();
+ uint8_t *next_bfr();
+ void post();
+
+ pthread_mutex_t draw;
+ void draw_lock() { pthread_mutex_lock(&draw); }
+ void draw_unlock() { pthread_mutex_unlock(&draw); }
+};
+
+gtk_window::gtk_window(int width, int height)
+{
+ this->width = width;
+ this->height = height;
+ this->linesize = width*3;
+ visual = gdk_visual_get_system();
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(window),"destroy",
+ GTK_SIGNAL_FUNC(dst_exit),NULL);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE);
+ /* try for shared image bfr, only seems to work with gtk_rgb */
+ img0 = gdk_image_new(GDK_IMAGE_SHARED, visual, width, height);
+ pbuf0 = gdk_pixbuf_new_from_data((const guchar *)img0->mem,
+ GDK_COLORSPACE_RGB,FALSE,8,width,height,linesize,NULL,NULL);
+ bfr0 = gdk_pixbuf_get_pixels(pbuf0);
+ memset(bfr0,0,height*linesize);
+ image = gtk_image_new_from_pixbuf(pbuf0);
+ /* double buffered */
+ img1 = gdk_image_new(GDK_IMAGE_SHARED, visual, width, height);
+ pbuf1 = gdk_pixbuf_new_from_data((const guchar *)img1->mem,
+ GDK_COLORSPACE_RGB,FALSE,8,width,height,linesize,NULL,NULL);
+ bfr1 = gdk_pixbuf_get_pixels(pbuf1);
+ memset(bfr1,0,height*linesize);
+
+ row0 = new unsigned char *[height];
+ row1 = new unsigned char *[height];
+
+ for( int i=0; i<height; ++i ) {
+ row0[i] = bfr0 + i*linesize;
+ row1[i] = bfr1 + i*linesize;
+ }
+ bfrs = bfr0;
+ rows = row0;
+
+ flip_bfrs = ((uint64_t)bfr0 ^ (uint64_t)bfr1);
+ flip_rows = ((uint64_t)row0 ^ (uint64_t)row1);
+ pthread_mutex_init(&draw, 0);
+ post();
+
+ panel_hbox = gtk_hbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(window), panel_hbox);
+ /* pack image into panel */
+ gtk_box_pack_start(GTK_BOX(panel_hbox), image, TRUE, TRUE, 0);
+ gtk_widget_show_all(window);
+}
+
+gtk_window::~gtk_window()
+{
+ delete [] row0;
+ delete [] row1;
+ pthread_mutex_destroy(&draw);
+}
+
+void *gtk_window::entry(void *t)
+{
+ return ((gtk_window*)t)->run();
+}
+
+void gtk_window::start()
+{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ done = 0;
+ pthread_create(&tid, &attr, &entry, this);
+ pthread_attr_destroy(&attr);
+}
+
+void *gtk_window::run()
+{
+ while( !done ) {
+ if( !gtk_events_pending() ) {
+ if( !bfr ) { usleep(10000); continue; }
+ GdkGC *blk = image->style->black_gc;
+ gdk_draw_rgb_image(image->window,blk, 0,0,width,height,
+ GDK_RGB_DITHER_NONE,bfr,linesize);
+ gdk_flush();
+ uint64_t *fbfrs = (uint64_t *)&bfrs; *fbfrs ^= flip_bfrs;
+ uint64_t *frows = (uint64_t *)&rows; *frows ^= flip_rows;
+ bfr = 0;
+ draw_unlock();
+ }
+ else
+ gtk_main_iteration();
+ }
+
+ return (void*)0;
+}
+
+uint8_t *gtk_window::next_bfr()
+{
+ return bfrs;
+}
+
+void gtk_window::post()
+{
+ draw_lock();
+ bfr = bfrs;
+}
+
+
+class ffcmpr {
+public:
+ ffcmpr();
+ ~ffcmpr();
+ AVPacket ipkt;
+ AVFormatContext *fmt_ctx;
+ AVFrame *ipic;
+ AVStream *st;
+ AVCodecContext *ctx;
+ AVPixelFormat pix_fmt;
+ double frame_rate;
+ int width, height;
+ int open_decoder(const char *filename, int vid_no);
+ void close_decoder();
+ AVFrame *read_frame();
+};
+
+ffcmpr::ffcmpr()
+{
+ av_init_packet(&this->ipkt);
+ this->fmt_ctx = 0;
+ this->ipic = 0;
+ this->st = 0;
+ this->ctx = 0 ;
+ this->frame_rate = 0;
+ this->pix_fmt = AV_PIX_FMT_NONE;
+ width = height = 0;
+}
+
+void ffcmpr::close_decoder()
+{
+ av_packet_unref(&ipkt);
+ if( !fmt_ctx ) return;
+ avformat_close_input(&fmt_ctx);
+ av_frame_free(&ipic);
+}
+
+ffcmpr::~ffcmpr()
+{
+ close_decoder();
+}
+
+int ffcmpr::open_decoder(const char *filename, int vid_no)
+{
+ struct stat fst;
+ if( stat(filename, &fst) ) return 1;
+
+ av_log_set_level(AV_LOG_VERBOSE);
+ fmt_ctx = 0;
+ AVDictionary *fopts = 0;
+ av_register_all();
+ av_dict_set(&fopts, "formatprobesize", "5000000", 0);
+ av_dict_set(&fopts, "scan_all_pmts", "1", 0);
+ av_dict_set(&fopts, "threads", "auto", 0);
+ int ret = avformat_open_input(&fmt_ctx, filename, NULL, &fopts);
+ av_dict_free(&fopts);
+ if( ret < 0 ) {
+ fprintf(stderr,"file open failed: %s\n", filename);
+ return ret;
+ }
+ ret = avformat_find_stream_info(fmt_ctx, NULL);
+ if( ret < 0 ) {
+ fprintf(stderr,"file probe failed: %s\n", filename);
+ return ret;
+ }
+
+ this->st = 0;
+ for( int i=0; !this->st && ret>=0 && i<(int)fmt_ctx->nb_streams; ++i ) {
+ AVStream *fst = fmt_ctx->streams[i];
+ AVMediaType type = fst->codec->codec_type;
+ if( type != AVMEDIA_TYPE_VIDEO ) continue;
+ if( --vid_no < 0 ) this->st = fst;
+ }
+
+ AVCodecID codec_id = st->codec->codec_id;
+ AVDictionary *copts = 0;
+ //av_dict_copy(&copts, opts, 0);
+ AVCodec *decoder = avcodec_find_decoder(codec_id);
+ if( avcodec_open2(st->codec, decoder, &copts) < 0 ) {
+ fprintf(stderr,"codec open failed: %s\n", filename);
+ return -1;
+ }
+ av_dict_free(&copts);
+ ipic = av_frame_alloc();
+
+ AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
+ this->frame_rate = !framerate.den ? 0 : (double)framerate.num / framerate.den;
+ this->ctx = st->codec;
+ this->pix_fmt = ctx->pix_fmt;
+ this->width = ctx->width;
+ this->height = ctx->height;
+ return 0;
+}
+
+AVFrame *ffcmpr::read_frame()
+{
+ av_frame_unref(ipic);
+
+ for( int retrys=1000; --retrys>=0; ) {
+ av_packet_unref(&ipkt);
+ int ret = av_read_frame(fmt_ctx, &ipkt);
+ if( ret == AVERROR_EOF ) break;
+ if( ret != 0 ) continue;
+ if( !ipkt.data ) continue;
+ if( ipkt.stream_index != st->index ) continue;
+ while( ipkt.size > 0 ) {
+ int got_frame = 0;
+ ret = avcodec_decode_video2(st->codec, ipic, &got_frame, &ipkt);
+ if( ret <= 0 ) break;
+ if( got_frame )
+ return ipic;
+ ipkt.data += ret;
+ ipkt.size -= ret;
+ }
+ }
+ return 0;
+}
+
+static int diff_frame(AVFrame *afrm, AVFrame *bfrm, uint8_t *fp, int w, int h)
+{
+ int n = 0, m = 0;
+ uint8_t *arow = afrm->data[0];
+ uint8_t *brow = bfrm->data[0];
+ int asz = afrm->linesize[0], bsz = afrm->linesize[0];
+ int rsz = w * 3;
+ for( int y=h; --y>=0; arow+=asz, brow+=bsz ) {
+ uint8_t *ap = arow, *bp = brow;
+ for( int x=rsz; --x>=0; ++ap,++bp ) {
+ int d = *ap - *bp, v = d + 128;
+ if( v > 255 ) v = 255;
+ else if( v < 0 ) v = 0;
+ *fp++ = v;
+ m += d;
+ if( d < 0 ) d = -d;
+ n += d;
+ }
+ }
+ int sz = h*rsz;
+ printf("%d %d %d %f", sz, m, n, (double)n/sz);
+ return n;
+}
+
+int main(int ac, char **av)
+{
+ int ret;
+ setbuf(stdout,NULL);
+
+ gtk_set_locale();
+ gtk_init(&ac, &av);
+
+ ffcmpr a, b;
+ if( a.open_decoder(av[1],0) ) return 1;
+ if( b.open_decoder(av[2],0) ) return 1;
+
+ printf("file a:%s\n", av[1]);
+ printf(" id 0x%06x:", a.ctx->codec_id);
+ const AVCodecDescriptor *adesc = avcodec_descriptor_get(a.ctx->codec_id);
+ printf(" video %s\n", adesc ? adesc->name : " (unkn)");
+ printf(" %dx%d %5.2f", a.width, a.height, a.frame_rate);
+ const char *apix = av_get_pix_fmt_name(a.pix_fmt);
+ printf(" pix %s\n", apix ? apix : "(unkn)");
+
+ printf("file b:%s\n", av[2]);
+ printf(" id 0x%06x:", b.ctx->codec_id);
+ const AVCodecDescriptor *bdesc = avcodec_descriptor_get(b.ctx->codec_id);
+ printf(" video %s\n", bdesc ? bdesc->name : " (unkn)");
+ printf(" %dx%d %5.2f", b.width, b.height, b.frame_rate);
+ const char *bpix = av_get_pix_fmt_name(b.pix_fmt);
+ printf(" pix %s\n", bpix ? bpix : "(unkn)");
+
+// if( a.ctx->codec_id != b.ctx->codec_id ) { printf("codec mismatch\n"); return 1;}
+ if( a.width != b.width ) { printf("width mismatch\n"); return 1;}
+ if( a.height != b.height ) { printf("height mismatch\n"); return 1;}
+ if( a.frame_rate != b.frame_rate ) { printf("framerate mismatch\n"); return 1;}
+// if( a.pix_fmt != b.pix_fmt ) { printf("format mismatch\n"); return 1;}
+
+ signal(SIGINT,sigint);
+
+ struct SwsContext *a_cvt = sws_getCachedContext(0, a.width, a.height, a.pix_fmt,
+ a.width, a.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
+ struct SwsContext *b_cvt = sws_getCachedContext(0, b.width, b.height, b.pix_fmt,
+ b.width, b.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
+ if( !a_cvt || !b_cvt ) {
+ printf("sws_getCachedContext() failed\n");
+ return 1;
+ }
+
+ AVFrame *afrm = av_frame_alloc();
+ av_image_alloc(afrm->data, afrm->linesize,
+ a.width, a.height, AV_PIX_FMT_RGB24, 1);
+
+ AVFrame *bfrm = av_frame_alloc();
+ av_image_alloc(bfrm->data, bfrm->linesize,
+ b.width, b.height, AV_PIX_FMT_RGB24, 1);
+
+ gtk_window gw(a.width, a.height);
+ gw.start();
+
+ int64_t err = 0;
+ int frm_no = 0;
+
+ while( !done ) {
+ AVFrame *ap = a.read_frame();
+ if( !ap ) break;
+ AVFrame *bp = b.read_frame();
+ if( !bp ) break;
+ sws_scale(a_cvt, ap->data, ap->linesize, 0, ap->height,
+ afrm->data, afrm->linesize);
+ sws_scale(b_cvt, bp->data, bp->linesize, 0, bp->height,
+ bfrm->data, bfrm->linesize);
+ uint8_t *fbfr = gw.next_bfr();
+ ret = diff_frame(afrm, bfrm, fbfr, ap->width, ap->height);
+ err += ret; ++frm_no;
+ printf(" %d\n",frm_no);
+ gw.post();
+ }
+
+ av_frame_free(&afrm);
+ av_frame_free(&bfrm);
+
+ b.close_decoder();
+ a.close_decoder();
+ return 0;
+}
+