void SndSysSpeexSoundData::Initialize() { ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; // Process enough to get the header. ogg_sync_init(&oy); oy.data = m_DataStore.data; oy.storage = (int)m_DataStore.length; ogg_sync_wrote(&oy, (long)m_DataStore.length); ogg_sync_pageout(&oy, &og); ogg_stream_init(&os, ogg_page_serialno(&og)); ogg_stream_pagein(&os, &og); ogg_stream_packetout(&os, &op); SpeexHeader* header = speex_packet_to_header((char*)op.packet, op.bytes); m_SoundFormat.Channels = header->nb_channels; m_SoundFormat.Freq = header->rate; // Is there a better way to do this? // Get number of packets (-1 as the second contains misc info). int count = -1; while(ogg_sync_pageout(&oy, &og)==1) { ogg_stream_pagein(&os, &og); count += ogg_page_packets(&og); } m_FrameCount = header->frames_per_packet * count; // Free memory. speex_header_free(header); ogg_stream_clear(&os); // No need to call this again. m_bInfoReady=true; }
bool Oggeyman::parse_headers() { bool found_theora = false; // The whole page belongs to one particular stream, but to find out what is the type of // the payload, we have to go all the way down to the packet level and try to decode it. // In this first loop we will only go through the header pages, and find which // stream belongs to theora - we will save that info in theora_stream_state structure // later, we will use that info to look only at the pages belonging to our stream of interest // (here I am counting on having (or following) only one stream- theora)s ogg_stream_state probe; int pagect = 0; while (next_page()) { pagect++; // is this a header page at all? if (!is_header_page(¤t_ogg_page)) break; bool wrote_stream_state = false; if (ogg_stream_init(&probe, ogg_page_serialno(¤t_ogg_page))) return false; // submit the completed page to the streaming layer with ogg_stream_pagein ogg_stream_pagein(&probe, ¤t_ogg_page); // if I understand this correctly, theora wants theora_decode_header to be called // "until it returns 0": theora_decode_header is wrapper for th_decode_headerin // [th_decode_headerin] Decodes one header packet. // This should be called repeatedly with the packets at the beginning of the // stream until it returns 0. // That is why we call is_theora_stream, even though we have initialized the theora stream_state. // (I.e. one would think that we need to find only one theora header, but no.) if (is_theora_stream(&probe, &ogg_theora_packet)) { found_theora = true; if (! ogg_theora_stream_state.serialno) { memcpy(&ogg_theora_stream_state, &probe, sizeof(probe)); wrote_stream_state = true; } } if (!wrote_stream_state) ogg_stream_clear(&probe); } return found_theora; }
/* Process the contents of a page. First find the demuxer associated with the page's serial number. If there is no such demuxer then either the OGG file is damaged (very rare) or the page simply belongs to a stream that the user didn't want extracted. If the demuxer is found then hand over all packets in this page to the associated packetizer. */ void ogm_reader_c::process_page(ogg_page *og) { ogm_demuxer_cptr dmx; int64_t granulepos; dmx = find_demuxer(ogg_page_serialno(og)); if (!dmx || !dmx->in_use) return; granulepos = ogg_page_granulepos(og); if ((-1 != granulepos) && (granulepos < dmx->last_granulepos)) { mxwarn_tid(m_ti.m_fname, dmx->track_id, Y("The timecodes for this stream have been reset in the middle of the file. This is not supported. The current packet will be discarded.\n")); return; } ogg_stream_pagein(&dmx->os, og); dmx->process_page(granulepos); dmx->last_granulepos = granulepos; }
BOOL OggDec::GetWaveformat(WAVEFORMATEX *wfx,char *buf) { char *buffer; if(NULL==wfx)return FALSE; if(bWaveGet){ memcpy(wfx,&wfmt,sizeof(WAVEFORMATEX)); return TRUE; }else if(NULL==buf){ return FALSE; } buffer=ogg_sync_buffer(&oy,4096); memcpy(buffer,buf,4096); ogg_sync_wrote(&oy,4096); ogg_sync_pageout(&oy,&og); ogg_stream_init(&os,ogg_page_serialno(&og)); vorbis_info_init(&vi); vorbis_comment_init(&vc); ogg_stream_pagein(&os,&og); ogg_stream_packetout(&os,&op); vorbis_synthesis_headerin(&vi,&vc,&op); wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->nChannels = vi.channels; wfx->wBitsPerSample = 16; wfx->nSamplesPerSec = vi.rate; wfx->nBlockAlign = wfx->nChannels * (wfx->wBitsPerSample/8); wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign; wfx->cbSize = 0; ogg_sync_clear(&oy); ogg_stream_clear(&os); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); return TRUE; }
nsresult OpusState::PageIn(ogg_page* aPage) { if (!mActive) return NS_OK; NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial, "Page must be for this stream!"); if (ogg_stream_pagein(&mState, aPage) == -1) return NS_ERROR_FAILURE; bool haveGranulepos; nsresult rv = PacketOutUntilGranulepos(haveGranulepos); if (NS_FAILED(rv) || !haveGranulepos || mPacketCount < 2) return rv; if(!ReconstructOpusGranulepos()) return NS_ERROR_FAILURE; for (uint32_t i = 0; i < mUnstamped.Length(); i++) { ogg_packet* packet = mUnstamped[i]; NS_ASSERTION(!IsHeader(packet), "Don't try to play a header packet"); NS_ASSERTION(packet->granulepos != -1, "Packet should have a granulepos"); mPackets.Append(packet); } mUnstamped.Clear(); return NS_OK; }
BOOL ovd_reparse_stream(ovd_stream_buf* buf) { ovd_handle* handle = (ovd_handle*)buf->handle; if (buf->len) { ogg_sync_wrote(handle->oy, buf->len); int result = ogg_sync_pageout(handle->oy, &handle->og); if (result == 0) { buf->len = OVD_STREAM_BUF_LEN; return TRUE; } else if (result < 0) return FALSE; } // ogg_stream_init(&handle->os, ogg_page_serialno(&handle->og)); handle->os = ogg_stream_create(ogg_page_serialno(&handle->og)); vorbis_info_init(&handle->vi); vorbis_comment_init(&handle->vc); if (ogg_stream_pagein(handle->os, &handle->og) < 0) return FALSE; if (ogg_stream_packetout(handle->os,&handle->op) != 1) return FALSE; if (vorbis_synthesis_headerin(&handle->vi, &handle->vc, &handle->op) < 0) return FALSE; handle->init = 0; handle->eof = 0; ovd_header_init(handle); buf->buf = ogg_sync_bufferin(handle->oy, OVD_STREAM_BUF_LEN); buf->len = OVD_STREAM_BUF_LEN; return TRUE; }
int process_first_packet__speex(StreamInfo *si, ogg_page *op, ogg_packet *opckt) { unsigned long serialnoatom[3] = { EndianU32_NtoB(sizeof(serialnoatom)), EndianU32_NtoB(kCookieTypeOggSerialNo), EndianS32_NtoB(ogg_page_serialno(op)) }; unsigned long atomhead[2] = { EndianU32_NtoB(opckt->bytes + sizeof(atomhead)), EndianU32_NtoB(kCookieTypeSpeexHeader) }; SpeexHeader *inheader = (SpeexHeader *) opckt->packet; si->si_speex.header.bitrate = EndianS32_LtoN(inheader->bitrate); si->si_speex.header.extra_headers = EndianS32_LtoN(inheader->extra_headers); si->si_speex.header.frame_size = EndianS32_LtoN(inheader->frame_size); si->si_speex.header.frames_per_packet = EndianS32_LtoN(inheader->frames_per_packet); si->si_speex.header.header_size = EndianS32_LtoN(inheader->header_size); si->si_speex.header.mode = EndianS32_LtoN(inheader->mode); si->si_speex.header.mode_bitstream_version = EndianS32_LtoN(inheader->mode_bitstream_version); si->si_speex.header.nb_channels = EndianS32_LtoN(inheader->nb_channels); si->si_speex.header.rate = EndianS32_LtoN(inheader->rate); si->si_speex.header.reserved1 = EndianS32_LtoN(inheader->reserved1); si->si_speex.header.reserved2 = EndianS32_LtoN(inheader->reserved2); si->si_speex.header.speex_version_id = EndianS32_LtoN(inheader->speex_version_id); si->si_speex.header.vbr = EndianS32_LtoN(inheader->vbr); //si->si_speex.header. = EndianS32_LtoN(inheader->); dbg_printf("! -- - speex_first_packet: ch: %d, rate: %ld\n", si->si_speex.header.nb_channels, si->si_speex.header.rate); si->numChannels = si->si_speex.header.nb_channels; si->rate = si->si_speex.header.rate; //si->lastMediaInserted = 0; si->mediaLength = 0; PtrAndHand(serialnoatom, si->soundDescExtension, sizeof(serialnoatom)); //check errors? PtrAndHand(atomhead, si->soundDescExtension, sizeof(atomhead)); //check errors? PtrAndHand(opckt->packet, si->soundDescExtension, opckt->bytes); //check errors? si->si_speex.state = kSStateReadingComments; return 0; };
int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ unsigned char *header=og->header; unsigned char *body=og->body; long bodysize=og->body_len; int segptr=0; int version=ogg_page_version(og); int continued=ogg_page_continued(og); int bos=ogg_page_bos(og); int eos=ogg_page_eos(og); ogg_int64_t granulepos=ogg_page_granulepos(og); int serialno=ogg_page_serialno(og); long pageno=ogg_page_pageno(og); int segments=header[26]; /* clean up 'returned data' */ { long lr=os->lacing_returned; long br=os->body_returned; /* body data */ if(br){ os->body_fill-=br; if(os->body_fill) memmove(os->body_data,os->body_data+br,os->body_fill); os->body_returned=0; } if(lr){ /* segment table */ if(os->lacing_fill-lr){ memmove(os->lacing_vals,os->lacing_vals+lr, (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); memmove(os->granule_vals,os->granule_vals+lr, (os->lacing_fill-lr)*sizeof(*os->granule_vals)); } os->lacing_fill-=lr; os->lacing_packet-=lr; os->lacing_returned=0; } } /* check the serial number */ if(serialno!=os->serialno)return(-1); if(version>0)return(-1); _os_lacing_expand(os,segments+1); /* are we in sequence? */ if(pageno!=os->pageno){ int i; /* unroll previous partial packet (if any) */ for(i=os->lacing_packet;i<os->lacing_fill;i++) os->body_fill-=os->lacing_vals[i]&0xff; os->lacing_fill=os->lacing_packet; /* make a note of dropped data in segment table */ if(os->pageno!=-1){ os->lacing_vals[os->lacing_fill++]=0x400; os->lacing_packet++; } /* are we a 'continued packet' page? If so, we'll need to skip some segments */ if(continued){ bos=0; for(;segptr<segments;segptr++){ int val=header[27+segptr]; body+=val; bodysize-=val; if(val<255){ segptr++; break; } } } } if(bodysize){ _os_body_expand(os,bodysize); memcpy(os->body_data+os->body_fill,body,bodysize); os->body_fill+=bodysize; } { int saved=-1; while(segptr<segments){ int val=header[27+segptr]; os->lacing_vals[os->lacing_fill]=val; os->granule_vals[os->lacing_fill]=-1; if(bos){ os->lacing_vals[os->lacing_fill]|=0x100; bos=0; } if(val<255)saved=os->lacing_fill; os->lacing_fill++; segptr++; if(val<255)os->lacing_packet=os->lacing_fill; } /* set the granulepos on the last granuleval of the last full packet */ if(saved!=-1){ os->granule_vals[saved]=granulepos; } } if(eos){ os->e_o_s=1; if(os->lacing_fill>0) os->lacing_vals[os->lacing_fill-1]|=0x200; } os->pageno=pageno+1; return(0); }
int input_calculate_ogg_sleep(ogg_page *page) { static ogg_stream_state os; ogg_packet op; static vorbis_info vi; static vorbis_comment vc; static input_type codec = ICES_INPUT_UNKNOWN; static int need_start_pos, need_headers, state_in_use = 0; static int serialno = 0; static uint64_t offset; static uint64_t first_granulepos; if (ogg_page_granulepos(page) == -1) { LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream."); return -1; } if (ogg_page_bos (page)) { control.oldsamples = 0; if (state_in_use) ogg_stream_clear (&os); ogg_stream_init (&os, ogg_page_serialno (page)); serialno = ogg_page_serialno (page); state_in_use = 1; vorbis_info_init (&vi); vorbis_comment_init (&vc); need_start_pos = 1; need_headers = 3; codec = ICES_INPUT_UNKNOWN; offset = (uint64_t)0; } if (need_start_pos) { int found_first_granulepos = 0; ogg_stream_pagein (&os, page); while (ogg_stream_packetout (&os, &op) == 1) { if (need_headers) { /* check for Vorbis. For Vorbis the Magic is {0x01|0x03|0x05}"vorbis" */ if (op.bytes > 7 && memcmp(op.packet+1, "vorbis", 6) == 0) { if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0) { LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis."); control.samplerate = 0; vorbis_info_clear (&vi); ogg_stream_clear (&os); return -1; } control.samplerate = vi.rate; codec = ICES_INPUT_VORBIS; } /* check for Opus. For Opus the magic is "OpusHead" */ else if (op.bytes == 19 && memcmp(op.packet, "OpusHead", 8) == 0) { if (op.packet[8] != 1) { LOG_ERROR0("Timing control: can't determine sample rate for input, unsupported Opus version."); control.samplerate = 0; vorbis_info_clear (&vi); ogg_stream_clear (&os); return -1; } /* Sample rate is fixed for Opus: 48kHz */ control.samplerate = 48000; codec = ICES_INPUT_OGG; /* No more headers after this one needed */ need_headers = 1; } else if (op.bytes >= 80 && memcmp(op.packet, "Speex ", 8) == 0) { if (__read_int32_le(op.packet+28) != 1 || __read_int32_le(op.packet+32) != op.bytes) { LOG_ERROR0("Timing control: can't determine sample rate for input, bad or unsupported Speex header."); control.samplerate = 0; vorbis_info_clear (&vi); ogg_stream_clear (&os); return -1; } control.samplerate = __read_int32_le(op.packet+36); codec = ICES_INPUT_OGG; /* No more headers after this one needed */ need_headers = 1; } else if (op.bytes >= 51 && memcmp(op.packet, "\177FLAC\1\0", 7) == 0 && memcmp(op.packet+9, "fLaC\0", 5) == 0) { control.samplerate = __read_int20_be(op.packet+27); codec = ICES_INPUT_OGG; /* No more headers after this one needed */ need_headers = 1; } else if (codec == ICES_INPUT_UNKNOWN) { LOG_ERROR0("Timing control: can't determine sample rate for input, unsupported input format."); control.samplerate = 0; vorbis_info_clear (&vi); ogg_stream_clear (&os); return -1; } need_headers--; if (need_headers == 0) { vorbis_comment_clear (&vc); first_granulepos = (uint64_t)0; return 0; } continue; } /* headers have been read */ if (first_granulepos == 0 && op.granulepos > 0) { first_granulepos = op.granulepos; found_first_granulepos = 1; } if (codec == ICES_INPUT_VORBIS) { offset += vorbis_packet_blocksize (&vi, &op) / 4; } } if (!found_first_granulepos) return 0; need_start_pos = 0; control.oldsamples = first_granulepos - offset; vorbis_info_clear (&vi); ogg_stream_clear (&os); state_in_use = 0; } if (serialno != ogg_page_serialno (page)) { LOG_ERROR0 ("Found page which does not belong to current logical stream"); return -1; } control.samples = ogg_page_granulepos (page) - control.oldsamples; control.oldsamples = ogg_page_granulepos (page); control.senttime += ((uint64_t)control.samples * 1000000 / (uint64_t)control.samplerate); return 0; }
static int spx_read_header (SF_PRIVATE * psf) { static SpeexStereoState STEREO_INIT = SPEEX_STEREO_STATE_INIT ; OGG_PRIVATE* odata = psf->container_data ; SPX_PRIVATE* spx = psf->codec_data ; ogg_int64_t page_granule = 0 ; int stream_init = 0 ; int page_nb_packets = 0 ; int packet_count = 0 ; int enh_enabled = 1 ; int force_mode = -1 ; char * data ; int nb_read ; int lookahead ; printf ("%s %d\n", __func__, __LINE__) ; psf_log_printf (psf, "Speex header\n") ; odata->eos = 0 ; /* Reset ogg stuff which has already been used in src/ogg.c. */ ogg_stream_reset (&odata->ostream) ; ogg_sync_reset (&odata->osync) ; /* Seek to start of stream. */ psf_fseek (psf, 0, SEEK_SET) ; /* Initialize. */ ogg_sync_init (&odata->osync) ; speex_bits_init (&spx->bits) ; /* Set defaults. */ psf->sf.channels = -1 ; psf->sf.samplerate = 0 ; spx->stereo = STEREO_INIT ; /* Get a pointer to the ogg buffer and read data into it. */ data = ogg_sync_buffer (&odata->osync, OGG_SPX_READ_SIZE) ; nb_read = psf_fread (data, 1, OGG_SPX_READ_SIZE, psf) ; ogg_sync_wrote (&odata->osync, nb_read) ; /* Now we chew on Ogg packets. */ while (ogg_sync_pageout (&odata->osync, &odata->opage) == 1) { if (stream_init == 0) { ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; stream_init = 1 ; } ; if (ogg_page_serialno (&odata->opage) != odata->ostream.serialno) { /* so all streams are read. */ ogg_stream_reset_serialno (&odata->ostream, ogg_page_serialno (&odata->opage)) ; } ; /*Add page to the bitstream*/ ogg_stream_pagein (&odata->ostream, &odata->opage) ; page_granule = ogg_page_granulepos (&odata->opage) ; page_nb_packets = ogg_page_packets (&odata->opage) ; /*Extract all available packets*/ while (odata->eos == 0 && ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1) { if (odata->opacket.bytes >= 8 && memcmp (odata->opacket.packet, "Speex ", 8) == 0) { spx->serialno = odata->ostream.serialno ; } ; if (spx->serialno == -1 || odata->ostream.serialno != spx->serialno) break ; if (packet_count == 0) { spx->state = spx_header_read (psf, &odata->opacket, enh_enabled, force_mode) ; if (! spx->state) break ; speex_decoder_ctl (spx->state, SPEEX_GET_LOOKAHEAD, &lookahead) ; if (spx->nframes == 0) spx->nframes = 1 ; } else if (packet_count == 1) { spx_print_comments ((const char*) odata->opacket.packet, odata->opacket.bytes) ; } else if (packet_count < 2 + spx->header.extra_headers) { /* Ignore extra headers */ } packet_count ++ ; } ; } ; psf_log_printf (psf, "End\n") ; psf_log_printf (psf, "packet_count %d\n", packet_count) ; psf_log_printf (psf, "page_nb_packets %d\n", page_nb_packets) ; psf_log_printf (psf, "page_granule %lld\n", page_granule) ; return 0 ; } /* spx_read_header */
int main(){ 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 */ char *buffer; int bytes; FILE *instream; FILE *outstream; char *inname = "01.ogg"; char *outname = "esmith2000-09-28d1t15.raw"; //#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */ // /* Beware the evil ifdef. We avoid these where we can, but this one we // cannot. Don't add any more, you'll probably go to hell if you do. */ // //_setmode( _fileno( stdin ), _O_BINARY ); // //_setmode( _fileno( stdout ), _O_BINARY ); //#endif #if defined(macintosh) && defined(__MWERKS__) { int argc; char **argv; argc=ccommand(&argv); /* get a "command line" from the Mac user */ /* this also lets the user set stdin and stdout */ } #endif /********** Decode setup ************/ //opening the file if( fopen_s( &instream, inname, "rb" ) != 0 ) { fprintf(stderr,"Can not open file %s\n", inname); exit(1); }; if( fopen_s( &outstream, outname, "wb" ) != 0 ) { fprintf(stderr,"Can not open file %s\n", outname); exit(1); } ogg_sync_init(&oy); /* Now we can read pages */ while(1){ /* we repeat if the bitstream is chained */ int eos=0; int i; /* grab some data at the head of the stream. We want the first page (which is guaranteed to be small and only contain the Vorbis stream initial header) We need the first page to get the stream serialno. */ /* submit a 4k block to libvorbis' Ogg layer */ buffer=ogg_sync_buffer(&oy,4096); //bytes=fread(buffer,1,4096,stdin); bytes=fread(buffer,1,4096,instream); ogg_sync_wrote(&oy,bytes); /* Get the first page. */ if(ogg_sync_pageout(&oy,&og)!=1){ /* have we simply run out of data? If so, we're done. */ if(bytes<4096)break; /* error case. Must not be Vorbis data */ fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n"); exit(1); } /* Get the serial number and set up the rest of decode. */ /* serialno first; use it to set up a logical stream */ ogg_stream_init(&os,ogg_page_serialno(&og)); /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ /* I handle the initial header first instead of just having the code read all three Vorbis headers at once because reading the initial header is an easy way to identify a Vorbis bitstream and it's useful to see that functionality seperated out. */ vorbis_info_init(&vi); vorbis_comment_init(&vc); if(ogg_stream_pagein(&os,&og)<0){ /* error; stream version mismatch perhaps */ fprintf(stderr,"Error reading first page of Ogg bitstream data.\n"); exit(1); } if(ogg_stream_packetout(&os,&op)!=1){ /* no page? must not be vorbis */ fprintf(stderr,"Error reading initial header packet.\n"); exit(1); } if(vorbis_synthesis_headerin(&vi,&vc,&op)<0){ /* error case; not a vorbis header */ fprintf(stderr,"This Ogg bitstream does not contain Vorbis " "audio data.\n"); exit(1); } /* At this point, we're sure we're Vorbis. We've set up the logical (Ogg) bitstream decoder. Get the comment and codebook headers and set up the Vorbis decoder */ /* The next two packets in order are the comment and codebook headers. They're likely large and may span multiple pages. Thus we read and submit data until we get our two packets, watching that no pages are missing. If a page is missing, error out; losing a header page is the only place where missing data is fatal. */ i=0; while(i<2){ while(i<2){ int result=ogg_sync_pageout(&oy,&og); if(result==0)break; /* Need more data */ /* Don't complain about missing or corrupt data yet. We'll catch it at the packet output phase */ if(result==1){ ogg_stream_pagein(&os,&og); /* we can ignore any errors here as they'll also become apparent at packetout */ while(i<2){ result=ogg_stream_packetout(&os,&op); if(result==0)break; if(result<0){ /* Uh oh; data at some point was corrupted or missing! We can't tolerate that in a header. Die. */ fprintf(stderr,"Corrupt secondary header. Exiting.\n"); exit(1); } result=vorbis_synthesis_headerin(&vi,&vc,&op); if(result<0){ fprintf(stderr,"Corrupt secondary header. Exiting.\n"); exit(1); } i++; } } } /* no harm in not checking before adding more */ buffer=ogg_sync_buffer(&oy,4096); //bytes=fread(buffer,1,4096,stdin); bytes=fread(buffer,1,4096,instream); if(bytes==0 && i<2){ fprintf(stderr,"End of file before finding all Vorbis headers!\n"); exit(1); } ogg_sync_wrote(&oy,bytes); } /* Throw the comments plus a few lines about the bitstream we're decoding */ { char **ptr=vc.user_comments; while(*ptr){ fprintf(stderr,"%s\n",*ptr); ++ptr; } fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi.channels,vi.rate); fprintf(stderr,"Encoded by: %s\n\n",vc.vendor); } convsize=4096/vi.channels; /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ if(vorbis_synthesis_init(&vd,&vi)==0){ /* central decode state */ vorbis_block_init(&vd,&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 */ /* The rest is just a straight decode loop until end of stream */ while(!eos){ while(!eos){ int result=ogg_sync_pageout(&oy,&og); if(result==0)break; /* need more data */ if(result<0){ /* missing or corrupt data at this page position */ fprintf(stderr,"Corrupt or missing data in bitstream; " "continuing...\n"); }else{ ogg_stream_pagein(&os,&og); /* can safely ignore errors at this point */ while(1){ result=ogg_stream_packetout(&os,&op); if(result==0)break; /* need more data */ if(result<0){ /* missing or corrupt data at this page position */ /* no reason to complain; already complained above */ }else{ /* we have a packet. Decode it */ float **pcm; int samples; if(vorbis_synthesis(&vb,&op)==0) /* test for success! */ vorbis_synthesis_blockin(&vd,&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(&vd,&pcm))>0){ int j; int clipflag=0; int bout=(samples<convsize?samples:convsize); /* convert floats to 16 bit signed ints (host order) and interleave */ for(i=0;i<vi.channels;i++){ ogg_int16_t *ptr=convbuffer+i; float *mono=pcm[i]; for(j=0;j<bout;j++){ #if 1 int val=floor(mono[j]*32767.f+.5f); #else /* optional dither */ int val=mono[j]*32767.f+drand48()-0.5f; #endif /* might as well guard against clipping */ if(val>32767){ val=32767; clipflag=1; } if(val<-32768){ val=-32768; clipflag=1; } *ptr=val; ptr+=vi.channels; } } if(clipflag) fprintf(stderr,"Clipping in frame %ld\n",(long)(vd.sequence)); //fwrite(convbuffer,2*vi.channels,bout,stdout); fwrite(convbuffer,2*vi.channels,bout,outstream); vorbis_synthesis_read(&vd,bout); /* tell libvorbis how many samples we actually consumed */ } } } if(ogg_page_eos(&og))eos=1; } } if(!eos){ buffer=ogg_sync_buffer(&oy,4096); //bytes=fread(buffer,1,4096,stdin); bytes=fread(buffer,1,4096,instream); ogg_sync_wrote(&oy,bytes); if(bytes==0)eos=1; } } /* ogg_page and ogg_packet structs always point to storage in libvorbis. They're never freed or manipulated directly */ vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); }else{ fprintf(stderr,"Error: Corrupt header during playback initialization.\n"); } /* clean up this logical bitstream; before exit we see if we're followed by another [chained] */ ogg_stream_clear(&os); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); /* must be called last */ } /* OK, clean up the framer */ ogg_sync_clear(&oy); fprintf(stderr,"Done.\n"); return(0); }
UInt32 OggSpeexDecoder::ReadAudio(AudioBufferList *bufferList, UInt32 frameCount) { if(!IsOpen() || NULL == bufferList || bufferList->mNumberBuffers != mFormat.mChannelsPerFrame || 0 == frameCount) return 0; UInt32 framesRead = 0; // Reset output buffer data size for(UInt32 i = 0; i < bufferList->mNumberBuffers; ++i) bufferList->mBuffers[i].mDataByteSize = 0; for(;;) { UInt32 framesRemaining = frameCount - framesRead; UInt32 framesToSkip = static_cast<UInt32>(bufferList->mBuffers[0].mDataByteSize / sizeof(float)); UInt32 framesInBuffer = static_cast<UInt32>(mBufferList->mBuffers[0].mDataByteSize / sizeof(float)); UInt32 framesToCopy = std::min(framesInBuffer, framesRemaining); // Copy data from the buffer to output for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) { float *floatBuffer = static_cast<float *>(bufferList->mBuffers[i].mData); memcpy(floatBuffer + framesToSkip, mBufferList->mBuffers[i].mData, framesToCopy * sizeof(float)); bufferList->mBuffers[i].mDataByteSize += static_cast<UInt32>(framesToCopy * sizeof(float)); // Move remaining data in buffer to beginning if(framesToCopy != framesInBuffer) { floatBuffer = static_cast<float *>(mBufferList->mBuffers[i].mData); memmove(floatBuffer, floatBuffer + framesToCopy, (framesInBuffer - framesToCopy) * sizeof(float)); } mBufferList->mBuffers[i].mDataByteSize -= static_cast<UInt32>(framesToCopy * sizeof(float)); } framesRead += framesToCopy; // All requested frames were read if(framesRead == frameCount) break; // EOS reached if(mSpeexEOSReached) break; // Attempt to process the desired number of packets unsigned packetsDesired = 1; while(0 < packetsDesired && !mSpeexEOSReached) { // Process any packets in the current page while(0 < packetsDesired && !mSpeexEOSReached) { // Grab a packet from the streaming layer ogg_packet oggPacket; int result = ogg_stream_packetout(&mOggStreamState, &oggPacket); if(-1 == result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding error: Ogg loss of streaming"); break; } // If result is 0, there is insufficient data to assemble a packet if(0 == result) break; // Otherwise, we got a valid packet for processing if(1 == result) { if(5 <= oggPacket.bytes && !memcmp(oggPacket.packet, "Speex", 5)) mSpeexSerialNumber = mOggStreamState.serialno; if(-1 == mSpeexSerialNumber || mOggStreamState.serialno != mSpeexSerialNumber) break; // Ignore the following: // - Speex comments in packet #2 // - Extra headers (optionally) in packets 3+ if(1 != mOggPacketCount && 1 + mExtraSpeexHeaderCount <= mOggPacketCount) { // Detect Speex EOS if(oggPacket.e_o_s && mOggStreamState.serialno == mSpeexSerialNumber) mSpeexEOSReached = true; // SPEEX_GET_FRAME_SIZE is in samples spx_int32_t speexFrameSize; speex_decoder_ctl(mSpeexDecoder, SPEEX_GET_FRAME_SIZE, &speexFrameSize); float buffer [(2 == mFormat.mChannelsPerFrame) ? 2 * speexFrameSize : speexFrameSize]; // Copy the Ogg packet to the Speex bitstream speex_bits_read_from(&mSpeexBits, (char *)oggPacket.packet, static_cast<int>(oggPacket.bytes)); // Decode each frame in the Speex packet for(spx_int32_t i = 0; i < mSpeexFramesPerOggPacket; ++i) { result = speex_decode(mSpeexDecoder, &mSpeexBits, buffer); // -1 indicates EOS if(-1 == result) break; else if(-2 == result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding error: possible corrupted stream"); break; } if(0 > speex_bits_remaining(&mSpeexBits)) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding overflow: possible corrupted stream"); break; } // Normalize the values float maxSampleValue = 1u << 15; vDSP_vsdiv(buffer, 1, &maxSampleValue, buffer, 1, speexFrameSize); // Copy the frames from the decoding buffer to the output buffer, skipping over any frames already decoded framesInBuffer = static_cast<UInt32>(mBufferList->mBuffers[0].mDataByteSize / sizeof(float)); memcpy(static_cast<float *>(mBufferList->mBuffers[0].mData) + framesInBuffer, buffer, speexFrameSize * sizeof(float)); mBufferList->mBuffers[0].mDataByteSize += static_cast<UInt32>(speexFrameSize * sizeof(float)); // Process stereo channel, if present if(2 == mFormat.mChannelsPerFrame) { speex_decode_stereo(buffer, speexFrameSize, mSpeexStereoState); vDSP_vsdiv(buffer + speexFrameSize, 1, &maxSampleValue, buffer + speexFrameSize, 1, speexFrameSize); memcpy(static_cast<float *>(mBufferList->mBuffers[1].mData) + framesInBuffer, buffer + speexFrameSize, speexFrameSize * sizeof(float)); mBufferList->mBuffers[1].mDataByteSize += static_cast<UInt32>(speexFrameSize * sizeof(float)); } // Packet processing finished --packetsDesired; } } ++mOggPacketCount; } } // Grab a new Ogg page for processing, if necessary if(!mSpeexEOSReached && 0 < packetsDesired) { while(1 != ogg_sync_pageout(&mOggSyncState, &mOggPage)) { // Get the ogg buffer for writing char *data = ogg_sync_buffer(&mOggSyncState, READ_SIZE_BYTES); // Read bitstream from input file ssize_t bytesRead = GetInputSource()->Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Unable to read from the input file"); break; } ogg_sync_wrote(&mOggSyncState, bytesRead); // No more data available from input file if(0 == bytesRead) break; } // Ensure all Ogg streams are read if(ogg_page_serialno(&mOggPage) != mOggStreamState.serialno) ogg_stream_reset_serialno(&mOggStreamState, ogg_page_serialno(&mOggPage)); // Get the resultant Ogg page int result = ogg_stream_pagein(&mOggStreamState, &mOggPage); if(0 != result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Error reading Ogg page"); break; } } } } mCurrentFrame += framesRead; if(0 == framesRead && mSpeexEOSReached) mTotalFrames = mCurrentFrame; return framesRead; }
static GstFlowReturn gst_ogg_avi_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstFlowReturn result = GST_FLOW_OK; GstOggAviParse *ogg; guint size; gchar *oggbuf; gint ret = -1; ogg = GST_OGG_AVI_PARSE (parent); size = gst_buffer_get_size (buffer); GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", size); if (GST_BUFFER_IS_DISCONT (buffer)) { ogg_sync_reset (&ogg->sync); ogg->discont = TRUE; } /* write data to sync layer */ oggbuf = ogg_sync_buffer (&ogg->sync, size); gst_buffer_extract (buffer, 0, oggbuf, size); ogg_sync_wrote (&ogg->sync, size); gst_buffer_unref (buffer); /* try to get as many packets out of the stream as possible */ do { ogg_page page; /* try to swap out a page */ ret = ogg_sync_pageout (&ogg->sync, &page); if (ret == 0) { GST_DEBUG_OBJECT (ogg, "need more data"); break; } else if (ret == -1) { GST_DEBUG_OBJECT (ogg, "discont in pages"); ogg->discont = TRUE; } else { /* new unknown stream, init the ogg stream with the serial number of the * page. */ if (ogg->serial == -1) { ogg->serial = ogg_page_serialno (&page); ogg_stream_init (&ogg->stream, ogg->serial); } /* submit page */ if (ogg_stream_pagein (&ogg->stream, &page) != 0) { GST_WARNING_OBJECT (ogg, "ogg stream choked on page resetting stream"); ogg_sync_reset (&ogg->sync); ogg->discont = TRUE; continue; } /* try to get as many packets as possible out of the page */ do { ogg_packet packet; ret = ogg_stream_packetout (&ogg->stream, &packet); GST_LOG_OBJECT (ogg, "packetout gave %d", ret); switch (ret) { case 0: break; case -1: /* out of sync, We mark a DISCONT. */ ogg->discont = TRUE; break; case 1: result = gst_ogg_avi_parse_push_packet (ogg, &packet); if (result != GST_FLOW_OK) goto done; break; default: GST_WARNING_OBJECT (ogg, "invalid return value %d for ogg_stream_packetout, resetting stream", ret); break; } } while (ret != 0); } } while (ret != 0); done: return result; }
void probe_ogg(info_t *ipipe) { ogg_sync_state sync; ogg_page page; ogg_packet pack; char *buf; int nread, np, sno, nvtracks = 0, natracks = 0, i, idx; //int endofstream = 0, k, n; struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS]; int fdin = -1; char vid_codec[5]; ogm_stream_header *sth; fdin = ipipe->fd_in; if (fdin == -1) { tc_log_error(__FILE__, "Could not open file."); goto ogg_out; } ipipe->probe_info->magic=TC_MAGIC_OGG; memset(streams, 0, sizeof(streams)); for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++) streams[i].serial = -1; ogg_sync_init(&sync); while (1) { np = ogg_sync_pageseek(&sync, &page); if (np < 0) { tc_log_error(__FILE__, "ogg_sync_pageseek failed"); goto ogg_out; } if (np == 0) { buf = ogg_sync_buffer(&sync, BLOCK_SIZE); if (!buf) { tc_log_error(__FILE__, "ogg_sync_buffer failed"); goto ogg_out; } if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) { } ogg_sync_wrote(&sync, nread); continue; } if (!ogg_page_bos(&page)) { break; } else { ogg_stream_state sstate; vorbis_info *inf = tc_malloc (sizeof(vorbis_info)); vorbis_comment *com = tc_malloc (sizeof(vorbis_comment)); if (!inf || !com) { tc_log_error(__FILE__, "Out of Memory at %d", __LINE__); goto ogg_out; } sno = ogg_page_serialno(&page); if (ogg_stream_init(&sstate, sno)) { tc_log_error(__FILE__, "ogg_stream_init failed"); goto ogg_out; } ogg_stream_pagein(&sstate, &page); ogg_stream_packetout(&sstate, &pack); switch (ogm_packet_type(pack)) { case Vorbis: vorbis_info_init(inf); vorbis_comment_init(com); if(vorbis_synthesis_headerin(inf, com, &pack) < 0) { tc_log_warn(__FILE__, "Could not decode vorbis header " "packet - invalid vorbis stream ()"); } else { #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; " "rate: %ldHz, channels: %d, bitrate %3.2f kb/s", natracks + 1, natracks + nvtracks + 1, inf->rate, inf->channels, (double)inf->bitrate_nominal/1000.0); #endif ipipe->probe_info->track[natracks].samplerate = inf->rate; ipipe->probe_info->track[natracks].chan = inf->channels; ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/ ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS; ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[natracks].serial = sno; streams[natracks].vorbis = 1; ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate)); natracks++; } break; #ifdef HAVE_THEORA case Theora: { theora_info ti; theora_comment tc; theora_decode_header(&ti, &tc, &pack); ipipe->probe_info->width = ti.width; ipipe->probe_info->height = ti.height; ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator; tc_frc_code_from_ratio(&(ipipe->probe_info->frc), ti.fps_numerator, ti.fps_denominator); ipipe->probe_info->codec=TC_CODEC_THEORA; idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; break; } #endif case DirectShow: if ((*(int32_t*)(pack.packet+96) == 0x05589f80) && (pack.bytes >= 184)) { tc_log_warn(__FILE__, "(v%d/%d) Found old video " "header. Not supported.", nvtracks + 1, natracks + nvtracks + 1); } else if (*(int32_t*)pack.packet+96 == 0x05589F81) { tc_log_warn(__FILE__, "(a%d/%d) Found old audio " "header. Not supported.", natracks + 1, natracks + nvtracks + 1); } break; case StreamHeader: sth = (ogm_stream_header *)(pack.packet + 1); if (!strncmp(sth->streamtype, "video", 5)) { #ifdef OGM_DEBUG unsigned long codec; codec = (sth->subtype[0] << 24) + (sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3]; tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d " "codec: %p (%c%c%c%c)", nvtracks + 1, natracks + nvtracks + 1, (double)10000000 / (double)sth->time_unit, sth->sh.video.width, sth->sh.video.height, (void *)codec, sth->subtype[0], sth->subtype[1], sth->subtype[2], sth->subtype[3]); #endif vid_codec[0] = sth->subtype[0]; vid_codec[1] = sth->subtype[1]; vid_codec[2] = sth->subtype[2]; vid_codec[3] = sth->subtype[3]; vid_codec[4] = '\0'; //ipipe->probe_info->frames = AVI_video_frames(avifile); ipipe->probe_info->width = sth->sh.video.width; ipipe->probe_info->height = sth->sh.video.height; ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit; tc_frc_code_from_value(&(ipipe->probe_info->frc), ipipe->probe_info->fps); ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten if(strlen(vid_codec)==0) { ipipe->probe_info->codec=TC_CODEC_RGB24; } else { if(strcasecmp(vid_codec,"dvsd")==0) ipipe->probe_info->codec=TC_CODEC_DV; if(strcasecmp(vid_codec,"DIV3")==0) ipipe->probe_info->codec=TC_CODEC_DIVX3; if(strcasecmp(vid_codec,"DIVX")==0) ipipe->probe_info->codec=TC_CODEC_DIVX4; if(strcasecmp(vid_codec,"DX50")==0) ipipe->probe_info->codec=TC_CODEC_DIVX5; if(strcasecmp(vid_codec,"XVID")==0) ipipe->probe_info->codec=TC_CODEC_XVID; if(strcasecmp(vid_codec,"MJPG")==0) ipipe->probe_info->codec=TC_CODEC_MJPEG; } idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; } else if (!strncmp(sth->streamtype, "audio", 5)) { int codec; char buf[5]; ac_memcpy(buf, sth->subtype, 4); buf[4] = 0; codec = strtoul(buf, NULL, 16); #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per " "sample: %d channels: %hd samples per second: %ld " "avgbytespersec: %hd blockalign: %d", natracks + 1, natracks + nvtracks + 1, codec, codec, codec == 0x1 ? "PCM" : codec == 55 ? "MP3" : codec == 0x55 ? "MP3" : codec == 0x2000 ? "AC3" : "unknown", sth->bits_per_sample, sth->sh.audio.channels, (long)sth->samples_per_unit, sth->sh.audio.avgbytespersec, sth->sh.audio.blockalign); #endif idx = natracks; ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit; ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels; ipipe->probe_info->track[natracks].bits = (sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample; ipipe->probe_info->track[natracks].format = codec; ipipe->probe_info->track[natracks].bitrate = 0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); natracks++; } else { tc_log_warn(__FILE__, "(%d) found new header of unknown/" "unsupported type\n", nvtracks + natracks + 1); } break; case none: tc_log_warn(__FILE__, "OGG stream %d is of an unknown type " "(bad header?)", nvtracks + natracks + 1); break; } /* switch type */ free(inf); free(com); ogg_stream_clear(&sstate); } /* beginning of page */ } /* while (1) */ ogg_out: //close(fdin); return; }
int main(int argc,char *argv[]){ int i,j; ogg_packet op; FILE *infile = stdin; #ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */ /* Beware the evil ifdef. We avoid these where we can, but this one we cannot. Don't add any more, you'll probably go to hell if you do. */ _setmode( _fileno( stdin ), _O_BINARY ); #endif /* open the input file if any */ if(argc==2){ infile=fopen(argv[1],"rb"); if(infile==NULL){ fprintf(stderr,"Unable to open '%s' for playback.\n", argv[1]); exit(1); } } if(argc>2){ usage(); exit(1); } /* start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Vorbis structures needed in header parsing */ vorbis_info_init(&vi); vorbis_comment_init(&vc); /* init supporting Theora structures needed in header parsing */ theora_comment_init(&tc); theora_info_init(&ti); /* Ogg file open; parse the headers */ /* Only interested in Vorbis/Theora streams */ while(!stateflag){ int ret=buffer_data(infile,&oy); if(ret==0)break; while(ogg_sync_pageout(&oy,&og)>0){ ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if(!ogg_page_bos(&og)){ /* don't leak the page; get it into the appropriate stream */ queue_page(&og); stateflag=1; break; } ogg_stream_init(&test,ogg_page_serialno(&og)); ogg_stream_pagein(&test,&og); ogg_stream_packetout(&test,&op); /* identify the codec: try theora */ if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){ /* it is theora */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){ /* it is vorbis */ memcpy(&vo,&test,sizeof(test)); vorbis_p=1; }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-bos page parsing */ } /* we're expecting more header packets. */ while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){ int ret; /* look for further theora headers */ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){ if(ret<0){ fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } if(theora_decode_header(&ti,&tc,&op)){ printf("Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } theora_p++; if(theora_p==3)break; } /* look for more vorbis header packets */ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){ if(ret<0){ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } if(vorbis_synthesis_headerin(&vi,&vc,&op)){ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } vorbis_p++; if(vorbis_p==3)break; } /* The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec */ if(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); /* demux into the appropriate stream */ }else{ int ret=buffer_data(infile,&oy); /* someone needs more data */ if(ret==0){ fprintf(stderr,"End of file while searching for codec headers.\n"); exit(1); } } } /* and now we have it all. initialize decoders */ if(theora_p){ theora_decode_init(&td,&ti); printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n", (unsigned int)to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator); if(ti.width!=ti.frame_width || ti.height!=ti.frame_height) printf(" Frame content is %dx%d with offset (%d,%d).\n", ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y); report_colorspace(&ti); dump_comments(&tc); }else{ /* tear down the partial theora setup */ theora_info_clear(&ti); theora_comment_clear(&tc); } if(vorbis_p){ vorbis_synthesis_init(&vd,&vi); vorbis_block_init(&vd,&vb); fprintf(stderr,"Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n", (unsigned int)vo.serialno,vi.channels,(int)vi.rate); }else{ /* tear down the partial vorbis setup */ vorbis_info_clear(&vi); vorbis_comment_clear(&vc); } /* open audio */ if(vorbis_p)open_audio(); /* open video */ if(theora_p)open_video(); /* install signal handler as SDL clobbered the default */ signal (SIGINT, sigint_handler); /* on to the main decode loop. We assume in this example that audio and video start roughly together, and don't begin playback until we have a start frame for both. This is not necessarily a valid assumption in Ogg A/V streams! It will always be true of the example_encoder (and most streams) though. */ stateflag=0; /* playback has not begun */ while(!got_sigint){ /* we want a video and audio frame ready to go at all times. If we have to buffer incoming, buffer the compressed data (ie, let ogg do the buffering) */ while(vorbis_p && !audiobuf_ready){ int ret; float **pcm; /* if there's pending, decoded audio, grab it */ if((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0){ int count=audiobuf_fill/2; int maxsamples=(audiofd_fragsize-audiobuf_fill)/2/vi.channels; for(i=0;i<ret && i<maxsamples;i++) for(j=0;j<vi.channels;j++){ int val=rint(pcm[j][i]*32767.f); if(val>32767)val=32767; if(val<-32768)val=-32768; audiobuf[count++]=val; } vorbis_synthesis_read(&vd,i); audiobuf_fill+=i*vi.channels*2; if(audiobuf_fill==audiofd_fragsize)audiobuf_ready=1; if(vd.granulepos>=0) audiobuf_granulepos=vd.granulepos-ret+i; else audiobuf_granulepos+=i; }else{ /* no pending audio; is there a pending packet to decode? */ if(ogg_stream_packetout(&vo,&op)>0){ if(vorbis_synthesis(&vb,&op)==0) /* test for success! */ vorbis_synthesis_blockin(&vd,&vb); }else /* we need more data; break out to suck in another page */ break; } } while(theora_p && !videobuf_ready){ /* theora is one in, one out... */ if(ogg_stream_packetout(&to,&op)>0){ theora_decode_packetin(&td,&op); videobuf_granulepos=td.granulepos; videobuf_time=theora_granule_time(&td,videobuf_granulepos); /* is it already too old to be useful? This is only actually useful cosmetically after a SIGSTOP. Note that we have to decode the frame even if we don't show it (for now) due to keyframing. Soon enough libtheora will be able to deal with non-keyframe seeks. */ if(videobuf_time>=get_time()) videobuf_ready=1; }else break; } if(!videobuf_ready && !audiobuf_ready && feof(infile))break; if(!videobuf_ready || !audiobuf_ready){ /* no data yet for somebody. Grab another page */ int bytes=buffer_data(infile,&oy); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* If playback has begun, top audio buffer off immediately. */ if(stateflag) audio_write_nonblocking(); /* are we at or past time for this video frame? */ if(stateflag && videobuf_ready && videobuf_time<=get_time()){ video_write(); videobuf_ready=0; } if(stateflag && (audiobuf_ready || !vorbis_p) && (videobuf_ready || !theora_p) && !got_sigint){ /* we have an audio frame ready (which means the audio buffer is full), it's not time to play video, so wait until one of the audio buffer is ready or it's near time to play video */ /* set up select wait on the audiobuffer and a timeout for video */ struct timeval timeout; fd_set writefs; fd_set empty; int n=0; FD_ZERO(&writefs); FD_ZERO(&empty); if(audiofd>=0){ FD_SET(audiofd,&writefs); n=audiofd+1; } if(theora_p){ long milliseconds=(videobuf_time-get_time())*1000-5; if(milliseconds>500)milliseconds=500; if(milliseconds>0){ timeout.tv_sec=milliseconds/1000; timeout.tv_usec=(milliseconds%1000)*1000; n=select(n,&empty,&writefs,&empty,&timeout); if(n)audio_calibrate_timer(0); } }else{ select(n,&empty,&writefs,&empty,NULL); } } /* if our buffers either don't exist or are ready to go, we can begin playback */ if((!theora_p || videobuf_ready) && (!vorbis_p || audiobuf_ready))stateflag=1; /* same if we've run out of input */ if(feof(infile))stateflag=1; } /* tear it all down */ audio_close(); SDL_Quit(); if(vorbis_p){ ogg_stream_clear(&vo); vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); } if(theora_p){ ogg_stream_clear(&to); theora_clear(&td); theora_comment_clear(&tc); theora_info_clear(&ti); } ogg_sync_clear(&oy); if(infile && infile!=stdin)fclose(infile); fprintf(stderr, "\r " "\nDone.\n"); return(0); }
static int vorbis_read_header (SF_PRIVATE *psf, int log_data) { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; char *buffer ; int bytes ; int i, nn ; odata->eos = 0 ; /* Weird stuff happens if these aren't called. */ ogg_stream_reset (&odata->ostream) ; ogg_sync_reset (&odata->osync) ; /* ** Grab some data at the head of the stream. We want the first page ** (which is guaranteed to be small and only contain the Vorbis ** stream initial header) We need the first page to get the stream ** serialno. */ /* Expose the buffer */ buffer = ogg_sync_buffer (&odata->osync, 4096L) ; /* Grab the part of the header that has already been read. */ memcpy (buffer, psf->header, psf->headindex) ; bytes = psf->headindex ; /* Submit a 4k block to libvorbis' Ogg layer */ bytes += psf_fread (buffer + psf->headindex, 1, 4096 - psf->headindex, psf) ; ogg_sync_wrote (&odata->osync, bytes) ; /* Get the first page. */ if ((nn = ogg_sync_pageout (&odata->osync, &odata->opage)) != 1) { /* Have we simply run out of data? If so, we're done. */ if (bytes < 4096) return 0 ; /* Error case. Must not be Vorbis data */ psf_log_printf (psf, "Input does not appear to be an Ogg bitstream.\n") ; return SFE_MALFORMED_FILE ; } ; /* ** Get the serial number and set up the rest of decode. ** Serialno first ; use it to set up a logical stream. */ ogg_stream_clear (&odata->ostream) ; ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0) { /* Error ; stream version mismatch perhaps. */ psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ; return SFE_MALFORMED_FILE ; } ; if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1) { /* No page? must not be vorbis. */ psf_log_printf (psf, "Error reading initial header packet.\n") ; return SFE_MALFORMED_FILE ; } ; /* ** This function (vorbis_read_header) gets called multiple times, so the OGG ** and vorbis structs have to be cleared every time we pass through to ** prevent memory leaks. */ vorbis_block_clear (&vdata->vblock) ; vorbis_dsp_clear (&vdata->vdsp) ; vorbis_comment_clear (&vdata->vcomment) ; vorbis_info_clear (&vdata->vinfo) ; /* ** Extract the initial header from the first page and verify that the ** Ogg bitstream is in fact Vorbis data. ** ** I handle the initial header first instead of just having the code ** read all three Vorbis headers at once because reading the initial ** header is an easy way to identify a Vorbis bitstream and it's ** useful to see that functionality seperated out. */ vorbis_info_init (&vdata->vinfo) ; vorbis_comment_init (&vdata->vcomment) ; if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0) { /* Error case ; not a vorbis header. */ psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ; return SFE_MALFORMED_FILE ; } ; /* ** Common Ogg metadata fields? ** TITLE, VERSION, ALBUM, TRACKNUMBER, ARTIST, PERFORMER, COPYRIGHT, LICENSE, ** ORGANIZATION, DESCRIPTION, GENRE, DATE, LOCATION, CONTACT, ISRC, */ if (log_data) { int k ; for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++) { char *dd ; dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ; if (dd == NULL) continue ; psf_store_string (psf, vorbis_metatypes [k].id, dd) ; } ; } ; /* ** At this point, we're sure we're Vorbis. We've set up the logical (Ogg) ** bitstream decoder. Get the comment and codebook headers and set up the ** Vorbis decoder. ** ** The next two packets in order are the comment and codebook headers. ** They're likely large and may span multiple pages. Thus we reead ** and submit data until we get our two pacakets, watching that no ** pages are missing. If a page is missing, error out ; losing a ** header page is the only place where missing data is fatal. */ i = 0 ; /* Count of number of packets read */ while (i < 2) { int result = ogg_sync_pageout (&odata->osync, &odata->opage) ; if (result == 0) { /* Need more data */ buffer = ogg_sync_buffer (&odata->osync, 4096) ; bytes = psf_fread (buffer, 1, 4096, psf) ; if (bytes == 0 && i < 2) { psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ; return SFE_MALFORMED_FILE ; } ; nn = ogg_sync_wrote (&odata->osync, bytes) ; } else if (result == 1) { /* ** Don't complain about missing or corrupt data yet. We'll ** catch it at the packet output phase. ** ** We can ignore any errors here as they'll also become apparent ** at packetout. */ nn = ogg_stream_pagein (&odata->ostream, &odata->opage) ; while (i < 2) { result = ogg_stream_packetout (&odata->ostream, &odata->opacket) ; if (result == 0) break ; if (result < 0) { /* Uh oh ; data at some point was corrupted or missing! ** We can't tolerate that in a header. Die. */ psf_log_printf (psf, "Corrupt secondary header. Exiting.\n") ; return SFE_MALFORMED_FILE ; } ; vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ; i++ ; } ; } ; } ; if (log_data) { int printed_metadata_msg = 0 ; int k ; psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ; psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ; /* Throw the comments plus a few lines about the bitstream we're decoding. */ for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++) { char *dd ; dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ; if (dd == NULL) continue ; if (printed_metadata_msg == 0) { psf_log_printf (psf, "Metadata :\n") ; printed_metadata_msg = 1 ; } ; psf_store_string (psf, vorbis_metatypes [k].id, dd) ; psf_log_printf (psf, " %-10s : %s\n", vorbis_metatypes [k].name, dd) ; } ; psf_log_printf (psf, "End\n") ; } ; psf->sf.samplerate = vdata->vinfo.rate ; psf->sf.channels = vdata->vinfo.channels ; psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; /* OK, got and parsed all three headers. Initialize the Vorbis ** packet->PCM decoder. ** Central decode state. */ vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ; /* 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. */ vorbis_block_init (&vdata->vdsp, &vdata->vblock) ; vdata->loc = 0 ; return 0 ; } /* vorbis_read_header */
static stream_processor * find_stream_processor (stream_set *set, ogg_page *page) { uint32_t serial = ogg_page_serialno (page) ; int i, invalid = 0 ; stream_processor *stream ; for (i = 0 ; i < set->used ; i++) { if (serial == set->streams [i].serial) { /* We have a match! */ stream = & (set->streams [i]) ; set->in_headers = 0 ; /* if we have detected EOS, then this can't occur here. */ if (stream->end) { stream->isillegal = 1 ; return stream ; } stream->isnew = 0 ; stream->end = ogg_page_eos (page) ; stream->serial = serial ; return stream ; } ; } ; /* If there are streams open, and we've reached the end of the ** headers, then we can't be starting a new stream. ** XXX: might this sometimes catch ok streams if EOS flag is missing, ** but the stream is otherwise ok? */ if (streams_open (set) && !set->in_headers) invalid = 1 ; set->in_headers = 1 ; if (set->allocated < set->used) stream = &set->streams [set->used] ; else { set->allocated += 5 ; set->streams = realloc (set->streams, sizeof (stream_processor) * set->allocated) ; stream = &set->streams [set->used] ; } ; set->used++ ; stream->isnew = 1 ; stream->isillegal = invalid ; { int res ; ogg_packet packet ; /* We end up processing the header page twice, but that's ok. */ ogg_stream_init (&stream->ostream, serial) ; ogg_stream_pagein (&stream->ostream, page) ; res = ogg_stream_packetout (&stream->ostream, &packet) ; if (res <= 0) return NULL ; else if (packet.bytes >= 7 && memcmp (packet.packet, "\x01vorbis", 7) == 0) { stream->lastgranulepos = 0 ; vorbis_comment_init (&stream->vcomment) ; vorbis_info_init (&stream->vinfo) ; } ; res = ogg_stream_packetout (&stream->ostream, &packet) ; /* re-init, ready for processing */ ogg_stream_clear (&stream->ostream) ; ogg_stream_init (&stream->ostream, serial) ; } stream->end = ogg_page_eos (page) ; stream->serial = serial ; return stream ; } /* find_stream_processor */
static int _fetch_headers(OggVorbis_File *vf, vorbis_info *vi, vorbis_comment *vc, ogg_uint32_t *serialno, ogg_page *og_ptr){ ogg_page og={0,0,0,0}; ogg_packet op={0,0,0,0,0,0}; int i,ret; if(!og_ptr){ ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); if(llret==OV_EREAD)return(OV_EREAD); if(llret<0)return OV_ENOTVORBIS; og_ptr=&og; } ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr)); if(serialno)*serialno=vf->os->serialno; vf->ready_state=STREAMSET; /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ vorbis_info_init(vi); vorbis_comment_init(vc); i=0; while(i<3){ ogg_stream_pagein(vf->os,og_ptr); while(i<3){ int result=ogg_stream_packetout(vf->os,&op); if(result==0)break; if(result==-1){ ret=OV_EBADHEADER; goto bail_header; } if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ goto bail_header; } i++; } if(i<3) if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ ret=OV_EBADHEADER; goto bail_header; } } ogg_packet_release(&op); ogg_page_release(&og); return 0; bail_header: ogg_packet_release(&op); ogg_page_release(&og); vorbis_info_clear(vi); vorbis_comment_clear(vc); vf->ready_state=OPENED; return ret; }
qboolean OGV_StartRead(cinematic_t *cin) { int status; ogg_page og; ogg_packet op; int i; cin->data = Com_Allocate(sizeof(cin_ogv_t)); Com_Memset(cin->data, 0, sizeof(cin_ogv_t)); ogg_sync_init(&g_ogm->oy); /* Now we can read pages */ //FIXME? can serialno be 0 in ogg? (better way to check inited?) //TODO: support for more than one audio stream? / detect files with one stream(or without correct ones) while (!g_ogm->os_audio.serialno || !g_ogm->os_video.serialno) { if (ogg_sync_pageout(&g_ogm->oy, &og) == 1) { if (strstr((char *)(og.body + 1), "vorbis")) { //FIXME? better way to find audio stream if (g_ogm->os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one audio stream, in ogm-file(%s) ... we will stay at the first one\n", cin->name); } else { ogg_stream_init(&g_ogm->os_audio, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm->os_audio, &og); } } if (strstr((char *)(og.body + 1), "theora")) { if (g_ogm->os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", cin->name); } else { ogg_stream_init(&g_ogm->os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm->os_video, &og); } } } else if (OGV_LoadBlockToSync(cin)) { break; } } if (!g_ogm->os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a audio(vorbis) stream in ogm-file (%s)\n", cin->name); return qfalse; } if (!g_ogm->os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a video stream in ogm-file (%s)\n", cin->name); return qfalse; } //load vorbis header vorbis_info_init(&g_ogm->vi); vorbis_comment_init(&g_ogm->vc); i = 0; while (i < 3) { status = ogg_stream_packetout(&g_ogm->os_audio, &op); if (status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading vorbis-headers, ogm-file(%s)\n", cin->name); return qfalse; } if (status > 0) { status = vorbis_synthesis_headerin(&g_ogm->vi, &g_ogm->vc, &op); if (i == 0 && status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain Vorbis audio data, ogm-file(%s)\n", cin->name); return qfalse; } ++i; } else if (OGV_LoadPagesToStreams(cin)) { if (OGV_LoadBlockToSync(cin)) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all vorbis headers before end of ogm-file (%s)\n", cin->name); return qfalse; } } } vorbis_synthesis_init(&g_ogm->vd, &g_ogm->vi); // Do init { theora_info_init(&g_ogm->th_info); theora_comment_init(&g_ogm->th_comment); i = 0; while (i < 3) { status = ogg_stream_packetout(&g_ogm->os_video, &op); if (status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading theora-headers, ogm-file(%s)\n", cin->name); return qfalse; } else if (status > 0) { status = theora_decode_header(&g_ogm->th_info, &g_ogm->th_comment, &op); if (i == 0 && status != 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain theora data, ogm-file(%s)\n", cin->name); return qfalse; } ++i; } else if (OGV_LoadPagesToStreams(cin)) { if (OGV_LoadBlockToSync(cin)) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all theora headers before end of ogm-file (%s)\n", cin->name); return qfalse; } } } theora_decode_init(&g_ogm->th_state, &g_ogm->th_info); g_ogm->Vtime_unit = ((ogg_int64_t) g_ogm->th_info.fps_denominator * 1000 * 10000 / g_ogm->th_info.fps_numerator); } Com_DPrintf("Theora init done (%s)\n", cin->name); return qtrue; }
/* this is void and does not propogate errors up because we want to be able to open and use damaged bitstreams as well as we can. Just watch out for missing information for links in the OggVorbis_File struct */ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ ogg_page og={0,0,0,0}; int i; ogg_int64_t ret; vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); for(i=0;i<vf->links;i++){ if(i==0){ /* we already grabbed the initial header earlier. Just set the offset */ vf->dataoffsets[i]=dataoffset; _seek_helper(vf,dataoffset); }else{ /* seek to the location of the initial header */ _seek_helper(vf,vf->offsets[i]); if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){ vf->dataoffsets[i]=-1; }else{ vf->dataoffsets[i]=vf->offset; } } /* fetch beginning PCM offset */ if(vf->dataoffsets[i]!=-1){ ogg_int64_t accumulated=0,pos; long lastblock=-1; int result; ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); while(1){ ogg_packet op={0,0,0,0,0,0}; ret=_get_next_page(vf,&og,-1); if(ret<0) /* this should not be possible unless the file is truncated/mangled */ break; if(ogg_page_serialno(&og)!=vf->serialnos[i]) break; pos=ogg_page_granulepos(&og); /* count blocksizes of all frames in the page */ ogg_stream_pagein(vf->os,&og); while((result=ogg_stream_packetout(vf->os,&op))){ if(result>0){ /* ignore holes */ long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); if(lastblock!=-1) accumulated+=(lastblock+thisblock)>>2; lastblock=thisblock; } } ogg_packet_release(&op); if(pos!=-1){ /* pcm offset of last packet on the first audio page */ accumulated= pos-accumulated; break; } } /* less than zero? This is a stream with samples trimmed off the beginning, a normal occurrence; set the offset to zero */ if(accumulated<0)accumulated=0; vf->pcmlengths[i*2]=accumulated; }
bool OggSpeexDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; // Initialize Ogg data struct ogg_sync_init(&mOggSyncState); // Get the ogg buffer for writing char *data = ogg_sync_buffer(&mOggSyncState, READ_SIZE_BYTES); // Read bitstream from input file ssize_t bytesRead = GetInputSource()->Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” could not be read."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Read error"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("Unable to read from the input file."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Tell the sync layer how many bytes were written to its internal buffer int result = ogg_sync_wrote(&mOggSyncState, bytesRead); if(-1 == result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Turn the data we wrote into an ogg page result = ogg_sync_pageout(&mOggSyncState, &mOggPage); if(1 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Initialize the stream and grab the serial number ogg_stream_init(&mOggStreamState, ogg_page_serialno(&mOggPage)); // Get the first Ogg page result = ogg_stream_pagein(&mOggStreamState, &mOggPage); if(0 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Get the first packet (should be the header) from the page ogg_packet op; result = ogg_stream_packetout(&mOggStreamState, &op); if(1 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } if(op.bytes >= 5 && !memcmp(op.packet, "Speex", 5)) mSpeexSerialNumber = mOggStreamState.serialno; ++mOggPacketCount; // Convert the packet to the Speex header SpeexHeader *header = speex_packet_to_header((char *)op.packet, static_cast<int>(op.bytes)); if(NULL == header) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg Speex file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg Speex file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } else if(SPEEX_NB_MODES <= header->mode) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The Speex mode in the file “%@” is not supported."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file mode"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("This file may have been encoded with a newer version of Speex."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } const SpeexMode *mode = speex_lib_get_mode(header->mode); if(mode->bitstream_version != header->mode_bitstream_version) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The Speex version in the file “%@” is not supported."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file version"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("This file was encoded with a different version of Speex."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } // Initialize the decoder mSpeexDecoder = speex_decoder_init(mode); if(NULL== mSpeexDecoder) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, CFCopyLocalizedString(CFSTR("Unable to initialize the Speex decoder."), "")); // CFDictionarySetValue(errorDictionary, // kCFErrorLocalizedFailureReasonKey, // CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file version"), "")); // CFDictionarySetValue(errorDictionary, // kCFErrorLocalizedRecoverySuggestionKey, // CFCopyLocalizedString(CFSTR("This file was encoded with a different version of Speex."), "")); *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } speex_decoder_ctl(mSpeexDecoder, SPEEX_SET_SAMPLING_RATE, &header->rate); mSpeexFramesPerOggPacket = (0 == header->frames_per_packet ? 1 : header->frames_per_packet); mExtraSpeexHeaderCount = header->extra_headers; // Initialize the speex bit-packing data structure speex_bits_init(&mSpeexBits); // Initialize the stereo mode mSpeexStereoState = speex_stereo_state_init(); if(2 == header->nb_channels) { SpeexCallback callback; callback.callback_id = SPEEX_INBAND_STEREO; callback.func = speex_std_stereo_request_handler; callback.data = mSpeexStereoState; speex_decoder_ctl(mSpeexDecoder, SPEEX_SET_HANDLER, &callback); } // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mSampleRate = header->rate; mFormat.mChannelsPerFrame = header->nb_channels; mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Set up the source format mSourceFormat.mFormatID = 'SPEE'; mSourceFormat.mSampleRate = header->rate; mSourceFormat.mChannelsPerFrame = header->nb_channels; switch(header->nb_channels) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; } speex_header_free(header), header = NULL; // Allocate the buffer list spx_int32_t speexFrameSize = 0; speex_decoder_ctl(mSpeexDecoder, SPEEX_GET_FRAME_SIZE, &speexFrameSize); mBufferList = AllocateABL(mFormat, speexFrameSize); if(NULL == mBufferList) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); speex_header_free(header), header = NULL; speex_stereo_state_destroy(mSpeexStereoState), mSpeexStereoState = NULL; speex_decoder_destroy(mSpeexDecoder), mSpeexDecoder = NULL; speex_bits_destroy(&mSpeexBits); ogg_sync_destroy(&mOggSyncState); return false; } for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) mBufferList->mBuffers[i].mDataByteSize = 0; mIsOpen = true; return true; }
int input_calculate_ogg_sleep(ogg_page *page) { static ogg_stream_state os; ogg_packet op; static vorbis_info vi; static vorbis_comment vc; static int need_start_pos, need_headers, state_in_use = 0; static int serialno = 0; static uint64_t offset; static uint64_t first_granulepos; if (ogg_page_granulepos(page) == -1) { LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream."); return -1; } if (ogg_page_bos (page)) { control.oldsamples = 0; if (state_in_use) ogg_stream_clear (&os); ogg_stream_init (&os, ogg_page_serialno (page)); serialno = ogg_page_serialno (page); state_in_use = 1; vorbis_info_init (&vi); vorbis_comment_init (&vc); need_start_pos = 1; need_headers = 3; offset = (uint64_t)0; } if (need_start_pos) { int found_first_granulepos = 0; ogg_stream_pagein (&os, page); while (ogg_stream_packetout (&os, &op) == 1) { if (need_headers) { if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0) { LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis."); control.samplerate = 0; return -1; } need_headers--; control.samplerate = vi.rate; if (need_headers == 0) { vorbis_comment_clear (&vc); first_granulepos = (uint64_t)0; return 0; } continue; } /* headers have been read */ if (first_granulepos == 0 && op.granulepos > 0) { first_granulepos = op.granulepos; found_first_granulepos = 1; } offset += vorbis_packet_blocksize (&vi, &op) / 4; } if (!found_first_granulepos) return 0; need_start_pos = 0; control.oldsamples = first_granulepos - offset; vorbis_info_clear (&vi); ogg_stream_clear (&os); state_in_use = 0; } if (serialno != ogg_page_serialno (page)) { LOG_ERROR0 ("Found page which does not belong to current logical stream"); return -1; } control.samples = ogg_page_granulepos (page) - control.oldsamples; control.oldsamples = ogg_page_granulepos (page); control.senttime += ((uint64_t)control.samples * 1000000 / (uint64_t)control.samplerate); return 0; }
void VideoStreamPlaybackTheora::set_file(const String &p_file) { ERR_FAIL_COND(playing); ogg_packet op; th_setup_info *ts = NULL; file_name = p_file; if (file) { memdelete(file); } file = FileAccess::open(p_file, FileAccess::READ); ERR_FAIL_COND(!file); #ifdef THEORA_USE_THREAD_STREAMING thread_exit = false; thread_eof = false; //pre-fill buffer int to_read = ring_buffer.space_left(); int read = file->get_buffer(read_buffer.ptr(), to_read); ring_buffer.write(read_buffer.ptr(), read); thread = Thread::create(_streaming_thread, this); #endif ogg_sync_init(&oy); /* init supporting Vorbis structures needed in header parsing */ vorbis_info_init(&vi); vorbis_comment_init(&vc); /* init supporting Theora structures needed in header parsing */ th_comment_init(&tc); th_info_init(&ti); theora_eos = false; vorbis_eos = false; /* Ogg file open; parse the headers */ /* Only interested in Vorbis/Theora streams */ int stateflag = 0; int audio_track_skip = audio_track; while (!stateflag) { int ret = buffer_data(); if (ret == 0) break; while (ogg_sync_pageout(&oy, &og) > 0) { ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if (!ogg_page_bos(&og)) { /* don't leak the page; get it into the appropriate stream */ queue_page(&og); stateflag = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&og)); ogg_stream_pagein(&test, &og); ogg_stream_packetout(&test, &op); /* identify the codec: try theora */ if (!theora_p && th_decode_headerin(&ti, &tc, &ts, &op) >= 0) { /* it is theora */ copymem(&to, &test, sizeof(test)); theora_p = 1; } else if (!vorbis_p && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) { /* it is vorbis */ if (audio_track_skip) { vorbis_info_clear(&vi); vorbis_comment_clear(&vc); ogg_stream_clear(&test); vorbis_info_init(&vi); vorbis_comment_init(&vc); audio_track_skip--; } else { copymem(&vo, &test, sizeof(test)); vorbis_p = 1; } } else { /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-bos page parsing */ } /* we're expecting more header packets. */ while ((theora_p && theora_p < 3) || (vorbis_p && vorbis_p < 3)) { int ret; /* look for further theora headers */ while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) { if (ret < 0) { fprintf(stderr, "Error parsing Theora stream headers; " "corrupt stream?\n"); clear(); return; } if (!th_decode_headerin(&ti, &tc, &ts, &op)) { fprintf(stderr, "Error parsing Theora stream headers; " "corrupt stream?\n"); clear(); return; } theora_p++; } /* look for more vorbis header packets */ while (vorbis_p && (vorbis_p < 3) && (ret = ogg_stream_packetout(&vo, &op))) { if (ret < 0) { fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n"); clear(); return; } ret = vorbis_synthesis_headerin(&vi, &vc, &op); if (ret) { fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n"); clear(); return; } vorbis_p++; if (vorbis_p == 3) break; } /* The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec */ if (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og); /* demux into the appropriate stream */ } else { int ret = buffer_data(); /* someone needs more data */ if (ret == 0) { fprintf(stderr, "End of file while searching for codec headers.\n"); clear(); return; } } } /* and now we have it all. initialize decoders */ if (theora_p) { td = th_decode_alloc(&ti, ts); printf("Ogg logical stream %lx is Theora %dx%d %.02f fps", to.serialno, ti.pic_width, ti.pic_height, (double)ti.fps_numerator / ti.fps_denominator); px_fmt = ti.pixel_fmt; switch (ti.pixel_fmt) { case TH_PF_420: printf(" 4:2:0 video\n"); break; case TH_PF_422: printf(" 4:2:2 video\n"); break; case TH_PF_444: printf(" 4:4:4 video\n"); break; case TH_PF_RSVD: default: printf(" video\n (UNKNOWN Chroma sampling!)\n"); break; } if (ti.pic_width != ti.frame_width || ti.pic_height != ti.frame_height) printf(" Frame content is %dx%d with offset (%d,%d).\n", ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y); th_decode_ctl(td, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max, sizeof(pp_level_max)); pp_level = 0; th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level)); pp_inc = 0; int w; int h; w = (ti.pic_x + ti.frame_width + 1 & ~1) - (ti.pic_x & ~1); h = (ti.pic_y + ti.frame_height + 1 & ~1) - (ti.pic_y & ~1); size.x = w; size.y = h; texture->create(w, h, Image::FORMAT_RGBA8, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE); } else { /* tear down the partial theora setup */ th_info_clear(&ti); th_comment_clear(&tc); } th_setup_free(ts); if (vorbis_p) { vorbis_synthesis_init(&vd, &vi); vorbis_block_init(&vd, &vb); fprintf(stderr, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n", vo.serialno, vi.channels, vi.rate); //_setup(vi.channels, vi.rate); } else { /* tear down the partial vorbis setup */ vorbis_info_clear(&vi); vorbis_comment_clear(&vc); } playing = false; buffering = true; time = 0; audio_frames_wrote = 0; };
static int ogg_stream_classify (SF_PRIVATE *psf, OGG_PRIVATE* odata) { char *buffer ; int bytes, nn ; /* Call this here so it only gets called once, so no memory is leaked. */ ogg_sync_init (&odata->osync) ; odata->eos = 0 ; /* Weird stuff happens if these aren't called. */ ogg_stream_reset (&odata->ostream) ; ogg_sync_reset (&odata->osync) ; /* ** Grab some data at the head of the stream. We want the first page ** (which is guaranteed to be small and only contain the Vorbis ** stream initial header) We need the first page to get the stream ** serialno. */ /* Expose the buffer */ buffer = ogg_sync_buffer (&odata->osync, 4096L) ; /* Grab the part of the header that has already been read. */ memcpy (buffer, psf->header, psf->headindex) ; bytes = psf->headindex ; /* Submit a 4k block to libvorbis' Ogg layer */ bytes += psf_fread (buffer + psf->headindex, 1, 4096 - psf->headindex, psf) ; ogg_sync_wrote (&odata->osync, bytes) ; /* Get the first page. */ if ((nn = ogg_sync_pageout (&odata->osync, &odata->opage)) != 1) { /* Have we simply run out of data? If so, we're done. */ if (bytes < 4096) return 0 ; /* Error case. Must not be Vorbis data */ psf_log_printf (psf, "Input does not appear to be an Ogg bitstream.\n") ; return SFE_MALFORMED_FILE ; } ; /* ** Get the serial number and set up the rest of decode. ** Serialno first ; use it to set up a logical stream. */ ogg_stream_clear (&odata->ostream) ; ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0) { /* Error ; stream version mismatch perhaps. */ psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ; return SFE_MALFORMED_FILE ; } ; if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1) { /* No page? must not be vorbis. */ psf_log_printf (psf, "Error reading initial header packet.\n") ; return SFE_MALFORMED_FILE ; } ; odata->codec = ogg_page_classify (psf, &odata->opage) ; switch (odata->codec) { case OGG_VORBIS : psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; return 0 ; case OGG_FLAC : case OGG_FLAC0 : psf->sf.format = SF_FORMAT_OGGFLAC ; return 0 ; case OGG_SPEEX : psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ; return 0 ; case OGG_PCM : psf_log_printf (psf, "Detected Ogg/PCM data. This is not supported yet.\n") ; return SFE_UNIMPLEMENTED ; default : break ; } ; psf_log_printf (psf, "This Ogg bitstream contains some uknown data type.\n") ; return SFE_UNIMPLEMENTED ; } /* ogg_stream_classify */
FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data) { static const size_t OGG_BYTES_CHUNK = 8192; const size_t bytes_requested = *bytes; /* * The FLAC decoding API uses pull-based reads, whereas Ogg decoding * is push-based. In libFLAC, when you ask to decode a frame, the * decoder will eventually call the read callback to supply some data, * but how much it asks for depends on how much free space it has in * its internal buffer. It does not try to grow its internal buffer * to accomodate a whole frame because then the internal buffer size * could not be limited, which is necessary in embedded applications. * * Ogg however grows its internal buffer until a whole page is present; * only then can you get decoded data out. So we can't just ask for * the same number of bytes from Ogg, then pass what's decoded down to * libFLAC. If what libFLAC is asking for will not contain a whole * page, then we will get no data from ogg_sync_pageout(), and at the * same time cannot just read more data from the client for the purpose * of getting a whole decoded page because the decoded size might be * larger than libFLAC's internal buffer. * * Instead, whenever this read callback wrapper is called, we will * continually request data from the client until we have at least one * page, and manage pages internally so that we can send pieces of * pages down to libFLAC in such a way that we obey its size * requirement. To limit the amount of callbacks, we will always try * to read in enough pages to return the full number of bytes * requested. */ *bytes = 0; while (*bytes < bytes_requested && !aspect->end_of_stream) { if (aspect->have_working_page) { if (aspect->have_working_packet) { size_t n = bytes_requested - *bytes; if ((size_t)aspect->working_packet.bytes <= n) { /* the rest of the packet will fit in the buffer */ n = aspect->working_packet.bytes; memcpy(buffer, aspect->working_packet.packet, n); *bytes += n; buffer += n; aspect->have_working_packet = false; } else { /* only n bytes of the packet will fit in the buffer */ memcpy(buffer, aspect->working_packet.packet, n); *bytes += n; buffer += n; aspect->working_packet.packet += n; aspect->working_packet.bytes -= n; } } else { /* try and get another packet */ const int ret = ogg_stream_packetout(&aspect->stream_state, &aspect->working_packet); if (ret > 0) { aspect->have_working_packet = true; /* if it is the first header packet, check for magic and a supported Ogg FLAC mapping version */ if (aspect->working_packet.bytes > 0 && aspect->working_packet.packet[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE) { const FLAC__byte *b = aspect->working_packet.packet; const unsigned header_length = FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + FLAC__OGG_MAPPING_MAGIC_LENGTH + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH; if (aspect->working_packet.bytes < (long)header_length) return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH; if (memcmp(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH)) return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; b += FLAC__OGG_MAPPING_MAGIC_LENGTH; aspect->version_major = (unsigned)(*b); b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH; aspect->version_minor = (unsigned)(*b); if (aspect->version_major != 1) return FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION; aspect->working_packet.packet += header_length; aspect->working_packet.bytes -= header_length; } } else if (ret == 0) { aspect->have_working_page = false; } else { /* ret < 0 */ /* lost sync, we'll leave the working page for the next call */ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; } } } else { /* try and get another page */ const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page); if (ret > 0) { /* got a page, grab the serial number if necessary */ if(aspect->need_serial_number) { aspect->stream_state.serialno = aspect->serial_number = ogg_page_serialno(&aspect->working_page); aspect->need_serial_number = false; } if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { aspect->have_working_page = true; aspect->have_working_packet = false; } /* else do nothing, could be a page from another stream */ } else if (ret == 0) { /* need more data */ const size_t ogg_bytes_to_read = flac_max(bytes_requested - *bytes, OGG_BYTES_CHUNK); char *oggbuf = ogg_sync_buffer(&aspect->sync_state, ogg_bytes_to_read); if(0 == oggbuf) { return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; } else { size_t ogg_bytes_read = ogg_bytes_to_read; switch(read_callback(decoder, (FLAC__byte*)oggbuf, &ogg_bytes_read, client_data)) { case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: break; case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: aspect->end_of_stream = true; break; case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; default: FLAC__ASSERT(0); } if(ogg_sync_wrote(&aspect->sync_state, ogg_bytes_read) < 0) { /* double protection; this will happen if the read callback returns more bytes than the max requested, which would overflow Ogg's internal buffer */ FLAC__ASSERT(0); return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; } } } else { /* ret < 0 */ /* lost sync, bail out */ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; } } } if (aspect->end_of_stream && *bytes == 0) { return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; } return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; }
/* Core streaming function for this module * This is what actually produces the data which gets streamed. * * returns: >0 Number of bytes read * 0 Non-fatal error. * <0 Fatal error. */ static int playlist_read(void *self, ref_buffer *rb) { playlist_state_t *pl = (playlist_state_t *)self; int bytes; unsigned char *buf; char *newfn; int result; ogg_page og; if (pl->errors > 5) { LOG_WARN0("Too many consecutive errors - exiting"); return -1; } if (!pl->current_file || pl->nexttrack) { pl->nexttrack = 0; if (pl->current_file && strcmp (pl->filename, "-")) { fclose(pl->current_file); pl->current_file = NULL; } if (pl->file_ended) { pl->file_ended(pl->data, pl->filename); } newfn = pl->get_filename(pl->data); if (!newfn) { LOG_INFO0("No more filenames available, end of playlist"); return -1; /* No more files available */ } if (strcmp (newfn, "-")) { if (!pl->allow_repeat && pl->filename && !strcmp(pl->filename, newfn)) { LOG_ERROR0("Cannot play same file twice in a row, skipping"); pl->errors++; pl->free_filename (pl->data, newfn); return 0; } pl->free_filename(pl->data, pl->filename); pl->filename = newfn; pl->current_file = fopen(pl->filename, "rb"); if (!pl->current_file) { LOG_WARN2("Error opening file \"%s\": %s",pl->filename, strerror(errno)); pl->errors++; return 0; } LOG_INFO1("Currently playing \"%s\"", pl->filename); } else { LOG_INFO0("Currently playing from stdin"); pl->current_file = stdin; pl->free_filename(pl->data, pl->filename); pl->filename = newfn; } /* Reinit sync, so that dead data from previous file is discarded */ ogg_sync_clear(&pl->oy); ogg_sync_init(&pl->oy); } input_sleep (); while(1) { result = ogg_sync_pageout(&pl->oy, &og); if(result < 0) LOG_WARN1("Corrupt or missing data in file (%s)", pl->filename); else if(result > 0) { if (ogg_page_bos (&og)) { if (ogg_page_serialno (&og) == pl->current_serial) LOG_WARN1 ("detected duplicate serial number reading \"%s\"", pl->filename); pl->current_serial = ogg_page_serialno (&og); } if (input_calculate_ogg_sleep (&og) < 0) { pl->nexttrack = 1; return 0; } rb->len = og.header_len + og.body_len; rb->buf = malloc(rb->len); rb->aux_data = og.header_len; memcpy(rb->buf, og.header, og.header_len); memcpy(rb->buf+og.header_len, og.body, og.body_len); if(ogg_page_granulepos(&og)==0) rb->critical = 1; break; } /* If we got to here, we didn't have enough data. */ buf = ogg_sync_buffer(&pl->oy, BUFSIZE); bytes = fread(buf,1, BUFSIZE, pl->current_file); if (bytes <= 0) { if (feof(pl->current_file)) { pl->nexttrack = 1; return playlist_read(pl,rb); } else { LOG_ERROR2("Read error from \"%s\": %s", pl->filename, strerror(errno)); fclose(pl->current_file); pl->current_file=NULL; pl->errors++; return 0; } } else ogg_sync_wrote(&pl->oy, bytes); } pl->errors=0; return rb->len; }
static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2, logical_stream_t *p_stream, int64_t *pi_kframe, int64_t *pi_frame ) { int64_t i_result; int64_t i_granulepos; int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1; int64_t i_bytes_read; int64_t i_pages_checked = 0; int64_t i_packets_checked; demux_sys_t *p_sys = p_demux->p_sys; ogg_packet op; seek_byte( p_demux, i_pos1 ); if ( i_pos1 == p_stream->i_data_start ) { /* set a dummy granulepos at data_start */ *pi_kframe = p_stream->i_keyframe_offset; *pi_frame = p_stream->i_keyframe_offset; p_sys->b_page_waiting = true; return p_sys->i_input_position; } if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ; while ( 1 ) { if ( p_sys->i_input_position >= i_pos2 ) { /* we reached the end and found no pages */ *pi_frame=-1; return -1; } /* read next chunk */ if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) ) { /* EOF */ *pi_frame = -1; return -1; } i_bytes_to_read = OGGSEEK_BYTES_TO_READ; i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page ); if ( i_result < 0 ) { /* found a page, sync to page start */ p_sys->i_input_position -= i_result; i_pos1 = p_sys->i_input_position; continue; } if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 && ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) ) { i_pos1 = p_sys->i_input_position; break; } p_sys->i_input_position += i_bytes_read; }; seek_byte( p_demux, p_sys->i_input_position ); ogg_stream_reset( &p_stream->os ); while( 1 ) { if ( p_sys->i_input_position >= i_pos2 ) { /* reached the end of the search region and nothing was found */ *pi_frame = -1; return p_sys->i_input_position; } p_sys->b_page_waiting = false; if ( ! ( i_result = oggseek_read_page( p_demux ) ) ) { /* EOF */ *pi_frame = -1; return p_sys->i_input_position; } // found a page if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) ) { /* page is not for this stream */ p_sys->i_input_position += i_result; if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position; continue; } ogg_stream_pagein( &p_stream->os, &p_sys->current_page ); i_pages_checked++; i_packets_checked = 0; if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) { i_packets_checked++; } if ( i_packets_checked ) { i_granulepos = ogg_page_granulepos( &p_sys->current_page ); oggseek_theora_index_entry_add( p_stream, i_granulepos, i_pos1 ); *pi_kframe = i_granulepos >> p_stream->i_granule_shift; *pi_frame = *pi_kframe + i_granulepos - ( *pi_kframe << p_stream->i_granule_shift ); p_sys->b_page_waiting = true; return i_pos1; } /* -> start of next page */ p_sys->i_input_position += i_result; } }
int lame_decode_ogg_initfile( lame_global_flags* gfp, FILE* fd, mp3data_struct* mp3data ) { lame_internal_flags *gfc = gfp->internal_flags; char *buffer; int bytes; int i; /********** Decode setup ************/ ogg_sync_init(&oy); /* Now we can read pages */ /* grab some data at the head of the stream. We want the first page (which is guaranteed to be small and only contain the Vorbis stream initial header) We need the first page to get the stream serialno. */ /* submit a 4k block to libvorbis' Ogg layer */ buffer=ogg_sync_buffer(&oy,4096); bytes=fread(buffer,1,4096,fd); ogg_sync_wrote(&oy,bytes); /* Get the first page. */ if(ogg_sync_pageout(&oy,&og)!=1){ /* error case. Must not be Vorbis data */ ERRORF( gfc, "Error initializing Ogg bitstream data.\n" ); return -1; } /* Get the serial number and set up the rest of decode. */ /* serialno first; use it to set up a logical stream */ ogg_stream_init(&os,ogg_page_serialno(&og)); /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ /* I handle the initial header first instead of just having the code read all three Vorbis headers at once because reading the initial header is an easy way to identify a Vorbis bitstream and it's useful to see that functionality seperated out. */ vorbis_info_init(&vi); vorbis_comment_init(&vc); if(ogg_stream_pagein(&os,&og)<0){ /* error; stream version mismatch perhaps */ ERRORF( gfc, "Error reading first page of Ogg bitstream data.\n" ); return -1; } if(ogg_stream_packetout(&os,&op)!=1){ /* no page? must not be vorbis */ ERRORF( gfc, "Error reading initial header packet.\n" ); return -1; } if(vorbis_synthesis_headerin(&vi,&vc,&op)<0){ /* error case; not a vorbis header */ ERRORF( gfc, "This Ogg bitstream does not contain Vorbis " "audio data.\n"); return -1; } /* At this point, we're sure we're Vorbis. We've set up the logical (Ogg) bitstream decoder. Get the comment and codebook headers and set up the Vorbis decoder */ /* The next two packets in order are the comment and codebook headers. They're likely large and may span multiple pages. Thus we reead and submit data until we get our two pacakets, watching that no pages are missing. If a page is missing, error out; losing a header page is the only place where missing data is fatal. */ i=0; while(i<2){ while(i<2){ int result=ogg_sync_pageout(&oy,&og); if(result==0)break; /* Need more data */ /* Don't complain about missing or corrupt data yet. We'll catch it at the packet output phase */ if(result==1){ ogg_stream_pagein(&os,&og); /* we can ignore any errors here as they'll also become apparent at packetout */ while(i<2){ result=ogg_stream_packetout(&os,&op); if(result==0)break; if(result==-1){ /* Uh oh; data at some point was corrupted or missing! We can't tolerate that in a header. Die. */ ERRORF( gfc, "Corrupt secondary header. Exiting.\n" ); return -1; } vorbis_synthesis_headerin(&vi,&vc,&op); i++; } } } /* no harm in not checking before adding more */ buffer=ogg_sync_buffer(&oy,4096); bytes=fread(buffer,1,4096,fd); if(bytes==0 && i<2){ ERRORF( gfc, "End of file before finding all Vorbis headers!\n" ); return -1; } ogg_sync_wrote(&oy,bytes); } /* Throw the comments plus a few lines about the bitstream we're decoding */ { /* char **ptr=vc.user_comments; while(*ptr){ MSGF( gfc, "%s\n", *ptr ); ++ptr; } MSGF( gfc, "\nBitstream is %d channel, %ldHz\n", vi.channels, vi.rate ); MSGF( gfc, "Encoded by: %s\n\n", vc.vendor ); */ } /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ vorbis_synthesis_init(&vd,&vi); /* central decode state */ vorbis_block_init(&vd,&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 */ mp3data->stereo = vi.channels; mp3data->samplerate = vi.rate; mp3data->bitrate = 0; //ov_bitrate_instant(&vf); mp3data->nsamp=MAX_U_32_NUM; return 0; }
int OggInit(CFile *f, OggData *data) { ogg_packet packet; int num_vorbis; #ifdef USE_THEORA int num_theora; #endif int stream_start; int ret; unsigned magic; f->read(&magic, sizeof(magic)); if (SDL_SwapLE32(magic) != 0x5367674F) { // "OggS" in ASCII return -1; } f->seek(0, SEEK_SET); ogg_sync_init(&data->sync); vorbis_info_init(&data->vinfo); vorbis_comment_init(&data->vcomment); #ifdef USE_THEORA theora_info_init(&data->tinfo); theora_comment_init(&data->tcomment); #endif #ifdef USE_THEORA num_theora = 0; #endif num_vorbis = 0; stream_start = 0; while (!stream_start) { ogg_stream_state test; if (OggGetNextPage(&data->page, &data->sync, f)) { return -1; } if (!ogg_page_bos(&data->page)) { if (num_vorbis) { ogg_stream_pagein(&data->astream, &data->page); } #ifdef USE_THEORA if (num_theora) { ogg_stream_pagein(&data->vstream, &data->page); } #endif stream_start = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&data->page)); ogg_stream_pagein(&test, &data->page); // initial codec headers while (ogg_stream_packetout(&test, &packet) == 1) { #ifdef USE_THEORA if (theora_decode_header(&data->tinfo, &data->tcomment, &packet) >= 0) { memcpy(&data->vstream, &test, sizeof(test)); ++num_theora; } else #endif if (!vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) { memcpy(&data->astream, &test, sizeof(test)); ++num_vorbis; } else { ogg_stream_clear(&test); } } } data->audio = num_vorbis; #ifdef USE_THEORA data->video = num_theora; #endif // remainint codec headers while ((num_vorbis && num_vorbis < 3) #ifdef USE_THEORA || (num_theora && num_theora < 3) ) { // are we in the theora page ? while (num_theora && num_theora < 3 && (ret = ogg_stream_packetout(&data->vstream, &packet))) { if (ret < 0) { return -1; } if (theora_decode_header(&data->tinfo, &data->tcomment, &packet)) { return -1; } ++num_theora; } #else ) { #endif // are we in the vorbis page ? while (num_vorbis && num_vorbis < 3 && (ret = ogg_stream_packetout(&data->astream, &packet))) { if (ret < 0) { return -1; } if (vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) { return -1; } ++num_vorbis; } if (OggGetNextPage(&data->page, &data->sync, f)) { break; } if (num_vorbis) { ogg_stream_pagein(&data->astream, &data->page); } #ifdef USE_THEORA if (num_theora) { ogg_stream_pagein(&data->vstream, &data->page); } #endif } if (num_vorbis) { vorbis_synthesis_init(&data->vdsp, &data->vinfo); vorbis_block_init(&data->vdsp, &data->vblock); } else { vorbis_info_clear(&data->vinfo); vorbis_comment_clear(&data->vcomment); } #ifdef USE_THEORA if (num_theora) { theora_decode_init(&data->tstate, &data->tinfo); data->tstate.internal_encode = NULL; // needed for a bug in libtheora (fixed in next release) } else { theora_info_clear(&data->tinfo); theora_comment_clear(&data->tcomment); } return !(num_vorbis || num_theora); #else return !num_vorbis; #endif }
/* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits * pages to output pad. */ static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstOggParse *ogg; GstFlowReturn result = GST_FLOW_OK; gint ret = -1; guint32 serialno; GstBuffer *pagebuffer; GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer); ogg = GST_OGG_PARSE (parent); GST_LOG_OBJECT (ogg, "Chain function received buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (buffer)); gst_ogg_parse_submit_buffer (ogg, buffer); while (ret != 0 && result == GST_FLOW_OK) { ogg_page page; /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can * track how many bytes the ogg layer discarded (in the case of sync errors, * etc.); this allows us to accurately track the current stream offset */ ret = ogg_sync_pageseek (&ogg->sync, &page); if (ret == 0) { /* need more data, that's fine... */ break; } else if (ret < 0) { /* discontinuity; track how many bytes we skipped (-ret) */ ogg->offset -= ret; } else { gint64 granule = ogg_page_granulepos (&page); #ifndef GST_DISABLE_GST_DEBUG int bos = ogg_page_bos (&page); #endif guint64 startoffset = ogg->offset; GstOggStream *stream; gboolean keyframe; serialno = ogg_page_serialno (&page); stream = gst_ogg_parse_find_stream (ogg, serialno); GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT, GST_TIME_ARGS (buffertimestamp)); if (stream) { buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream, granule); if (ogg->video_stream) { if (stream == ogg->video_stream) { keyframe = gst_ogg_stream_granulepos_is_key_frame (stream, granule); } else { keyframe = FALSE; } } else { keyframe = TRUE; } } else { buffertimestamp = GST_CLOCK_TIME_NONE; keyframe = TRUE; } pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, buffertimestamp); /* We read out 'ret' bytes, so we set the next offset appropriately */ ogg->offset += ret; GST_LOG_OBJECT (ogg, "processing ogg page (serial %08x, pageno %ld, " "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ") keyframe=%d", serialno, ogg_page_pageno (&page), granule, bos, startoffset, ogg->offset, keyframe); if (ogg_page_bos (&page)) { /* If we've seen this serialno before, this is technically an error, * we log this case but accept it - this one replaces the previous * stream with this serialno. We can do this since we're streaming, and * not supporting seeking... */ GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); if (stream != NULL) { GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %08x " "at offset %" G_GINT64_FORMAT, serialno, ogg->offset); } if (ogg->last_page_not_bos) { GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new " "chain starting with serial %u", serialno); gst_ogg_parse_delete_all_streams (ogg); } stream = gst_ogg_parse_new_stream (ogg, &page); ogg->last_page_not_bos = FALSE; gst_buffer_ref (pagebuffer); stream->headers = g_list_append (stream->headers, pagebuffer); if (!ogg->in_headers) { GST_LOG_OBJECT (ogg, "Found start of new chain at offset %" G_GUINT64_FORMAT, startoffset); ogg->in_headers = 1; } /* For now, we just keep the header buffer in the stream->headers list; * it actually gets output once we've collected the entire set */ } else { /* Non-BOS page. Either: we're outside headers, and this isn't a * header (normal data), outside headers and this is (error!), inside * headers, this is (append header), or inside headers and this isn't * (we've found the end of headers; flush the lot!) * * Before that, we flag that the last page seen (this one) was not a * BOS page; that way we know that when we next see a BOS page it's a * new chain, and we can flush all existing streams. */ page_type type; GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); if (!stream) { GST_LOG_OBJECT (ogg, "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT, ogg->offset); goto failure; } ogg->last_page_not_bos = TRUE; type = gst_ogg_parse_is_header (ogg, stream, &page); if (type == PAGE_PENDING && ogg->in_headers) { gst_buffer_ref (pagebuffer); stream->unknown_pages = g_list_append (stream->unknown_pages, pagebuffer); } else if (type == PAGE_HEADER) { if (!ogg->in_headers) { GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside " "headers at offset %" G_GINT64_FORMAT, ogg->offset); goto failure; } else { /* Append the header to the buffer list, after any unknown previous * pages */ stream->headers = g_list_concat (stream->headers, stream->unknown_pages); g_list_free (stream->unknown_pages); gst_buffer_ref (pagebuffer); stream->headers = g_list_append (stream->headers, pagebuffer); } } else { /* PAGE_DATA, or PAGE_PENDING but outside headers */ if (ogg->in_headers) { /* First non-header page... set caps, flush headers. * * First up, we build a single GValue list of all the pagebuffers * we're using for the headers, in order. * Then we set this on the caps structure. Then we can start pushing * buffers for the headers, and finally we send this non-header * page. */ GstCaps *caps; GstStructure *structure; GValue array = { 0 }; gint count = 0; gboolean found_pending_headers = FALSE; GSList *l; g_value_init (&array, GST_TYPE_ARRAY); for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; if (g_list_length (stream->headers) == 0) { GST_LOG_OBJECT (ogg, "No primary header found for stream %08x", stream->serialno); goto failure; } gst_ogg_parse_append_header (&array, GST_BUFFER (stream->headers->data)); count++; } for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GList *j; /* already appended the first header, now do headers 2-N */ for (j = stream->headers->next; j != NULL; j = j->next) { gst_ogg_parse_append_header (&array, GST_BUFFER (j->data)); count++; } } caps = gst_pad_query_caps (ogg->srcpad, NULL); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); gst_structure_take_value (structure, "streamheader", &array); gst_pad_set_caps (ogg->srcpad, caps); if (ogg->caps) gst_caps_unref (ogg->caps); ogg->caps = caps; GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers " "(one per page)", count); /* Now, we do the same thing, but push buffers... */ for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GstBuffer *buf = GST_BUFFER (stream->headers->data); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GList *j; /* pushed the first one for each stream already, now do 2-N */ for (j = stream->headers->next; j != NULL; j = j->next) { GstBuffer *buf = GST_BUFFER (j->data); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } } ogg->in_headers = 0; /* And finally the pending data pages */ for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GList *k; if (stream->unknown_pages == NULL) continue; if (found_pending_headers) { GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at " "approximate offset %" G_GINT64_FORMAT, ogg->offset); } found_pending_headers = TRUE; GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers", g_list_length (stream->unknown_pages) + 1); for (k = stream->unknown_pages; k != NULL; k = k->next) { GstBuffer *buf = GST_BUFFER (k->data); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL); g_list_free (stream->unknown_pages); stream->unknown_pages = NULL; } } if (granule == -1) { stream->stored_buffers = g_list_append (stream->stored_buffers, pagebuffer); } else { while (stream->stored_buffers) { GstBuffer *buf = stream->stored_buffers->data; buf = gst_buffer_make_writable (buf); GST_BUFFER_TIMESTAMP (buf) = buffertimestamp; if (!keyframe) { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); } else { keyframe = FALSE; } result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; stream->stored_buffers = g_list_delete_link (stream->stored_buffers, stream->stored_buffers); } pagebuffer = gst_buffer_make_writable (pagebuffer); if (!keyframe) { GST_BUFFER_FLAG_SET (pagebuffer, GST_BUFFER_FLAG_DELTA_UNIT); } else { keyframe = FALSE; } result = gst_pad_push (ogg->srcpad, pagebuffer); if (result != GST_FLOW_OK) return result; } } } } } return result; failure: gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ()); return GST_FLOW_ERROR; }