From 7c37edf153ed35a0e753b47fb573e155671f97d3 Mon Sep 17 00:00:00 2001 From: Andreas Heinchen Date: Wed, 30 Apr 2003 08:49:38 +0000 Subject: added support for theora CVS patchset: 4717 CVS date: 2003/04/30 08:49:38 --- ChangeLog | 3 + configure.ac | 21 ++- m4/Makefile.am | 1 + m4/theora.m4 | 96 ++++++++++++ src/demuxers/Makefile.am | 4 +- src/demuxers/demux_ogg.c | 263 +++++++++++++++++++++++++------ src/libtheora/Makefile.am | 34 ++++ src/libtheora/xine_decoder.c | 361 +++++++++++++++++++++++++++++++++++++++++++ src/xine-engine/buffer.h | 4 +- 9 files changed, 735 insertions(+), 52 deletions(-) create mode 100644 m4/theora.m4 create mode 100644 src/libtheora/Makefile.am create mode 100644 src/libtheora/xine_decoder.c diff --git a/ChangeLog b/ChangeLog index 1b71950fa..c0fe0c94b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +xine-lib (1-beta12) + * playback of theorastreams added + xine-lib (1-beta11) * fix bugs in selecting ogm subtitles * fix multiple lines subtitles' display in OGM container diff --git a/configure.ac b/configure.ac index 2bc910cf9..8449a3b28 100644 --- a/configure.ac +++ b/configure.ac @@ -604,6 +604,19 @@ AM_PATH_OGG( AC_MSG_RESULT([*** All of OGG/VORBIS dependent parts will be disabled ***])) AM_CONDITIONAL(HAVE_VORBIS, [test x"$no_ogg" != "xyes" -a x"$no_vorbis" != "xyes"]) +dnl --------------------------------------------- +dnl Ogg/Theora libs. +dnl --------------------------------------------- + +echo "these are your cflags before ampath_blabla $CFLAGS " + +AM_PATH_OGG( + [ AM_PATH_THEORA(AC_DEFINE(HAVE_THEORA,1,[Define this if you have theora]), + AC_MSG_RESULT([*** All OGG/THEORA dependent parts will be disabled ***])) + ], + AC_MSG_RESULT([*** All of OGG/Theora dependent parts will be disabled ***])) +AM_CONDITIONAL(HAVE_THEORA, [test x"$no_ogg" != "xyes" -a x"$no_theora" != "xyes"]) + dnl --------------------------------------------- dnl check for libFLAC dnl --------------------------------------------- @@ -1339,6 +1352,7 @@ src/libspudec/Makefile src/libspucc/Makefile src/libsputext/Makefile src/libvorbis/Makefile +src/libtheora/Makefile src/libw32dll/Makefile src/libw32dll/wine/Makefile src/libw32dll/DirectShow/Makefile @@ -1439,7 +1453,7 @@ echo " - ac3" if test x"$enable_asf" = "xyes"; then echo " - asf" fi -if test x"$no_oggvorbis" = "xno"; then +if test x"$no_vorbis" != "xyes"; then echo " - ogg" fi if test x"$have_libmng" = "xyes"; then @@ -1470,6 +1484,9 @@ fi if test x"$have_libpng" = "xyes"; then echo " - png" fi +if test x"$no_theora" != "xyes"; then + echo " - theora" +fi echo "" dnl audio decoders @@ -1480,7 +1497,7 @@ echo " - linear pcm - roqaudio" echo " - log pcm - gsm610" echo " - ffmpeg - interplayaudio" echo " - nosefart" -if test x"$no_oggvorbis" = "xno"; then +if test x"$no_vorbis" != "xyes"; then echo " - vorbis" fi if test x"$enable_w32dll" = "xyes"; then diff --git a/m4/Makefile.am b/m4/Makefile.am index 435152e61..b9b7a4736 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -31,6 +31,7 @@ EXTRA_DIST = \ progtest.m4 \ sdl.m4 \ vorbis.m4 \ + theora.m4 \ xine.m4 \ _xine.m4 \ xvid.m4 diff --git a/m4/theora.m4 b/m4/theora.m4 new file mode 100644 index 000000000..2769c9d10 --- /dev/null +++ b/m4/theora.m4 @@ -0,0 +1,96 @@ +# Configure paths for libtheora +# Andreas Heinchen 04-18-2003 +# Shamelessly adapted from Jack Moffitt's version for libvorbis +# who had stolen it from Owen Taylor and Manish Singh + +dnl AM_PATH_THEORA([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for libtheora, and define THEORA_CFLAGS and THEORA_LIBS +dnl +AC_DEFUN([AM_PATH_THEORA], +[dnl +dnl Get the cflags and libraries +dnl +AC_ARG_WITH(theora-prefix,[ --with-theora-prefix=PFX Prefix where libtheora is installed (optional)], theora_prefix="$withval", theora_prefix="") +AC_ARG_ENABLE(theoratest, [ --disable-theoratest Do not try to compile and run a test Vorbis program],, enable_theoratest=yes) + + if test x$theora_prefix != x ; then + theora_args="$theora_args --prefix=$theora_prefix" + THEORA_CFLAGS="-I$theora_prefix/include" + THEORA_LIBDIR="-L$theora_prefix/lib" + fi + + THEORA_LIBS="$THEORA_LIBDIR -ltheora -lm" + + AC_MSG_CHECKING(for Theora) + no_theora="" + + + if test "x$enable_theoratest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $THEORA_CFLAGS" + echo "these are your cflags $CFLAGS" + LIBS="$LIBS $THEORA_LIBS $OGG_LIBS" +dnl +dnl Now check if the installed Theora is sufficiently new. +dnl + rm -f conf.theoratest + AC_TRY_RUN([ +#include +#include +#include +#include + +int main () +{ + system("touch conf.theoratest"); + return 0; +} + +],, no_theora=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_theora" = x ; then + AC_MSG_RESULT(yes) + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT(no) + if test -f conf.theoratest ; then + : + else + echo "*** Could not run Theora test program, checking why..." + CFLAGS="$CFLAGS $THEORA_CFLAGS" + LIBS="$LIBS $THEORA_LIBS $OGG_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding Theora or finding the wrong" + echo "*** version of Theora. If it is not finding Theora, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means Theora was incorrectly installed" + echo "*** or that you have moved Theora since it was installed." ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + THEORA_CFLAGS="" + THEORA_LIBS="" + THEORAFILE_LIBS="" + THEORAENC_LIBS="" + ifelse([$2], , :, [$2]) + fi + AC_SUBST(THEORA_CFLAGS) + AC_SUBST(THEORA_LIBS) + AC_SUBST(THEORAFILE_LIBS) + AC_SUBST(THEORAENC_LIBS) + rm -f conf.theoratest +]) diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index c709b8734..d627e56a3 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = $(OGG_CFLAGS) @ANSI_FLAGS@ +AM_CFLAGS = $(THEORA_CFLAGS) $(OGG_CFLAGS) @ANSI_FLAGS@ LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic @@ -46,7 +46,7 @@ lib_LTLIBRARIES = $(ogg_module) $(asf_module) $(mng_module) $(image_module) \ xineplug_dmx_yuv_frames.la xineplug_dmx_ogg_la_SOURCES = demux_ogg.c -xineplug_dmx_ogg_la_LIBADD = $(OGG_LIBS) $(VORBIS_LIBS) $(XINELIB) +xineplug_dmx_ogg_la_LIBADD = $(OGG_LIBS) $(VORBIS_LIBS) $(THEORA_LIBS) $(XINELIB) xineplug_dmx_ogg_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ xineplug_dmx_avi_la_SOURCES = demux_avi.c diff --git a/src/demuxers/demux_ogg.c b/src/demuxers/demux_ogg.c index cffe156c3..713e4f79b 100644 --- a/src/demuxers/demux_ogg.c +++ b/src/demuxers/demux_ogg.c @@ -1,4 +1,3 @@ - /* * Copyright (C) 2000-2003 the xine project * @@ -18,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: demux_ogg.c,v 1.85 2003/04/27 17:40:59 heinchen Exp $ + * $Id: demux_ogg.c,v 1.86 2003/04/30 08:49:39 heinchen Exp $ * * demultiplexer for ogg streams * @@ -39,13 +38,17 @@ #include #include +#ifdef HAVE_THEORA +#include +#endif + #include "xine_internal.h" #include "xineutils.h" #include "demux.h" -/* + #define LOG - */ + #define CHUNKSIZE 8500 #define PACKET_TYPE_HEADER 0x01 @@ -75,8 +78,12 @@ typedef struct demux_ogg_s { input_plugin_t *input; +#ifdef HAVE_THEORA + theora_info t_info; +#endif + int status; - + int frame_duration; ogg_sync_state oy; @@ -87,8 +94,8 @@ typedef struct demux_ogg_s { uint32_t buf_types[MAX_STREAMS]; int preview_buffers[MAX_STREAMS]; int64_t header_granulepos[MAX_STREAMS]; - int factor[MAX_STREAMS]; - int quotient[MAX_STREAMS]; + int64_t factor[MAX_STREAMS]; + int64_t quotient[MAX_STREAMS]; int num_streams; @@ -104,6 +111,7 @@ typedef struct demux_ogg_s { int keyframe_needed; int ignore_keyframes; int time_length; + } demux_ogg_t ; @@ -117,6 +125,15 @@ typedef struct { config_values_t *config; } demux_ogg_class_t; +static int intlog(int num) { + int ret=0; + while(num>0){ + num=num/2; + ret=ret+1; + } + return(ret); +} + static int get_stream (demux_ogg_t *this, int serno) { int i; @@ -129,13 +146,30 @@ static int get_stream (demux_ogg_t *this, int serno) } static int get_pts (demux_ogg_t *this, int stream_num , int64_t granulepos ) { - if (this->quotient[stream_num]) + /*calculates an pts from an granulepos*/ + if (granulepos<0) { + if ( this->header_granulepos[stream_num]>=0 ) + return get_pts (this, stream_num, this->header_granulepos[stream_num]); + else + return 0; + +#ifdef HAVE_THEORA + } else if (this->buf_types[stream_num]==BUF_VIDEO_THEORA) { + int64_t iframe,pframe; + int keyframe_granule_shift; + keyframe_granule_shift=intlog(this->t_info.keyframe_frequency_force-1); + iframe=granulepos>>keyframe_granule_shift; + pframe=granulepos-(iframe<frame_duration); +#endif + + } else if (this->quotient[stream_num]) return (granulepos*this->factor[stream_num]/this->quotient[stream_num]); else return 0; } -static int read_ogg_page (demux_ogg_t *this) { +static int read_ogg_packet (demux_ogg_t *this) { char *buffer; long bytes; while (ogg_sync_pageout(&this->oy,&this->og)!=1) { @@ -162,6 +196,69 @@ static void hex_dump (uint8_t *p, int length) { } } +static void send_ogg_packet (demux_ogg_t *this, + fifo_buffer_t *fifo, + ogg_packet *op, + int64_t pts, + uint32_t decoder_flags, + int stream_num) { + /*this little function is used to send an entire ogg-packet through + xine buffers to the appropiate decoder, where recieve_ogg_packet should be called to collect the + buffers and reassemble them to an ogg packet*/ + + buf_element_t *buf; + + int done=0,todo=op->bytes; + int op_size = sizeof(ogg_packet); + + /* nasty hack to pack op as well as (vorbis) content + in one xine buffer */ + + buf = fifo->buffer_pool_alloc (fifo); + memcpy (buf->content, op, op_size); + + if ( buf->max_size > op_size + todo ) { + memcpy (buf->content + op_size , op->packet, todo); + done=todo; + buf->decoder_flags = BUF_FLAG_FRAME_START | BUF_FLAG_FRAME_END | decoder_flags; + buf->size = op_size + done; + } else { + memcpy (buf->content + op_size , op->packet, buf->max_size - op_size ); + done=done+ buf->max_size - op_size; + buf->decoder_flags = BUF_FLAG_FRAME_START | decoder_flags; + buf->size = buf->max_size; + } + + buf->pts = pts; + buf->extra_info->input_pos = this->input->get_current_pos (this->input); + buf->extra_info->input_time = buf->pts / 90 ; + buf->type = this->buf_types[stream_num] ; + + this->video_fifo->put (this->video_fifo, buf); + + while (donebuffer_pool_alloc (fifo); + if (done+buf->max_size < todo) { + memcpy (buf->content, op->packet+done, buf->max_size); + buf->size = buf->max_size; + done=done+buf->max_size; + buf->decoder_flags = decoder_flags; + } else { + memcpy (buf->content, op->packet+done, todo-done); + buf->size = todo-done; + done=todo; + buf->decoder_flags = BUF_FLAG_FRAME_END | decoder_flags; + } + + buf->pts = pts; + buf->extra_info->input_pos = this->input->get_current_pos (this->input); + buf->extra_info->input_time = buf->pts / 90 ; + buf->type = this->buf_types[stream_num] ; + + fifo->put (fifo, buf); + } +} + /* redefine abs as macro to handle 64-bit diffs. i guess llabs may not be available everywhere */ #define abs(x) ( ((x)<0) ? -(x) : (x) ) @@ -225,7 +322,7 @@ static void send_ogg_buf (demux_ogg_t *this, if ( this->audio_fifo && (this->buf_types[stream_num] & 0xFF000000) == BUF_AUDIO_BASE) { buf_element_t *buf; - + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); if ((this->buf_types[stream_num] & 0xFFFF0000) == BUF_AUDIO_VORBIS) { @@ -273,8 +370,34 @@ static void send_ogg_buf (demux_ogg_t *this, this->audio_fifo->put (this->audio_fifo, buf); +#ifdef HAVE_THEORA + } else if ((this->buf_types[stream_num] & 0xFFFF0000) == BUF_VIDEO_THEORA) { + + int64_t pts; + theora_info test; + + /*Lets see if this is an Header*/ + if ((theora_decode_header(&test,op))>=0) { + decoder_flags=decoder_flags|BUF_FLAG_HEADER; +#ifdef LOG + printf ("demux_ogg: found an header\n"); +#endif + } + + if (op->granulepos!=-1) { + pts = get_pts (this, stream_num, op->granulepos); + check_newpts( this, pts, PTS_VIDEO, decoder_flags ); + } else if (this->header_granulepos[stream_num]!=-1) { + pts = get_pts (this, stream_num, this->header_granulepos[stream_num]); + this->header_granulepos[stream_num]=-1; + check_newpts( this, pts, PTS_VIDEO, decoder_flags ); + } else + pts=0; + send_ogg_packet (this, this->video_fifo, op, pts, decoder_flags, stream_num); +#endif + } else if ((this->buf_types[stream_num] & 0xFF000000) == BUF_VIDEO_BASE) { - + buf_element_t *buf; int todo, done; @@ -314,7 +437,7 @@ static void send_ogg_buf (demux_ogg_t *this, buf->pts = 0; #ifdef LOG - printf ("demux_ogg: video granulepos %lld, pts %lld, time %d\n", op->granulepos, buf->pts, buf->pts / 90); + printf ("demux_ogg: video granulepos %lld, pts %lld, time %lld\n", op->granulepos, buf->pts, buf->pts / 90); #endif buf->extra_info->input_pos = this->input->get_current_pos (this->input); @@ -350,6 +473,9 @@ static void send_ogg_buf (demux_ogg_t *this, #endif } else { subtitle = (char *)&op->packet[hdrlen + 1]; +#ifdef LOG + printf ("demux_ogg: subtitle %d -> %d :%s\n",start,end,subtitle); +#endif if ((strlen(subtitle) > 1) || (*subtitle != ' ')) { start = op->granulepos; @@ -387,7 +513,7 @@ static void demux_ogg_send_header (demux_ogg_t *this) { int filelength,position; ogg_packet op; - + #ifdef LOG printf ("demux_ogg: detecting stream types...\n"); #endif @@ -395,7 +521,7 @@ static void demux_ogg_send_header (demux_ogg_t *this) { this->ignore_keyframes = 0; while (!done) { - if (!read_ogg_page(this)) { + if (!read_ogg_packet(this)) { this->status = DEMUX_FINISHED; return; } @@ -497,7 +623,7 @@ static void demux_ogg_send_header (demux_ogg_t *this) { if( !this->buf_types[stream_num] ) this->buf_types[stream_num] = BUF_VIDEO_UNKNOWN; this->buf_types[stream_num] |= channel; - this->preview_buffers[stream_num] = 1; /* FIXME: don't know */ + this->preview_buffers[stream_num] = 5; /* FIXME: don't know */ #ifdef LOG printf ("demux_ogg: subtype %.4s\n", &locsubtype); @@ -623,7 +749,7 @@ static void demux_ogg_send_header (demux_ogg_t *this) { buf->decoder_info[3] = locchannels; this->audio_fifo->put (this->audio_fifo, buf); - this->preview_buffers[stream_num] = 1; /* FIXME: don't know */ + this->preview_buffers[stream_num] = 5; /* FIXME: don't know */ this->factor[stream_num] = 90000; this->quotient[stream_num] = locsamples_per_unit; @@ -654,7 +780,7 @@ static void demux_ogg_send_header (demux_ogg_t *this) { printf ("demux_ogg: older direct show filter-generated stream header detected.\n"); hex_dump (op.packet, op.bytes); #endif - this->preview_buffers[stream_num] = 1; /* FIXME: don't know */ + this->preview_buffers[stream_num] = 5; /* FIXME: don't know */ if ( (*(int32_t*)(op.packet+96)==0x05589f80) && (op.bytes>=184)) { @@ -763,9 +889,6 @@ static void demux_ogg_send_header (demux_ogg_t *this) { } } else if (!strncmp (&op.packet[1], "text", 4)) { int channel=0; - uint32_t *val; - buf_element_t *buf; - #ifdef LOG printf ("demux_ogg: textstream detected.\n"); #endif @@ -773,15 +896,38 @@ static void demux_ogg_send_header (demux_ogg_t *this) { channel= this->num_spu_streams++; this->buf_types[stream_num] = BUF_SPU_OGM | channel; - /*send an empty spu to inform the video_decoder, that there is a stream*/ - buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); - buf->type = this->buf_types[stream_num]; - buf->pts = 0; - val = (uint32_t * )buf->content; - *val++=0; - *val++=0; - *val++=0; - this->video_fifo->put (this->video_fifo, buf); +#ifdef HAVE_THEORA + } else if (!strncmp (&op.packet[1], "theora", 4)) { + int channel; + printf ("demux_ogg: Theorastreamsupport is highly alpha at the moment\n"); + + if (theora_decode_header(&this->t_info, &op)>=0) { + + this->num_video_streams++; + + this->factor[stream_num] = (int64_t) 90000 * (int64_t) this->t_info.fps_denominator; + this->quotient[stream_num] = this->t_info.fps_numerator; + + this->frame_duration = ((int64_t) 90000*this->t_info.fps_denominator)/this->t_info.fps_numerator; + + this->preview_buffers[stream_num]=1; + this->buf_types[stream_num] = BUF_VIDEO_THEORA; + + this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] + = this->t_info.width; + this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] + = this->t_info.height; + this->stream->stream_info[XINE_STREAM_INFO_FRAME_DURATION] + = ((int64_t) 90000*this->t_info.fps_denominator)/this->t_info.fps_numerator; + + + } else { + /*Rejected stream*/ + printf ("demux_ogg: A theora header was rejected by libtheora\n"); + this->buf_types[stream_num] = BUF_CONTROL_NOP; + this->preview_buffers[stream_num] = 5; /* FIXME: don't know */ + } +#endif } else { printf ("demux_ogg: unknown stream type (signature >%.8s<). hex dump of bos packet follows:\n", @@ -838,16 +984,15 @@ static void demux_ogg_send_header (demux_ogg_t *this) { this->demux_plugin.seek((demux_plugin_t *)this, (off_t) filelength-65536 ,0); done=0; while (!done) { - if (!read_ogg_page (this)) + if (!read_ogg_packet (this)) return; stream_num=get_stream(this, ogg_page_serialno (&this->og) ); if (stream_num!=-1) { - if (this->time_length < get_pts(this, stream_num, ogg_page_granulepos(&this->og))) + if (this->time_length < (get_pts(this, stream_num, ogg_page_granulepos(&this->og) / 90))) this->time_length = get_pts(this, stream_num, ogg_page_granulepos(&this->og)) / 90; } } - this->demux_plugin.seek((demux_plugin_t *)this, position,0); } } } @@ -863,13 +1008,14 @@ static void demux_ogg_send_content (demux_ogg_t *this) { printf ("demux_ogg: send package...\n"); #endif - if (!read_ogg_page(this)) { + if (!read_ogg_packet(this)) { this->status = DEMUX_FINISHED; #ifdef LOG - printf ("demux_ogg: EOF\n"); + printf ("demux_ogg: EOF\n"); #endif return; } + /* now we've got one new page */ cur_serno = ogg_page_serialno (&this->og); @@ -894,10 +1040,10 @@ static void demux_ogg_send_content (demux_ogg_t *this) { ogg_stream_pagein(&this->oss[stream_num], &this->og); if (ogg_page_bos(&this->og)) { -#ifdef LOG + printf ("demux_ogg: beginning of stream\ndemux_ogg: serial number %d - discard\n", ogg_page_serialno (&this->og)); -#endif + while (ogg_stream_packetout(&this->oss[stream_num], &op) == 1) ; return; } @@ -906,7 +1052,7 @@ static void demux_ogg_send_content (demux_ogg_t *this) { /* printf("demux_ogg: packet: %.8s\n", op.packet); */ /* printf("demux_ogg: got a packet\n"); */ - if (*op.packet & PACKET_TYPE_HEADER) { + if ((*op.packet & PACKET_TYPE_HEADER) && (this->buf_types[stream_num]!=BUF_VIDEO_THEORA)) { if (op.granulepos!=-1) { this->header_granulepos[stream_num]=op.granulepos; #ifdef LOG @@ -920,7 +1066,7 @@ static void demux_ogg_send_content (demux_ogg_t *this) { continue; } - if (this->buf_flag_seek) + if ((this->buf_flag_seek) && (this->buf_types[stream_num]!=BUF_VIDEO_THEORA)) if ((op.granulepos==-1) && (this->header_granulepos[stream_num]==-1)) continue; @@ -928,7 +1074,30 @@ static void demux_ogg_send_content (demux_ogg_t *this) { #ifdef LOG printf ("demux_ogg: keyframe needed... buf_type=%08x\n", this->buf_types[stream_num]); #endif - if (((this->buf_types[stream_num] & 0xFF000000) == BUF_VIDEO_BASE) && + if (this->buf_types[stream_num] == BUF_VIDEO_THEORA) { +#ifdef HAVE_THEORA + + int keyframe_granule_shift; + int64_t pframe=-1,iframe=-1; + + keyframe_granule_shift=intlog(this->t_info.keyframe_frequency_force-1); + + /*replace the 1 with 0 and you will deactivate keyframeseeking*/ + if (1) { + if(op.granulepos>=0){ + iframe=op.granulepos>>keyframe_granule_shift; + pframe=op.granulepos-(iframe<keyframe_needed = 0; + } else + this->keyframe_needed = 0; + +#endif + } else if (((this->buf_types[stream_num] & 0xFF000000) == BUF_VIDEO_BASE) && (*op.packet == PACKET_IS_SYNCPOINT)) { /* printf("keyframe: l%ld b%ld e%ld g%ld p%ld str%d\n", @@ -1018,12 +1187,19 @@ static int demux_ogg_seek (demux_plugin_t *this_gen, /* * seek to start position */ +#ifdef LOG + printf ("demux_ogg: seek called\n"); +#endif if (this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) { this->keyframe_needed = (this->num_video_streams>0); if ( (!start_pos) && (start_time)) { - start_pos = start_time * this->avg_bitrate/8; + if (this->time_length!=-1) + start_pos = start_time * 1000 * this->input->get_length(this->input) / this->time_length ; + else + start_pos = start_time * this->avg_bitrate/8; + #ifdef LOG printf ("demux_ogg: seeking to %d seconds => %lld bytes\n", start_time, start_pos); @@ -1031,11 +1207,6 @@ static int demux_ogg_seek (demux_plugin_t *this_gen, } ogg_sync_reset(&this->oy); - - /*some strange streams have no syncpoint flag set at the beginning*/ - if (start_pos == 0) - this->keyframe_needed = 0; - this->input->seek (this->input, start_pos, SEEK_SET); } @@ -1224,7 +1395,7 @@ static void *init_class (xine_t *xine, void *data) { plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ - { PLUGIN_DEMUX, 21, "ogg", XINE_VERSION_CODE, NULL, init_class }, + { PLUGIN_DEMUX, 20, "ogg", XINE_VERSION_CODE, NULL, init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; diff --git a/src/libtheora/Makefile.am b/src/libtheora/Makefile.am new file mode 100644 index 000000000..7ac49ef19 --- /dev/null +++ b/src/libtheora/Makefile.am @@ -0,0 +1,34 @@ +## +## Process this file with automake to produce Makefile.in +## + +AM_CFLAGS = $(THEORA_CFLAGS) + +LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic + +libdir = $(XINE_PLUGINDIR) + +if HAVE_THEORA +theora_module = xineplug_decode_theora.la +endif + +lib_LTLIBRARIES = $(theora_module) + +xineplug_decode_theora_la_SOURCES = xine_decoder.c +xineplug_decode_theora_la_LIBADD = $(OGG_LIBS) $(THEORA_LIBS) +xineplug_decode_theora_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ + +debug: +# @$(MAKE) CFLAGS="$(DEBUG_CFLAGS) $(OGG_CFLAGS) $(THEORA_CFLAGS)" + @$(MAKE) CFLAGS="$(DEBUG_CFLAGS)" + +install-debug: debug + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/libtheora/xine_decoder.c b/src/libtheora/xine_decoder.c new file mode 100644 index 000000000..136a24e88 --- /dev/null +++ b/src/libtheora/xine_decoder.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2001-2003 the xine project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: xine_decoder.c,v 1.1 2003/04/30 08:49:40 heinchen Exp $ + * + * xine decoder plugin using libtheora + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xine_internal.h" +#include "video_out.h" +#include "buffer.h" +#include "metronom.h" +#include "xineutils.h" + +/* +#define LOG +*/ + +typedef struct theora_class_s { + video_decoder_class_t decoder_class; +} theora_class_t; + +typedef struct theora_decoder_s { + video_decoder_t theora_decoder; + theora_class_t *class; + theora_info t_info; + theora_state t_state; + ogg_packet op; + yuv_buffer yuv; + xine_stream_t* stream; + int reject; + int op_max_size; + char* packet; + int done; + int width, height; + int initialized; + int frame_duration; + int keyframe_granule_shift; + int skipframes; +} theora_decoder_t; + +static int intlog(int num) { + int ret=0; + while(num>0){ + num=num/2; + ret=ret+1; + } + return(ret); +} + +static void readin_op (theora_decoder_t *this, char* src, int size) { + if ( this->done+size > this->op_max_size) { + while (this->op_max_size < this->done+size) + this->op_max_size=this->op_max_size*2; + this->packet=realloc(this->packet, this->op_max_size); + this->op.packet=this->packet; + } + memcpy ( this->packet+this->done, src, size); + this->done=this->done+size; +} + +static void show_op_stats (theora_decoder_t *this) { + printf (" : size %ld\n",this->op.bytes); +} + +static void yuv2frame(yuv_buffer *yuv, vo_frame_t *frame) { + int i; + for(i=0;iy_height;i++) + memcpy(frame->base[0]+yuv->y_width*i, + yuv->y+yuv->y_stride*i, + yuv->y_width); + for(i=0;iuv_height;i++){ + memcpy(frame->base[2]+yuv->uv_width*i, + yuv->v+yuv->uv_stride*i, + yuv->uv_width); + memcpy(frame->base[1]+yuv->uv_width*i, + yuv->u+yuv->uv_stride*i, + yuv->uv_width); + } +} + +static int collect_data (theora_decoder_t *this, buf_element_t *buf ) { + /* Assembles an ogg_packet which was send with send_ogg_packet over xinebuffers */ + /* this->done, this->rejected, this->op and this->decoder->flags are needed*/ + int op_size = sizeof (ogg_packet); + + if (buf->decoder_flags & BUF_FLAG_FRAME_START) { + this->done=0; /*start from the beginnig*/ + this->reject=0;/*new packet - new try*/ + + /*copy the ogg_packet struct and the sum, correct the adress of the packet*/ + memcpy (&this->op, buf->content, op_size); + this->op.packet=this->packet; + + readin_op (this, buf->content + op_size, buf->size - op_size ); + /*read the rest of the data*/ + + } else { + if (this->done==0 || this->reject) { + /*we are starting to collect an packet without the beginnig + reject the rest*/ + printf ("libtheora: rejecting packet\n"); + this->reject=1; + return 0; + } + readin_op (this, buf->content, buf->size ); + } + + if ((buf->decoder_flags & BUF_FLAG_FRAME_END) && !this->reject) { + if ( this->done != this->op.bytes ) { + printf ("libtheora: A packet changed its size during transfer - rejected\n"); + printf (" size %d should be %ld\n", this->done , this->op.bytes); + show_op_stats(this); + this->op.bytes=this->done; + } + return 1; + } + return 0; +} + +static void theora_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { + /* + * decode data from buf and feed decoded frames to + * video output + */ + theora_decoder_t *this = (theora_decoder_t *) this_gen; + vo_frame_t *frame; + yuv_buffer yuv; + int ret; + + if (!collect_data(this, buf)) return; + + if ((buf->decoder_flags & BUF_FLAG_HEADER) && !this->initialized) { + if (theora_decode_header(&this->t_info,&this->op)>=0) { + theora_decode_init (&this->t_state,&this->t_info); + this->keyframe_granule_shift=intlog(this->t_info.keyframe_frequency_force-1); + this->initialized=1; + this->frame_duration=((int64_t)90000*this->t_info.fps_denominator)/this->t_info.fps_numerator; +#ifdef LOG + printf("libtheora: theora stream is Theora %dx%d %.02f fps video.\n", + this->t_info.width,this->t_info.height, + (double)this->t_info.fps_numerator/this->t_info.fps_denominator); +#endif + this->width=this->t_info.width; + this->height=this->t_info.height; + } else + printf ("libtheora: Header could not be decoded.\n"); + } else if (buf->decoder_flags & BUF_FLAG_HEADER) { + theora_clear(&this->t_state); + theora_decode_header(&this->t_info,&this->op); + theora_decode_init(&this->t_state,&this->t_info); + } else if (!(buf->decoder_flags & BUF_FLAG_PREVIEW)) { + + if (!this->initialized) { + return; + } + +#ifdef LOG + printf ("libtheora: decoding i %lld p %lld\n",this->iframe,this->pframe); +#endif + + ret=theora_decode_packetin( &this->t_state, &this->op); + + if ( ret!=0) { + printf ("libtheora:Recieved an bad packet\n"); + } else if (!this->skipframes) { + + int64_t iframe,pframe; + + theora_decode_YUVout(&this->t_state,&yuv); + + iframe=this->t_state.granulepos>>this->keyframe_granule_shift; + pframe=this->t_state.granulepos-(iframe<keyframe_granule_shift); +#ifdef LOG + printf ("libtheora: decoded i %lld p %lld\n",iframe,pframe); +#endif + frame = this->stream->video_out->get_frame( this->stream->video_out, + this->width, this->height, + ASPECT_SQUARE, + XINE_IMGFMT_YV12, + VO_BOTH_FIELDS); + yuv2frame(&yuv, frame); + + frame->pts = this->frame_duration* (iframe+pframe); + + frame->duration=this->frame_duration; + this->skipframes=frame->draw(frame, this->stream); + frame->free(frame); + this->done=0; + } else + this->skipframes=0; + theora_decode_YUVout(&this->t_state,&yuv); + } +} + + +static void theora_flush (video_decoder_t *this_gen) { + /* + * flush out any frames that are still stored in the decoder + */ + theora_decoder_t *this = (theora_decoder_t *) this_gen; + this->skipframes=0; +} + +static void theora_reset (video_decoder_t *this_gen) { + /* + * reset decoder after engine flush (prepare for new + * video data not related to recently decoded data) + */ + theora_decoder_t *this = (theora_decoder_t *) this_gen; + this->skipframes=0; +} + +static void theora_discontinuity (video_decoder_t *this_gen) { + /* + * inform decoder that a time reference discontinuity has happened. + * that is, it must forget any currently held pts value + */ + theora_decoder_t *this = (theora_decoder_t *) this_gen; + this->skipframes=0; +} + +static void theora_dispose (video_decoder_t *this_gen) { + /* + * close down, free all resources + */ + + theora_decoder_t *this = (theora_decoder_t *) this_gen; + + printf ("libtheora: dispose \n"); + + theora_clear (&this->t_state); + this->stream->video_out->close(this->stream->video_out, this->stream); + free (this->packet); + free (this); +} + +static video_decoder_t *theora_open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) { + + /* + * open a new instance of this plugin class + */ + + theora_decoder_t *this ; + + printf ("You are trying to decoda an theorastream. Theora is in the moment\n"); + printf ("aplha, xiph is developing on it. If the stream could not be played back\n"); + printf ("go to http://xine.sourceforge.net and grab the latest release of xine.\n"); + + this = (theora_decoder_t *) malloc (sizeof (theora_decoder_t)); + memset(this, 0, sizeof (theora_decoder_t)); + + this->theora_decoder.decode_data = theora_decode_data; + this->theora_decoder.flush = theora_flush; + this->theora_decoder.reset = theora_reset; + this->theora_decoder.discontinuity = theora_discontinuity; + this->theora_decoder.dispose = theora_dispose; + + this->stream = stream; + this->class = (theora_class_t *) class_gen; + + this->op_max_size = 4096; + this->packet = malloc(this->op_max_size); + + this->done = 0; + + this->stream = stream; + + this->initialized = 0; + + stream->video_out->open (stream->video_out, stream); + + return &this->theora_decoder; + +} + +/* + * theora plugin class + */ + +static char *theora_get_identifier (video_decoder_class_t *this) { + /* + * return short, human readable identifier for this plugin class + */ + return "theora video"; +} + +static char *theora_get_description (video_decoder_class_t *this) { + /* + * return human readable (verbose = 1 line) description for + * this plugin class + */ + return "experimental theora video decoder plugin"; +} + +static void theora_dispose_class (video_decoder_class_t *this) { + /* + * free all class-related resources + */ + free (this); +} + +static void *init_plugin (xine_t *xine, void *data) { + /*initialize our plugin*/ + theora_class_t *this; + + this = (theora_class_t *) malloc (sizeof (theora_class_t)); + + this->decoder_class.open_plugin = theora_open_plugin; + this->decoder_class.get_identifier = theora_get_identifier; + this->decoder_class.get_description = theora_get_description; + this->decoder_class.dispose = theora_dispose_class; + + return this; +} + +/* + * exported plugin catalog entry + */ + +static uint32_t supported_types[] = { BUF_VIDEO_THEORA, 0 }; + +static decoder_info_t dec_info_video = { + supported_types, /* supported types */ + 5 /* priority */ +}; + +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_DECODER, 14, "theora", XINE_VERSION_CODE, &dec_info_video, init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h index afe8defdc..ae9c30365 100644 --- a/src/xine-engine/buffer.h +++ b/src/xine-engine/buffer.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: buffer.h,v 1.109 2003/04/23 16:15:01 mroi Exp $ + * $Id: buffer.h,v 1.110 2003/04/30 08:49:40 heinchen Exp $ * * * contents: @@ -144,7 +144,7 @@ extern "C" { #define BUF_VIDEO_YUV_FRAMES 0x023B0000 /* uncompressed YUV, delivered by v4l input plugin */ #define BUF_VIDEO_HUFFYUV 0x023C0000 #define BUF_VIDEO_IMAGE 0x023D0000 - +#define BUF_VIDEO_THEORA 0x023E0000 /* audio buffer types: (please keep in sync with buffer_types.c) */ -- cgit v1.2.3