From: Alan Nisota Date: Tue, 24 Jun 2008 14:19:34 -0700 Subject: Add support for CoreAVC via dshowserver It implements support for communicating with the dshowserver (coreavc-for-linux.googlecode.com) This is mainly useful for using CoreAVC in Linux (a very fast H264 decoder), however it could be made to work with other win32 codecs. The code is based on mplayer's libloader (which xine already includes in modified form) but using a separate server allows for several benefits: a) easy to support in multiple players (mythtv, xine, mplayer) since the dshowserver patch is small, and all the win32 stuff is centralized b) can use 32bit win32 codecs with players compiled in 64-bit mode c) a crash in the codec does not bring down the player diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -2588,6 +2588,14 @@ fi AM_CONDITIONAL(HAVE_W32DLL, test "x$enable_w32dll" != "xno") +AC_ARG_ENABLE([dshowserver], + AS_HELP_STRING([--disable-dshowserver], [Disable dshowserver support])) + +if test "x$arch_x86" == "xno"; then + enable_dshowserver="no" +fi + +AM_CONDITIONAL(HAVE_DSHOWSERVER, test "x$enable_dshowserver" != "xno") dnl --------------------------------------------- dnl some include paths ( !!! DO NOT REMOVE !!! ) diff --git a/src/libxinevdec/Makefile.am b/src/libxinevdec/Makefile.am --- a/src/libxinevdec/Makefile.am +++ b/src/libxinevdec/Makefile.am @@ -18,9 +18,14 @@ if HAVE_THEORA theora_module = xineplug_decode_theora.la endif +if HAVE_DSHOWSERVER +dshowserver_module = xineplug_decode_dshowserver.la +endif + xineplug_LTLIBRARIES = $(image_module) \ $(gdkpixbuf_module) \ $(theora_module) \ + $(dshowserver_module) \ xineplug_decode_bitplane.la \ xineplug_decode_rgb.la \ xineplug_decode_yuv.la @@ -45,3 +50,8 @@ xineplug_decode_gdk_pixbuf_la_LIBADD = $ xineplug_decode_theora_la_SOURCES = xine_theora_decoder.c xineplug_decode_theora_la_CFLAGS = $(AM_CFLAGS) $(OGG_CFLAGS) $(THEORA_CFLAGS) xineplug_decode_theora_la_LIBADD = $(XINE_LIB) $(OGG_LIBS) $(THEORA_LIBS) $(LTLIBINTL) + +xineplug_decode_dshowserver_la_SOURCES = dshowserver.c nal_parser.c +xineplug_decode_dshowserver_la_LIBADD = $(XINE_LIB) $(RT_LIBS) $(PTHREAD_LIBS) + +noinst_HEADERS = nal_parser.h diff --git a/src/libxinevdec/dshowserver.c b/src/libxinevdec/dshowserver.c new file mode 100644 --- /dev/null +++ b/src/libxinevdec/dshowserver.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2008 Alan Nisota + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * Support for dshowserver, which enables using w32 codecs in a seperate process. + * This is useful to run 32bit codecs in a 64bit xine. + * It is mostly tageted at supporting CoreAVC at the moment. + */ +#define _GNU_SOURCE //needed for asprintf +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "xine_internal.h" +#include "video_out.h" +#include "buffer.h" +#include "xineutils.h" + +#include "nal_parser.h" + +struct vd_struct { + union { + uint32_t ret; + uint32_t cmd; + }; + uint32_t buflen; + uint64_t pts; + uint32_t unused[8]; +} __attribute__((__packed__)); + +enum { + VD_END = 1, + VD_DECODE = 2, + VD_SEEK = 3, + VD_RELOAD_BIH = 4, + VD_HAS_BIH = 0x10000, + VD_VERSION_MASK = 0xFFFF, +}; + +typedef struct { + int fd; + void *mem; + char *data; + char *picture; + char *pagestart; + int picsize; + int pagesize; + sem_t *sem_rd; + sem_t *sem_wr; + struct vd_struct *vd; +} ds_mpi_t; + +typedef struct { + video_decoder_class_t decoder_class; +} dshowserver_class_t; + +typedef struct { + video_decoder_t video_decoder; + + xine_stream_t *stream; + dshowserver_class_t *class; + + int64_t video_step; + int decoder_ok; + int decoder_initialized; + + int stream_id; + int skipframes; + + int size; /* the current size of buf */ + int width; /* the width of a video frame */ + int height; /* the height of a video frame */ + uint32_t compression; /* the compression format */ + double ratio; /* the width to height ratio */ + int bpp; + int numpages; + int extra; /* width & 0x0f */ + ds_mpi_t ds_mpi; + + xine_bmiheader *bih; + unsigned char *extradata; + int extradata_size; + struct nal_parser *parser; /* Parser for H264 */ + +} dshowserver_decoder_t; + +static int sem_twait(sem_t *sem, int t) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += t; + return(sem_timedwait(sem, &ts)); +} + +static void make_bih(dshowserver_decoder_t *this) +{ + if(this->bih) { + this->bih = realloc(this->bih, sizeof(xine_bmiheader) + this->extradata_size); + } else { + this->bih = xine_xmalloc(sizeof(xine_bmiheader) + this->extradata_size); + this->bih->biWidth = this->width; + this->bih->biHeight = this->height; + this->bih->biPlanes = 1; + this->bih->biBitCount = 24; + this->bih->biCompression = this->compression; + this->bih->biSizeImage = 0; + this->bih->biXPelsPerMeter=10000; + this->bih->biYPelsPerMeter=10000; + this->bih->biClrUsed=0; + this->bih->biClrImportant=0; + } + if(this->extradata_size) + memcpy((unsigned char *)this->bih + sizeof(xine_bmiheader), this->extradata, this->extradata_size); + this->bih->biSize = sizeof(xine_bmiheader) + this->extradata_size; +} + +static void dshowserver_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { + dshowserver_decoder_t *this = (dshowserver_decoder_t *) this_gen; + int ret = 0; + vo_frame_t *img = 0; /* video out frame */ + int size_from_nal = 0; + + lprintf ("processing packet type = %08x, buf->decoder_flags=%08x\n", + buf->type, buf->decoder_flags); + + if (buf->decoder_flags & BUF_FLAG_PREVIEW) + return; + + if (buf->decoder_flags & BUF_FLAG_FRAMERATE) { + this->video_step = buf->decoder_info[0]; + _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->video_step); + + lprintf ("video_step is %lld\n", this->video_step); + } + + if (buf->decoder_flags & BUF_FLAG_SPECIAL) { + /* store extradata */ + if (buf->decoder_info[1] == BUF_SPECIAL_DECODER_CONFIG && + !this->extradata_size) + { + lprintf("BUF_SPECIAL_DECODER_CONFIG\n"); + this->extradata_size = buf->decoder_info[2]; + this->extradata = xine_xmalloc(buf->decoder_info[2]); + memcpy(this->extradata, buf->decoder_info_ptr[2], + buf->decoder_info[2]); + lprintf("Extradata: %d\n", this->extradata_size); + } + } + + if(! this->decoder_ok && ! (buf->decoder_flags & BUF_FLAG_STDHEADER)) { + int len = 0; + unsigned char *tmpbuf; + int tmplen; + if(! this->parser) + this->parser = init_parser(); + + while(len < buf->size) { + len += parse_frame(this->parser, buf->content + len, buf->size - len, + &tmpbuf, &tmplen); + if(tmpbuf) + free(tmpbuf); + if(this->parser->current_nal->sps != NULL) + { + this->width = this->parser->current_nal->sps->pic_width; + this->height = this->parser->current_nal->sps->pic_height; + this->compression = 0x34363248; //H264 + size_from_nal = 1; + } + } + } + if (! this->decoder_ok && (buf->decoder_flags & BUF_FLAG_STDHEADER || size_from_nal)) { + if(! size_from_nal) { + int size = ((xine_bmiheader *) buf->content)->biSize; + this->bih = xine_xmalloc(size); + memcpy(this->bih, buf->content, size); + this->width = this->bih->biWidth; + this->height = this->bih->biHeight; + this->compression = this->bih->biCompression; + } + this->extra = this->width & 0x0F; + this->bpp = 16; //YUY2 + this->numpages = 10; + + if ( buf->type & 0xff ) + return; + + lprintf ("processing header ...\n"); + + /* init package containing bih */ + + this->ratio = (double)this->width/(double)this->height; + + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height); + + //Do dshowserver init here + this->decoder_ok = 1; + + if (! this->decoder_ok) { + xine_log (this->stream->xine, XINE_LOG_MSG, + _("dshowserver: decoder failed to start\n")); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0); + _x_message(this->stream, XINE_MSG_LIBRARY_LOAD_ERROR, + "dshowserver", NULL); + } + } else if (this->decoder_ok) { + lprintf ("processing packet ...\n"); + if(! this->decoder_initialized) { + //delay initialization until 1st frame in case extradata comes. + int memsize = 0; + char *cmd, *shm, *sem1, *sem2; + + this->decoder_initialized = 1; + asprintf(&cmd, "dshowserver -c CoreAVCDecoder.ax -s %dx%d " + "-g 09571a4b-f1fe-4c60-9760de6d310c7c31 " + "-f 0x%08x -b %d -o 0x%08x -p %d -i %x -n %d %s&", + this->width, this->height, + this->compression, this->bpp, XINE_IMGFMT_YUY2, getpid(), + *(int *)pthread_self(), this->numpages, ""); + asprintf(&shm, "/dshow_shm.%x", *(int *)pthread_self()); + asprintf(&sem1, "/dshow_sem1.%x", *(int *)pthread_self()); + asprintf(&sem2, "/dshow_sem2.%x", *(int *)pthread_self()); + + this->ds_mpi.fd = shm_open(shm, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + this->ds_mpi.picsize = this->width * this->height * this->bpp / 8; + this->ds_mpi.pagesize = this->ds_mpi.picsize + 1024; + memsize = sizeof(struct vd_struct) + this->width * this->height + this->ds_mpi.picsize + this->extra + this->ds_mpi.pagesize * this->numpages; + ftruncate(this->ds_mpi.fd, memsize); + this->ds_mpi.mem = mmap(NULL, memsize, PROT_READ | PROT_WRITE, MAP_SHARED, this->ds_mpi.fd, 0); + if(this->ds_mpi.mem == MAP_FAILED) { + xine_log (this->stream->xine, XINE_LOG_MSG, _("dschowserver: mmap failed\n")); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0); + _x_message(this->stream, XINE_MSG_LIBRARY_LOAD_ERROR, + "dshowserver", NULL); + return; + } + memset((char *)this->ds_mpi.mem, 0, sizeof(struct vd_struct)); + this->ds_mpi.vd = (struct vd_struct *)this->ds_mpi.mem; + this->ds_mpi.data = ((char *)this->ds_mpi.mem) + sizeof(struct vd_struct); + this->ds_mpi.picture = this->ds_mpi.data + this->width * this->height; + this->ds_mpi.pagestart = this->ds_mpi.picture + this->ds_mpi.picsize + this->extra; + if (this->extra) + memset((char *)this->ds_mpi.picture + this->ds_mpi.picsize, 0, this->extra); + + if(this->extradata) + make_bih(this); + if(this->bih && this->bih->biSize > sizeof(xine_bmiheader)) { + this->ds_mpi.vd->cmd |= VD_HAS_BIH; //Use embedded bih + memcpy(this->ds_mpi.data, this->bih, this->bih->biSize); + } + //Create read/write semaphores in locked state + this->ds_mpi.sem_wr = sem_open(sem1, O_CREAT, 0644, 0); + this->ds_mpi.sem_rd = sem_open(sem2, O_CREAT, 0644, 0); + system(cmd); + ret = sem_twait(this->ds_mpi.sem_rd, 10); + shm_unlink(shm); + sem_unlink(sem1); + sem_unlink(sem2); + free(cmd); + free(shm); + free(sem1); + free(sem2); + if(ret != 0) { + xine_log (this->stream->xine, XINE_LOG_MSG, + _("dshowserver: decoder failed to start\n")); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0); + _x_message(this->stream, XINE_MSG_LIBRARY_LOAD_ERROR, + "dshowserver", NULL); + return; + } + lprintf("Found DirectShow filter\n"); + } + + if( (int) buf->size <= 0 ) + return; + + if( this->stream_id < 0 ) + this->stream_id = buf->type & 0xff; + + if( this->stream_id != (buf->type & 0xff) ) + return; + + xine_fast_memcpy (this->ds_mpi.data + this->size, buf->content, buf->size); + this->size += buf->size; + if (! (buf->decoder_flags & BUF_FLAG_FRAME_END)) + return; + this->ds_mpi.vd->cmd = VD_DECODE; //'2' is cmd for decoding + this->ds_mpi.vd->pts = buf->pts; + this->ds_mpi.vd->buflen = this->size; + this->size = 0; + sem_post(this->ds_mpi.sem_wr); + ret = sem_twait(this->ds_mpi.sem_rd, 10); + lprintf("ret: %d/%d PTS: %lld -> %lld\n", ret, this->ds_mpi.vd->ret, buf->pts, this->ds_mpi.vd->pts); + //lprintf("PTS (%d): %f(%d) -> %d\n", this->ds_mpi.vd->ret, sh->buffered_pts[0], pts-1, this->ds_mpi.vd->pts); + if(ret == 0 && this->ds_mpi.vd->ret && ! (this->ds_mpi.vd->ret & (1<<31))) { + int flags = VO_BOTH_FIELDS; + if(this->ds_mpi.vd->ret & 0x10) + flags |= VO_INTERLACED_FLAG; + else + flags &= ~VO_INTERLACED_FLAG; + + img = this->stream->video_out->get_frame (this->stream->video_out, + this->width, this->height, + this->ratio, XINE_IMGFMT_YUY2, + flags); + if(this->ds_mpi.vd->pts) + img->pts = this->ds_mpi.vd->pts; + else + img->pts = buf->pts; + img->bad_frame = 0; + img->duration = this->video_step; + if(this->ds_mpi.vd->ret & 0x02) { + unsigned char page = this->ds_mpi.vd->ret >> 8; + memcpy(img->base[0], this->ds_mpi.pagestart + page * this->ds_mpi.pagesize, this->ds_mpi.picsize); + } else { + memcpy(img->base[0], this->ds_mpi.picture, this->ds_mpi.picsize); + } + img->draw(img, this->stream); + img->free(img); + } + } +} + +/* flush gets called by video-decoder to force outputting frames (?) */ +static void dshowserver_flush (video_decoder_t *this_gen) { +} + +/* reset seems to be called after a seek */ +static void dshowserver_reset (video_decoder_t *this_gen) { + dshowserver_decoder_t *this = (dshowserver_decoder_t *) this_gen; + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "dshowserver: called reset\n"); + if(this->decoder_initialized) { + this->ds_mpi.vd->cmd = VD_SEEK; //'3' is cmd for seek + sem_post(this->ds_mpi.sem_wr); + sem_twait(this->ds_mpi.sem_rd, 10); + } + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "dshowserver: reset done\n"); +} + +static void dshowserver_discontinuity (video_decoder_t *this_gen) { +} + +static void dshowserver_dispose (video_decoder_t *this_gen) { + dshowserver_decoder_t *this = (dshowserver_decoder_t *) this_gen; + if( this->decoder_initialized ) { + this->decoder_ok = 0; + this->stream->video_out->close(this->stream->video_out, this->stream); + this->ds_mpi.vd->cmd = VD_END; //'1' is cmd for terminating + sem_post(this->ds_mpi.sem_wr); + close(this->ds_mpi.fd); + sem_close(this->ds_mpi.sem_wr); + sem_close(this->ds_mpi.sem_rd); + } + if(this->parser) + free_parser(this->parser); + if(this->bih) + free(this->bih); + free (this); +} + +static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) { + + dshowserver_decoder_t *this = (dshowserver_decoder_t *) xine_xmalloc (sizeof (dshowserver_decoder_t)); + + this->video_decoder.decode_data = dshowserver_decode_data; + this->video_decoder.flush = dshowserver_flush; + this->video_decoder.reset = dshowserver_reset; + this->video_decoder.discontinuity = dshowserver_discontinuity; + this->video_decoder.dispose = dshowserver_dispose; + + this->stream = stream; + this->class = (dshowserver_class_t *) class_gen; + + this->decoder_ok = 0; + return &this->video_decoder; +} + +static char *get_identifier (video_decoder_class_t *this) { + return "dshowserver"; +} + +static char *get_description (video_decoder_class_t *this) { + return "Dshowserver video decoder plugin (mostly used for CoreAVC windows codec)"; +} + +static void dispose_class (video_decoder_class_t *this) { + free (this); +} + +static void *init_plugin (xine_t *xine, void *data) { + + dshowserver_class_t *this = (dshowserver_class_t *) xine_xmalloc (sizeof (dshowserver_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.get_identifier = get_identifier; + this->decoder_class.get_description = get_description; + this->decoder_class.dispose = dispose_class; + + return this; +} + +static uint32_t video_types[] = { + BUF_VIDEO_H264, + 0 +}; + +static const decoder_info_t dec_info_video = { + video_types, /* supported types */ + 1 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_DECODER, 18, "dshowserver", XINE_VERSION_CODE, &dec_info_video, init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/libxinevdec/nal_parser.c b/src/libxinevdec/nal_parser.c new file mode 100644 --- /dev/null +++ b/src/libxinevdec/nal_parser.c @@ -0,0 +1,520 @@ +#include +#include + +#include "nal_parser.h" + +struct buf_reader { + uint8_t *buf; + uint8_t *cur_pos; + int len; + int cur_offset; +}; + +static inline uint32_t read_bits(struct buf_reader *buf, int len); +uint32_t read_exp_golomb(struct buf_reader *buf); +int32_t read_exp_golomb_s(struct buf_reader *buf); +void skip_scaling_list(struct buf_reader *buf, int size); +int parse_nal_header(struct buf_reader *buf, struct nal_unit *nal); +uint8_t parse_sps(struct buf_reader *buf, struct seq_parameter_set_rbsp *sps); +uint8_t parse_pps(struct buf_reader *buf, struct pic_parameter_set_rbsp *pps); +uint8_t parse_slice_header(struct buf_reader *buf, struct nal_unit *nal); + + +static void decode_nal(uint8_t **ret, int *len_ret, uint8_t *buf, int buf_len) +{ + uint8_t *end = &buf[buf_len]; + uint8_t *pos = malloc(buf_len); + + *ret = pos; + while(buf < end) { + if(buf < end - 3 && buf[0] == 0x00 && buf[1] == 0x00 && + buf[2] == 0x03) { + + *pos++ = 0x00; + *pos++ = 0x00; + + buf += 3; + continue; + } + *pos++ = *buf++; + } + + *len_ret = pos - *ret; +} + +/*uint32_t read_bits(struct buf_reader *buf, int len) +{ + uint32_t bits = 0x00; + int i, j; + for(i=0, j=0; icur_offset >= 8) { + buf->cur_pos++; + buf->cur_offset -= 8; + } + uint8_t bit = (*buf->cur_pos >> (7 - buf->cur_offset)) & 0x01; + bits |= ((uint32_t)bit) << i; + buf->cur_offset++; + } +printf("ret: 0x%08x\n", bits); + return bits; +}*/ + +static inline uint32_t read_bits (struct buf_reader *buf, int len) +{ + static uint32_t i_mask[33] = + { 0x00, + 0x01, 0x03, 0x07, 0x0f, + 0x1f, 0x3f, 0x7f, 0xff, + 0x1ff, 0x3ff, 0x7ff, 0xfff, + 0x1fff, 0x3fff, 0x7fff, 0xffff, + 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, + 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, + 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, + 0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff}; + + int i_shr; + uint32_t bits = 0; + + while(len > 0 && (buf->cur_pos - buf->buf) < buf->len) { + if((i_shr = buf->cur_offset-len) >= 0) { + bits |= (*buf->cur_pos >> i_shr)&i_mask[len]; + buf->cur_offset -= len; + if(buf->cur_offset == 0) { + buf->cur_pos++; + buf->cur_offset = 8; + } + return bits; + } else { + bits |= (*buf->cur_pos & i_mask[buf->cur_offset]) << -i_shr; + len -= buf->cur_offset; + buf->cur_pos++; + buf->cur_offset = 8; + } + } + return bits; +} + +uint32_t read_exp_golomb(struct buf_reader *buf) +{ + int leading_zero_bits = 0; + + while(read_bits(buf, 1) == 0 && leading_zero_bits < 32) + leading_zero_bits++; + + uint32_t code = (1<len < 1) + return -1; + int ret = -1; + + nal->nal_ref_idc = (buf->buf[0] >> 5) & 0x03; + nal->nal_unit_type = buf->buf[0] & 0x1f; + + buf->cur_pos = buf->buf + 1; + //printf("NAL: %d\n", nal->nal_unit_type); + + struct buf_reader ibuf; + ibuf.cur_offset = 8; + + switch(nal->nal_unit_type) { + case NAL_SPS: + decode_nal(&ibuf.buf, &ibuf.len, buf->cur_pos, buf->len-1); + ibuf.cur_pos = ibuf.buf; + if(!nal->sps) + nal->sps = malloc(sizeof(struct seq_parameter_set_rbsp)); + else + memset(nal->sps, 0x00, sizeof(struct seq_parameter_set_rbsp)); + + parse_sps(&ibuf, nal->sps); + free(ibuf.buf); + ret = NAL_SPS; + break; + case NAL_PPS: + if(!nal->pps) + nal->pps = malloc(sizeof(struct pic_parameter_set_rbsp)); + else + memset(nal->pps, 0x00, sizeof(struct pic_parameter_set_rbsp)); + + parse_pps(buf, nal->pps); + ret = NAL_PPS; + break; + case NAL_SLICE: + case NAL_PART_A: + case NAL_PART_B: + case NAL_PART_C: + case NAL_SLICE_IDR: + if(nal->sps && nal->pps) { + if(!nal->slc) + nal->slc = malloc(sizeof(struct slice_header)); + else + memset(nal->slc, 0x00, sizeof(struct slice_header)); + + parse_slice_header(buf, nal); + ret = nal->nal_unit_type; + } + break; + default: + ret = nal->nal_unit_type; + break; + } + + return ret; +} + +void skip_scaling_list(struct buf_reader *buf, int size) +{ + int i; + for(i = 0; i < size; i++) { + read_exp_golomb_s(buf); + } +} + +uint8_t parse_sps(struct buf_reader *buf, struct seq_parameter_set_rbsp *sps) +{ + sps->profile_idc = buf->buf[0]; + sps->constraint_setN_flag = (buf->buf[1] >> 4) & 0x0f; + sps->level_idc = buf->buf[2]; + + buf->cur_pos = buf->buf+3; + sps->seq_parameter_set_id = read_exp_golomb(buf); + if(sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 144) { + sps->chroma_format_idc = read_exp_golomb(buf); + if(sps->chroma_format_idc == 3) { + sps->residual_colour_transform_flag = read_bits(buf, 1); + } + + sps->bit_depth_luma_minus8 = read_exp_golomb(buf); + sps->bit_depth_chroma_minus8 = read_exp_golomb(buf); + sps->qpprime_y_zero_transform_bypass_flag = read_bits(buf, 1); + sps->seq_scaling_matrix_present_flag = read_bits(buf, 1); + if(sps->seq_scaling_matrix_present_flag) { + sps->seq_scaling_lists_present_flag = read_bits(buf, 8); + int i; + for(i=0; i<8; i++) { + if((sps->seq_scaling_lists_present_flag >> (7-i)) & 0x01) { + // NOTE: just skip the scaling lists, as we do not + // need their data for parsing + if(i<6) + skip_scaling_list(buf, 16); + else + skip_scaling_list(buf, 64); + } + } + } + } + + sps->log2_max_frame_num_minus4 = read_exp_golomb(buf); + + sps->pic_order_cnt_type = read_exp_golomb(buf); + if(!sps->pic_order_cnt_type) + sps->log2_max_pic_order_cnt_lsb_minus4 = read_exp_golomb(buf); + else { + sps->delta_pic_order_always_zero_flag = read_bits(buf, 1); + sps->offset_for_non_ref_pic = read_exp_golomb_s(buf); + sps->offset_for_top_to_bottom_field = read_exp_golomb_s(buf); + sps->num_ref_frames_in_pic_order_cnt_cycle = read_exp_golomb(buf); + int i; + for(i=0; inum_ref_frames_in_pic_order_cnt_cycle; i++) { + sps->offset_for_ref_frame[i] = read_exp_golomb_s(buf); + } + } + sps->num_ref_frames = read_exp_golomb(buf); + sps->gaps_in_frame_num_value_allowed_flag = read_bits(buf, 1); + + /*sps->pic_width_in_mbs_minus1 = read_exp_golomb(buf); + sps->pic_height_in_map_units_minus1 = read_exp_golomb(buf);*/ + sps->pic_width = 16 * (read_exp_golomb(buf) + 1); + sps->pic_height = 16 * (read_exp_golomb(buf) + 1); + + sps->frame_mbs_only_flag = read_bits(buf, 1); + + /* compute the height correctly even for interlaced material */ + sps->pic_height = (2-sps->frame_mbs_only_flag) * sps->pic_height; + //printf("res: %dx%d\n", sps->pic_width, sps->pic_height); + + if(!sps->frame_mbs_only_flag) + sps->mb_adaptive_frame_field_flag = read_bits(buf, 1); + + sps->direct_8x8_inference_flag = read_bits(buf, 1); + sps->frame_cropping_flag = read_bits(buf, 1); + if(sps->frame_cropping_flag) { + sps->frame_crop_left_offset = read_exp_golomb(buf); + sps->frame_crop_right_offset = read_exp_golomb(buf); + sps->frame_crop_top_offset = read_exp_golomb(buf); + sps->frame_crop_bottom_offset = read_exp_golomb(buf); + } + sps->vui_parameters_present_flag = read_bits(buf, 1); + /*if(sps->vui_parameters_present_flag) + printf("ERROR: vui_parameters is not implemented\n");*/ + + return 0; +} + +uint8_t parse_pps(struct buf_reader *buf, struct pic_parameter_set_rbsp *pps) +{ + pps->pic_parameter_set_id = read_exp_golomb(buf); + pps->seq_parameter_set_id = read_exp_golomb(buf); + pps->entropy_coding_mode_flag = read_bits(buf, 1); + pps->pic_order_present_flag = read_bits(buf, 1); + return 0; +} + +uint8_t parse_slice_header(struct buf_reader *buf, struct nal_unit *nal) +{ + struct seq_parameter_set_rbsp *sps = nal->sps; + struct pic_parameter_set_rbsp *pps = nal->pps; + struct slice_header *slc = nal->slc; + if(!sps || !pps) + return -1; + + slc->first_mb_in_slice = read_exp_golomb(buf); + slc->slice_type = read_exp_golomb(buf); + slc->pic_parameter_set_id = read_exp_golomb(buf); + slc->frame_num = read_bits(buf, sps->log2_max_frame_num_minus4 + 4); + if(!sps->frame_mbs_only_flag) { + slc->field_pic_flag = read_bits(buf, 1); + if(slc->field_pic_flag) + slc->bottom_field_flag = read_bits(buf, 1); + else + slc->bottom_field_flag = -1; + } else { + slc->field_pic_flag = 0; + slc->bottom_field_flag = -1; + } + + if(nal->nal_unit_type == NAL_SLICE_IDR) + slc->idr_pic_id = read_exp_golomb(buf); + + if(!sps->pic_order_cnt_type) { + slc->pic_order_cnt_lsb = read_bits(buf, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + if(pps->pic_order_present_flag && !slc->field_pic_flag) + slc->delta_pic_order_cnt_bottom = read_exp_golomb_s(buf); + } else if (sps->pic_order_cnt_type == 1) { + slc->delta_pic_order_cnt[0] = read_exp_golomb_s(buf); + if(pps->pic_order_present_flag && !slc->field_pic_flag) + slc->delta_pic_order_cnt[1] = read_exp_golomb_s(buf); + } + /* do not need more information for packetizing */ + + return 0; +} + + + +/* ----------------- NAL parser ----------------- */ + +struct nal_parser* init_parser() +{ + struct nal_parser *parser = malloc(sizeof(struct nal_parser)); + memset(parser->buf, 0x00, MAX_FRAME_SIZE); + parser->buf_len = 0; + parser->found_sps = 0; + parser->found_pps = 0; + parser->nal0 = malloc(sizeof(struct nal_unit)); + memset(parser->nal0, 0x00, sizeof(struct nal_unit)); + parser->nal1 = malloc(sizeof(struct nal_unit)); + memset(parser->nal1, 0x00, sizeof(struct nal_unit)); + parser->current_nal = parser->nal0; + parser->last_nal = parser->nal1; + + parser->last_nal_res = 0; + parser->slice = 0; + parser->field = -1; + parser->have_top = 0; + + return parser; +} + +void free_parser(struct nal_parser *parser) +{ + free(parser->nal0); + free(parser->nal1); + free(parser); +} + +int parse_frame(struct nal_parser *parser, uint8_t *inbuf, int inbuf_len, + uint8_t **ret_buf, int *ret_len) +{ + int next_nal; + int parsed_len = 0; + int search_offset = 0; + + while((next_nal = seek_for_nal(inbuf+search_offset, inbuf_len-parsed_len)) >= 0) { + // save buffer up to the nal-start + if(parser->buf_len + next_nal + search_offset > MAX_FRAME_SIZE) { + printf("buf underrun!!\n"); + *ret_len = 0; + *ret_buf = NULL; + return parsed_len; + } + //if(parser->last_nal_res != 1) { + xine_fast_memcpy(&parser->buf[parser->buf_len], inbuf, next_nal+search_offset); + parser->buf_len += next_nal+search_offset; + //} + inbuf += next_nal+search_offset; + parsed_len += next_nal+search_offset; + + if((parser->last_nal_res = parse_nal(inbuf+4, inbuf_len-parsed_len, parser)) == 1 + && parser->buf_len>0) { + // parse_nal returned 1 --> detected a frame_boundary + *ret_buf = malloc(parser->buf_len); + xine_fast_memcpy(*ret_buf, parser->buf, parser->buf_len); + *ret_len = parser->buf_len; + + //memset(parser->buf, 0x00, parser->buf_len); + parser->buf_len = 0; + parser->last_nal_res = 0; + return parsed_len; + } + + search_offset = 4; + } + + // no further NAL found, copy the rest of the stream + // into the buffer +// if(parser->last_nal_res != 1) { + xine_fast_memcpy(&parser->buf[parser->buf_len], inbuf, inbuf_len-parsed_len); + parser->buf_len += inbuf_len-parsed_len; +// } + + parsed_len += (inbuf_len-parsed_len); + *ret_len = 0; + *ret_buf = NULL; + + return parsed_len; +} + +int parse_nal(uint8_t *buf, int buf_len, struct nal_parser *parser) +{ + struct buf_reader bufr; + + bufr.buf = buf; + bufr.cur_pos = buf; + bufr.cur_offset = 8; + bufr.len = buf_len; + + struct nal_unit *nal = parser->current_nal; + struct nal_unit *last_nal = parser->last_nal; + + int res = parse_nal_header(&bufr, nal); + + if(res >= NAL_SLICE && res <= NAL_SLICE_IDR) { + // now detect if it's a new frame! + int ret = 0; + if(nal->slc->field_pic_flag == 1) + parser->field = nal->slc->bottom_field_flag; + else { + parser->have_top = 1; + parser->field = -1; + } + + if(nal->slc->field_pic_flag == 1 && nal->slc->bottom_field_flag == 0) + parser->have_top = 1; + + parser->slice = 1; + + if(nal->slc == NULL || last_nal->slc == NULL) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->slc->frame_num != last_nal->slc->frame_num)) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->slc->pic_parameter_set_id != last_nal->slc->pic_parameter_set_id)) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->slc->field_pic_flag != last_nal->slc->field_pic_flag)) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->slc->bottom_field_flag != -1 && + last_nal->slc->bottom_field_flag != -1 && + nal->slc->bottom_field_flag != last_nal->slc->bottom_field_flag)) { + ret = 1; + } + if(nal->nal_ref_idc != last_nal->nal_ref_idc && + (nal->nal_ref_idc == 0 || last_nal->nal_ref_idc == 0)) { + ret = 1; + } + if(nal->sps && nal->slc && last_nal->slc && + (nal->sps->pic_order_cnt_type == 0 && + (nal->slc->pic_order_cnt_lsb != last_nal->slc->pic_order_cnt_lsb || + nal->slc->delta_pic_order_cnt_bottom != last_nal->slc->delta_pic_order_cnt_bottom))) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->sps->pic_order_cnt_type == 1 && + (nal->slc->delta_pic_order_cnt[0] != last_nal->slc->delta_pic_order_cnt[0] || + nal->slc->delta_pic_order_cnt[1] != last_nal->slc->delta_pic_order_cnt[1]))) { + ret = 1; + } + if(nal->nal_unit_type != last_nal->nal_unit_type && + (nal->nal_unit_type == 5 || last_nal->nal_unit_type == 5)) { + ret = 1; + } + if(nal->slc && last_nal->slc && + (nal->nal_unit_type == 5 && last_nal->nal_unit_type == 5 && + nal->slc->idr_pic_id != last_nal->slc->idr_pic_id)) { + ret = 1; + } + + if(parser->current_nal == parser->nal0) { + parser->current_nal = parser->nal1; + parser->last_nal = parser->nal0; + } + else { + parser->current_nal = parser->nal0; + parser->last_nal = parser->nal1; + } + if(parser->current_nal->sps == NULL) + parser->current_nal->sps = parser->last_nal->sps; + if(parser->current_nal->pps == NULL) + parser->current_nal->pps = parser->last_nal->pps; + + /*if(ret) + parser->slice = 0;*/ + /*if(parser->slice && parser->have_top && parser->field != 0) { + parser->have_frame = 1; + parser->have_top = 0; + parser->slice = 0; + return ret; + }*/ + return 0; + } else if(res == NAL_PPS || res == NAL_SPS) { + return 1; + } else if (res == NAL_AU_DELIMITER || res == NAL_SEI || + (res >= 13 && res <= 18)) { + //printf("New Frame\n"); + return 1; + } + + return 0; +} + +int seek_for_nal(uint8_t *buf, int buf_len) +{ + int i; + for(i=0; i + +#include "xine_internal.h" + +enum nal_unit_types { + NAL_UNSPECIFIED = 0, + NAL_SLICE, + NAL_PART_A, + NAL_PART_B, + NAL_PART_C, + NAL_SLICE_IDR, + NAL_SEI, + NAL_SPS, + NAL_PPS, + NAL_AU_DELIMITER, + NAL_END_OF_SEQUENCE, + NAL_END_OF_STREAM, + NAL_FILLER_DATA, + NAL_SPS_EXT +}; + + + +struct nal_unit { + uint8_t nal_ref_idc; // 0x03 + uint8_t nal_unit_type; // 0x1f + + struct seq_parameter_set_rbsp *sps; + struct pic_parameter_set_rbsp *pps; + struct slice_header *slc; +}; + +struct seq_parameter_set_rbsp { + uint8_t profile_idc; // 0xff + uint8_t constraint_setN_flag; // 0x0f + uint8_t level_idc; // 0xff + uint32_t seq_parameter_set_id; + uint32_t chroma_format_idc; + uint8_t residual_colour_transform_flag; // 0x01 + uint32_t bit_depth_luma_minus8; + uint32_t bit_depth_chroma_minus8; + uint8_t qpprime_y_zero_transform_bypass_flag; + uint8_t seq_scaling_matrix_present_flag; + uint8_t seq_scaling_lists_present_flag; // each bit = 1 list flag + // TODO: here would be the scaling lists... + uint32_t log2_max_frame_num_minus4; + uint32_t pic_order_cnt_type; + // if pic_order_cnt_type==0 + uint32_t log2_max_pic_order_cnt_lsb_minus4; + // else + uint8_t delta_pic_order_always_zero_flag; + int32_t offset_for_non_ref_pic; + int32_t offset_for_top_to_bottom_field; + uint8_t num_ref_frames_in_pic_order_cnt_cycle; + int32_t offset_for_ref_frame[256]; + // TODO: some more ignored here + uint32_t num_ref_frames; + uint8_t gaps_in_frame_num_value_allowed_flag; + /*uint32_t pic_width_in_mbs_minus1; + uint32_t pic_height_in_map_units_minus1;*/ + uint32_t pic_width; + uint32_t pic_height; + uint8_t frame_mbs_only_flag; + uint8_t mb_adaptive_frame_field_flag; + uint8_t direct_8x8_inference_flag; + uint8_t frame_cropping_flag; + uint32_t frame_crop_left_offset; + uint32_t frame_crop_right_offset; + uint32_t frame_crop_top_offset; + uint32_t frame_crop_bottom_offset; + uint8_t vui_parameters_present_flag; + // TODO: add vui_parameters, rtbsp_trailing_bits + +}; + +struct pic_parameter_set_rbsp { + uint32_t pic_parameter_set_id; + uint32_t seq_parameter_set_id; + uint8_t entropy_coding_mode_flag; + uint8_t pic_order_present_flag; + + /* we ignore further data, because it's not needed + for packetization */ +}; + +struct slice_header { + uint32_t first_mb_in_slice; + uint32_t slice_type; + uint32_t pic_parameter_set_id; + uint32_t frame_num; + int8_t field_pic_flag; + int8_t bottom_field_flag; + uint32_t idr_pic_id; + + /* sps->pic_order_cnt_type == 0 */ + uint32_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + /* sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag */ + int32_t delta_pic_order_cnt[2]; + + /* not needed for packetizing */ + /*int32_t redundant_pic_cnt; + uint8_t direct_spatial_mv_pred_flag; + uint8_t num_ref_idx_active_override_flag; + uint32_t num_ref_idx_l0_active_minus1; + uint32_t num_ref_idx_l1_active_minus1;*/ + +}; + + +#define MAX_FRAME_SIZE 1024*1024 + +struct nal_parser { + uint8_t buf[MAX_FRAME_SIZE]; + int buf_len; + int found_sps; + int found_pps; + int last_nal_res; + int field; // 0=top, 1=bottom, -1=both + int slice; + int have_top; + int have_frame; + struct nal_unit *nal0; + struct nal_unit *nal1; + struct nal_unit *current_nal; + struct nal_unit *last_nal; +}; + +int parse_nal(uint8_t *buf, int buf_len, struct nal_parser *parser); + +int seek_for_nal(uint8_t *buf, int buf_len); + +struct nal_parser* init_parser(); +void free_parser(struct nal_parser *parser); +int parse_frame(struct nal_parser *parser, uint8_t *inbuf, int inbuf_len, + uint8_t **ret_buf, int *ret_len); + +#endif