# HG changeset patch # User albeu # Date 1016289123 0 # Node ID 9841a86d66f9ec62789287092c9498f8ffd39679 # Parent cfccb5dbe992ade436760a1a328c49d8be8beaff Initial ogg demuxer. No seeking, a/v sync is broken. Support avi with ogg/vorbis audio. diff -r cfccb5dbe992 -r 9841a86d66f9 dec_audio.c --- a/dec_audio.c Sat Mar 16 06:01:13 2002 +0000 +++ b/dec_audio.c Sat Mar 16 14:32:03 2002 +0000 @@ -73,13 +73,8 @@ #include #include +// This struct is also defined in demux_ogg.c => common header ? typedef struct ov_struct_st { - ogg_sync_state oy; /* sync and verify incoming physical bitstream */ - ogg_stream_state os; /* take physical pages, weld into a logical - stream of packets */ - ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet op; /* one raw packet of data for decode */ - vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ vorbis_comment vc; /* struct that stores all the bitstream user comments */ @@ -389,6 +384,7 @@ driver=0; #else /* OggVorbis audio via libvorbis, compatible with files created by nandub and zorannt codec */ + // Is there always 1024 samples/frame ? ***** Albeu sh_audio->audio_out_minsize=1024*4; // 1024 samples/frame #endif break; @@ -759,124 +755,61 @@ } #ifdef HAVE_OGGVORBIS case AFM_VORBIS: { - // OggVorbis Audio: -#if 0 /* just here for reference - atmos */ - ogg_sync_state oy; /* sync and verify incoming physical bitstream */ - ogg_stream_state os; /* take physical pages, weld into a logical - stream of packets */ - ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet op; /* one raw packet of data for decode */ - - vorbis_info vi; /* struct that stores all the static vorbis bitstream - settings */ - vorbis_comment vc; /* struct that stores all the bitstream user comments */ - vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ - vorbis_block vb; /* local working space for packet->PCM decode */ -#else - /* nix, nada, rien, nothing, nem, nüx */ -#endif - - uint32_t hdrsizes[3];/* stores vorbis header sizes from AVI audio header, - maybe use ogg_uint32_t */ - //int i; - int ret; - char *buffer; - ogg_packet hdr; - //ov_struct_t *s=&sh_audio->ov; - sh_audio->ov=malloc(sizeof(ov_struct_t)); - //s=&sh_audio->ov; - - vorbis_info_init(&sh_audio->ov->vi); - vorbis_comment_init(&sh_audio->ov->vc); + ogg_packet op; + vorbis_comment vc; + struct ov_struct_st *ov; - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"OggVorbis: cbsize: %i\n", sh_audio->wf->cbSize); - memcpy(hdrsizes, ((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX), 3*sizeof(uint32_t)); - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"OggVorbis: Read header sizes: initial: %i comment: %i codebook: %i\n", hdrsizes[0], hdrsizes[1], hdrsizes[2]); - /*for(i=12; i <= 40; i+=2) { // header bruteforce :) - memcpy(hdrsizes, ((unsigned char*)sh_audio->wf)+i, 3*sizeof(uint32_t)); - printf("OggVorbis: Read header sizes (%i): %ld %ld %ld\n", i, hdrsizes[0], hdrsizes[1], hdrsizes[2]); - }*/ - - /* read headers */ // FIXME disable sound on errors here, we absolutely need this headers! - atmos - hdr.packet=NULL; - hdr.b_o_s = 1; /* beginning of stream for first packet */ - hdr.bytes = hdrsizes[0]; - hdr.packet = realloc(hdr.packet,hdr.bytes); - memcpy(hdr.packet,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t),hdr.bytes); - if(vorbis_synthesis_headerin(&sh_audio->ov->vi,&sh_audio->ov->vc,&hdr)<0) - mp_msg(MSGT_DECAUDIO,MSGL_WARN,"OggVorbis: initial (identification) header broken!\n"); - hdr.b_o_s = 0; - hdr.bytes = hdrsizes[1]; - hdr.packet = realloc(hdr.packet,hdr.bytes); - memcpy(hdr.packet,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t)+hdrsizes[0],hdr.bytes); - if(vorbis_synthesis_headerin(&sh_audio->ov->vi,&sh_audio->ov->vc,&hdr)<0) - mp_msg(MSGT_DECAUDIO,MSGL_WARN,"OggVorbis: comment header broken!\n"); - hdr.bytes = hdrsizes[2]; - hdr.packet = realloc(hdr.packet,hdr.bytes); - memcpy(hdr.packet,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t)+hdrsizes[0]+hdrsizes[1],hdr.bytes); - if(vorbis_synthesis_headerin(&sh_audio->ov->vi,&sh_audio->ov->vc,&hdr)<0) + /// Init the decoder with the 3 header packets + ov = (struct ov_struct_st*)malloc(sizeof(struct ov_struct_st)); + vorbis_info_init(&ov->vi); + vorbis_comment_init(&vc); + op.bytes = ds_get_packet(sh_audio->ds,&op.packet); + op.b_o_s = 1; + /// Header + if(vorbis_synthesis_headerin(&ov->vi,&vc,&op) <0) { + mp_msg(MSGT_DECAUDIO,MSGL_ERR,"OggVorbis: initial (identification) header broken!\n"); + driver = 0; + free(ov); + break; + } + op.bytes = ds_get_packet(sh_audio->ds,&op.packet); + op.b_o_s = 0; + /// Comments + if(vorbis_synthesis_headerin(&ov->vi,&vc,&op) <0) { + mp_msg(MSGT_DECAUDIO,MSGL_ERR,"OggVorbis: comment header broken!\n"); + driver = 0; + free(ov); + break; + } + op.bytes = ds_get_packet(sh_audio->ds,&op.packet); + //// Codebook + if(vorbis_synthesis_headerin(&ov->vi,&vc,&op)<0) { mp_msg(MSGT_DECAUDIO,MSGL_WARN,"OggVorbis: codebook header broken!\n"); - hdr.bytes=0; - hdr.packet = realloc(hdr.packet,hdr.bytes); /* free */ - /* done with the headers */ - - - /* Throw the comments plus a few lines about the bitstream we're - decoding */ - { - char **ptr=sh_audio->ov->vc.user_comments; + driver = 0; + free(ov); + break; + } else { /// Print the infos + char **ptr=vc.user_comments; while(*ptr){ mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbisComment: %s\n",*ptr); ++ptr; } - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Bitstream is %d channel, %ldHz, %ldkbit/s %cBR\n",sh_audio->ov->vi.channels,sh_audio->ov->vi.rate,sh_audio->ov->vi.bitrate_nominal/1000, (sh_audio->ov->vi.bitrate_lower!=sh_audio->ov->vi.bitrate_nominal)||(sh_audio->ov->vi.bitrate_upper!=sh_audio->ov->vi.bitrate_nominal)?'V':'C'); - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Encoded by: %s\n",sh_audio->ov->vc.vendor); + mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Bitstream is %d channel, %ldHz, %ldkbit/s %cBR\n",ov->vi.channels,ov->vi.rate,ov->vi.bitrate_nominal/1000, (ov->vi.bitrate_lower!=ov->vi.bitrate_nominal)||(ov->vi.bitrate_upper!=ov->vi.bitrate_nominal)?'V':'C'); + mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Encoded by: %s\n",vc.vendor); } - sh_audio->channels=sh_audio->ov->vi.channels; - sh_audio->samplerate=sh_audio->ov->vi.rate; - sh_audio->i_bps=sh_audio->ov->vi.bitrate_nominal/8; - -// printf("[\n"); -// sh_audio->a_buffer_len=sh_audio->audio_out_minsize;///ov->vi.channels; -// printf("]\n"); - - /* OK, got and parsed all three headers. Initialize the Vorbis - packet->PCM decoder. */ - vorbis_synthesis_init(&sh_audio->ov->vd,&sh_audio->ov->vi); /* central decode state */ - vorbis_block_init(&sh_audio->ov->vd,&sh_audio->ov->vb); /* local state for most of the decode - so multiple block decodes can - proceed in parallel. We could init - multiple vorbis_block structures - for vd here */ - //printf("OggVorbis: synthesis and block init done.\n"); - ogg_sync_init(&sh_audio->ov->oy); /* Now we can read pages */ - while((ret = ogg_sync_pageout(&sh_audio->ov->oy,&sh_audio->ov->og))!=1) { - if(ret == -1) - mp_msg(MSGT_DECAUDIO,MSGL_WARN,"OggVorbis: Pageout: not properly synced, had to skip some bytes.\n"); - else - if(ret == 0) { - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Pageout: need more data to verify page, reading more data.\n"); - /* submit a a_buffer_len block to libvorbis' Ogg layer */ - buffer=ogg_sync_buffer(&sh_audio->ov->oy,256); - ogg_sync_wrote(&sh_audio->ov->oy,demux_read_data(sh_audio->ds,buffer,256)); - } - } - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Pageout: successfull.\n"); - ogg_stream_pagein(&sh_audio->ov->os,&sh_audio->ov->og); /* we can ignore any errors here - as they'll also become apparent - at packetout */ + // Setup the decoder + sh_audio->channels=ov->vi.channels; + sh_audio->samplerate=ov->vi.rate; + sh_audio->i_bps=ov->vi.bitrate_nominal/8; + sh_audio->context = ov; - /* Get the serial number and set up the rest of decode. */ - /* serialno first; use it to set up a logical stream */ - ogg_stream_init(&sh_audio->ov->os,ogg_page_serialno(&sh_audio->ov->og)); - + /// Finish the decoder init + vorbis_synthesis_init(&ov->vd,&ov->vi); + vorbis_block_init(&ov->vd,&ov->vb); mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Init OK!\n"); - - break; -} +} break; #endif - #ifdef USE_LIBMAD case AFM_MAD: { @@ -982,117 +915,64 @@ // len=MP3_DecodeFrame(buf,3); break; #ifdef HAVE_OGGVORBIS - case AFM_VORBIS: { // OggVorbis - /* note: good minlen would be 4k or 8k IMHO - atmos */ - int ret; - char *buffer; - int bytes; - int samples; - float **pcm; - //ogg_int16_t convbuffer[4096]; -// int convsize; - int readlen=1024; - len=0; -// convsize=minlen/sh_audio->ov->vi.channels; - - while(len < minlen) { /* double loop allows for break in inner loop */ - while(len < minlen) { /* without aborting the outer loop - atmos */ - ret=ogg_stream_packetout(&sh_audio->ov->os,&sh_audio->ov->op); - if(ret==0) { - int xxx=0; - //printf("OggVorbis: Packetout: need more data, paging!\n"); - while((ret = ogg_sync_pageout(&sh_audio->ov->oy,&sh_audio->ov->og))!=1) { - if(ret == -1) - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Pageout: not properly synced, had to skip some bytes.\n"); - else - if(ret == 0) { - //printf("OggVorbis: Pageout: need more data to verify page, reading more data.\n"); - /* submit a readlen k block to libvorbis' Ogg layer */ - buffer=ogg_sync_buffer(&sh_audio->ov->oy,readlen); - bytes=demux_read_data(sh_audio->ds,buffer,readlen); - xxx+=bytes; - ogg_sync_wrote(&sh_audio->ov->oy,bytes); - if(bytes==0) - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: 0Bytes written, possible End of Stream\n"); - } - } - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"\n[sync: %d ]\n",xxx); - //printf("OggVorbis: Pageout: successfull, pagin in.\n"); - if(ogg_stream_pagein(&sh_audio->ov->os,&sh_audio->ov->og)<0) - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Pagein failed!\n"); - break; - } else if(ret<0) { - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Packetout: missing or corrupt data, skipping packet!\n"); - break; - } else { + case AFM_VORBIS: { // Vorbis + int samples; + float **pcm; + ogg_packet op; + char* np; + struct ov_struct_st *ov = sh_audio->context; + len = 0; + op.b_o_s = op.e_o_s = 0; + while(len < minlen) { + op.bytes = ds_get_packet(sh_audio->ds,&op.packet); + if(!op.packet) + break; + if(vorbis_synthesis(&ov->vb,&op)==0) /* test for success! */ + vorbis_synthesis_blockin(&ov->vd,&ov->vb); + while((samples=vorbis_synthesis_pcmout(&ov->vd,&pcm))>0){ + int i,j; + int clipflag=0; + int convsize=(maxlen-len)/(2*ov->vi.channels); // max size! + int bout=(samplesov->vb,&sh_audio->ov->op)==0) /* test for success! */ - vorbis_synthesis_blockin(&sh_audio->ov->vd,&sh_audio->ov->vb); - - /* **pcm is a multichannel float vector. In stereo, for - example, pcm[0] is left, and pcm[1] is right. samples is - the size of each channel. Convert the float values - (-1.<=range<=1.) to whatever PCM format and write it out */ - - while((samples=vorbis_synthesis_pcmout(&sh_audio->ov->vd,&pcm))>0){ - int i,j; - int clipflag=0; - int convsize=(maxlen-len)/(2*sh_audio->ov->vi.channels); // max size! - int bout=(samplesov->vi.channels;i++){ - ogg_int16_t *convbuffer=(ogg_int16_t *)(&buf[len]); - ogg_int16_t *ptr=convbuffer+i; - float *mono=pcm[i]; - for(j=0;jvi.channels;i++){ + ogg_int16_t *convbuffer=(ogg_int16_t *)(&buf[len]); + ogg_int16_t *ptr=convbuffer+i; + float *mono=pcm[i]; + for(j=0;j32767){ - val=32767; - clipflag=1; + /* might as well guard against clipping */ + if(val>32767){ + val=32767; + clipflag=1; + } + if(val<-32768){ + val=-32768; + clipflag=1; + } + *ptr=val; + ptr+=ov->vi.channels; } - if(val<-32768){ - val=-32768; - clipflag=1; - } - *ptr=val; - ptr+=sh_audio->ov->vi.channels; } - } - if(clipflag) - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"Clipping in frame %ld\n",(long)(sh_audio->ov->vd.sequence)); - - //fwrite(convbuffer,2*sh_audio->ov->vi.channels,bout,stderr); //dump pcm to file for debugging - //memcpy(buf+len,convbuffer,2*sh_audio->ov->vi.channels*bout); - len+=2*sh_audio->ov->vi.channels*bout; - - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"\n[decoded: %d / %d ]\n",bout,samples); - - vorbis_synthesis_read(&sh_audio->ov->vd,bout); /* tell libvorbis how - many samples we - actually consumed */ - } - } // from else, packetout ok - } // while len - } // outer while len - if(ogg_page_eos(&sh_audio->ov->og)) - mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: End of Stream reached!\n"); // FIXME clearup decoder, notify mplayer - atmos - - mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"\n[len: %d ]\n",len); - - break; - } + if(clipflag) + mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"Clipping in frame %ld\n",(long)(ov->vd.sequence)); + len+=2*ov->vi.channels*bout; + mp_msg(MSGT_DECAUDIO,MSGL_DBG2,"\n[decoded: %d / %d ]\n",bout,samples); + vorbis_synthesis_read(&ov->vd,bout); /* tell libvorbis how + many samples we + actually consumed */ + } + } + } break; #endif case AFM_PCM: // AVI PCM len=demux_read_data(sh_audio->ds,buf,minlen); @@ -1337,13 +1217,6 @@ MP3_DecodeFrame(NULL,-2); // resync MP3_DecodeFrame(NULL,-2); // resync break; -#ifdef HAVE_OGGVORBIS - case AFM_VORBIS: - //printf("OggVorbis: resetting stream.\n"); - ogg_sync_reset(&sh_audio->ov->oy); - ogg_stream_reset(&sh_audio->ov->os); - break; -#endif #ifdef USE_LIBAC3 case AFM_AC3: ac3_bitstream_reset(); // reset AC3 bitstream buffer diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/Makefile --- a/libmpdemux/Makefile Sat Mar 16 06:01:13 2002 +0000 +++ b/libmpdemux/Makefile Sat Mar 16 14:32:03 2002 +0000 @@ -3,7 +3,7 @@ include ../config.mak -SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c tvi_bsdbt848.c frequencies.c demux_fli.c demux_real.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c demux_nuv.c demux_film.c demux_roq.c mf.c demux_mf.c demux_audio.c demux_demuxers.c opt-reg.c mpdemux.c +SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c tvi_bsdbt848.c frequencies.c demux_fli.c demux_real.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c demux_nuv.c demux_film.c demux_roq.c mf.c demux_mf.c demux_audio.c demux_demuxers.c opt-reg.c mpdemux.c demux_ogg.c ifeq ($(STREAMING),yes) SRCS += asf_streaming.c url.c http.c network.c rtp.c endif diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/demux_ogg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmpdemux/demux_ogg.c Sat Mar 16 14:32:03 2002 +0000 @@ -0,0 +1,597 @@ + +#include "config.h" + + +#include +#include + +#include "../mp_msg.h" +#include "../help_mp.h" +#include "stream.h" +#include "demuxer.h" +#include "stheader.h" + +#ifndef HAVE_OGGVORBIS +/// Some dummy function to use when no Ogg and Vorbis lib are avaible +int demux_ogg_open(demuxer_t* demuxer) { + return 0; +} + +int demux_ogg_fill_buffer(demuxer_t *d) { + return 0; +} + +demuxer_t* init_avi_with_ogg(demuxer_t* demuxer) { + mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_NoOggVorbis); + // disable audio + demuxer->audio->id = -2; + return demuxer; +} +#else + +#include +#include + +#define BLOCK_SIZE 1024 + +/// Vorbis decoder context : we need the vorbis_info for vorbis timestamping +/// Shall we put this struct def in a common header ? +typedef struct ov_struct_st { + vorbis_info vi; /* struct that stores all the static vorbis bitstream + settings */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ +} ov_struct_t; + +//// OggDS headers +// Header for the new header format +typedef struct stream_header_video +{ + ogg_int32_t width; + ogg_int32_t height; +} stream_header_video; + +typedef struct stream_header_audio +{ + ogg_int16_t channels; + ogg_int16_t blockalign; + ogg_int32_t avgbytespersec; +} stream_header_audio; + +typedef struct stream_header +{ + char streamtype[8]; + char subtype[4]; + + ogg_int32_t size; // size of the structure + + ogg_int64_t time_unit; // in reference time + ogg_int64_t samples_per_unit; + ogg_int32_t default_len; // in media time + + ogg_int32_t buffersize; + ogg_int16_t bits_per_sample; + + union + { + // Video specific + stream_header_video video; + // Audio specific + stream_header_audio audio; + } sh; +} stream_header; + +/// Our private datas + +/// A logical stream +typedef struct ogg_stream { + /// Timestamping stuff + float samplerate; /// granulpos 2 time + int64_t lastpos; + int32_t lastsize; + + // Logical stream state + ogg_stream_state stream; +} ogg_stream_t; + +typedef struct ogg_demuxer { + /// Physical stream state + ogg_sync_state sync; + /// Current page + ogg_page page; + /// Logical streams + ogg_stream_t *subs; + int num_sub; +} ogg_demuxer_t; + +/// Some defines from OggDS +#define PACKET_TYPE_HEADER 0x01 +#define PACKET_TYPE_BITS 0x07 +#define PACKET_LEN_BITS01 0xc0 +#define PACKET_LEN_BITS2 0x02 +#define PACKET_IS_SYNCPOINT 0x08 + + +// get the logical stream of the current page +// fill os if non NULL and return the stream id +static int demux_ogg_get_page_stream(ogg_demuxer_t* ogg_d,ogg_stream_state** os) { + int id,s_no; + ogg_page* page = &ogg_d->page; + + s_no = ogg_page_serialno(page); + + for(id= 0; id < ogg_d->num_sub ; id++) { + if(s_no == ogg_d->subs[id].stream.serialno) + break; + } + + if(id == ogg_d->num_sub) + return -1; + + if(os) + *os = &ogg_d->subs[id].stream; + + return id; + +} + +/// Calculate the timestamp and add the packet to the demux stream +// return 1 if the packet was added, 0 otherwise +static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* pack) { + demuxer_t* d = ds->demuxer; + demux_packet_t* dp; + unsigned char* data; + float pts = 0; + int flags = 0; + + // If packet is an header we jump it except for vorbis + if((*pack->packet & PACKET_TYPE_HEADER) && + (ds == d->video || (ds == d->audio && ((sh_audio_t*)ds->sh)->format != 0xFFFE ))) + return 0; + + // For vorbis packet the packet is the data, for other codec we must jump the header + if(ds == d->audio && ((sh_audio_t*)ds->sh)->format == 0xFFFE) { + data = pack->packet; + if(*pack->packet & PACKET_TYPE_HEADER) + pts = 0; + else { + vorbis_info* vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi; + // When we dump the audio, there is no vi, but we dont care of timestamp in this case + if(vi) { + int32_t blocksize = vorbis_packet_blocksize(vi,pack) / vi->channels; + // Calculate the timestamp if the packet don't have any + if(pack->granulepos == -1) { + pack->granulepos = os->lastpos; + if(os->lastsize > 0) + pack->granulepos += os->lastsize; + } + pts = pack->granulepos / (float)vi->rate; + os->lastsize = blocksize; + os->lastpos = pack->granulepos; + } + } + } else { + // Find data start + int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6; + hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1; + data = pack->packet + 1 + hdrlen; + // Calculate the timestamp + if(pack->granulepos == -1) + pack->granulepos = os->lastpos + os->lastsize; + // If we alredy have a timestamp it can be a syncpoint + else if(*pack->packet & PACKET_IS_SYNCPOINT) + flags = 1; + pts = pack->granulepos/os->samplerate; + // Save the packet length and timestamp + os->lastsize = 0; + while(hdrlen) { + os->lastsize <<= 8; + os->lastsize |= pack->packet[hdrlen]; + hdrlen--; + } + os->lastpos = pack->granulepos; + } + + /// Send the packet + dp = new_demux_packet(pack->bytes-(data-pack->packet)); + memcpy(dp->buffer,data,pack->bytes-(data-pack->packet)); + ds->pts = pts; + ds->flags = flags; + ds_add_packet(ds,dp); + return 1; +} + +/// Open an ogg physical stream +int demux_ogg_open(demuxer_t* demuxer) { + ogg_demuxer_t* ogg_d; + stream_t *s; + char* buf; + int np,s_no, n_audio = 0, n_video = 0; + ogg_sync_state* sync; + ogg_page* page; + ogg_packet pack; + sh_audio_t* sh_a; + sh_video_t* sh_v; + + s = demuxer->stream; + + ogg_d = (ogg_demuxer_t*)calloc(1,sizeof(ogg_demuxer_t)); + sync = &ogg_d->sync; + page = &ogg_d->page; + + ogg_sync_init(&ogg_d->sync); + + while(1) { + /// Try to get a page + np = ogg_sync_pageout(sync,page); + /// Error + if(np < 0) { + mp_msg(MSGT_DEMUX,MSGL_DBG2,"OGG demuxer : Bad page sync\n"); + return 0; + } + /// Need some more data + if(np <= 0) { + int len; + buf = ogg_sync_buffer(sync,BLOCK_SIZE); + len = stream_read(s,buf,BLOCK_SIZE); + if(len == 0 && s->eof) { + free(ogg_d); + return 0; + } + ogg_sync_wrote(sync,len); + continue; + } + + // We got one page now + + if( ! ogg_page_bos(page) ) { // It's not a begining page + // Header parsing end here, we need to get the page otherwise it will be lost + int id = demux_ogg_get_page_stream(ogg_d,NULL); + if(id >= 0) + ogg_stream_pagein(&ogg_d->subs[id].stream,page); + else + mp_msg(MSGT_DEMUX,MSGL_ERR,"OGG : Warning found none bos page from unknow stream %d\n",ogg_page_serialno(page)); + break; + } + + /// Init the data structure needed for a logical stream + ogg_d->subs = (ogg_stream_t*)realloc(ogg_d->subs,(ogg_d->num_sub+1)*sizeof(ogg_stream_t)); + memset(&ogg_d->subs[ogg_d->num_sub],0,sizeof(ogg_stream_t)); + /// Get the stream serial number + s_no = ogg_page_serialno(page); + ogg_stream_init(&ogg_d->subs[ogg_d->num_sub].stream,s_no); + mp_msg(MSGT_DEMUX,MSGL_DBG2,"OGG : Found a stream with serial=%d\n",s_no); + // Take the first page + ogg_stream_pagein(&ogg_d->subs[ogg_d->num_sub].stream,page); + // Get first packet of the page + ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack); + + // Reset our vars + sh_a = NULL; + sh_v = NULL; + + // Check for Vorbis + if(pack.bytes >= 7 && ! strncmp(&pack.packet[1],"vorbis", 6) ) { + sh_a = new_sh_audio(demuxer,ogg_d->num_sub); + sh_a->format = 0xFFFE; + n_audio++; + mp_msg(MSGT_DEMUX,MSGL_V,"OGG : stream %d is vorbis\n",ogg_d->num_sub); + + /// Check for old header + } else if(pack.bytes >= 142 && ! strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg",35) ) { + + // Old video header + if(*(int32_t*)(pack.packet+96) == 0x05589f80 && pack.bytes >= 184) { + sh_v = new_sh_video(demuxer,ogg_d->num_sub); + sh_v->bih = (BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); + sh_v->format = mmioFOURCC(pack.packet[68],pack.packet[69], + pack.packet[70],pack.packet[71]); + sh_v->frametime = (*(int64_t*)(pack.packet+164))*0.0000001; + sh_v->fps = 1/sh_v->frametime; + sh_v->disp_w = sh_v->bih->biWidth = *(int32_t*)(pack.packet+176); + sh_v->disp_h = sh_v->bih->biHeight = *(int32_t*)(pack.packet+180); + sh_v->bih->biBitCount = *(int16_t*)(pack.packet+182); + + ogg_d->subs[ogg_d->num_sub].samplerate = sh_v->fps; + n_video++; + mp_msg(MSGT_DEMUX,MSGL_V,"OGG stream %d is video\n",ogg_d->num_sub); + + // Old audio header + } else if(*(int32_t*)pack.packet+96 == 0x05589F81) { + unsigned int extra_size; + sh_a = new_sh_audio(demuxer,ogg_d->num_sub); + extra_size = *(int16_t*)(pack.packet+140); + sh_a->wf = (WAVEFORMATEX*)calloc(1,sizeof(WAVEFORMATEX)+extra_size); + sh_a->format = sh_a->wf->wFormatTag = *(int16_t*)(pack.packet+124); + sh_a->channels = sh_a->wf->nChannels = *(int16_t*)(pack.packet+126); + sh_a->samplerate = sh_a->wf->nSamplesPerSec = *(int32_t*)(pack.packet+128); + sh_a->wf->nAvgBytesPerSec = *(int32_t*)(pack.packet+132); + sh_a->wf->nBlockAlign = *(int16_t*)(pack.packet+136); + sh_a->wf->wBitsPerSample = *(int16_t*)(pack.packet+138); + sh_a->samplesize = (sh_a->wf->wBitsPerSample+7)/8; + sh_a->wf->cbSize = extra_size; + if(extra_size > 0) + memcpy(sh_a->wf+sizeof(WAVEFORMATEX),pack.packet+142,extra_size); + + ogg_d->subs[ogg_d->num_sub].samplerate = sh_a->samplerate * sh_a->channels; + n_audio++; + mp_msg(MSGT_DEMUX,MSGL_V,"OGG stream %d is audio\n",ogg_d->num_sub); + } else + mp_msg(MSGT_DEMUX,MSGL_WARN,"OGG stream %d contain an old header but the header type is unknow\n",ogg_d->num_sub); + + // Check new header + } else if ( (*pack.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER && + pack.bytes >= (int)sizeof(stream_header)+1) { + stream_header *st = (stream_header*)(pack.packet+1); + /// New video header + if(strncmp(st->streamtype,"video",5) == 0) { + sh_v = new_sh_video(demuxer,ogg_d->num_sub); + sh_v->bih = (BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); + sh_v->format = mmioFOURCC(st->subtype[0],st->subtype[1], + st->subtype[2],st->subtype[3]); + sh_v->frametime = st->time_unit*0.0000001; + sh_v->fps = 1/sh_v->frametime; + sh_v->bih->biSize = st->buffersize; + sh_v->bih->biBitCount = st->bits_per_sample; + sh_v->disp_w = sh_v->bih->biWidth = st->sh.video.width; + sh_v->disp_h = sh_v->bih->biHeight = st->sh.video.height; + + ogg_d->subs[ogg_d->num_sub].samplerate= sh_v->fps; + n_video++; + mp_msg(MSGT_DEMUX,MSGL_V,"OGG stream %d is video\n",ogg_d->num_sub); + /// New audio header + } else if(strncmp(st->streamtype,"audio",5) == 0) { + char buffer[5]; + unsigned int extra_size = st->size - sizeof(stream_header); + memcpy(buffer,st->subtype,4); + buffer[4] = '\0'; + sh_a = new_sh_audio(demuxer,ogg_d->num_sub); + sh_a->wf = (WAVEFORMATEX*)calloc(1,sizeof(WAVEFORMATEX)+extra_size); + sh_a->format = sh_a->wf->wFormatTag = atoi(buffer); + sh_a->channels = sh_a->wf->nChannels = st->sh.audio.channels; + sh_a->samplerate = sh_a->wf->nSamplesPerSec = st->samples_per_unit; + sh_a->wf->nAvgBytesPerSec = st->sh.audio.avgbytespersec; + sh_a->wf->nBlockAlign = st->sh.audio.blockalign; + sh_a->wf->wBitsPerSample = st->bits_per_sample; + sh_a->samplesize = (sh_a->wf->wBitsPerSample+7)/8; + sh_a->wf->cbSize = extra_size; + if(extra_size) + memcpy(sh_a->wf+sizeof(WAVEFORMATEX),st+1,extra_size); + + ogg_d->subs[ogg_d->num_sub].samplerate = sh_a->samplerate * sh_a->channels; + n_audio++; + mp_msg(MSGT_DEMUX,MSGL_V,"OGG stream %d is audio\n",ogg_d->num_sub); + + /// Check for text (subtitles) header + } else if(strncmp(st->streamtype,"text",4) == 0) { + mp_msg(MSGT_DEMUX,MSGL_WARN,"OGG text stream are not supported\n"); + //// Unknow header type + } else + mp_msg(MSGT_DEMUX,MSGL_ERR,"OGG stream %d has a header marker but is of an unknow type\n",ogg_d->num_sub); + /// Unknow (invalid ?) header + } else + mp_msg(MSGT_DEMUX,MSGL_ERR,"OGG stream %d is of an unknow type\n",ogg_d->num_sub); + + if(sh_a || sh_v) { + demux_stream_t* ds = NULL; + if(sh_a) { + // If the audio stream is not defined we took the first one + if(demuxer->audio->id == -1) { + demuxer->audio->id = ogg_d->num_sub; + demuxer->audio->sh = sh_a; + sh_a->ds = demuxer->audio; + } + /// Is it the stream we want + if(demuxer->audio->id == ogg_d->num_sub) + ds = demuxer->audio; + } + if(sh_v) { + /// Also for video + if(demuxer->video->id == -1) { + demuxer->video->id = ogg_d->num_sub; + demuxer->video->sh = sh_v; + sh_v->ds = demuxer->video; + } + if(demuxer->video->id == ogg_d->num_sub) + ds = demuxer->video; + } + /// Add the packet contained in this page + if(ds) { + /// Finish the page, otherwise packets will be lost + do { + demux_ogg_add_packet(ds,&ogg_d->subs[ogg_d->num_sub],&pack); + } while(ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack) == 1); + + } + } + ogg_d->num_sub++; + } + + /// Finish to setup the demuxer + demuxer->priv = ogg_d; + /// We can't seek :( + demuxer->seekable = 0; + + if(!n_video) + demuxer->video->id = -2; + if(!n_audio) + demuxer->audio->id = -2; + + mp_msg(MSGT_DEMUX,MSGL_V,"OGG demuxer : found %d audio stream and %d video stream\n",n_audio,n_video); + + return 1; +} + + +int demux_ogg_fill_buffer(demuxer_t *d) { + ogg_demuxer_t* ogg_d; + stream_t *s; + demux_stream_t *ds; + ogg_sync_state* sync; + ogg_stream_state* os; + ogg_page* page; + ogg_packet pack; + int np = 0, id=0; + + s = d->stream; + ogg_d = d->priv; + sync = &ogg_d->sync; + page = &ogg_d->page; + + /// Find the stream we are working on + if ( (id = demux_ogg_get_page_stream(ogg_d,&os)) < 0) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"OGG demuxer : can't get current stream\n"); + return 0; + } + + while(1) { + np = 0; + ds = NULL; + /// Try to get some packet from the current page + while( (np = ogg_stream_packetout(os,&pack)) != 1) { + /// No packet we go the next page + if(np == 0) { + while(1) { + int pa,len; + char *buf; + /// Get the next page from the physical stream + while( (pa = ogg_sync_pageout(sync,page)) != 1) { + /// Error : we skip some bytes + if(pa < 0) { + mp_msg(MSGT_DEMUX,MSGL_WARN,"OGG : Page out not synced, we skip some bytes\n"); + continue; + } + /// We need more data + buf = ogg_sync_buffer(sync,BLOCK_SIZE); + len = stream_read(s,buf,BLOCK_SIZE); + if(len == 0 && s->eof) { + mp_msg(MSGT_DEMUX,MSGL_DBG2,"OGG : Stream EOF !!!!\n"); + return 0; + } + ogg_sync_wrote(sync,len); + } /// Page loop + + /// Find the page's logical stream + if( (id = demux_ogg_get_page_stream(ogg_d,&os)) < 0) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"OGG demuxer error : we met an unknow stream\n"); + return 0; + } + /// Take the page + if(ogg_stream_pagein(os,page) == 0) + break; + /// Page was invalid => retry + mp_msg(MSGT_DEMUX,MSGL_WARN,"OGG demuxer : got invalid page !!!!!\n"); + } + } else /// Packet was corrupted + mp_msg(MSGT_DEMUX,MSGL_WARN,"OGG : bad packet in stream %d\n",id); + } /// Packet loop + + /// Is the actual logical stream in use ? + if(id == d->audio->id) + ds = d->audio; + else if(id == d->video->id) + ds = d->video; + + if(ds) { + if(!demux_ogg_add_packet(ds,&ogg_d->subs[id],&pack)) + continue; /// Unuseful packet, get another + return 1; + } + + } /// while(1) + +} + +/// For avi with Ogg audio stream we have to create an ogg demuxer for this +// stream, then we join the avi and ogg demuxer with a demuxers demuxer +demuxer_t* init_avi_with_ogg(demuxer_t* demuxer) { + demuxer_t *od; + ogg_demuxer_t *ogg_d; + stream_t* s; + uint32_t hdrsizes[3]; + demux_packet_t *dp; + sh_audio_t *sh_audio = demuxer->audio->sh; + int np; + unsigned char *p = NULL,*buf; + int plen; + + /// Check that the cbSize is enouth big for the following reads + if(sh_audio->wf->cbSize < 3*sizeof(uint32_t)) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI OGG : Initial audio header is too small !!!!!\n"); + goto fallback; + } + /// Get the size of the 3 header packet + memcpy(hdrsizes, ((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX), 3*sizeof(uint32_t)); + + /// Check the size + if(sh_audio->wf->cbSize < 3*sizeof(uint32_t) + sizeof(uint32_t)+hdrsizes[0]+hdrsizes[1] + hdrsizes[2]) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI OGG : Audio header is too small !!!!!\n"); + goto fallback; + } + + // Build the ogg demuxer private datas + ogg_d = (ogg_demuxer_t*)calloc(1,sizeof(ogg_demuxer_t)); + ogg_d->num_sub = 1; + ogg_d->subs = (ogg_stream_t*)malloc(sizeof(ogg_stream_t)); + + // Init the ogg physical stream + ogg_sync_init(&ogg_d->sync); + + // Get the first page of the stream : we assume there only 1 logical stream + while((np = ogg_sync_pageout(&ogg_d->sync,&ogg_d->page)) <= 0 ) { + if(np < 0) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI OGG error : Can't init using first stream packets\n"); + free(ogg_d); + goto fallback; + } + // Add some data + plen = ds_get_packet(demuxer->audio,&p); + buf = ogg_sync_buffer(&ogg_d->sync,plen); + memcpy(buf,p,plen); + ogg_sync_wrote(&ogg_d->sync,plen); + } + // Init the logical stream + mp_msg(MSGT_DEMUX,MSGL_DBG2,"AVI OGG found page with serial %d\n",ogg_page_serialno(&ogg_d->page)); + ogg_stream_init(&ogg_d->subs[0].stream,ogg_page_serialno(&ogg_d->page)); + // Write the page + ogg_stream_pagein(&ogg_d->subs[0].stream,&ogg_d->page); + + // Create the ds_stream and the ogg demuxer + s = new_ds_stream(demuxer->audio); + od = new_demuxer(s,DEMUXER_TYPE_OGG,0,-2,-2); + + /// Add the header packets in the ogg demuxer audio stream + // Initial header + dp = new_demux_packet(hdrsizes[0]); + memcpy(dp->buffer,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t),hdrsizes[0]); + ds_add_packet(od->audio,dp); + /// Comments + dp = new_demux_packet(hdrsizes[1]); + memcpy(dp->buffer,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t)+hdrsizes[0],hdrsizes[1]); + ds_add_packet(od->audio,dp); + /// Code book + dp = new_demux_packet(hdrsizes[2]); + memcpy(dp->buffer,((unsigned char*)sh_audio->wf)+2*sizeof(WAVEFORMATEX)+3*sizeof(uint32_t)+hdrsizes[0]+hdrsizes[1],hdrsizes[2]); + ds_add_packet(od->audio,dp); + + // Finish setting up the ogg demuxer + od->priv = ogg_d; + sh_audio = new_sh_audio(od,0); + od->audio->id = 0; + od->video->id = -2; + od->audio->sh = sh_audio; + sh_audio->ds = od->audio; + sh_audio->format = 0xFFFE; + + /// Return the joined demuxers + return new_demuxers_demuxer(demuxer,od,demuxer); + + fallback: + demuxer->audio->id = -2; + return demuxer; + +} + +/// TODO : Seeking 8-) + +#endif diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/demuxer.c --- a/libmpdemux/demuxer.c Sat Mar 16 06:01:13 2002 +0000 +++ b/libmpdemux/demuxer.c Sat Mar 16 14:32:03 2002 +0000 @@ -218,6 +218,7 @@ int demux_y4m_fill_buffer(demuxer_t *demux); int demux_audio_fill_buffer(demux_stream_t *ds); extern int demux_demuxers_fill_buffer(demuxer_t *demux,demux_stream_t *ds); +extern int demux_ogg_fill_buffer(demuxer_t *d); int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){ // Note: parameter 'ds' can be NULL! @@ -243,6 +244,7 @@ case DEMUXER_TYPE_Y4M: return demux_y4m_fill_buffer(demux); case DEMUXER_TYPE_AUDIO: return demux_audio_fill_buffer(ds); case DEMUXER_TYPE_DEMUXERS: return demux_demuxers_fill_buffer(demux,ds); + case DEMUXER_TYPE_OGG: return demux_ogg_fill_buffer(demux); } return 0; } @@ -434,6 +436,9 @@ extern int nuv_check_file(demuxer_t *demuxer); extern void demux_open_nuv(demuxer_t *demuxer); extern int demux_audio_open(demuxer_t* demuxer); +extern int demux_ogg_open(demuxer_t* demuxer); + +extern demuxer_t* init_avi_with_ogg(demuxer_t* demuxer); static demuxer_t* demux_open_stream(stream_t *stream,int file_format,int audio_id,int video_id,int dvdsub_id){ @@ -593,6 +598,17 @@ demuxer = NULL; } } +//=============== Try to open as Ogg file: ================= +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_OGG){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_OGG,audio_id,video_id,dvdsub_id); + if(demux_ogg_open(demuxer)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,"Detected OGG format\n"); + file_format=DEMUXER_TYPE_OGG; + } else { + free_demuxer(demuxer); + demuxer = NULL; + } +} //=============== Try to open as MPEG-PS file: ================= if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MPEG_PS){ int pes=1; @@ -719,7 +735,12 @@ break; } case DEMUXER_TYPE_AVI: { - return (demuxer_t*) demux_open_avi(demuxer); + sh_audio_t* sh_a; + demuxer = (demuxer_t*) demux_open_avi(demuxer); + sh_a = (sh_audio_t*)demuxer->audio->sh; + if(demuxer->audio->id != -2 && sh_a && sh_a->format == 0xFFFE) + demuxer = init_avi_with_ogg(demuxer); + return demuxer; // break; } case DEMUXER_TYPE_NUV: { diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/demuxer.h --- a/libmpdemux/demuxer.h Sat Mar 16 06:01:13 2002 +0000 +++ b/libmpdemux/demuxer.h Sat Mar 16 14:32:03 2002 +0000 @@ -22,9 +22,10 @@ #define DEMUXER_TYPE_ROQ 15 #define DEMUXER_TYPE_MF 16 #define DEMUXER_TYPE_AUDIO 17 +#define DEMUXER_TYPE_OGG 18 // This should always match the higest demuxer type number. // Unless you want to disallow users to force the demuxer to some types -#define DEMUXER_TYPE_MAX 17 +#define DEMUXER_TYPE_MAX 18 #define DEMUXER_TYPE_DEMUXERS (1<<16) // A virtual demuxer type for the network code @@ -167,6 +168,8 @@ int ds_get_packet(demux_stream_t *ds,unsigned char **start); int ds_get_packet_sub(demux_stream_t *ds,unsigned char **start); +// This is defined here because demux_stream_t ins't defined in stream.h +stream_t* new_ds_stream(demux_stream_t *ds); static inline int avi_stream_id(unsigned int id){ unsigned char *p=(unsigned char *)&id; diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/stream.c --- a/libmpdemux/stream.c Sat Mar 16 06:01:13 2002 +0000 +++ b/libmpdemux/stream.c Sat Mar 16 14:32:03 2002 +0000 @@ -14,6 +14,7 @@ #include "help_mp.h" #include "stream.h" +#include "demuxer.h" extern int verbose; // defined in mplayer.c @@ -75,6 +76,9 @@ break; } #endif + case STREAMTYPE_DS: + len = demux_read_data((demux_stream_t*)s->priv,s->buffer,STREAM_BUFFER_SIZE); + break; default: len=0; } if(len<=0){ s->eof=1; s->buf_pos=s->buf_len=0; return 0; } @@ -236,3 +240,8 @@ free(s); } +stream_t* new_ds_stream(demux_stream_t *ds) { + stream_t* s = new_stream(-1,STREAMTYPE_DS); + s->priv = ds; + return s; +} diff -r cfccb5dbe992 -r 9841a86d66f9 libmpdemux/stream.h --- a/libmpdemux/stream.h Sat Mar 16 06:01:13 2002 +0000 +++ b/libmpdemux/stream.h Sat Mar 16 14:32:03 2002 +0000 @@ -14,6 +14,7 @@ #define STREAMTYPE_TV 5 #define STREAMTYPE_PLAYLIST 6 #define STREAMTYPE_MF 7 +#define STREAMTYPE_DS 8 #define VCD_SECTOR_SIZE 2352 #define VCD_SECTOR_OFFS 24