static bool vorbis_encoder_tag(struct encoder *_encoder, const struct tag *tag, G_GNUC_UNUSED GError **error) { struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; vorbis_comment comment; /* write the vorbis_comment object */ vorbis_comment_init(&comment); copy_tag_to_vorbis_comment(&comment, tag); /* reset ogg_stream_state and begin a new stream */ ogg_stream_reset_serialno(&encoder->os, g_random_int()); /* send that vorbis_comment to the ogg_stream_state */ vorbis_encoder_headerout(encoder, &comment); vorbis_comment_clear(&comment); /* the next vorbis_encoder_read() call should flush the ogg_stream_state */ encoder->flush = true; return true; }
static PyObject * py_ogg_ogg_stream_reset_serialno(PyObject *self, PyObject *args) { int c_out; ogg_stream_state * os; int serialno; int size; PyArg_ParseTuple(args, "s#i", &os, &size, &serialno); c_out = ogg_stream_reset_serialno(os, serialno); return Py_BuildValue("i", c_out); };
/* we no longer preload all vorbis_info (and the associated codec_setup) structs. Call this to seek and fetch the info from the bitstream, if needed */ static int _set_link_number(OggVorbis_File *vf,int link){ if(link != vf->current_link) _decode_clear(vf); if(vf->ready_state<STREAMSET){ _seek_helper(vf,vf->offsets[link]); ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); vf->current_serialno=vf->serialnos[link]; vf->current_link=link; return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL); } return 0; }
/* uses the local ogg_stream storage in vf; this is important for non-streaming input sources */ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, long *serialno,ogg_page *og_ptr){ ogg_page og; ogg_packet op; int i,ret; if(!og_ptr){ ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); if(llret==OV_EREAD)return(OV_EREAD); if(llret<0)return OV_ENOTVORBIS; og_ptr=&og; } ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); if(serialno)*serialno=vf->os.serialno; vf->ready_state=STREAMSET; /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ vorbis_info_init(vi); vorbis_comment_init(vc); i=0; while(i<3){ ogg_stream_pagein(&vf->os,og_ptr); while(i<3){ int result=ogg_stream_packetout(&vf->os,&op); if(result==0)break; if(result==-1){ ret=OV_EBADHEADER; goto bail_header; } if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ goto bail_header; } i++; } if(i<3) if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ ret=OV_EBADHEADER; goto bail_header; } } return 0; bail_header: vorbis_info_clear(vi); vorbis_comment_clear(vc); vf->ready_state=OPENED; return ret; }
static int spx_read_header (SF_PRIVATE * psf) { static SpeexStereoState STEREO_INIT = SPEEX_STEREO_STATE_INIT ; OGG_PRIVATE* odata = psf->container_data ; SPX_PRIVATE* spx = psf->codec_data ; ogg_int64_t page_granule = 0 ; int stream_init = 0 ; int page_nb_packets = 0 ; int packet_count = 0 ; int enh_enabled = 1 ; int force_mode = -1 ; char * data ; int nb_read ; int lookahead ; printf ("%s %d\n", __func__, __LINE__) ; psf_log_printf (psf, "Speex header\n") ; odata->eos = 0 ; /* Reset ogg stuff which has already been used in src/ogg.c. */ ogg_stream_reset (&odata->ostream) ; ogg_sync_reset (&odata->osync) ; /* Seek to start of stream. */ psf_fseek (psf, 0, SEEK_SET) ; /* Initialize. */ ogg_sync_init (&odata->osync) ; speex_bits_init (&spx->bits) ; /* Set defaults. */ psf->sf.channels = -1 ; psf->sf.samplerate = 0 ; spx->stereo = STEREO_INIT ; /* Get a pointer to the ogg buffer and read data into it. */ data = ogg_sync_buffer (&odata->osync, OGG_SPX_READ_SIZE) ; nb_read = psf_fread (data, 1, OGG_SPX_READ_SIZE, psf) ; ogg_sync_wrote (&odata->osync, nb_read) ; /* Now we chew on Ogg packets. */ while (ogg_sync_pageout (&odata->osync, &odata->opage) == 1) { if (stream_init == 0) { ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; stream_init = 1 ; } ; if (ogg_page_serialno (&odata->opage) != odata->ostream.serialno) { /* so all streams are read. */ ogg_stream_reset_serialno (&odata->ostream, ogg_page_serialno (&odata->opage)) ; } ; /*Add page to the bitstream*/ ogg_stream_pagein (&odata->ostream, &odata->opage) ; page_granule = ogg_page_granulepos (&odata->opage) ; page_nb_packets = ogg_page_packets (&odata->opage) ; /*Extract all available packets*/ while (odata->eos == 0 && ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1) { if (odata->opacket.bytes >= 8 && memcmp (odata->opacket.packet, "Speex ", 8) == 0) { spx->serialno = odata->ostream.serialno ; } ; if (spx->serialno == -1 || odata->ostream.serialno != spx->serialno) break ; if (packet_count == 0) { spx->state = spx_header_read (psf, &odata->opacket, enh_enabled, force_mode) ; if (! spx->state) break ; speex_decoder_ctl (spx->state, SPEEX_GET_LOOKAHEAD, &lookahead) ; if (spx->nframes == 0) spx->nframes = 1 ; } else if (packet_count == 1) { spx_print_comments ((const char*) odata->opacket.packet, odata->opacket.bytes) ; } else if (packet_count < 2 + spx->header.extra_headers) { /* Ignore extra headers */ } packet_count ++ ; } ; } ; psf_log_printf (psf, "End\n") ; psf_log_printf (psf, "packet_count %d\n", packet_count) ; psf_log_printf (psf, "page_nb_packets %d\n", page_nb_packets) ; psf_log_printf (psf, "page_granule %lld\n", page_granule) ; return 0 ; } /* spx_read_header */
UInt32 OggSpeexDecoder::ReadAudio(AudioBufferList *bufferList, UInt32 frameCount) { if(!IsOpen() || NULL == bufferList || bufferList->mNumberBuffers != mFormat.mChannelsPerFrame || 0 == frameCount) return 0; UInt32 framesRead = 0; // Reset output buffer data size for(UInt32 i = 0; i < bufferList->mNumberBuffers; ++i) bufferList->mBuffers[i].mDataByteSize = 0; for(;;) { UInt32 framesRemaining = frameCount - framesRead; UInt32 framesToSkip = static_cast<UInt32>(bufferList->mBuffers[0].mDataByteSize / sizeof(float)); UInt32 framesInBuffer = static_cast<UInt32>(mBufferList->mBuffers[0].mDataByteSize / sizeof(float)); UInt32 framesToCopy = std::min(framesInBuffer, framesRemaining); // Copy data from the buffer to output for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) { float *floatBuffer = static_cast<float *>(bufferList->mBuffers[i].mData); memcpy(floatBuffer + framesToSkip, mBufferList->mBuffers[i].mData, framesToCopy * sizeof(float)); bufferList->mBuffers[i].mDataByteSize += static_cast<UInt32>(framesToCopy * sizeof(float)); // Move remaining data in buffer to beginning if(framesToCopy != framesInBuffer) { floatBuffer = static_cast<float *>(mBufferList->mBuffers[i].mData); memmove(floatBuffer, floatBuffer + framesToCopy, (framesInBuffer - framesToCopy) * sizeof(float)); } mBufferList->mBuffers[i].mDataByteSize -= static_cast<UInt32>(framesToCopy * sizeof(float)); } framesRead += framesToCopy; // All requested frames were read if(framesRead == frameCount) break; // EOS reached if(mSpeexEOSReached) break; // Attempt to process the desired number of packets unsigned packetsDesired = 1; while(0 < packetsDesired && !mSpeexEOSReached) { // Process any packets in the current page while(0 < packetsDesired && !mSpeexEOSReached) { // Grab a packet from the streaming layer ogg_packet oggPacket; int result = ogg_stream_packetout(&mOggStreamState, &oggPacket); if(-1 == result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding error: Ogg loss of streaming"); break; } // If result is 0, there is insufficient data to assemble a packet if(0 == result) break; // Otherwise, we got a valid packet for processing if(1 == result) { if(5 <= oggPacket.bytes && !memcmp(oggPacket.packet, "Speex", 5)) mSpeexSerialNumber = mOggStreamState.serialno; if(-1 == mSpeexSerialNumber || mOggStreamState.serialno != mSpeexSerialNumber) break; // Ignore the following: // - Speex comments in packet #2 // - Extra headers (optionally) in packets 3+ if(1 != mOggPacketCount && 1 + mExtraSpeexHeaderCount <= mOggPacketCount) { // Detect Speex EOS if(oggPacket.e_o_s && mOggStreamState.serialno == mSpeexSerialNumber) mSpeexEOSReached = true; // SPEEX_GET_FRAME_SIZE is in samples spx_int32_t speexFrameSize; speex_decoder_ctl(mSpeexDecoder, SPEEX_GET_FRAME_SIZE, &speexFrameSize); float buffer [(2 == mFormat.mChannelsPerFrame) ? 2 * speexFrameSize : speexFrameSize]; // Copy the Ogg packet to the Speex bitstream speex_bits_read_from(&mSpeexBits, (char *)oggPacket.packet, static_cast<int>(oggPacket.bytes)); // Decode each frame in the Speex packet for(spx_int32_t i = 0; i < mSpeexFramesPerOggPacket; ++i) { result = speex_decode(mSpeexDecoder, &mSpeexBits, buffer); // -1 indicates EOS if(-1 == result) break; else if(-2 == result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding error: possible corrupted stream"); break; } if(0 > speex_bits_remaining(&mSpeexBits)) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Ogg Speex decoding overflow: possible corrupted stream"); break; } // Normalize the values float maxSampleValue = 1u << 15; vDSP_vsdiv(buffer, 1, &maxSampleValue, buffer, 1, speexFrameSize); // Copy the frames from the decoding buffer to the output buffer, skipping over any frames already decoded framesInBuffer = static_cast<UInt32>(mBufferList->mBuffers[0].mDataByteSize / sizeof(float)); memcpy(static_cast<float *>(mBufferList->mBuffers[0].mData) + framesInBuffer, buffer, speexFrameSize * sizeof(float)); mBufferList->mBuffers[0].mDataByteSize += static_cast<UInt32>(speexFrameSize * sizeof(float)); // Process stereo channel, if present if(2 == mFormat.mChannelsPerFrame) { speex_decode_stereo(buffer, speexFrameSize, mSpeexStereoState); vDSP_vsdiv(buffer + speexFrameSize, 1, &maxSampleValue, buffer + speexFrameSize, 1, speexFrameSize); memcpy(static_cast<float *>(mBufferList->mBuffers[1].mData) + framesInBuffer, buffer + speexFrameSize, speexFrameSize * sizeof(float)); mBufferList->mBuffers[1].mDataByteSize += static_cast<UInt32>(speexFrameSize * sizeof(float)); } // Packet processing finished --packetsDesired; } } ++mOggPacketCount; } } // Grab a new Ogg page for processing, if necessary if(!mSpeexEOSReached && 0 < packetsDesired) { while(1 != ogg_sync_pageout(&mOggSyncState, &mOggPage)) { // Get the ogg buffer for writing char *data = ogg_sync_buffer(&mOggSyncState, READ_SIZE_BYTES); // Read bitstream from input file ssize_t bytesRead = GetInputSource()->Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Unable to read from the input file"); break; } ogg_sync_wrote(&mOggSyncState, bytesRead); // No more data available from input file if(0 == bytesRead) break; } // Ensure all Ogg streams are read if(ogg_page_serialno(&mOggPage) != mOggStreamState.serialno) ogg_stream_reset_serialno(&mOggStreamState, ogg_page_serialno(&mOggPage)); // Get the resultant Ogg page int result = ogg_stream_pagein(&mOggStreamState, &mOggPage); if(0 != result) { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Error reading Ogg page"); break; } } } } mCurrentFrame += framesRead; if(0 == framesRead && mSpeexEOSReached) mTotalFrames = mCurrentFrame; return framesRead; }
/* this is void and does not propogate errors up because we want to be able to open and use damaged bitstreams as well as we can. Just watch out for missing information for links in the OggVorbis_File struct */ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ ogg_page og={0,0,0,0}; int i; ogg_int64_t ret; vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); for(i=0;i<vf->links;i++){ if(i==0){ /* we already grabbed the initial header earlier. Just set the offset */ vf->dataoffsets[i]=dataoffset; _seek_helper(vf,dataoffset); }else{ /* seek to the location of the initial header */ _seek_helper(vf,vf->offsets[i]); if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){ vf->dataoffsets[i]=-1; }else{ vf->dataoffsets[i]=vf->offset; } } /* fetch beginning PCM offset */ if(vf->dataoffsets[i]!=-1){ ogg_int64_t accumulated=0,pos; long lastblock=-1; int result; ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); while(1){ ogg_packet op={0,0,0,0,0,0}; ret=_get_next_page(vf,&og,-1); if(ret<0) /* this should not be possible unless the file is truncated/mangled */ break; if(ogg_page_serialno(&og)!=vf->serialnos[i]) break; pos=ogg_page_granulepos(&og); /* count blocksizes of all frames in the page */ ogg_stream_pagein(vf->os,&og); while((result=ogg_stream_packetout(vf->os,&op))){ if(result>0){ /* ignore holes */ long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); if(lastblock!=-1) accumulated+=(lastblock+thisblock)>>2; lastblock=thisblock; } } ogg_packet_release(&op); if(pos!=-1){ /* pcm offset of last packet on the first audio page */ accumulated= pos-accumulated; break; } } /* less than zero? This is a stream with samples trimmed off the beginning, a normal occurrence; set the offset to zero */ if(accumulated<0)accumulated=0; vf->pcmlengths[i*2]=accumulated; }
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 && op.bytes>=8) { if (!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); else ret = celt_decode(st, NULL, 0, output); /*for (i=0;i<frame_size*channels;i++) printf ("%d\n", (int)output[i]);*/ if (ret!=0) { fprintf (stderr, "Decoding error: corrupted stream?\n"); 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 void ReleaseStream(ogg* p,oggstream* Stream) { ogg_stream_reset_serialno(Stream->OggStream,Stream->Stream.Id); Stream->NeedMorePage = 1; Stream->MediaTime = Stream->Stream.Reader->FilePos>0 && Stream->PacketNo>3 ? -1:0; }
void SndSysSpeexSoundStream::AdvancePosition(size_t frame_delta) { if (m_bPaused || m_bPlaybackReadComplete || frame_delta==0) return; // Figure out how many bytes we need to fill for this advancement size_t needed_bytes = frame_delta * (m_RenderFormat.Bits/8) * m_RenderFormat.Channels; // If we need more space than is available in the whole cyclic buffer, then we already underbuffered, reduce to just 1 cycle full if ((size_t)needed_bytes > m_pCyclicBuffer->GetLength()) needed_bytes=(size_t)(m_pCyclicBuffer->GetLength() & 0x7FFFFFFF); // Free space in the cyclic buffer if necessary if ((size_t)needed_bytes > m_pCyclicBuffer->GetFreeBytes()) m_pCyclicBuffer->AdvanceStartValue(needed_bytes - (size_t)(m_pCyclicBuffer->GetFreeBytes() & 0x7FFFFFFF)); // Fill in leftover decoded data if needed if (m_PreparedDataBufferUsage > 0) needed_bytes-=CopyBufferBytes(needed_bytes); while (needed_bytes > 0) { if(newPage) { if(ogg_sync_pageout(&oy, &og) != 1) { // Mark as complete if not looping. if (!m_bLooping) { m_bPlaybackReadComplete = true; } // Reset stream. ResetPosition(); return; } if (!stream_init) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = true; } if (ogg_page_serialno(&og) != os.serialno) { ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); } ogg_stream_pagein(&os, &og); newPage = false; } if(ogg_stream_packetout(&os, &op) != 1) { newPage = true; continue; } // First packets contain header data. if(packet_count == 0) { if(header) { speex_header_free(header); } header = speex_packet_to_header((char*)op.packet, op.bytes); // const_cast for version compatibility. SpeexMode* mode = const_cast<SpeexMode*>(speex_lib_get_mode (header->mode)); state = speex_decoder_init(mode); speex_decoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &header->rate); m_OutputFrequency=m_NewOutputFrequency; // Create the pcm sample converter if it's not yet created if (m_pPCMConverter == 0) m_pPCMConverter = new PCMSampleConverter ( m_RenderFormat.Channels, m_RenderFormat.Bits, m_RenderFormat.Freq); // Calculate the size of one source sample int source_sample_size = m_RenderFormat.Channels * m_RenderFormat.Bits; // Calculate the needed buffer size for this conversion int needed_buffer = (m_pPCMConverter->GetRequiredOutputBufferMultiple ( m_RenderFormat.Channels, m_RenderFormat.Bits, m_OutputFrequency) * (4096 + source_sample_size))/1024; // Allocate a new buffer. if (m_PreparedDataBufferSize < needed_buffer) { delete[] m_pPreparedDataBuffer; m_pPreparedDataBuffer = new char[needed_buffer]; m_PreparedDataBufferSize=needed_buffer; } } if(packet_count++ < uint(2+header->extra_headers)) { continue; } // Read and decode. speex_bits_read_from(&bits, (char*)op.packet, op.bytes); speex_decode_int(state, &bits, (int16*)m_pPreparedDataBuffer); // Frame size is in shorts. speex_decoder_ctl(state, SPEEX_GET_FRAME_SIZE, &m_PreparedDataBufferUsage); m_PreparedDataBufferUsage *= sizeof(short); if (m_PreparedDataBufferUsage > 0) needed_bytes -= CopyBufferBytes (needed_bytes); } }
int main(int argc, char **argv) { int c; int option_index = 0; char *inFile, *outFile; FILE *fin, *fout=NULL, *frange=NULL; float *output; int frame_size=0; OpusMSDecoder *st=NULL; opus_int64 packet_count=0; int total_links=0; int stream_init = 0; int quiet = 0; ogg_int64_t page_granule=0; ogg_int64_t link_out=0; 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}, {"gain", required_argument, NULL, 0}, {"no-dither", no_argument, NULL, 0}, {"packet-loss", required_argument, NULL, 0}, {"save-range", required_argument, NULL, 0}, {0, 0, 0, 0} }; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int close_in=0; int eos=0; ogg_int64_t audio_size=0; double last_coded_seconds=0; float loss_percent=-1; float manual_gain=0; int channels=-1; int mapping_family; int rate=0; int wav_format=0; int preskip=0; int gran_offset=0; int has_opus_stream=0; ogg_int32_t opus_serialno; int dither=1; shapestate shapemem; SpeexResamplerState *resampler=NULL; float gain=1; int streams=0; size_t last_spin=0; #ifdef WIN_UNICODE int argc_utf8; char **argv_utf8; #endif if(query_cpu_support()){ fprintf(stderr,"\n\n** WARNING: This program with compiled with SSE%s\n",query_cpu_support()>1?"2":""); fprintf(stderr," but this CPU claims to lack these instructions. **\n\n"); } #ifdef WIN_UNICODE (void)argc; (void)argv; init_console_utf8(); init_commandline_arguments_utf8(&argc_utf8, &argv_utf8); #endif output=0; shapemem.a_buf=0; shapemem.b_buf=0; shapemem.mute=960; shapemem.fs=0; /*Process options*/ while(1) { c = getopt_long (argc_utf8, argv_utf8, "hV", long_options, &option_index); if (c==-1) break; switch(c) { case 0: if (strcmp(long_options[option_index].name,"help")==0) { usage(); quit(0); } else if (strcmp(long_options[option_index].name,"quiet")==0) { quiet = 1; } else if (strcmp(long_options[option_index].name,"version")==0) { version(); quit(0); } else if (strcmp(long_options[option_index].name,"version-short")==0) { version_short(); quit(0); } else if (strcmp(long_options[option_index].name,"no-dither")==0) { dither=0; } else if (strcmp(long_options[option_index].name,"rate")==0) { rate=atoi (optarg); } else if (strcmp(long_options[option_index].name,"gain")==0) { manual_gain=atof (optarg); }else if(strcmp(long_options[option_index].name,"save-range")==0){ frange=fopen_utf8(optarg,"w"); if(frange==NULL){ perror(optarg); fprintf(stderr,"Could not open save-range file: %s\n",optarg); fprintf(stderr,"Must provide a writable file name.\n"); quit(1); } } else if (strcmp(long_options[option_index].name,"packet-loss")==0) { loss_percent = atof(optarg); } break; case 'h': usage(); quit(0); break; case 'V': version(); quit(0); break; case '?': usage(); quit(1); break; } } if (argc_utf8-optind!=2 && argc_utf8-optind!=1) { usage(); quit(1); } inFile=argv_utf8[optind]; /*Output to a file or playback?*/ if (argc_utf8-optind==2){ /*If we're outputting to a file, should we apply a wav header?*/ int i; char *ext; outFile=argv_utf8[optind+1]; ext=".wav"; i=strlen(outFile)-4; wav_format=i>=0; while(wav_format&&ext&&outFile[i]) { wav_format&=tolower(outFile[i++])==*ext++; } }else { outFile=""; wav_format=0; /*If playing to audio out, default the rate to 48000 instead of the original rate. The original rate is only important for minimizing surprise about the rate of output files and preserving length, which aren't relevant for playback. Many audio devices sound better at 48kHz and not resampling also saves CPU.*/ if(rate==0)rate=48000; } /*Open input file*/ if (strcmp(inFile, "-")==0) { #if defined WIN32 || defined _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif fin=stdin; } else { fin = fopen_utf8(inFile, "rb"); if (!fin) { perror(inFile); quit(1); } close_in=1; } /* .opus files use the Ogg container to provide framing and timekeeping. * http://tools.ietf.org/html/draft-terriberry-oggopus * The easiest way to decode the Ogg container is to use libogg, so * thats what we do here. * Using libogg is fairly straight forward-- you take your stream of bytes * and feed them to ogg_sync_ and it periodically returns Ogg pages, you * check if the pages belong to the stream you're decoding then you give * them to libogg and it gives you packets. You decode the packets. The * pages also provide timing information.*/ 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); /*Extract all available packets*/ while (ogg_stream_packetout(&os, &op) == 1) { /*OggOpus streams are identified by a magic string in the initial stream header.*/ if (op.b_o_s && op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) { if(!has_opus_stream) { opus_serialno = os.serialno; has_opus_stream = 1; link_out = 0; packet_count = 0; eos = 0; total_links++; } else { fprintf(stderr,"Warning: ignoring opus stream %" I64FORMAT "\n",(long long)os.serialno); } } if (!has_opus_stream || os.serialno != opus_serialno) break; /*If first packet in a logical stream, process the Opus header*/ if (packet_count==0) { st = process_header(&op, &rate, &mapping_family, &channels, &preskip, &gain, manual_gain, &streams, wav_format, quiet); if (!st) quit(1); /*Remember how many samples at the front we were told to skip so that we can adjust the timestamp counting.*/ gran_offset=preskip; /*Setup the memory for the dithered output*/ if(!shapemem.a_buf) { shapemem.a_buf=calloc(channels,sizeof(float)*4); shapemem.b_buf=calloc(channels,sizeof(float)*4); shapemem.fs=rate; } if(!output)output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels); /*Normal players should just play at 48000 or their maximum rate, as described in the OggOpus spec. But for commandline tools like opusdec it can be desirable to exactly preserve the original sampling rate and duration, so we have a resampler here.*/ if (rate != 48000) { int err; resampler = speex_resampler_init(channels, 48000, rate, 5, &err); if (err!=0) fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err)); speex_resampler_skip_zeros(resampler); } if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels); } else if (packet_count==1) { if (!quiet) print_comments((char*)op.packet, op.bytes); } else { int ret; opus_int64 maxout; opus_int64 outsamp; 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 == opus_serialno)eos=1; /* don't care for anything except opus eos */ /*Are we simulating loss for this packet?*/ if (!lost){ /*Decode Opus packet*/ ret = opus_multistream_decode_float(st, (unsigned char*)op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); } else { /*Extract the original duration. Normally you wouldn't have it for a lost packet, but normally the transports used on lossy channels will effectively tell you. This avoids opusdec squaking when the decoded samples and granpos mismatches.*/ opus_int32 lost_size; lost_size = MAX_FRAME_SIZE; if(op.bytes>0){ opus_int32 spp; spp=opus_packet_get_nb_frames(op.packet,op.bytes); if(spp>0){ spp*=opus_packet_get_samples_per_frame(op.packet,48000/*decoding_rate*/); if(spp>0)lost_size=spp; } } /*Invoke packet loss concealment.*/ ret = opus_multistream_decode_float(st, NULL, 0, output, lost_size, 0); } if(!quiet){ /*Display a progress spinner while decoding.*/ static const char spinner[]="|/-\\"; double coded_seconds = (double)audio_size/(channels*rate*sizeof(short)); if(coded_seconds>=last_coded_seconds+1){ fprintf(stderr,"\r[%c] %02d:%02d:%02d", spinner[last_spin&3], (int)(coded_seconds/3600),(int)(coded_seconds/60)%60, (int)(coded_seconds)%60); fflush(stderr); last_spin++; last_coded_seconds=coded_seconds; } } /*If the decoder returned less than zero, we have an error.*/ if (ret<0) { fprintf (stderr, "Decoding error: %s\n", opus_strerror(ret)); break; } frame_size = ret; /*If we're collecting --save-range debugging data, collect it now.*/ if(frange!=NULL){ OpusDecoder *od; opus_uint32 rngs[256]; for(i=0;i<streams;i++){ ret=opus_multistream_decoder_ctl(st,OPUS_MULTISTREAM_GET_DECODER_STATE(i,&od)); ret=opus_decoder_ctl(od,OPUS_GET_FINAL_RANGE(&rngs[i])); } save_range(frange,frame_size*(48000/48000/*decoding_rate*/),op.packet,op.bytes, rngs,streams); } /*Apply header gain, if we're not using an opus library new enough to do this internally.*/ if (gain!=0){ for (i=0;i<frame_size*channels;i++) output[i] *= gain; } /*This handles making sure that our output duration respects the final end-trim by not letting the output sample count get ahead of the granpos indicated value.*/ maxout=((page_granule-gran_offset)*rate/48000)-link_out; outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout); link_out+=outsamp; audio_size+=sizeof(short)*outsamp*channels; } packet_count++; } /*We're done, drain the resampler if we were using it.*/ if(eos && resampler) { float *zeros; int drain; zeros=(float *)calloc(100*channels,sizeof(float)); drain = speex_resampler_get_input_latency(resampler); do { opus_int64 outsamp; int tmp = drain; if (tmp > 100) tmp = 100; outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out); link_out+=outsamp; audio_size+=sizeof(short)*outsamp*channels; drain -= tmp; } while (drain>0); free(zeros); speex_resampler_destroy(resampler); resampler=NULL; } if(eos) { has_opus_stream=0; if(st)opus_multistream_decoder_destroy(st); st=NULL; } } if (feof(fin)) { if(!quiet) { fprintf(stderr, "\rDecoding complete. \n"); fflush(stderr); } break; } } /*If we were writing wav, go set the duration.*/ if (strlen(outFile)!=0 && fout && wav_format>=0 && audio_size<0x7FFFFFFF) { if (fseek(fout,4,SEEK_SET)==0) { int tmp; tmp = le_int(audio_size+20+wav_format); if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing end length.\n"); if (fseek(fout,16+wav_format,SEEK_CUR)==0) { tmp = le_int(audio_size); if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing header length.\n"); } else { fprintf (stderr, "First seek worked, second didn't\n"); } } else { fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n"); } } /*Did we make it to the end without recovering ANY opus logical streams?*/ if(!total_links)fprintf (stderr, "This doesn't look like a Opus 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(shapemem.a_buf)free(shapemem.a_buf); if(shapemem.b_buf)free(shapemem.b_buf); if(output)free(output); if(frange)fclose(frange); if (close_in) fclose(fin); if (fout != NULL) fclose(fout); #ifdef WIN_UNICODE free_commandline_arguments_utf8(&argc_utf8, &argv_utf8); uninit_console_utf8(); #endif return 0; }
static int _fetch_headers(OggVorbis_File *vf, vorbis_info *vi, vorbis_comment *vc, ogg_uint32_t *serialno, ogg_page *og_ptr){ ogg_page og={0,0,0,0}; ogg_packet op={0,0,0,0,0,0}; int i,ret; if(vf->ready_state>OPENED)_decode_clear(vf); if(!og_ptr){ ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); if(llret==OV_EREAD)return OV_EREAD; if(llret<0)return OV_ENOTVORBIS; og_ptr=&og; } ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr)); if(serialno)*serialno=vf->os->serialno; /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ vorbis_info_init(vi); vorbis_comment_init(vc); i=0; while(i<3){ ogg_stream_pagein(vf->os,og_ptr); while(i<3){ int result=ogg_stream_packetout(vf->os,&op); if(result==0)break; if(result==-1){ ret=OV_EBADHEADER; goto bail_header; } if((ret=vorbis_dsp_headerin(vi,vc,&op))){ goto bail_header; } i++; } if(i<3) if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ ret=OV_EBADHEADER; goto bail_header; } } ogg_packet_release(&op); ogg_page_release(&og); vf->ready_state=LINKSET; return 0; bail_header: ogg_packet_release(&op); ogg_page_release(&og); vorbis_info_clear(vi); vorbis_comment_clear(vc); vf->ready_state=OPENED; return ret; }
static int read_packet(struct ast_filestream *fs) { struct speex_desc *s = (struct speex_desc *)fs->_private; char *buffer; int result; size_t bytes; while (1) { /* Get one packet */ result = ogg_stream_packetout(&s->os, &s->op); if (result > 0) { if (s->op.bytes >= 5 && !memcmp(s->op.packet, "Speex", 5)) { s->serialno = s->os.serialno; } if (s->serialno == -1 || s->os.serialno != s->serialno) { continue; } return 0; } if (result < 0) { ast_log(LOG_WARNING, "Corrupt or missing data at this page position; continuing...\n"); } /* No more packets left in the current page... */ if (s->eos) { /* No more pages left in the stream */ return -1; } while (!s->eos) { /* See if OGG has any pages in it's internal buffers */ result = ogg_sync_pageout(&s->oy, &s->og); if (result > 0) { /* Read all streams. */ if (ogg_page_serialno(&s->og) != s->os.serialno) { ogg_stream_reset_serialno(&s->os, ogg_page_serialno(&s->og)); } /* Yes, OGG has more pages in it's internal buffers, add the page to the stream state */ result = ogg_stream_pagein(&s->os, &s->og); if (result == 0) { /* Yes, got a new, valid page */ if (ogg_page_eos(&s->og) && ogg_page_serialno(&s->og) == s->serialno) s->eos = 1; break; } ast_log(LOG_WARNING, "Invalid page in the bitstream; continuing...\n"); } if (result < 0) { ast_log(LOG_WARNING, "Corrupt or missing data in bitstream; continuing...\n"); } /* No, we need to read more data from the file descrptor */ /* get a buffer from OGG to read the data into */ buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE); bytes = fread(buffer, 1, BLOCK_SIZE, fs->f); ogg_sync_wrote(&s->oy, bytes); if (bytes == 0) { s->eos = 1; } } } }