/** central buffer management function * @param openmaxStandComp the component handle * @param inputbuffer contains the input ogg file content * @param outputbuffer is returned along with its output pcm file content that is produced as a result of this function execution */ void omx_vorbisdec_component_BufferMgmtCallbackVorbis(OMX_COMPONENTTYPE *openmaxStandComp, OMX_BUFFERHEADERTYPE* inputbuffer, OMX_BUFFERHEADERTYPE* outputbuffer) { omx_vorbisdec_component_PrivateType* omx_vorbisdec_component_Private = openmaxStandComp->pComponentPrivate; OMX_U8* outputCurrBuffer; OMX_U32 outputLength; OMX_S32 result; float **pcm; OMX_S32 samples; OMX_S32 i, j; OMX_S32 bout; OMX_S32 clipflag=0; int val; float *mono; int eos=0; char *vorbis_buffer; ogg_int16_t convbuffer[4096]; DEBUG(DEB_LEV_FULL_SEQ, "input buf %x filled len : %d \n", (int)inputbuffer->pBuffer, (int)inputbuffer->nFilledLen); /** Fill up the current input buffer when a new buffer has arrived */ if(omx_vorbisdec_component_Private->isNewBuffer) { omx_vorbisdec_component_Private->inputCurrBuffer = inputbuffer->pBuffer; omx_vorbisdec_component_Private->inputCurrLength = inputbuffer->nFilledLen; omx_vorbisdec_component_Private->positionInOutBuf = 0; DEBUG(DEB_LEV_SIMPLE_SEQ, "new -- input buf %x filled len : %d \n", (int)inputbuffer->pBuffer, (int)inputbuffer->nFilledLen); /** for each new input buffer --- copy buffer content into into ogg sync state structure data */ vorbis_buffer = ogg_sync_buffer(&omx_vorbisdec_component_Private->oy, inputbuffer->nAllocLen); memcpy(vorbis_buffer, inputbuffer->pBuffer, inputbuffer->nFilledLen); ogg_sync_wrote(&omx_vorbisdec_component_Private->oy, inputbuffer->nFilledLen); DEBUG(DEB_LEV_FULL_SEQ,"***** bytes read to buffer (of first header): %d \n",(int)inputbuffer->nFilledLen); } outputCurrBuffer = outputbuffer->pBuffer; outputLength = outputbuffer->nAllocLen; outputbuffer->nFilledLen = 0; outputbuffer->nOffset = 0; if(omx_vorbisdec_component_Private->packetNumber < 3) { omx_vorbisdec_component_Private->isNewBuffer = 0; if(omx_vorbisdec_component_Private->packetNumber == 0) { DEBUG(DEB_LEV_SIMPLE_SEQ, "in processing the first header buffer\n"); if(ogg_sync_pageout(&omx_vorbisdec_component_Private->oy, &omx_vorbisdec_component_Private->og) != 1) { DEBUG(DEB_LEV_ERR, "this input stream is not an Ogg stream\n"); return; } ogg_stream_init(&omx_vorbisdec_component_Private->os, ogg_page_serialno(&omx_vorbisdec_component_Private->og)); vorbis_info_init(&omx_vorbisdec_component_Private->vi); vorbis_comment_init(&omx_vorbisdec_component_Private->vc); if(ogg_stream_pagein(&omx_vorbisdec_component_Private->os, &omx_vorbisdec_component_Private->og) < 0) { DEBUG(DEB_LEV_ERR, "Error reading first page of Ogg bitstream data.\n"); return; } if(ogg_stream_packetout(&omx_vorbisdec_component_Private->os, &omx_vorbisdec_component_Private->op) != 1) { DEBUG(DEB_LEV_ERR, "Error reading initial header packet.\n"); return; } omx_vorbisdec_component_Private->packetNumber++; if(vorbis_synthesis_headerin(&omx_vorbisdec_component_Private->vi, &omx_vorbisdec_component_Private->vc, &omx_vorbisdec_component_Private->op) < 0) { DEBUG(DEB_LEV_ERR, "This Ogg bitstream does not contain Vorbis audio data\n"); return; } } while(omx_vorbisdec_component_Private->packetNumber < 3) { int result=ogg_sync_pageout(&omx_vorbisdec_component_Private->oy,&omx_vorbisdec_component_Private->og); if(result==0) { //break; /* Need more data */ omx_vorbisdec_component_Private->isNewBuffer = 1; inputbuffer->nFilledLen = 0; return; } /* Don't complain about missing or corrupt data yet. We'll catch it at the packet output phase */ if(result==1) { ogg_stream_pagein(&omx_vorbisdec_component_Private->os,&omx_vorbisdec_component_Private->og); /* we can ignore any errors here as they'll also become apparent at packetout */ while(omx_vorbisdec_component_Private->packetNumber < 3) { result=ogg_stream_packetout(&omx_vorbisdec_component_Private->os,&omx_vorbisdec_component_Private->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. */ DEBUG(DEB_LEV_ERR,"Corrupt secondary header. Exiting.\n"); break; }//end if omx_vorbisdec_component_Private->packetNumber++; vorbis_synthesis_headerin(&omx_vorbisdec_component_Private->vi,&omx_vorbisdec_component_Private->vc,&omx_vorbisdec_component_Private->op); }//end while }//end if }//end while omx_vorbisdec_component_Private->isNewBuffer = 1; inputbuffer->nFilledLen = 0; return; } /* A Vorbis logical bitstream begins with 3 headers. Once the last of these has been processed, * we can report the metadata and set up the output audio port appropriately. */ if(omx_vorbisdec_component_Private->packetNumber == 3) { /* Throw the comments plus a few lines about the bitstream we're decoding */ { // ptr should be declared earlier// char **ptr=omx_vorbisdec_component_Private->vc.user_comments; while(*ptr){ DEBUG(DEB_LEV_ERR,"%s\n",*ptr); ++ptr; } DEBUG(DEB_LEV_ERR,"Bitstream is %d channel, %ldHz\n",omx_vorbisdec_component_Private->vi.channels,omx_vorbisdec_component_Private->vi.rate); DEBUG(DEB_LEV_ERR,"Encoded by: %s\n\n",omx_vorbisdec_component_Private->vc.vendor); } /* Update pAudioVorbis settings */ omx_vorbisdec_component_Private->pAudioVorbis.nSampleRate = omx_vorbisdec_component_Private->vi.rate; omx_vorbisdec_component_Private->pAudioVorbis.nChannels = omx_vorbisdec_component_Private->vi.channels; /* Update audio port settings for this Vorbis bitstream */ if ((omx_vorbisdec_component_Private->pAudioPcmMode.nSamplingRate != omx_vorbisdec_component_Private->pAudioVorbis.nSampleRate) || (omx_vorbisdec_component_Private->pAudioPcmMode.nChannels != omx_vorbisdec_component_Private->pAudioVorbis.nChannels)) { omx_vorbisdec_component_Private->pAudioPcmMode.nSamplingRate = omx_vorbisdec_component_Private->pAudioVorbis.nSampleRate; omx_vorbisdec_component_Private->pAudioPcmMode.nChannels = omx_vorbisdec_component_Private->pAudioVorbis.nChannels; /*Send Port Settings changed call back*/ (*(omx_vorbisdec_component_Private->callbacks->EventHandler)) (openmaxStandComp, omx_vorbisdec_component_Private->callbackData, OMX_EventPortSettingsChanged, /* The command was completed */ 0, 1, /* This is the output port index */ NULL); } omx_vorbisdec_component_Private->convsize=inputbuffer->nFilledLen/omx_vorbisdec_component_Private->vi.channels; /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ vorbis_synthesis_init(&omx_vorbisdec_component_Private->vd,&omx_vorbisdec_component_Private->vi); /* central decode state */ vorbis_block_init(&omx_vorbisdec_component_Private->vd,&omx_vorbisdec_component_Private->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 */ } DEBUG(DEB_LEV_FULL_SEQ,"***** now the decoding will start *****\n"); if(omx_vorbisdec_component_Private->isNewBuffer) { omx_vorbisdec_component_Private->isNewBuffer=0; int result=ogg_sync_pageout(&omx_vorbisdec_component_Private->oy,&omx_vorbisdec_component_Private->og); DEBUG(DEB_LEV_FULL_SEQ," ---> page (read in decoding) - header len : %ld body len : %ld \n",omx_vorbisdec_component_Private->og.header_len,omx_vorbisdec_component_Private->og.body_len); if(result == 0) { omx_vorbisdec_component_Private->isNewBuffer = 1; inputbuffer->nFilledLen = 0; return; } if(result<0) { /* missing or corrupt data at this page position */ DEBUG(DEB_LEV_ERR,"Corrupt or missing data in bitstream; continuing...\n"); } else { ogg_stream_pagein(&omx_vorbisdec_component_Private->os,&omx_vorbisdec_component_Private->og); /* can safely ignore errors at */ } } result=ogg_stream_packetout(&omx_vorbisdec_component_Private->os,&omx_vorbisdec_component_Private->op); DEBUG(DEB_LEV_FULL_SEQ," packet length (read in decoding a particular page): %ld \n",omx_vorbisdec_component_Private->op.bytes); if(result == 0) { omx_vorbisdec_component_Private->isNewBuffer = 1; inputbuffer->nFilledLen = 0; return; } if(result<0) { /* missing or corrupt data at this page position */ /* no reason to complain; already complained above */ DEBUG(DEB_LEV_ERR,"Corrupt or missing data in bitstream; continuing...\n"); } else { /* we have a packet. Decode it */ omx_vorbisdec_component_Private->packetNumber++; if(vorbis_synthesis(&omx_vorbisdec_component_Private->vb,&omx_vorbisdec_component_Private->op)==0) /* test for success! */ vorbis_synthesis_blockin(&omx_vorbisdec_component_Private->vd,&omx_vorbisdec_component_Private->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(&omx_vorbisdec_component_Private->vd,&pcm))>0) { bout=(samples<omx_vorbisdec_component_Private->convsize?samples:omx_vorbisdec_component_Private->convsize); /* convert floats to 16 bit signed ints (host order) and interleave */ for(i=0;i<omx_vorbisdec_component_Private->vi.channels;i++) { ogg_int16_t *ptr=convbuffer+i; mono=pcm[i]; for(j=0;j<bout;j++) { #if 1 val=mono[j]*32767.f; #else /* optional dither */ 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+=omx_vorbisdec_component_Private->vi.channels; } } outputbuffer->nFilledLen=2*omx_vorbisdec_component_Private->vi.channels*bout; memcpy(outputCurrBuffer,(char *)convbuffer,outputbuffer->nFilledLen); if(clipflag) { DEBUG(DEB_LEV_FULL_SEQ,"Clipping in frame %ld\n",(long)(omx_vorbisdec_component_Private->vd.sequence)); } vorbis_synthesis_read(&omx_vorbisdec_component_Private->vd,bout); /* tell libvorbis how many samples we actually consumed */ } } if(ogg_page_eos(&omx_vorbisdec_component_Private->og)) { DEBUG(DEB_LEV_FULL_SEQ, "In %s EOS Detected\n",__func__); eos=1; } DEBUG(DEB_LEV_FULL_SEQ, "One output buffer %x len=%d is full returning\n", (int)outputbuffer->pBuffer, (int)outputbuffer->nFilledLen); }
void test_pack(const int *pl, const int **headers){ unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ long inptr=0; long outptr=0; long deptr=0; long depacket=0; long granule_pos=7,pageno=0; int i,j,packets,pageout=0; int eosflag=0; int bosflag=0; ogg_stream_reset(&os_en); ogg_stream_reset(&os_de); ogg_sync_reset(&oy); for(packets=0;;packets++)if(pl[packets]==-1)break; for(i=0;i<packets;i++){ /* construct a test packet */ ogg_packet op; int len=pl[i]; op.packet=data+inptr; op.bytes=len; op.e_o_s=(pl[i+1]<0?1:0); op.granulepos=granule_pos; granule_pos+=1024; for(j=0;j<len;j++)data[inptr++]=i+j; /* submit the test packet */ ogg_stream_packetin(&os_en,&op); /* retrieve any finished pages */ { ogg_page og; while(ogg_stream_pageout(&os_en,&og)){ /* We have a page. Check it carefully */ fprintf(stderr,"%ld, ",pageno); if(headers[pageno]==NULL){ fprintf(stderr,"coded too many pages!\n"); exit(1); } check_page(data+outptr,headers[pageno],&og); outptr+=og.body_len; pageno++; /* have a complete page; submit it to sync/decode */ { ogg_page og_de; ogg_packet op_de,op_de2; char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len); memcpy(buf,og.header,og.header_len); memcpy(buf+og.header_len,og.body,og.body_len); ogg_sync_wrote(&oy,og.header_len+og.body_len); while(ogg_sync_pageout(&oy,&og_de)>0){ /* got a page. Happy happy. Verify that it's good. */ check_page(data+deptr,headers[pageout],&og_de); deptr+=og_de.body_len; pageout++; /* submit it to deconstitution */ ogg_stream_pagein(&os_de,&og_de); /* packets out? */ while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ ogg_stream_packetpeek(&os_de,NULL); ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ /* verify peek and out match */ if(memcmp(&op_de,&op_de2,sizeof(ogg_packet))){ fprintf(stderr,"packetout != packetpeek! pos=%ld\n", depacket); exit(1); } /* verify the packet! */ /* check data */ if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", depacket); exit(1); } /* check bos flag */ if(bosflag==0 && op_de.b_o_s==0){ fprintf(stderr,"b_o_s flag not set on packet!\n"); exit(1); } if(bosflag && op_de.b_o_s){ fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); exit(1); } bosflag=1; depacket+=op_de.bytes; /* check eos flag */ if(eosflag){ fprintf(stderr,"Multiple decoded packets with eos flag!\n"); exit(1); } if(op_de.e_o_s)eosflag=1; /* check granulepos flag */ if(op_de.granulepos!=-1){ fprintf(stderr," granule:%ld ",(long)op_de.granulepos); } } } } } } } _ogg_free(data); if(headers[pageno]!=NULL){ fprintf(stderr,"did not write last page!\n"); exit(1); } if(headers[pageout]!=NULL){ fprintf(stderr,"did not decode last page!\n"); exit(1); } if(inptr!=outptr){ fprintf(stderr,"encoded page data incomplete!\n"); exit(1); } if(inptr!=deptr){ fprintf(stderr,"decoded page data incomplete!\n"); exit(1); } if(inptr!=depacket){ fprintf(stderr,"decoded packet data incomplete!\n"); exit(1); } if(!eosflag){ fprintf(stderr,"Never got a packet with EOS set!\n"); exit(1); } fprintf(stderr,"ok.\n"); }
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 = 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; }
/* Read the speex header. Return 0 on error. */ static int read_speex_header (struct spx_data *data) { int packet_count = 0; int stream_init = 0; char *buf; ssize_t nb_read; int header_packets = 2; while (packet_count < header_packets) { /* Get the ogg buffer for writing */ buf = ogg_sync_buffer (&data->oy, 200); /* Read bitstream from input file */ nb_read = io_read (data->stream, buf, 200); if (nb_read < 0) { decoder_error (&data->error, ERROR_FATAL, 0, "Can't open speex file: IO error: %s", io_strerror(data->stream)); return 0; } if (nb_read == 0) { decoder_error (&data->error, ERROR_FATAL, 0, "Can't open speex header"); return 0; } ogg_sync_wrote (&data->oy, nb_read); /* Loop for all complete pages we got (most likely only one) */ while (ogg_sync_pageout(&data->oy, &data->og) == 1) { if (stream_init == 0) { ogg_stream_init(&data->os, ogg_page_serialno(&data->og)); stream_init = 1; } /* Add page to the bitstream */ ogg_stream_pagein (&data->os, &data->og); /* Extract all available packets FIXME: EOS! */ while (ogg_stream_packetout(&data->os, &data->op) == 1) { /* If first packet, process as Speex header */ if (packet_count == 0) { data->st = process_header (data); if (!data->st) { ogg_stream_clear (&data->os); return 0; } data->rate = data->header->rate; data->nchannels = data->header->nb_channels; data->frames_per_packet = data->header->frames_per_packet; /*data->vbr = data->header->vbr; */ if (!data->frames_per_packet) data->frames_per_packet=1; data->output = xmalloc (data->frame_size * data->nchannels * data->frames_per_packet * sizeof(int16_t)); data->output_start = 0; data->output_left = 0; header_packets += data->header->extra_headers; } else if (packet_count == 1) { data->comment_packet_len = data->op.bytes; data->comment_packet = xmalloc ( sizeof(char) * data->comment_packet_len); memcpy (data->comment_packet, data->op.packet, data->comment_packet_len); } packet_count++; } } } return 1; }
static int spx_decode (void *prv_data, char *sound_buf, int nbytes, struct sound_params *sound_params) { struct spx_data *data = (struct spx_data *)prv_data; int bytes_requested = nbytes; int16_t *out = (int16_t *)sound_buf; sound_params->channels = data->nchannels; sound_params->rate = data->rate; sound_params->fmt = SFMT_S16 | SFMT_NE; while (nbytes) { int j; /* First see if there is anything left in the output buffer and * empty it out */ if (data->output_left > 0) { int to_copy = nbytes / sizeof(int16_t); to_copy = MIN(data->output_left, to_copy); memcpy (out, data->output + data->output_start, to_copy * sizeof(int16_t)); out += to_copy; data->output_start += to_copy; data->output_left -= to_copy; nbytes -= to_copy * sizeof(int16_t); } else if (ogg_stream_packetout (&data->os, &data->op) == 1) { int16_t *temp_output = data->output; /* Decode some more samples */ /* Copy Ogg packet to Speex bitstream */ speex_bits_read_from (&data->bits, (char*)data->op.packet, data->op.bytes); for (j = 0; j < data->frames_per_packet; j++) { /* Decode frame */ speex_decode_int (data->st, &data->bits, temp_output); if (data->nchannels == 2) speex_decode_stereo_int (temp_output, data->frame_size, &data->stereo); speex_decoder_ctl (data->st, SPEEX_GET_BITRATE, &data->bitrate); /*data->samples_decoded += data->frame_size;*/ temp_output += data->frame_size * data->nchannels; } /*logit ("Read %d bytes from page", data->frame_size * data->nchannels * data->frames_per_packet);*/ data->output_start = 0; data->output_left = data->frame_size * data->nchannels * data->frames_per_packet; } else if (ogg_sync_pageout(&data->oy, &data->og) == 1) { /* Read in another ogg page */ ogg_stream_pagein (&data->os, &data->og); debug ("Granulepos: %"PRId64, ogg_page_granulepos(&data->og)); } else if (!io_eof(data->stream)) { /* Finally, pull in some more data and try again on the next pass */ get_more_data (data); } else break; } return bytes_requested - nbytes; }
UInt32 SFB::Audio::OggSpeexDecoder::_ReadAudio(AudioBufferList *bufferList, UInt32 frameCount) { if(bufferList->mNumberBuffers != mFormat.mChannelsPerFrame) { LOGGER_WARNING("org.sbooth.AudioEngine.Decoder.OggSpeex", "_ReadAudio() called with invalid parameters"); 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 = (UInt32)(bufferList->mBuffers[0].mDataByteSize / sizeof(float)); UInt32 framesInBuffer = (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 = (float *)bufferList->mBuffers[i].mData; memcpy(floatBuffer + framesToSkip, mBufferList->mBuffers[i].mData, framesToCopy * sizeof(float)); bufferList->mBuffers[i].mDataByteSize += framesToCopy * sizeof(float); // Move remaining data in buffer to beginning if(framesToCopy != framesInBuffer) { floatBuffer = (float *)mBufferList->mBuffers[i].mData; memmove(floatBuffer, floatBuffer + framesToCopy, (framesInBuffer - framesToCopy) * sizeof(float)); } mBufferList->mBuffers[i].mDataByteSize -= 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.Decoder.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, (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.Decoder.OggSpeex", "Ogg Speex decoding error: possible corrupted stream"); break; } if(0 > speex_bits_remaining(&mSpeexBits)) { LOGGER_ERR("org.sbooth.AudioEngine.Decoder.OggSpeex", "Ogg Speex decoding overflow: possible corrupted stream"); break; } // Normalize the values float maxSampleValue = 1u << 15; vDSP_vsdiv(buffer, 1, &maxSampleValue, buffer, 1, (vDSP_Length)speexFrameSize); // Copy the frames from the decoding buffer to the output buffer, skipping over any frames already decoded framesInBuffer = mBufferList->mBuffers[0].mDataByteSize / sizeof(float); memcpy((float *)mBufferList->mBuffers[0].mData + framesInBuffer, buffer, (size_t)speexFrameSize * sizeof(float)); mBufferList->mBuffers[0].mDataByteSize += (size_t)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, (vDSP_Length)speexFrameSize); memcpy((float *)mBufferList->mBuffers[1].mData + framesInBuffer, buffer + speexFrameSize, (size_t)speexFrameSize * sizeof(float)); mBufferList->mBuffers[1].mDataByteSize += (size_t)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 = (ssize_t)GetInputSource().Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { LOGGER_ERR("org.sbooth.AudioEngine.Decoder.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.Decoder.OggSpeex", "Error reading Ogg page"); break; } } } } mCurrentFrame += framesRead; if(0 == framesRead && mSpeexEOSReached) mTotalFrames = mCurrentFrame; return framesRead; }
BOOL getogg_open(GETSND snd, UINT8 *ptr, UINT size) { __OV *ov; char *buffer; int bytes; int i; int result; snd->datptr = ptr; snd->datsize = size; ov = (__OV *)_MALLOC(sizeof(__OV), "__OV"); if (ov == NULL) { goto ovopn_err0; } ZeroMemory(ov, sizeof(__OV)); buffer = ogg_sync_buffer(&ov->oy, 4096); bytes = snd_read(snd, buffer, 4096); ogg_sync_wrote(&ov->oy, bytes); if (ogg_sync_pageout(&ov->oy, &ov->og) != 1) { TRACEOUT(("Input does not appear to be an Ogg bitstream.")); goto ovopn_err1; } ogg_stream_init(&ov->os, ogg_page_serialno(&ov->og)); vorbis_info_init(&ov->vi); vorbis_comment_init(&ov->vc); if (ogg_stream_pagein(&ov->os, &ov->og) < 0) { TRACEOUT(("Error reading first page of Ogg bitstream data.")); goto ovopn_err1; } if (ogg_stream_packetout(&ov->os, &ov->op) != 1) { TRACEOUT(("Error reading initial header packet.")); goto ovopn_err1; } if (vorbis_synthesis_headerin(&ov->vi, &ov->vc, &ov->op) < 0) { TRACEOUT(("This Ogg bitstream does not contain Vorbis audio data.")); goto ovopn_err1; } i = 0; while(i < 2) { while(i < 2) { result = ogg_sync_pageout(&ov->oy, &ov->og); if (result == 0) { break; } if (result == 1) { ogg_stream_pagein(&ov->os, &ov->og); while(i < 2) { result = ogg_stream_packetout(&ov->os, &ov->op); if (result == 0) { break; } if (result < 0) { TRACEOUT(("Corrupt secondary header. Exiting.")); goto ovopn_err1; } vorbis_synthesis_headerin(&ov->vi, &ov->vc, &ov->op); i++; } } } buffer = ogg_sync_buffer(&ov->oy, 4096); bytes = snd_read(snd, buffer, 4096); if ((bytes == 0) && (i < 2)) { TRACEOUT(("End of file before finding all Vorbis headers!")); return(-1); } ogg_sync_wrote(&ov->oy, bytes); } snd->snd = ov; snd->dec = (GSDEC)ogg_dec; snd->decend = ogg_decend; snd->samplingrate = ov->vi.rate; snd->channels = ov->vi.channels; snd->blocksize = 4096 * 2; snd->blocksamples = 4096 / ov->vi.channels; snd->bit = 16; vorbis_synthesis_init(&ov->vd, &ov->vi); vorbis_block_init(&ov->vd, &ov->vb); return(SUCCESS); ovopn_err1: ogg_sync_clear(&ov->oy); _MFREE(ov); ovopn_err0: return(FAILURE); }
int main(int argc, char *argv[]) { daala_packet dp; ogg_packet op; int long_option_index; int c; ogg_sync_state oy; ogg_page og; ogg_stream_state to; daala_info di; daala_comment dc; daala_setup_info *ds; daala_dec_ctx *dd; daala_image img; const char *optstring = "o:r"; struct option options [] = { { "output", required_argument, NULL, 'o' }, { "raw", no_argument, NULL, 'r' }, /*Disable YUV4MPEG2 headers:*/ { "version", no_argument, NULL, 0}, { NULL, 0, NULL, 0 } }; int frames = 0; int pix_fmt = 1; int daala_p = 0; int daala_processing_headers = 0; int stateflag = 0; /* single frame video buffering */ int videobuf_ready = 0; int raw = 0; FILE *outfile = NULL; ogg_int32_t pic_width = 0; ogg_int32_t pic_height = 0; ogg_int32_t fps_num = 0; ogg_int32_t fps_denom = 0; FILE *infile = stdin; outfile = stdout; dd = NULL; ds = NULL; daala_log_init(); #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on 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 /* Process option arguments. */ while ((c = getopt_long(argc, argv, optstring, options, &long_option_index)) != EOF) { switch (c) { case 'o': { if (strcmp(optarg, "-") != 0) { outfile = fopen(optarg, "wb"); if (outfile == NULL) { fprintf(stderr, "Unable to open output file '%s'\n", optarg); exit(1); } } else { outfile = stdout; } break; } case 'r': { raw = 1; break; } case 0: { if (strcmp(options[long_option_index].name, "version") == 0) { version(); } break; } default: usage(); break; } } if (optind < argc) { infile = fopen(argv[optind], "rb"); if (infile == NULL) { fprintf(stderr, "Unable to open '%s' for extraction.\n", argv[optind]); exit(1); } if (++optind < argc) { usage(); exit(1); } } /*Ok, Ogg parsing. The idea here is we have a bitstream that is made up of Ogg pages. The libogg sync layer will find them for us. There may be pages from several logical streams interleaved; we find the first daala stream and ignore any others. Then we pass the pages for our stream to the libogg stream layer which assembles our original set of packets out of them. start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Theora structures needed in header parsing */ daala_comment_init(&dc); daala_info_init(&di); /*Ogg file open; parse the headers. Theora (like Vorbis) depends on some initial header packets for decoder setup and initialization. We retrieve these first before entering the main decode loop.*/ /* Only interested in Daala streams */ while (!stateflag) { int ret = buffer_data(infile, &oy); if (ret == 0) break; while (ogg_sync_pageout(&oy, &og) > 0) { int got_packet; 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, &to, daala_p); stateflag = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&og)); ogg_stream_pagein(&test, &og); got_packet = ogg_stream_packetpeek(&test, &op); ogg_to_daala_packet(&dp, &op); /* identify the codec: try daala */ if ((got_packet == 1) && !daala_p && (daala_processing_headers = daala_decode_header_in(&di, &dc, &ds, &dp)) >= 0) { /* it is daala -- save this stream state */ memcpy(&to, &test, sizeof(test)); daala_p = 1; /*Advance past the successfully processed header.*/ if (daala_processing_headers) ogg_stream_packetout(&to, NULL); } 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 (daala_p && daala_processing_headers) { int ret; /* look for further daala headers */ while (daala_processing_headers && (ret = ogg_stream_packetpeek(&to, &op))) { if (ret < 0) continue; ogg_to_daala_packet(&dp, &op); daala_processing_headers = daala_decode_header_in(&di, &dc, &ds, &dp); if (daala_processing_headers < 0) { fprintf(stderr, "Error parsing Daala stream headers; " "corrupt stream?\n"); exit(1); } else if (daala_processing_headers >= 0) { /*Advance past the successfully processed header.*/ ogg_stream_packetout(&to, NULL); } daala_p++; } /*Stop now so we don't fail if there aren't enough pages in a short stream.*/ if (!(daala_p && daala_processing_headers)) 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, &to, daala_p); /* demux into the appropriate stream */ } else { if (buffer_data(infile, &oy) == 0) { /* someone needs more data */ fprintf(stderr, "End of file while searching for codec headers.\n"); exit(1); } } } /* and now we have it all. initialize decoders */ if (daala_p) { dump_comments(&dc); dd = daala_decode_create(&di, ds); fprintf(stderr, "Ogg logical stream %lx is Daala %dx%d %.02f fps video\n", to.serialno, di.pic_width, di.pic_height, di.timebase_numerator/(double)di.timebase_denominator*di.frame_duration); } else { /* tear down the partial daala setup */ daala_info_clear(&di); daala_comment_clear(&dc); } /*Either way, we're done with the codec setup data.*/ daala_setup_free(ds); if (!raw && outfile) { static const char *CHROMA_TYPES[5] = { "420jpeg", NULL, "422jpeg", "444", "mono" }; pic_width = di.pic_width; pic_height = di.pic_height; fps_num = di.timebase_numerator; fps_denom = di.timebase_denominator*di.frame_duration; if (di.nplanes > 1) { /*calculate pixel_fmt based on the xdec & ydec values from one of the chroma planes.*/ if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 1) { pix_fmt = 0; } else if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 0) { pix_fmt = 2; } else if (di.plane_info[1].xdec == 0 && di.plane_info[1].ydec == 0) { pix_fmt = 3; } } else { pix_fmt = 4; } if (pix_fmt >= 5 || pix_fmt == 1) { fprintf(stderr, "Unknown pixel format: %i\n", pix_fmt); exit(1); } /*Store header information*/ fprintf(outfile, "YUV4MPEG2 W%d H%d F%d:%d Ip A%d:%d C%s\n", pic_width, pic_height, fps_num, fps_denom, di.pixel_aspect_numerator, di.pixel_aspect_denominator, CHROMA_TYPES[pix_fmt]); } /* install signal handler */ signal(SIGINT, sigint_handler); /*Finally the main decode loop. It's one Daala packet per frame, so this is pretty straightforward if we're not trying to maintain sync with other multiplexed streams. The videobuf_ready flag is used to maintain the input buffer in the libogg stream state. If there's no output frame available at the end of the decode step, we must need more input data. We could simplify this by just using the return code on ogg_page_packetout(), but the flag system extends easily to the case where you care about more than one multiplexed stream (like with audio playback). In that case, just maintain a flag for each decoder you care about, and pull data when any one of them stalls.*/ stateflag = 0; /* playback has not begun */ /* queue any remaining pages from data we buffered but that did not contain headers */ while (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og, &to, daala_p); } while (!got_sigint) { while (daala_p && !videobuf_ready) { if (ogg_stream_packetout(&to, &op) > 0) { ogg_to_daala_packet(&dp, &op); if (daala_decode_packet_in(dd, &dp) >= 0) { videobuf_ready = 1; frames++; } } else break; } if (!videobuf_ready && feof(infile)) break; if (!videobuf_ready) { /* no data yet for somebody. Grab another page */ buffer_data(infile, &oy); while (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og, &to, daala_p); } } /* dumpvideo frame, and get new one */ else if (outfile && daala_decode_img_out(dd, &img)) { video_write(outfile, &img, raw); } videobuf_ready = 0; } /*Flush the output frame buffer of decoder.*/ while (!got_sigint && daala_decode_img_out(dd, &img)) { video_write(outfile, &img, raw); } /* end of decoder loop -- close everything */ if (daala_p) { ogg_stream_clear(&to); daala_decode_free(dd); daala_comment_clear(&dc); daala_info_clear(&di); } ogg_sync_clear(&oy); if (infile && infile != stdin) fclose(infile); if (outfile && outfile != stdout) fclose(outfile); fprintf(stderr, "\n\n%d frames\n", frames); fprintf(stderr, "\nDone.\n"); return 0; }
static int play_vorbis(lua_State *lstate) { char buf[BLOCKSIZE]; char *pt, *oggbuf, **comm, *mark; int n, sock, sr_err, port; const char *host, *mount; VORBIS_FEED *feed; lua_pushstring(lstate, "host"); lua_gettable(lstate, -2); lua_pushstring(lstate, "port"); lua_gettable(lstate, -3); lua_pushstring(lstate, "mount"); lua_gettable(lstate, -4); mount = lua_tostring(lstate, -1); port = lua_tointeger(lstate, -2); host = lua_tostring(lstate, -3); sock = stream_connect(host, port, mount, buf, &mark); lua_pop(lstate, 3); if (sock == 0) { lua_pop(lstate, 1); return 0; } lua_pushstring(lstate, "intern"); lua_gettable(lstate, -2); feed = (VORBIS_FEED *)lua_touserdata(lstate, -1); lua_pop(lstate, 1); feed->base.sock = sock; pthread_mutex_init(&(feed->base.thread_lock), NULL); pthread_cond_init(&(feed->base.data_ready), NULL); ogg_sync_init(&(feed->oy)); oggbuf = ogg_sync_buffer(&(feed->oy), BLOCKSIZE); n = BLOCKSIZE - (mark - buf); memcpy(oggbuf, mark, n); read_sock(feed->base.sock, oggbuf + n, BLOCKSIZE - n); ogg_sync_wrote(&(feed->oy), BLOCKSIZE); if ((n = ogg_sync_pageout(&(feed->oy), &(feed->og))) != 1) { logmsg("out of data: %d\n", n); free_vorbis((FEED *)feed); lua_pop(lstate, 1); return 0; } ogg_stream_init(&(feed->os), ogg_page_serialno(&(feed->og))); vorbis_info_init(&(feed->vi)); vorbis_comment_init(&(feed->vc)); if (ogg_stream_pagein(&(feed->os), &(feed->og)) < 1) { logmsg("error reading first ogg page\n"); //free_feed(feed); //return 0; } if (ogg_stream_packetout(&(feed->os), &(feed->op)) != 1) { logmsg("error reading first header packet\n"); free_vorbis((FEED *)feed); lua_pop(lstate, 1); return 0; } if (vorbis_synthesis_headerin(&(feed->vi), &(feed->vc), &(feed->op)) < 0) { logmsg("stream is not vorbis\n"); free_vorbis((FEED *)feed); lua_pop(lstate, 1); return 0; } vorbis_headers(feed); add_table(lstate, "info"); add_table(lstate, "comments"); comm = feed->vc.user_comments; while (*comm) { if ((pt = index(*comm, '=')) != NULL) { *pt++ = '\0'; set_string(lstate, *comm, pt); } ++comm; } lua_pop(lstate, 1); // comments feed->base.channels = feed->vi.channels; set_integer(lstate, "channels", feed->base.channels); set_integer(lstate, "srate", feed->vi.rate); lua_pop(lstate, 1); // info feed->base.cbuf = new_ringbuf(feed->vi.rate, feed->base.channels, BUFSECS, 0.333, 0.667); if (jack_sr != feed->vi.rate) { feed->base.converter = src_new(SRC_SINC_MEDIUM_QUALITY, feed->base.channels, &sr_err); feed->base.src_data_in = (float *)malloc(SRC_DATA_FRAMES * feed->base.channels * sizeof(float)); feed->base.src_data.data_in = feed->base.src_data_in; feed->base.src_data_remain = 0; feed->base.src_data.src_ratio = jack_sr / (double)feed->vi.rate; feed->base.src_data_out = (float *)malloc( (int)ceil(SRC_DATA_FRAMES * feed->base.channels * sizeof(float) * feed->base.src_data.src_ratio)); } feed->base.init = 1; lua_pop(lstate, 1); pthread_create(&(feed->base.thread_id), NULL, vorbis_thread, feed); return 0; }
static int ReadHeader(OGG_context *ctx, SYS_FILEHANDLE file) { const size_t size = 4096; int theError; void *buffer; int i, serialNo; size_t bytes = 0; ogg_sync_init(&ctx->oy); /* Now we can read pages */ /* submit a 4k block to libvorbis' Ogg layer */ if (!ctx->eos) { buffer = ogg_sync_buffer(&ctx->oy, (int32_t)size); bytes = FIO_cur->fread(buffer, 1 , size, file); ogg_sync_wrote(&ctx->oy, (int32_t)size); } /* Get the first page. */ theError = ogg_sync_pageout(&ctx->oy, &ctx->og); SYS_ASSERT(theError == 1); if (theError!=1) { /* error case. Must not be Vorbis data */ SYS_ASSERT(bytes<size); return 0; } /* Get the serial number and set up the rest of decode. */ /* serialno first; use it to set up a logical stream */ serialNo = ogg_page_serialno(&ctx->og); theError = ogg_stream_init(&ctx->os, serialNo); SYS_ASSERT(theError==0); /* 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. */ theError = ogg_stream_pagein(&ctx->os, &ctx->og); SYS_ASSERT(theError>=0); if (theError < 0) { /* error; stream version mismatch perhaps */ return 0; } theError = ogg_stream_packetout(&ctx->os, &ctx->op); SYS_ASSERT(theError == 1); if (theError!=1) { /* no page? must not be vorbis */ return 0; } theError = vorbis_synthesis_headerin(&ctx->vi, &ctx->vc, &ctx->op); SYS_ASSERT(theError >=0); if (theError < 0) { return 0; } /* 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(&ctx->oy, &ctx->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(&ctx->os, &ctx->og); /* we can ignore any errors here as they'll also become apparent at packetout */ while(i<2) { result = ogg_stream_packetout(&ctx->os, &ctx->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. */ SYS_ASSERT(0); return 0; } vorbis_synthesis_headerin(&ctx->vi, &ctx->vc, &ctx->op); i++; } } } buffer = ogg_sync_buffer(&ctx->oy, (int32_t)size); bytes = FIO_cur->fread(buffer, 1 , size, file); SYS_ASSERT(!(bytes==0 && i<2)); ogg_sync_wrote(&ctx->oy,(int32_t) size); } /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ vorbis_synthesis_init(&ctx->vd, &ctx->vi); /* central decode state */ vorbis_block_init(&ctx->vd, &ctx->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 */ ctx->input_size = size; ctx->state = 0; return 1; }
/* this is called for each file to process */ enum codec_status codec_run(void) { int error = CODEC_ERROR; long action; intptr_t param; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int64_t page_granule = 0; int stream_init = -1; int sample_rate = 48000; OpusDecoder *st = NULL; OpusHeader header; int ret; unsigned long strtoffset; int skip = 0; int64_t seek_target; uint64_t granule_pos; ogg_malloc_init(); action = CODEC_ACTION_NULL; param = ci->id3->elapsed; strtoffset = ci->id3->offset; #if defined(CPU_COLDFIRE) /* EMAC rounding is disabled because of MULT16_32_Q15, which will be inaccurate with rounding in its current incarnation */ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); #endif /* pre-init the ogg_sync_state buffer, so it won't need many reallocs */ ogg_sync_init(&oy); oy.storage = 64*1024; oy.data = _ogg_malloc(oy.storage); /* allocate output buffer */ uint16_t *output = (uint16_t*) _ogg_malloc(MAX_FRAME_SIZE*sizeof(uint16_t)); ci->seek_buffer(0); ci->set_elapsed(0); goto next_page; /* need to decode header before we do anything else */ while (1) { action = ci->get_command(¶m); process_action: if (action == CODEC_ACTION_SEEK_TIME) { if (st != NULL) { /* calculate granule to seek to (including seek rewind) */ seek_target = (48LL * param) + header.preskip; skip = MIN(seek_target, SEEK_REWIND); seek_target -= skip; LOGF("Opus seek page:%lld,%lld,%ld\n", seek_target, page_granule, (long)param); opus_seek_page_granule(seek_target, page_granule, &oy, &os); } ci->set_elapsed(param); ci->seek_complete(); param = 0; } else if (action == CODEC_ACTION_HALT) break; next_page: /*Get the ogg buffer for writing*/ if (get_more_data(&oy) < 1) { goto done; } /* Loop for all complete pages we got (most likely only one) */ while (ogg_sync_pageout(&oy, &og) == 1) { if (stream_init != 0) { stream_init = ogg_stream_init(&os, ogg_page_serialno(&og)); if (stream_init != 0) { LOGF("Stream init failed"); goto done; } } /* Add page to the bitstream */ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); granule_pos = page_granule; while ((ogg_stream_packetout(&os, &op) == 1) && !op.e_o_s) { if (op.packetno == 0){ /* identification header */ if (opus_header_parse(op.packet, op.bytes, &header) == 0) { LOGF("Could not parse header"); goto done; } skip = header.preskip; st = opus_decoder_create(sample_rate, header.channels, &ret); if (ret != OPUS_OK) { LOGF("opus_decoder_create failed %d", ret); goto done; } LOGF("Decoder inited"); codec_set_replaygain(ci->id3); opus_decoder_ctl(st, OPUS_SET_GAIN(header.gain)); ci->configure(DSP_SET_FREQUENCY, sample_rate); ci->configure(DSP_SET_SAMPLE_DEPTH, 16); ci->configure(DSP_SET_STEREO_MODE, (header.channels == 2) ? STEREO_INTERLEAVED : STEREO_MONO); if (strtoffset) seek_ogg_page(strtoffset); else if (param) { action = CODEC_ACTION_SEEK_TIME; goto process_action; } else { /* seek buffer directly to the first audio packet to avoid allocating space for huge comment packets (embedded Album Art) */ if (seek_opus_tags()) seek_ogg_page(ci->curpos); else { LOGF("Could not find comment header"); goto done; } } LOGF("sync reset"); ogg_sync_reset(&oy); /* next page */ break; } else { /* report progress */ ci->set_offset((size_t) ci->curpos); ci->set_elapsed((granule_pos - header.preskip) / 48); /* Decode audio packets */ ret = opus_decode(st, op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); if (ret > skip) { /* part of or entire output buffer is played */ ret -= skip; ci->pcmbuf_insert(&output[skip * header.channels], NULL, ret); skip = 0; } else { if (ret < 0) { LOGF("opus_decode failed %d", ret); goto done; } else if (ret == 0) break; else { /* entire output buffer is skipped */ skip -= ret; ret = 0; } } } } } } LOGF("Returned OK"); error = CODEC_OK; done: ogg_malloc_destroy(); return error; }
// Decode static int Decode(void *context, const void *dataRAW, size_t sizeRAW, void **ppdataPCM, size_t *sizePCM) { OGG_context *ctx = (OGG_context*)context; *sizePCM = 0; *ppdataPCM = 0; if (!dataRAW) return 0; switch(ctx->state) { // Sync buffer case 0xff: { sysMemCpy(ogg_sync_buffer(&ctx->oy, (int32_t)sizeRAW), dataRAW, sizeRAW); ctx->input_size = sizeRAW; ogg_sync_wrote(&ctx->oy, (int32_t)ctx->input_size); ctx->state = 0; } break; // Sync page out case 0: { int result = ogg_sync_pageout(&ctx->oy, &ctx->og); if (result == 0) { ctx->state = 0xff; } else if (result < 0) { ctx->state = -1; } else { ogg_stream_pagein(&ctx->os, &ctx->og); /* can safely ignore errors at this point */ ctx->state = 1; } } break; // Stream packet out case 1: { int result = ogg_stream_packetout(&ctx->os, &ctx->op); if (result == 0) { ctx->state = 0; } else if (result < 0) { ctx->state = -2; } else { /* we have a packet. Decode it */ if (vorbis_synthesis(&ctx->vb, &ctx->op)==0) /* test for success! */ vorbis_synthesis_blockin(&ctx->vd, &ctx->vb); ctx->state = 3; } } break; // synthetis pcm out case 3: { size_t samples; float **pcm; samples = vorbis_synthesis_pcmout(&ctx->vd, &pcm); if (samples<=0) { ctx->state = 1; } else { size_t convsize = ctx->input_size / ctx->vi.channels; size_t bytesCount = samples < convsize ? samples : convsize; SYS_ASSERT(bytesCount < OGG_BUFFER_SIZE); #ifdef SUPPORT_IEEE_AUDIO DeinterleaveAudio(ctx->uncompressed, pcm, ctx->vi.channels, bytesCount); *sizePCM = 4 * ctx->vi.channels * bytesCount; #else ConvertFloatToInteger(ctx->uncompressed, pcm, ctx->vi.channels, bytesCount); *sizePCM = 2 * ctx->vi.channels * bytesCount; #endif /* tell libvorbis how many samples we actually consumed */ vorbis_synthesis_read(&ctx->vd, (int32_t)bytesCount); *ppdataPCM = ctx->uncompressed; } } break; // Invalid state default: SYS_ASSERT(0); return -1; break; } return ctx->state != 0xff ? 1 : 0; }
static gboolean xmms_speex_init (xmms_xform_t *xform) { gint pe; xmms_config_property_t *val; xmms_speex_data_t *data; xmms_error_t error; g_return_val_if_fail (xform, FALSE); data = g_new0 (xmms_speex_data_t, 1); g_return_val_if_fail (data, FALSE); xmms_xform_private_data_set (xform, data); ogg_sync_init (&data->sync_state); speex_bits_init (&data->speex_bits); /* Find the speex header */ while (42) { gint ret; data->ogg_data = ogg_sync_buffer (&data->sync_state, 1024); ret = xmms_xform_read (xform, data->ogg_data, 1024, &error); ogg_sync_wrote (&data->sync_state, ret); if (ret <= 0) { return FALSE; } if (ogg_sync_pageout (&data->sync_state, &data->ogg_page) == 1) { break; } } ogg_stream_init (&data->stream_state, ogg_page_serialno (&data->ogg_page)); if (ogg_stream_pagein (&data->stream_state, &data->ogg_page) < 0) { return FALSE; } if (ogg_stream_packetout (&data->stream_state, &data->ogg_packet) != 1) { return FALSE; } data->speexheader = speex_packet_to_header ((char *)data->ogg_packet.packet, data->ogg_packet.bytes); data->speex_state = speex_decoder_init (speex_mode_list[data->speexheader->mode]); val = xmms_xform_config_lookup (xform, "perceptual_enhancer"); pe = xmms_config_property_get_int (val); speex_decoder_ctl (data->speex_state, SPEEX_SET_ENH, &pe); ogg_sync_pageout (&data->sync_state, &data->ogg_page); ogg_stream_pagein (&data->stream_state, &data->ogg_page); ogg_stream_packetout (&data->stream_state, &data->ogg_packet); data->samples_buf = g_new (gint16, data->speexheader->frames_per_packet * data->speexheader->frame_size * data->speexheader->nb_channels); data->samples_start = data->samples_buf; data->samples_count = 0; xmms_speex_read_metadata (xform, data); xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/pcm", XMMS_STREAM_TYPE_FMT_FORMAT, XMMS_SAMPLE_FORMAT_S16, XMMS_STREAM_TYPE_FMT_CHANNELS, data->speexheader->nb_channels, XMMS_STREAM_TYPE_FMT_SAMPLERATE, data->speexheader->rate, XMMS_STREAM_TYPE_END); return TRUE; }
static gint xmms_speex_read (xmms_xform_t *xform, gpointer buf, gint len, xmms_error_t *err) { gint ret = 0, n; gfloat outfloat [2000]; gint16 *outbuf = (gint16 *) buf; xmms_speex_data_t *data; xmms_error_t error; SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT; g_return_val_if_fail (xform, -1); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); /* convert from bytes to samples */ len /= 2; /* first, copy already decoded samples over if we have any. */ if (data->samples_count) { n = MIN (data->samples_count, len); memcpy (outbuf, data->samples_start, n * 2); data->samples_count -= n; if (!data->samples_count) { data->samples_start = data->samples_buf; } else { data->samples_start += n; } /* convert from samples to bytes */ return n * 2; } while (42) { gint samples_per_frame; samples_per_frame = data->speexheader->frame_size * data->speexheader->nb_channels; while (ogg_stream_packetout (&data->stream_state, &data->ogg_packet) == 1) { gint frame; speex_bits_read_from (&data->speex_bits, (char *)data->ogg_packet.packet, data->ogg_packet.bytes); for (frame = 0; frame < data->speexheader->frames_per_packet; frame++) { gint cnt; speex_decode (data->speex_state, &data->speex_bits, outfloat); if (data->speexheader->nb_channels == 2) { speex_decode_stereo (outfloat, data->speexheader->frame_size,&stereo); } n = MIN (samples_per_frame, len); /* copy as many samples to the output buffer as * possible. */ for (cnt = 0; cnt < n; cnt++) { *outbuf++ = outfloat[cnt]; len--; ret += 2; } /* store the remaining samples for later use */ for (; cnt < samples_per_frame; cnt++) { data->samples_buf[data->samples_count++] = outfloat[cnt]; } } return ret; } /* Need more data */ do { gint ret; data->ogg_data = ogg_sync_buffer (&data->sync_state, 200); ret = xmms_xform_read (xform, data->ogg_data, 200, &error); ogg_sync_wrote (&data->sync_state, ret); if (ret <= 0) { return ret; } } while (ogg_sync_pageout (&data->sync_state, &data->ogg_page) != 1); ogg_stream_pagein (&data->stream_state, &data->ogg_page); } }
/* main plugin handler for getting a buffer for the queue. In here we * just add an incoming page to the codecs and process it until either * more data is needed or we prodice a buffer for the queue. */ static refbuf_t *ogg_get_buffer (source_t *source) { ogg_state_t *ogg_info = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0, total = 0; while (total < 15000) { while (1) { ogg_page page; refbuf_t *refbuf = NULL; ogg_codec_t *codec = ogg_info->current; /* if a codec has just been given a page then process it */ if (codec && codec->process) { refbuf = codec->process (ogg_info, codec); if (refbuf) return complete_buffer (source, refbuf); ogg_info->current = NULL; } if (ogg_sync_pageout (&ogg_info->oy, &page) > 0) { if (ogg_page_bos (&page)) { process_initial_page (source->format, &page); } else { ogg_info->bos_completed = 1; refbuf = process_ogg_page (ogg_info, &page); } if (ogg_info->error) { ERROR0 ("Problem processing stream"); source->flags &= ~SOURCE_RUNNING; return NULL; } if (refbuf) return complete_buffer (source, refbuf); continue; } /* need more stream data */ break; } /* we need more data to continue getting pages */ data = ogg_sync_buffer (&ogg_info->oy, 4096); bytes = client_read_bytes (source->client, data, 4096); if (bytes <= 0) { ogg_sync_wrote (&ogg_info->oy, 0); source->client->schedule_ms += 50; break; } total += bytes; format->read_bytes += bytes; rate_add (source->in_bitrate, bytes, source->client->worker->current_time.tv_sec); ogg_sync_wrote (&ogg_info->oy, bytes); } return NULL; }
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=pp_level_max; pp_level=0; th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level)); pp_inc=0; /*{ int arg = 0xffff; th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg)); th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg)); th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg)); arg=10; th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg)); }*/ 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_RGBA,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; };
int main(int argc,char *argv[]){ ogg_packet op; int long_option_index; int c; FILE *infile = stdin; outfile = stdout; #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on 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 /* Process option arguments. */ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){ switch(c){ case 'o': outfile=fopen(optarg,"wb"); if(outfile==NULL){ fprintf(stderr,"Unable to open output file '%s'\n", optarg); exit(1); } break; default: usage(); } } if(optind<argc){ infile=fopen(argv[optind],"rb"); if(infile==NULL){ fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]); exit(1); } if(++optind<argc){ usage(); exit(1); } } /* Ok, Ogg parsing. The idea here is we have a bitstream that is made up of Ogg pages. The libogg sync layer will find them for us. There may be pages from several logical streams interleaved; we find the first theora stream and ignore any others. Then we pass the pages for our stream to the libogg stream layer which assembles our original set of packets out of them. It's the packets that libtheora actually knows how to handle. */ /* start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Theora structures needed in header parsing */ theora_comment_init(&tc); theora_info_init(&ti); /* Ogg file open; parse the headers */ /* Vorbis and Theora both depend on some initial header packets for decoder setup and initialization. We retrieve these first before entering the main decode loop. */ /* Only interested in 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 -- save this stream state */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-initial page parsing */ } /* we're expecting more header packets. */ while(theora_p && theora_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; } /* 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 stream state */ }else{ int ret=buffer_data(infile,&oy); /* need more data */ if(ret==0){ fprintf(stderr,"End of file while searching for codec headers.\n"); exit(1); } } } /* Now we have all the required headers. initialize the decoder. */ if(theora_p){ theora_decode_init(&td,&ti); fprintf(stderr,"Ogg logical stream %x is Theora %dx%d %.02f fps video\nEncoded frame content is %dx%d with %dx%d offset\n", to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator, ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y); }else{ /* tear down the partial theora setup */ theora_info_clear(&ti); theora_comment_clear(&tc); } /* open video */ if(theora_p)open_video(); /* install signal handler */ signal (SIGINT, sigint_handler); /* Finally the main decode loop. It's one Theora packet per frame, so this is pretty straightforward if we're not trying to maintain sync with other multiplexed streams. the videobuf_ready flag is used to maintain the input buffer in the libogg stream state. If there's no output frame available at the end of the decode step, we must need more input data. We could simplify this by just using the return code on ogg_page_packetout(), but the flag system extends easily to the case were you care about more than one multiplexed stream (like with audio playback). In that case, just maintain a flag for each decoder you care about, and pull data when any one of them stalls. videobuf_time holds the presentation time of the currently buffered video frame. We ignore this value. */ stateflag=0; /* playback has not begun */ /* queue any remaining pages from data we buffered but that did not contain headers */ while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } while(!got_sigint){ 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); videobuf_ready=1; }else break; } if(!videobuf_ready && feof(infile))break; if(!videobuf_ready){ /* no data yet for somebody. Grab another page */ int ret=buffer_data(infile,&oy); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* dumpvideo frame, and get new one */ else video_write(); videobuf_ready=0; } /* end of decoder loop -- close everything */ 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); }
void VideoStreamPlaybackTheora::update(float p_delta) { if (!file) return; if (!playing || paused) { //printf("not playing\n"); return; }; #ifdef THEORA_USE_THREAD_STREAMING thread_sem->post(); #endif //double ctime =AudioServer::get_singleton()->get_mix_time(); //print_line("play "+rtos(p_delta)); time+=p_delta; if (videobuf_time>get_time()) { return; //no new frames need to be produced } bool frame_done=false; bool audio_done=false; bool theora_done=false; while (!frame_done || (!audio_done && !vorbis_eos)) { //a frame needs to be produced ogg_packet op; bool audio_pending = false; bool no_vorbis=false; bool no_theora=false; while (vorbis_p) { int ret; float **pcm; bool buffer_full=false; /* if there's pending, decoded audio, grab it */ if ((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0) { const int AUXBUF_LEN=4096; int to_read = ret; int16_t aux_buffer[AUXBUF_LEN]; while(to_read) { int m = MIN(AUXBUF_LEN/vi.channels,to_read); int count = 0; for(int j=0;j<m;j++){ for(int i=0;i<vi.channels;i++){ int val=Math::fast_ftoi(pcm[i][j]*32767.f); if(val>32767)val=32767; if(val<-32768)val=-32768; aux_buffer[count++] = val; } } if (mix_callback) { int mixed = mix_callback(mix_udata,aux_buffer,m); to_read-=mixed; if (mixed!=m) { //could mix no more buffer_full=true; break; } } else { to_read-=m; //just pretend we sent the audio } } int tr = vorbis_synthesis_read(&vd, ret-to_read); audio_pending=true; if (vd.granulepos>=0) { // print_line("wrote: "+itos(audio_frames_wrote)+" gpos: "+itos(vd.granulepos)); } //print_line("mix audio!"); audio_frames_wrote+=ret-to_read; //print_line("AGP: "+itos(vd.granulepos)+" added "+itos(ret-to_read)); } 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 */ //printf("need moar data\n"); no_vorbis=true; break; }; } audio_done = videobuf_time < (audio_frames_wrote/float(vi.rate)); if (buffer_full) break; } while(theora_p && !frame_done){ /* theora is one in, one out... */ if(ogg_stream_packetout(&to,&op)>0){ if(false && pp_inc){ pp_level+=pp_inc; th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level, sizeof(pp_level)); pp_inc=0; } /*HACK: This should be set after a seek or a gap, but we might not have a granulepos for the first packet (we only have them for the last packet on a page), so we just set it as often as we get it. To do this right, we should back-track from the last packet on the page and compute the correct granulepos for the first packet after a seek or a gap.*/ if(op.granulepos>=0){ th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos, sizeof(op.granulepos)); } ogg_int64_t videobuf_granulepos; if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){ videobuf_time=th_granule_time(td,videobuf_granulepos); //printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready); /* 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()) { frame_done=true; } else{ /*If we are too slow, reduce the pp level.*/ pp_inc=pp_level>0?-1:0; } } else { } } else { no_theora=true; break; } } //print_line("no theora: "+itos(no_theora)+" theora eos: "+itos(theora_eos)+" frame done "+itos(frame_done)); #ifdef THEORA_USE_THREAD_STREAMING if (file && thread_eof && no_theora && theora_eos && ring_buffer.data_left()==0) { #else if (file && /*!videobuf_ready && */ no_theora && theora_eos) { #endif printf("video done, stopping\n"); stop(); return; }; #if 0 if (!videobuf_ready || audio_todo > 0){ /* no data yet for somebody. Grab another page */ buffer_data(); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } #else if (!frame_done || !audio_done){ //what's the point of waiting for audio to grab a page? buffer_data(); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } #endif /* 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(videobuf_ready && videobuf_time<=get_time()){ //video_write(); //videobuf_ready=0; } else { //printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready); } float tdiff=videobuf_time-get_time(); /*If we have lots of extra time, increase the post-processing level.*/ if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){ pp_inc=pp_level<pp_level_max?1:0; } else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){ pp_inc=pp_level>0?-1:0; } } video_write(); }; void VideoStreamPlaybackTheora::play() { if (!playing) time=0; else { stop(); } playing = true; delay_compensation=Globals::get_singleton()->get("audio/video_delay_compensation_ms"); delay_compensation/=1000.0; }; void VideoStreamPlaybackTheora::stop() { if (playing) { clear(); set_file(file_name); //reset } playing = false; time=0; }; bool VideoStreamPlaybackTheora::is_playing() const { return playing; }; void VideoStreamPlaybackTheora::set_paused(bool p_paused) { paused=p_paused; //pau = !p_paused; }; bool VideoStreamPlaybackTheora::is_paused(bool p_paused) const { return paused; }; void VideoStreamPlaybackTheora::set_loop(bool p_enable) { }; bool VideoStreamPlaybackTheora::has_loop() const { return false; }; float VideoStreamPlaybackTheora::get_length() const { return 0; }; String VideoStreamPlaybackTheora::get_stream_name() const { return ""; }; int VideoStreamPlaybackTheora::get_loop_count() const { return 0; }; float VideoStreamPlaybackTheora::get_pos() const { return get_time(); }; void VideoStreamPlaybackTheora::seek_pos(float p_time) { // no }; void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback,void *p_userdata) { mix_callback=p_callback; mix_udata=p_userdata; }
bool SFB::Audio::OggSpeexDecoder::_Open(CFErrorRef *error) { // 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 = (ssize_t)GetInputSource().Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { if(error) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” could not be read."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Read error"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("Unable to read from the input file."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } 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) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } 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) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } 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) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } 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) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } 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, (int)op.bytes); if(nullptr == header) { if(error) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Speex file."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Not an Ogg Speex file"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::FileFormatNotRecognizedError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } ogg_sync_destroy(&mOggSyncState); return false; } else if(SPEEX_NB_MODES <= header->mode) { if(error) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The Speex mode in the file “%@” is not supported."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file mode"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("This file may have been encoded with a newer version of Speex."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::FileFormatNotSupportedError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } speex_header_free(header); header = nullptr; 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) { SFB::CFString description(CFCopyLocalizedString(CFSTR("The Speex version in the file “%@” is not supported."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file version"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("This file was encoded with a different version of Speex."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::FileFormatNotSupportedError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } speex_header_free(header); header = nullptr; ogg_sync_destroy(&mOggSyncState); return false; } // Initialize the decoder mSpeexDecoder = speex_decoder_init(mode); if(nullptr== mSpeexDecoder) { if(error) { SFB::CFString description(CFCopyLocalizedString(CFSTR("Unable to initialize the Speex decoder."), "")); SFB::CFString failureReason(CFCopyLocalizedString(CFSTR("Error initializing Speex decoder"), "")); SFB::CFString recoverySuggestion(CFCopyLocalizedString(CFSTR("An unknown error occurred."), "")); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } speex_header_free(header); header = nullptr; 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 = (UInt32)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 = (UInt32)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 = (UInt32)header->nb_channels; switch(header->nb_channels) { case 1: mChannelLayout = ChannelLayout::ChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = ChannelLayout::ChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; } speex_header_free(header); header = nullptr; // Allocate the buffer list spx_int32_t speexFrameSize = 0; speex_decoder_ctl(mSpeexDecoder, SPEEX_GET_FRAME_SIZE, &speexFrameSize); if(!mBufferList.Allocate(mFormat, (UInt32)speexFrameSize)) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, nullptr); speex_stereo_state_destroy(mSpeexStereoState); mSpeexStereoState = nullptr; speex_decoder_destroy(mSpeexDecoder); mSpeexDecoder = nullptr; speex_bits_destroy(&mSpeexBits); ogg_sync_destroy(&mOggSyncState); return false; } for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) mBufferList->mBuffers[i].mDataByteSize = 0; return true; }
bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = stream; // start up Ogg stream synchronization layer ogg_sync_init(&_oggSync); // init supporting Vorbis structures needed in header parsing vorbis_info_init(&_vorbisInfo); vorbis_comment vorbisComment; vorbis_comment_init(&vorbisComment); // init supporting Theora structures needed in header parsing th_info theoraInfo; th_info_init(&theoraInfo); th_comment theoraComment; th_comment_init(&theoraComment); th_setup_info *theoraSetup = 0; uint theoraPackets = 0, vorbisPackets = 0; // Ogg file open; parse the headers // Only interested in Vorbis/Theora streams bool foundHeader = false; while (!foundHeader) { int ret = bufferData(); if (ret == 0) break; // FIXME: Shouldn't this error out? while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { ogg_stream_state test; // is this a mandated initial header? If not, stop parsing if (!ogg_page_bos(&_oggPage)) { // don't leak the page; get it into the appropriate stream queuePage(&_oggPage); foundHeader = true; break; } ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); ogg_stream_pagein(&test, &_oggPage); ogg_stream_packetout(&test, &_oggPacket); // identify the codec: try theora if (theoraPackets == 0 && th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket) >= 0) { // it is theora memcpy(&_theoraOut, &test, sizeof(test)); theoraPackets = 1; _hasVideo = true; } else if (vorbisPackets == 0 && vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket) >= 0) { // it is vorbis memcpy(&_vorbisOut, &test, sizeof(test)); vorbisPackets = 1; _hasAudio = true; } 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 ((theoraPackets && theoraPackets < 3) || (vorbisPackets && vorbisPackets < 3)) { int ret; // look for further theora headers while (theoraPackets && (theoraPackets < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { if (ret < 0) error("Error parsing Theora stream headers; corrupt stream?"); if (!th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket)) error("Error parsing Theora stream headers; corrupt stream?"); theoraPackets++; } // look for more vorbis header packets while (vorbisPackets && (vorbisPackets < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { if (ret < 0) error("Error parsing Vorbis stream headers; corrupt stream?"); if (vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket)) error("Error parsing Vorbis stream headers; corrupt stream?"); vorbisPackets++; if (vorbisPackets == 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(&_oggSync, &_oggPage) > 0) { queuePage(&_oggPage); // demux into the appropriate stream } else { ret = bufferData(); // someone needs more data if (ret == 0) error("End of file while searching for codec headers."); } } // And now we have it all. Initialize decoders next if (_hasVideo) { _videoTrack = new TheoraVideoTrack(getDefaultHighColorFormat(), theoraInfo, theoraSetup); addTrack(_videoTrack); } th_info_clear(&theoraInfo); th_comment_clear(&theoraComment); th_setup_free(theoraSetup); if (_hasAudio) { _audioTrack = new VorbisAudioTrack(_soundType, _vorbisInfo); // Get enough audio data to start us off while (!_audioTrack->hasAudio()) { // Queue more data bufferData(); while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) queuePage(&_oggPage); queueAudio(); } addTrack(_audioTrack); } vorbis_comment_clear(&vorbisComment); return true; }
static UINT ogg_dec(GETSND snd, short *dst) { __OV *ov; int result; char *buffer; int bytes; float **pcm; int samples; int i; int j; float *mono; short *ptr; long val; ov = (__OV *)snd->snd; do { switch(ov->phase) { case OVPHASE_HEAD: result = ogg_sync_pageout(&ov->oy, &ov->og); if (result > 0) { ogg_stream_pagein(&ov->os, &ov->og); ov->phase = OVPHASE_STREAMIN; } else if (result == 0) { ov->phase = OVPHASE_NEXT; } else { TRACEOUT(("Corrupt or missing data in bitstream")); } break; case OVPHASE_STREAMIN: result = ogg_stream_packetout(&ov->os, &ov->op); if (result > 0) { if (vorbis_synthesis(&ov->vb, &ov->op) == 0) { vorbis_synthesis_blockin(&ov->vd, &ov->vb); } ov->phase = OVPHASE_GETPCM; } else if (result == 0) { if (!ogg_page_eos(&ov->og)) { ov->phase = OVPHASE_NEXT; } else { ov->phase = OVPHASE_CLOSE; } } break; case OVPHASE_GETPCM: samples = vorbis_synthesis_pcmout(&ov->vd, &pcm); if (samples > 0) { if (samples > (int)snd->blocksamples) { samples = (int)snd->blocksamples; } for (i=0; i<ov->vi.channels; i++) { ptr = dst + i; mono = pcm[i]; for (j=0; j<samples; j++) { val = (long)(mono[j] * 32767.f); if (val > 32767) { val = 32767; } if (val < -32768) { val = -32768; } *ptr = (short)val; ptr += ov->vi.channels; } } vorbis_synthesis_read(&ov->vd, samples); return((UINT)samples); } ov->phase = OVPHASE_STREAMIN; break; case OVPHASE_NEXT: buffer = ogg_sync_buffer(&ov->oy, 4096); bytes = snd_read(snd, buffer, 4096); ogg_sync_wrote(&ov->oy, bytes); #if 1 ov->phase = OVPHASE_HEAD; #else if (bytes) { ov->phase = OVPHASE_HEAD; } else { ov->phase = OVPHASE_CLOSE; } #endif break; case OVPHASE_CLOSE: return(0); } } while(1); }
int sndogg_open(SMIXTRACK trk) { __OV *ov; char *buffer; UINT r; int bytes; int i; int result; ov = (__OV *)_MALLOC(sizeof(__OV), "__OV"); if (ov == NULL) { goto ovopn_next; } ZeroMemory(ov, sizeof(__OV)); r = sndmix_dataload(trk, trk->maxdatas); if ((r == (UINT)-1) || (r == 0)) { goto ovopn_next2; } buffer = ogg_sync_buffer(&ov->oy, trk->maxdatas); CopyMemory(buffer, trk->data, r); ogg_sync_wrote(&ov->oy, (int)r); if (ogg_sync_pageout(&ov->oy, &ov->og) != 1) { TRACEOUT(("Input does not appear to be an Ogg bitstream.")); goto ovopn_next2; } ogg_stream_init(&ov->os, ogg_page_serialno(&ov->og)); sndmix_datatrash(trk, (UINT)-1); vorbis_info_init(&ov->vi); vorbis_comment_init(&ov->vc); if (ogg_stream_pagein(&ov->os, &ov->og) < 0) { TRACEOUT(("Error reading first page of Ogg bitstream data.")); goto ovopn_err1; } if (ogg_stream_packetout(&ov->os, &ov->op) != 1) { TRACEOUT(("Error reading initial header packet.")); goto ovopn_err1; } if (vorbis_synthesis_headerin(&ov->vi, &ov->vc, &ov->op) < 0) { TRACEOUT(("This Ogg bitstream does not contain Vorbis audio data.")); goto ovopn_err1; } i = 0; while(i < 2) { while(i < 2) { result = ogg_sync_pageout(&ov->oy, &ov->og); if (result == 0) { break; } if (result == 1) { ogg_stream_pagein(&ov->os, &ov->og); while(i < 2) { result = ogg_stream_packetout(&ov->os, &ov->op); if (result == 0) { break; } if (result < 0) { TRACEOUT(("Corrupt secondary header. Exiting.")); goto ovopn_err1; } vorbis_synthesis_headerin(&ov->vi, &ov->vc, &ov->op); i++; } } } buffer = ogg_sync_buffer(&ov->oy, 4096); bytes = sndmix_dataread(trk, buffer, 4096); if ((bytes == 0) && (i < 2)) { TRACEOUT(("End of file before finding all Vorbis headers!")); return(SNDMIX_FAILURE); } ogg_sync_wrote(&ov->oy, bytes); } trk->snd = ov; trk->dec = (DECFN)ogg_dec; trk->decend = ogg_decend; trk->samprate = ov->vi.rate; trk->channels = ov->vi.channels; trk->block = 4096; trk->blksamp = 4096 / ov->vi.channels; trk->bit = 16; #if defined(SOUND_MOREINFO) trk->bps = 0; trk->fmt = WAVEFMT_OGG; CopyMemory(trk->info, "Ogg vorbis", 11); #endif vorbis_synthesis_init(&ov->vd, &ov->vi); vorbis_block_init(&ov->vd, &ov->vb); return(SNDMIX_SUCCESS); ovopn_err1: ogg_sync_clear(&ov->oy); _MFREE(ov); return(SNDMIX_FAILURE); ovopn_next2: _MFREE(ov); ovopn_next: return(SNDMIX_NOTSUPPORT); }
static int spx_seek (void *prv_data, int sec) { struct spx_data *data = (struct spx_data *)prv_data; off_t begin = 0, end, old_pos; assert (sec >= 0); end = io_file_size (data->stream); if (end == -1) return -1; old_pos = io_tell (data->stream); debug ("Seek request to %ds", sec); while (1) { off_t middle = (end + begin) / 2; ogg_int64_t granule_pos; int position_seconds; debug ("Seek to %"PRId64, middle); if (io_seek(data->stream, middle, SEEK_SET) == -1) { io_seek (data->stream, old_pos, SEEK_SET); ogg_stream_reset (&data->os); ogg_sync_reset (&data->oy); return -1; } debug ("Syncing..."); /* Sync to page and read it */ ogg_sync_reset (&data->oy); while (!io_eof(data->stream)) { if (ogg_sync_pageout(&data->oy, &data->og) == 1) { debug ("Sync"); break; } if (!io_eof(data->stream)) { debug ("Need more data"); get_more_data (data); } } if (io_eof(data->stream)) { debug ("EOF when syncing"); return -1; } granule_pos = ogg_page_granulepos(&data->og); position_seconds = granule_pos / data->rate; debug ("We are at %ds", position_seconds); if (position_seconds == sec) { ogg_stream_pagein (&data->os, &data->og); debug ("We have it at granulepos %"PRId64, granule_pos); break; } else if (sec < position_seconds) { end = middle; debug ("going back"); } else { begin = middle; debug ("going forward"); } debug ("begin - end %"PRId64" - %"PRId64, begin, end); if (end - begin <= 200) { /* Can't find the exact position. */ sec = position_seconds; break; } } ogg_sync_reset (&data->oy); ogg_stream_reset (&data->os); return sec; }
static UINT ogg_dec(SMIXTRACK trk, SINT16 *dst) { __OV *ov; int result; char *buffer; UINT r; float **pcm; int samples; int i; int j; float *mono; SINT16 *ptr; SINT32 val; ov = (__OV *)trk->snd; do { switch(ov->phase) { case OVPHASE_HEAD: result = ogg_sync_pageout(&ov->oy, &ov->og); if (result > 0) { ogg_stream_pagein(&ov->os, &ov->og); ov->phase = OVPHASE_STREAMIN; } else if (result == 0) { ov->phase = OVPHASE_NEXT; } else { TRACEOUT(("Corrupt or missing data in bitstream")); } break; case OVPHASE_STREAMIN: result = ogg_stream_packetout(&ov->os, &ov->op); if (result > 0) { if (vorbis_synthesis(&ov->vb, &ov->op) == 0) { vorbis_synthesis_blockin(&ov->vd, &ov->vb); } ov->phase = OVPHASE_GETPCM; } else if (result == 0) { if (!ogg_page_eos(&ov->og)) { ov->phase = OVPHASE_NEXT; } else { ov->phase = OVPHASE_CLOSE; } } break; case OVPHASE_GETPCM: samples = vorbis_synthesis_pcmout(&ov->vd, &pcm); if (samples > 0) { if (samples > (int)trk->blksamp) { samples = (int)trk->blksamp; } for (i=0; i<ov->vi.channels; i++) { ptr = dst + i; mono = pcm[i]; for (j=0; j<samples; j++) { val = (long)(mono[j] * 32767.f); if (val > 32767) { val = 32767; } if (val < -32768) { val = -32768; } *ptr = (SINT16)val; ptr += ov->vi.channels; } } vorbis_synthesis_read(&ov->vd, samples); return((UINT)samples); } ov->phase = OVPHASE_STREAMIN; break; case OVPHASE_NEXT: buffer = ogg_sync_buffer(&ov->oy, trk->block); r = sndmix_dataread(trk, buffer, trk->block); ogg_sync_wrote(&ov->oy, (int)r); if (r) { ov->phase = OVPHASE_HEAD; } else { ov->phase = OVPHASE_CLOSE; } break; case OVPHASE_CLOSE: ov->phase = OVPHASE_HEAD; // ��[�� return(0); } } while(1); }
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; #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 ************/ 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); 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 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<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); } 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,stdin); 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. */ 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 */ /* 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=mono[j]*32767.f; #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); 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); ogg_sync_wrote(&oy,bytes); if(bytes==0)eos=1; } } /* clean up this logical bitstream; before exit we see if we're followed by another [chained] */ ogg_stream_clear(&os); /* 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); 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); }
void JVideoServer::Init() {return; memset( &m_StreamState, 0, sizeof( m_StreamState ) ); memset( &m_SyncState, 0, sizeof( m_SyncState ) ); memset( &m_Page, 0, sizeof( m_Page ) ); memset( &m_Packet, 0, sizeof( m_Packet ) ); memset( &m_Comment, 0, sizeof( m_Comment ) ); memset( &m_Info, 0, sizeof( m_Info ) ); memset( &m_State, 0, sizeof( m_State ) ); memset( &m_YUVBuffer, 0, sizeof( m_YUVBuffer ) ); ogg_stream_clear( &m_StreamState ); ogg_sync_init( &m_SyncState ); theora_comment_init( &m_Comment ); theora_info_init( &m_Info ); // теперь ищем начало логического потока theora bool bStartHeader = true; int nHeaderPackets = 0; // число обработанных пакетов заголовков theora do { if (LoadChunk( m_File, &m_SyncState ) ==0) { // кончился файл, на данном этапе это ошибка assert( "!eof searched, terminate..."); } // ogg_sync_pageout - формирует страницу while (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // 1-больше данных не требуется // 0-требуется больше данных для создания страницы { // что страница сформирована успешно // это страница заголовков? если нет, кончаем искать заголовки if (ogg_page_bos( &m_Page ) == false) { // нет, это не страница заголовков // значит, страницы заголовков всех логических потоков кончились // и начались данные этих потоков // таким образом надо переходить к чтению страниц данных // закидываем эту страничку в логический видеопоток PushPage( &m_Page ); // PushPage - закидывает страничку // в логический поток theora, если // совпадает идентификатор логического потока // в противном случае страница игнорируется // выходим из циклов bStartHeader = false; break; } else { // да, это страница заголовков // тестовый логический поток ogg_stream_state m_StreamStateTest; memset(&m_StreamStateTest, 0x00, sizeof(ogg_stream_state)); // инициализируем тестовый поток на тот же поток с таким же // идентификатором потока, как и у текущей странички if(0!= ogg_stream_init(&m_StreamStateTest,ogg_page_serialno(&m_Page)) ) assert( "!error during ogg_stream_init"); // добавляем страницу в тестовый поток if(0!= ogg_stream_pagein(&m_StreamStateTest,&m_Page) ) assert( "!error during ogg_stream_pagein"); // декодируем данные из этого тестового потока в пакет if( ogg_stream_packetout(&m_StreamStateTest,&m_Packet) ==-1) assert( "!error during ogg_stream_packetout"); // nHeaderPackets - число прочитанных // заголовочных ПАКЕТОВ theora (не страниц) // по спецификации theora таких пакетов должно быть три if(nHeaderPackets==0) { int dhr = theora_decode_header (&m_Info, &m_Comment, &m_Packet); // декодируем заголовок theora if(dhr<0) { // это не заголовок theora // очищаем структуру тестового потока ogg_stream_clear(&m_StreamStateTest); //и продолжаем цикл в поисках заголовков theora } else { // это заголовок theora! // вот таким образом "инициализируем" логический поток theora: memcpy(&m_StreamState, &m_StreamStateTest, sizeof(m_StreamStateTest)); // теперь из этого потока будут всегда сыпаться пакеты theora nHeaderPackets++; // после того, как мы нашли заголовочную страницу логического потока theora, // нам необходимо прочитать все остальные заголовочные страницы // других потоков и отбросить их (если таковые, конечно, имеются) } } } } } while (bStartHeader); // сейчас надо получить еще два пакета заголовков theora (см. её документацию) // и можно переходить к потоковому воспроизведению while(nHeaderPackets<3) { int result=ogg_stream_packetout(&m_StreamState,&m_Packet); // если функция возвращает нуль, значит не хватает данных для декодирования // почему то этого НЕТ в спецификации libogg, или я плохо искал if (result < 0) { // ошибка декодирования, поврежденный поток assert( "!error during ogg_stream_packetout"); } if (result > 0) { // удалось успешно извлечь пакет информации theora int result2 = theora_decode_header( &m_Info, &m_Comment, &m_Packet ); if(result2<0) { // ошибка декодирования, поврежденный поток rlog.err("VIDEO: error during theora_decode_header (corrupt stream)"); } ++nHeaderPackets; } // эту страничку обработали, надо извлечь новую // для этого проверяем буфер чтения, вдруг там осталось что-нить похожее // на страничку. Если не осталось, тогда просто читаем эти данные из файла: if (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // ogg_sync_pageout - функция, берет данные из буфера приема ogg // и записывает их в ogg_page { //мы нашли страничку в буфере и... PushPage( &m_Page ); // ...пихаем эти данные в подходящий поток } else { // ничего мы в буфере не нашли int ret = LoadChunk( m_File, &m_SyncState ); // надо больше данных! читаем их из файла if (ret == 0) { // опять файл кончился! rlog.err("VIDEO: eof searched. terminate..."); } } } // init videostream theora_decode_init( &m_State, &m_Info ); switch(m_Info.colorspace) { case OC_CS_UNSPECIFIED: // nothing to report break; case OC_CS_ITU_REC_470M: rlog.msg("Encoder specified ITU Rec 470M (NTSC) color."); // выводим в лог информацию о цветовом пространстве break; case OC_CS_ITU_REC_470BG: rlog.msg("Encoder specified ITU Rec 470BG (PAL) color."); break; default: rlog.msg("Warning: encoder specified unknown colorspace."); break; } // theora processing... while (ogg_stream_packetout( &m_StreamState, &m_Packet ) <= 0) { // не хватает данных в логическом потоке theora // надо надергать данных из физического потока и затолкать их в логический поток // читаем данные из файла int ret = LoadChunk( m_File, &m_SyncState ); if (ret == 0) { // файл кончился, необходимо выполнить закрывающие действия // и выйти из приложения TheoraClose(); return; } while (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // декодируем данные из буфера в страницы (ogg_page) // пока они не кончатся в буфере { // пихаем эти страницы в соотв. логические потоки PushPage( &m_Page ); } } // удачно декодировали. в пакете содержится декодированная ogg-информация // (то бишь закодированная theora-информация) // загружаем пакет в декодер theora if (theora_decode_packetin(&m_State,&m_Packet) == OC_BADPACKET) { // ошибка декодирования rlog.err( "error during theora_decode_packetin..." ); } // все данные получены, готовим кадр // декодируем страничку в YUV-виде в спец. структуру yuv_buffer if (theora_decode_YUVout( &m_State, &m_YUVBuffer ) != 0) { // ошибка декодирования rlog.err( "error during theora_decode_YUVout..."); } // если это первый кадр, то создаем буфер кадра BYTE* frame = new BYTE[m_YUVBuffer.y_height*m_YUVBuffer.y_width*4]; // yuv to rgb for (int cy = 0; cy < m_YUVBuffer.y_height; cy++) { int nYShift = m_YUVBuffer.y_stride*cy; int nUVShift = m_YUVBuffer.uv_stride*(cy >> 1); for (int cx = 0; cx < m_YUVBuffer.y_width; cx++) { int nHX = (cx >> 1); BYTE nY = *(BYTE*)(m_YUVBuffer.y + nYShift + cx ); BYTE nU = *(BYTE*)(m_YUVBuffer.u + nUVShift + nHX ); BYTE nV = *(BYTE*)(m_YUVBuffer.v + nUVShift + nHX ); int index = (cy*m_YUVBuffer.y_width + cx)*4; float r = nY + 1.371f*(nV - 128); float g = nY - 0.698f*(nV - 128) - 0.336f*(nU - 128); float b = nY + 1.732f*(nU - 128); frame[index + 0] = (BYTE)clamp( r, 0.0f, 255.0f ); frame[index + 1] = (BYTE)clamp( g, 0.0f, 255.0f ); frame[index + 2] = (BYTE)clamp( b, 0.0f, 255.0f ); frame[index + 3] = 255; } } } // JVideoServer::Init
int main(void){ ogg_stream_init(&os_en,0x04030201); ogg_stream_init(&os_de,0x04030201); ogg_sync_init(&oy); /* Exercise each code path in the framing code. Also verify that the checksums are working. */ { /* 17 only */ const int packets[]={17, -1}; const int *headret[]={head1_0,NULL}; fprintf(stderr,"testing single page encoding... "); test_pack(packets,headret); } { /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; const int *headret[]={head1_1,head2_1,NULL}; fprintf(stderr,"testing basic page encoding... "); test_pack(packets,headret); } { /* nil packets; beginning,middle,end */ const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; const int *headret[]={head1_2,head2_2,NULL}; fprintf(stderr,"testing basic nil packets... "); test_pack(packets,headret); } { /* large initial packet */ const int packets[]={4345,259,255,-1}; const int *headret[]={head1_3,head2_3,NULL}; fprintf(stderr,"testing initial-packet lacing > 4k... "); test_pack(packets,headret); } { /* continuing packet test */ const int packets[]={0,4345,259,255,-1}; const int *headret[]={head1_4,head2_4,head3_4,NULL}; fprintf(stderr,"testing single packet page span... "); test_pack(packets,headret); } /* page with the 255 segment limit */ { const int packets[]={0,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,50,-1}; const int *headret[]={head1_5,head2_5,head3_5,NULL}; fprintf(stderr,"testing max packet segments... "); test_pack(packets,headret); } { /* packet that overspans over an entire page */ const int packets[]={0,100,9000,259,255,-1}; const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; fprintf(stderr,"testing very large packets... "); test_pack(packets,headret); } { /* term only page. why not? */ const int packets[]={0,100,4080,-1}; const int *headret[]={head1_7,head2_7,head3_7,NULL}; fprintf(stderr,"testing zero data page (1 nil packet)... "); test_pack(packets,headret); } { /* build a bunch of pages for testing */ unsigned char *data=_ogg_malloc(1024*1024); int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; int inptr=0,i,j; ogg_page og[5]; ogg_stream_reset(&os_en); for(i=0;pl[i]!=-1;i++){ ogg_packet op; int len=pl[i]; op.packet=data+inptr; op.bytes=len; op.e_o_s=(pl[i+1]<0?1:0); op.granulepos=(i+1)*1000; for(j=0;j<len;j++)data[inptr++]=i+j; ogg_stream_packetin(&os_en,&op); } _ogg_free(data); /* retrieve finished pages */ for(i=0;i<5;i++){ if(ogg_stream_pageout(&os_en,&og[i])==0){ fprintf(stderr,"Too few pages output building sync tests!\n"); exit(1); } copy_page(&og[i]); } /* Test lost pages on pagein/packetout: no rollback */ { ogg_page temp; ogg_packet test; fprintf(stderr,"Testing loss of pages... "); ogg_sync_reset(&oy); ogg_stream_reset(&os_de); for(i=0;i<5;i++){ memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, og[i].header_len); ogg_sync_wrote(&oy,og[i].header_len); memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); ogg_sync_wrote(&oy,og[i].body_len); } ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); /* skip */ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); /* do we get the expected results/packets? */ if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,100,1,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,4079,2,3000); if(ogg_stream_packetout(&os_de,&test)!=-1){ fprintf(stderr,"Error: loss of page did not return error\n"); exit(1); } if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,76,5,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,34,6,-1); fprintf(stderr,"ok.\n"); } /* Test lost pages on pagein/packetout: rollback with continuation */ { ogg_page temp; ogg_packet test; fprintf(stderr,"Testing loss of pages (rollback required)... "); ogg_sync_reset(&oy); ogg_stream_reset(&os_de); for(i=0;i<5;i++){ memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, og[i].header_len); ogg_sync_wrote(&oy,og[i].header_len); memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); ogg_sync_wrote(&oy,og[i].body_len); } ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); /* skip */ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); /* do we get the expected results/packets? */ if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,100,1,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,4079,2,3000); if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,2956,3,4000); if(ogg_stream_packetout(&os_de,&test)!=-1){ fprintf(stderr,"Error: loss of page did not return error\n"); exit(1); } if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,300,13,14000); fprintf(stderr,"ok.\n"); } /* the rest only test sync */ { ogg_page og_de; /* Test fractional page inputs: incomplete capture */ fprintf(stderr,"Testing sync on partial inputs... "); ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, 3); ogg_sync_wrote(&oy,3); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete fixed header */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete header */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, 5); ogg_sync_wrote(&oy,5); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete body */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, og[1].header_len-28); ogg_sync_wrote(&oy,og[1].header_len-28); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); ogg_sync_wrote(&oy,1000); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, og[1].body_len-1000); ogg_sync_wrote(&oy,og[1].body_len-1000); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); fprintf(stderr,"ok.\n"); } /* Test fractional page inputs: page + incomplete capture */ { ogg_page og_de; fprintf(stderr,"Testing sync on 1+partial inputs... "); ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, og[1].header_len-20); ogg_sync_wrote(&oy,og[1].header_len-20); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); fprintf(stderr,"ok.\n"); } /* Test recapture: garbage + page */ { ogg_page og_de; fprintf(stderr,"Testing search for capture... "); ogg_sync_reset(&oy); /* 'garbage' */ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)>0)error(); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, og[2].header_len-20); ogg_sync_wrote(&oy,og[2].header_len-20); memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, og[2].body_len); ogg_sync_wrote(&oy,og[2].body_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); fprintf(stderr,"ok.\n"); } /* Test recapture: page + garbage + page */ { ogg_page og_de; fprintf(stderr,"Testing recapture... "); ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, og[2].header_len); ogg_sync_wrote(&oy,og[2].header_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, og[2].header_len); ogg_sync_wrote(&oy,og[2].header_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, og[2].body_len-5); ogg_sync_wrote(&oy,og[2].body_len-5); memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, og[3].header_len); ogg_sync_wrote(&oy,og[3].header_len); memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, og[3].body_len); ogg_sync_wrote(&oy,og[3].body_len); if(ogg_sync_pageout(&oy,&og_de)>0)error(); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); fprintf(stderr,"ok.\n"); } } return(0); }
int main(int argc, char **argv) { int c; int option_index = 0; char *inFile, *outFile; FILE *fin, *fout=NULL; short out[MAX_FRAME_SIZE]; short output[MAX_FRAME_SIZE]; int frame_size=0, granule_frame_size=0; void *st=NULL; CELTMode *mode=NULL; int packet_count=0; int stream_init = 0; int quiet = 0; ogg_int64_t page_granule=0, last_granule=0; int skip_samples=0, page_nb_packets; struct option long_options[] = { {"help", no_argument, NULL, 0}, {"quiet", no_argument, NULL, 0}, {"version", no_argument, NULL, 0}, {"version-short", no_argument, NULL, 0}, {"rate", required_argument, NULL, 0}, {"mono", no_argument, NULL, 0}, {"stereo", no_argument, NULL, 0}, {"packet-loss", required_argument, NULL, 0}, {0, 0, 0, 0} }; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int enh_enabled; int nframes=2; int print_bitrate=0; int close_in=0; int eos=0; int forceMode=-1; int audio_size=0; float loss_percent=-1; int channels=-1; int rate=0; int extra_headers=0; int wav_format=0; int lookahead=0; int celt_serialno = -1; int firstpacket = 1; enh_enabled = 1; /*Process options*/ while(1) { c = getopt_long (argc, argv, "hvV", long_options, &option_index); if (c==-1) break; switch(c) { case 0: if (strcmp(long_options[option_index].name,"help")==0) { usage(); exit(0); } else if (strcmp(long_options[option_index].name,"quiet")==0) { quiet = 1; } else if (strcmp(long_options[option_index].name,"version")==0) { version(); exit(0); } else if (strcmp(long_options[option_index].name,"version-short")==0) { version_short(); exit(0); } else if (strcmp(long_options[option_index].name,"mono")==0) { channels=1; } else if (strcmp(long_options[option_index].name,"stereo")==0) { channels=2; } else if (strcmp(long_options[option_index].name,"rate")==0) { rate=atoi (optarg); } else if (strcmp(long_options[option_index].name,"packet-loss")==0) { loss_percent = atof(optarg); } break; case 'h': usage(); exit(0); break; case 'v': version(); exit(0); break; case 'V': print_bitrate=1; break; case '?': usage(); exit(1); break; } } if (argc-optind!=2 && argc-optind!=1) { usage(); exit(1); } inFile=argv[optind]; if (argc-optind==2) outFile=argv[optind+1]; else outFile = ""; wav_format = strlen(outFile)>=4 && ( strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0); /*Open input file*/ if (strcmp(inFile, "-")==0) { #if defined WIN32 || defined _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif fin=stdin; } else { fin = fopen(inFile, "rb"); if (!fin) { perror(inFile); exit(1); } close_in=1; } /*Init Ogg data struct*/ ogg_sync_init(&oy); /*Main decoding loop*/ while (1) { char *data; int i, nb_read; /*Get the ogg buffer for writing*/ data = ogg_sync_buffer(&oy, 200); /*Read bitstream from input file*/ nb_read = fread(data, sizeof(char), 200, fin); ogg_sync_wrote(&oy, nb_read); /*Loop for all complete pages we got (most likely only one)*/ while (ogg_sync_pageout(&oy, &og)==1) { if (stream_init == 0) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = 1; } if (ogg_page_serialno(&og) != os.serialno) { /* so all streams are read. */ ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); } /*Add page to the bitstream*/ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); page_nb_packets = ogg_page_packets(&og); if (page_granule>0 && frame_size) { /* FIXME: shift the granule values if --force-* is specified */ skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size; if (ogg_page_eos(&og)) skip_samples = -skip_samples; /*else if (!ogg_page_bos(&og)) skip_samples = 0;*/ } else { skip_samples = 0; } /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/ last_granule = page_granule; /*Extract all available packets*/ while (!eos && ogg_stream_packetout(&os, &op) == 1) { if (op.bytes>=8 && !memcmp(op.packet, "CELT ", 8)) { celt_serialno = os.serialno; } if (celt_serialno == -1 || os.serialno != celt_serialno) break; /*If first packet, process as CELT header*/ if (packet_count==0) { st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &lookahead, &extra_headers, quiet, &mode); if (!st) exit(1); if (!nframes) nframes=1; fout = out_file_open(outFile, rate, &channels); } else if (packet_count==1) { if (!quiet) print_comments((char*)op.packet, op.bytes); } else if (packet_count<=1+extra_headers) { /* Ignore extra headers */ } else { int lost=0; if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent) lost=1; /*End of stream condition*/ if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */ eos=1; { int ret; /*Decode frame*/ if (!lost) ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output, frame_size); else ret = celt_decode(st, NULL, 0, output, frame_size); /*for (i=0;i<frame_size*channels;i++) printf ("%d\n", (int)output[i]);*/ if (ret<0) { fprintf (stderr, "Decoding error: %s\n", celt_strerror(ret)); break; } if (print_bitrate) { celt_int32 tmp=op.bytes; char ch=13; fputc (ch, stderr); fprintf (stderr, "Bitrate in use: %d bytes/packet ", tmp); } /*Convert to short and save to output file*/ if (strlen(outFile)!=0) { for (i=0;i<frame_size*channels;i++) out[i]=le_short(output[i]); } else { for (i=0;i<frame_size*channels;i++) out[i]=output[i]; } { int frame_offset = 0; int new_frame_size = frame_size; /*printf ("packet %d %d\n", packet_no, skip_samples);*/ /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/ if (firstpacket == 1) { /*printf ("chopping first packet\n");*/ new_frame_size -= lookahead; frame_offset = lookahead; firstpacket = 0; } if (new_frame_size>0) { #if defined WIN32 || defined _WIN32 if (strlen(outFile)==0) WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels); else #endif fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout); audio_size+=sizeof(short)*new_frame_size*channels; } } } } packet_count++; } } if (feof(fin)) break; } if (fout && wav_format) { if (fseek(fout,4,SEEK_SET)==0) { int tmp; tmp = le_int(audio_size+36); fwrite(&tmp,4,1,fout); if (fseek(fout,32,SEEK_CUR)==0) { tmp = le_int(audio_size); fwrite(&tmp,4,1,fout); } else { fprintf (stderr, "First seek worked, second didn't\n"); } } else { fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n"); } } if (st) { celt_decoder_destroy(st); celt_mode_destroy(mode); } else { fprintf (stderr, "This doesn't look like a CELT file\n"); } if (stream_init) ogg_stream_clear(&os); ogg_sync_clear(&oy); #if defined WIN32 || defined _WIN32 if (strlen(outFile)==0) WIN_Audio_close (); #endif if (close_in) fclose(fin); if (fout != NULL) fclose(fout); return 0; }
static int decode(quicktime_t *file, int16_t *output_i, float *output_f, long samples, int track, int channel) { int result = 0; int bytes; int i, j; quicktime_audio_map_t *track_map = &(file->atracks[track]); quicktime_trak_t *trak = track_map->track; quicktime_vorbis_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv; long current_position = track_map->current_position; long end_position = current_position + samples; unsigned char *buffer; // End of data in ogg buffer int eos = 0; // End of file int eof = 0; float *pcm; int have_chunk = 0; if(samples > OUTPUT_ALLOCATION) printf("vorbis.c decode: can't read more than %p samples at a time.\n", OUTPUT_ALLOCATION); if(output_i) bzero(output_i, sizeof(int16_t) * samples); if(output_f) bzero(output_f, sizeof(float) * samples); // Seeked outside output buffer's range or not initialized: restart if(current_position < codec->output_position - codec->output_size || current_position > codec->output_position || !codec->decode_initialized) { quicktime_chunk_of_sample(&codec->output_position, &codec->chunk, trak, current_position); // We know the first ogg packet in the chunk has a pcm_offset from the encoding. codec->output_size = 0; codec->output_end = 0; codec->chunk_samples = 0; // Initialize and load initial buffer for decoding if(!codec->decode_initialized) { int init_chunk = 1; codec->decode_initialized = 1; codec->output = malloc(sizeof(float*) * track_map->channels); for(i = 0; i < track_map->channels; i++) { codec->output[i] = malloc(sizeof(float) * OUTPUT_ALLOCATION); } codec->output_allocated = OUTPUT_ALLOCATION; ogg_sync_init(&codec->dec_oy); /* Now we can read pages */ READ_CHUNK(init_chunk); init_chunk++; if(ogg_sync_pageout(&codec->dec_oy, &codec->dec_og)!=1) { fprintf(stderr, "decode: ogg_sync_pageout: Must not be Vorbis data\n"); return 1; } ogg_stream_init(&codec->dec_os, ogg_page_serialno(&codec->dec_og)); vorbis_info_init(&codec->dec_vi); vorbis_comment_init(&codec->dec_vc); if(ogg_stream_pagein(&codec->dec_os, &codec->dec_og) < 0) { fprintf(stderr,"decode: ogg_stream_pagein: stream version mismatch perhaps.\n"); return 1; } if(ogg_stream_packetout(&codec->dec_os, &codec->dec_op) != 1) { fprintf(stderr, "decode: ogg_stream_packetout: Must not be Vorbis data\n"); return 1; } if(vorbis_synthesis_headerin(&codec->dec_vi, &codec->dec_vc, &codec->dec_op) < 0) { fprintf(stderr, "decode: vorbis_synthesis_headerin: not a vorbis header\n"); return 1; } i = 0; while(i < 2) { while(i < 2) { result = ogg_sync_pageout(&codec->dec_oy, &codec->dec_og); if(result == 0) break; if(result == 1) { ogg_stream_pagein(&codec->dec_os, &codec->dec_og); while(i < 2) { result = ogg_stream_packetout(&codec->dec_os, &codec->dec_op); if(result == 0) break; if(result < 0) { fprintf(stderr, "decode: ogg_stream_packetout: corrupt secondary header\n"); return 1; } vorbis_synthesis_headerin(&codec->dec_vi, &codec->dec_vc, &codec->dec_op); i++; } } } if(i < 2) { READ_CHUNK(init_chunk); init_chunk++; } // Header should never span more than one chunk so assume it's done here } vorbis_synthesis_init(&codec->dec_vd, &codec->dec_vi); vorbis_block_init(&codec->dec_vd, &codec->dec_vb); // Also the first chunk needed in decoding so don't reread after this. if(codec->chunk == init_chunk - 1) { have_chunk = 1; codec->chunk++; } } // Don't already have initial chunk from header if(!have_chunk) { // Get initial chunk for decoding at new location // From vorbisfile.c /* clear out decoding machine state */ ogg_stream_clear(&codec->dec_os); vorbis_dsp_clear(&codec->dec_vd); vorbis_block_clear(&codec->dec_vb); ogg_sync_reset(&codec->dec_oy); ogg_stream_init(&codec->dec_os, ogg_page_serialno(&codec->dec_og)); ogg_sync_init(&codec->dec_oy); vorbis_synthesis_init(&codec->dec_vd, &codec->dec_vi); vorbis_block_init(&codec->dec_vd, &codec->dec_vb); READ_CHUNK(codec->chunk); codec->chunk++; have_chunk = 1; } } // Assume the chunk exists by now and rely on libogg to say if it's out of // data. have_chunk = 1; // Read chunks until output buffer is on or after end_position result = 0; while(codec->output_position < end_position) { // Read chunk to decode if it hasn't been read yet. if(!have_chunk) { codec->chunk_samples = 0; READ_CHUNK(codec->chunk); if(result) break; codec->chunk++; } eos = 0; while(!eos) { result = ogg_sync_pageout(&codec->dec_oy, &codec->dec_og); // Need more data from chunk if(result == 0) { // End of chunk eos = 1; } else // This stage checks for OggS and a checksum error. // It doesn't tell if it's the end of a chunk. Need to manually parse OggS // pages to figure out how big the chunk is. if(result < 0) { //printf("ogg_sync_pageout=-1\n"); ; } else { ogg_stream_pagein(&codec->dec_os, &codec->dec_og); while(!eos) { //printf("decode 7\n"); result = ogg_stream_packetout(&codec->dec_os, &codec->dec_op); //printf("decode 8 %d\n", result); if(result == 0) { //printf("ogg_stream_packetout=0\n"); // End of page eos = 1; } else // This stage doesn't check for OggS. if(result < 0) { //printf("ogg_stream_packetout=-1\n"); } else { float **pcm; if(vorbis_synthesis(&codec->dec_vb, &codec->dec_op) == 0) { vorbis_synthesis_blockin(&codec->dec_vd, &codec->dec_vb); } while((result = vorbis_synthesis_pcmout(&codec->dec_vd, &pcm)) > 0) { //printf("vorbis_synthesis_pcmout=%x\n", result); for(i = 0; i < track_map->channels; i++) { float *output_channel = codec->output[i]; float *input_channel = pcm[i]; int k = codec->output_end; for(j = 0; j < result; j++) { output_channel[k++] = input_channel[j]; if(k >= codec->output_allocated) k = 0; } if(i == track_map->channels - 1) codec->output_end = k; } //printf("codec->output_end = %d\n", codec->output_end); codec->output_position += result; codec->output_size += result; codec->chunk_samples += result; if(codec->output_size > codec->output_allocated) codec->output_size = codec->output_allocated; vorbis_synthesis_read(&codec->dec_vd, result); } } //printf("decode 11\n"); } // Reset end of page so it isn't interpreted as an end of chunk eos = 0; } } // Next chunk if(eos) { //printf("decode 12 got=%x\n", codec->chunk_samples); have_chunk = 0; } } // Fill silence while(codec->output_position < end_position) { for(i = 0; i < track_map->channels; i++) codec->output[i][codec->output_end] = 0; codec->output_end++; if(codec->output_end >= codec->output_allocated) codec->output_end = 0; codec->output_position++; } //printf("decode 15\n"); //printf("decode 2 codec->output_position=%lld codec->output_end=%d codec->output_size=%d\n", // codec->output_position, codec->output_end, codec->output_size); current_position = track_map->current_position; i = codec->output_end - (codec->output_position - current_position); j = 0; while(i < 0) i += codec->output_allocated; pcm = codec->output[channel]; if(output_i) { for( ; j < samples; j++) { int sample = pcm[i] * 32767; CLAMP(sample, -32768, 32767); output_i[j] = sample; i++; if(i >= codec->output_allocated) i = 0; } } else if(output_f) { for( ; j < samples; j++) { output_f[j] = pcm[i]; i++; if(i >= codec->output_allocated) i = 0; } } //printf("decode 16\n"); return 0; }
int main (int argc, char **argv) { int indexing, ch; FILE *in; ogg_sync_state oy; ogg_page og; ogg_stream_state os; indexing = 0; while ((ch = getopt(argc, argv, "io:")) != -1) { switch (ch) { case 'i': indexing = 1; break; case 'o': break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc == 0) { usage (); return 1; } in = fopen (argv[0], "rb"); if (in == NULL) { perror ("error opening input"); return -1; } /* initialise ogg_sync_state */ ogg_sync_init (&oy); /* process all the headers of the Ogg container */ while (1) { int ret = buffer_data (in, &oy); if (ret == 0) break; while (ogg_sync_pageout (&oy, &og) > 0) { ogg_packet op; int got_packet; ogg_stream_init (&os, ogg_page_serialno (&og)); ogg_stream_pagein (&os, &og); if ((got_packet = ogg_stream_packetout (&os, &op))) { } } } fclose (in); return 0; }