/* This file is in the public domain. */ /* * This file exists because the codec include files are severely * abusive of their caller's namespace; as one simple example, they * #define fprintf please_use_av_log_instead_of_fprintf * apparently for internal error-checking - but this gets imposed on * clients of the interface too(!), because avcodec_open2, an * interface intended for users to use, takes an AVDictionary pointer, * and AVDictionary is defined in the file that does that #define. * * So we sequester all this grossness off in its own file. * Unfortunately it is difficult to print anything here without * undeffing avcodec.h's overrides - including even so much as * implementing our av_log_set_callback argument! I really have to * wonder how this interface was designed to be used. * * For now, we assume we have at most one video stream being decoded. */ #include #include #include #include "avcodec.h" #include "avutil-mem.h" #include "avutil-pixdesc.h" #include "avutil-imgutils.h" #include "codecintf.h" extern AVCodec ff_h264_decoder; static AVCodec *c; static AVCodecContext *cc; static AVFrame *frame; static void (*framefn)(int, int, const unsigned char *); static AVPacket pkt; static unsigned char *pktbuf; static int pktbuf_alloc; static unsigned char *extrabuf; static int loglev; static char *logbuf; static int logalloc; static int loglen; static unsigned char (*rgbdata)[3]; static int rgbwidth; static int rgbheight; #undef malloc #undef free #undef realloc #undef exit #undef printf #undef fprintf static void gen_rgb(void) { int y; int x; int ss; unsigned char *sp; unsigned char (*dp)[3]; if (rgbdata) free(rgbdata); rgbwidth = cc->width; rgbheight = cc->height; rgbdata = malloc(rgbwidth*rgbheight*3); switch (cc->pix_fmt) { case PIX_FMT_YUV420P: ss = frame->linesize[0]; sp = frame->data[0]; dp = rgbdata; for (y=cc->height;y>0;y--) { for (x=cc->width;x>0;x--) { (*dp)[0] = *sp; dp ++; sp ++; } sp += ss - cc->width; } ss = frame->linesize[1]; sp = frame->data[1]; dp = rgbdata; for (y=cc->height>>1;y>0;y--) { for (x=cc->width>>1;x>0;x--) { (*dp)[1] = *sp; dp ++; (*dp)[1] = *sp; dp ++; sp ++; } sp -= cc->width >> 1; for (x=cc->width>>1;x>0;x--) { (*dp)[1] = *sp; dp ++; (*dp)[1] = *sp; dp ++; sp ++; } sp += ss - (cc->width >> 1); } ss = frame->linesize[2]; sp = frame->data[2]; dp = rgbdata; for (y=cc->height>>1;y>0;y--) { for (x=cc->width>>1;x>0;x--) { (*dp)[2] = *sp; dp ++; (*dp)[2] = *sp; dp ++; sp ++; } sp -= cc->width >> 1; for (x=cc->width>>1;x>0;x--) { (*dp)[2] = *sp; dp ++; (*dp)[2] = *sp; dp ++; sp ++; } sp += ss - (cc->width >> 1); } dp = rgbdata; for (y=cc->height;y>0;y--) { for (x=cc->width;x>0;x--) { int Y; int U; int V; int R; int G; int B; Y = (*dp)[0]; U = (*dp)[1] - 128; V = (*dp)[2] - 128; Y = (Y * 255) / 219; U = (U * 255) / 224; V = (V * 255) / 224; R = (65536 * Y) + (91880 * V); G = (65536 * Y) - (22580 * U) - (46799 * V); B = (65536 * Y) + (116128 * U); if (R > 0xffffff) R = 0xff; else R >>= 16; if (G > 0xffffff) G = 0xff; else if (G < 0) G = 0; else G >>= 16; if (B > 0xffffff) B = 0xff; else B >>= 16; (*dp)[0] = R; (*dp)[1] = G; (*dp)[2] = B; dp ++; } } break; default: printf("*** don't know how to convert colour space %d (%s) to RGB\n", (int)cc->pix_fmt, av_pix_fmt_descriptors[(int)cc->pix_fmt].name); exit(1); break; } } static void logcb(void *arg __attribute__((__unused__)), int lev, const char *fmt, va_list ap) { char *s; char *s0; int sl; char *nl; int n; int anylines; if ((loglen > 0) && (lev != loglev)) { printf("*** log level changed %d -> %d\n",loglev,lev); } loglev = lev; anylines = 0; sl = vasprintf(&s,fmt,ap); s0 = s; while (sl > 0) { nl = memchr(s,'\n',sl); if (! nl) { if (loglen+sl > logalloc) logbuf = realloc(logbuf,logalloc=loglen+sl); bcopy(s,logbuf+loglen,sl); loglen += sl; break; } n = nl - s; printf("*** [%d] ",loglev); if (loglen > 0) fwrite(logbuf,1,loglen,stdout); fwrite(s,1,n+1,stdout); anylines = 1; sl -= n + 1; s += n + 1; loglen = 0; } if (anylines) fflush(stdout); free(s0); } static void decode_loop(void) { int l; int gotframe; bzero(pktbuf+pkt.size,FF_INPUT_BUFFER_PADDING_SIZE); while (pkt.size > 0) { l = avcodec_decode_video2(cc,frame,&gotframe,&pkt); if (l < 0) { printf("*** error decoding video\n"); exit(1); } if (gotframe) { gen_rgb(); (*framefn)(rgbwidth,rgbheight,&rgbdata[0][0]); } else if (l == 0) { printf("*** decoder returned 0 but no frame\n"); exit(1); } pkt.size -= l; pkt.data += l; } } void codec_init(void (*cb)(int, int, const unsigned char *)) { framefn = cb; av_log_set_callback(&logcb); av_init_packet(&pkt); c = &ff_h264_decoder; cc = avcodec_alloc_context3(c); frame = avcodec_alloc_frame(); pktbuf = 0; pktbuf_alloc = 0; logbuf = 0; logalloc = 0; loglen = 0; rgbdata = 0; } void codec_prefix(const void *data, int len) { extrabuf = malloc(len+FF_INPUT_BUFFER_PADDING_SIZE); bcopy(data,extrabuf,len); bzero(&extrabuf+len,FF_INPUT_BUFFER_PADDING_SIZE); cc->extradata = extrabuf; cc->extradata_size = len; } static void pktbuf_ensure_size(int nb) { if (nb > pktbuf_alloc) { free(pktbuf); pktbuf = malloc(pktbuf_alloc=nb); } } void codec_data(const void *data, int len) { // cc->debug = ~0U; if (!avcodec_is_open(cc) && (avcodec_open2(cc,c,0) < 0)) { printf("*** codec open failed\n"); exit(1); } pktbuf_ensure_size(len+FF_INPUT_BUFFER_PADDING_SIZE); bcopy(data,pktbuf,len); pkt.size = len; pkt.data = pktbuf; decode_loop(); } void codec_done(void) { pkt.size = 0; pkt.data = 0; decode_loop(); avcodec_close(cc); av_free(cc); av_free(frame); }