/*********************************************************************** * Flush *********************************************************************** * **********************************************************************/ static hb_buffer_t * Flush( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; int64_t blocksize = 0; if( vorbis_analysis_blockout( &pv->vd, &pv->vb ) == 1 ) { ogg_packet op; vorbis_analysis( &pv->vb, NULL ); vorbis_bitrate_addblock( &pv->vb ); if( vorbis_bitrate_flushpacket( &pv->vd, &op ) ) { buf = hb_buffer_init( sizeof( ogg_packet ) + op.bytes ); memcpy( buf->data, &op, sizeof( ogg_packet ) ); memcpy( buf->data + sizeof( ogg_packet ), op.packet, op.bytes ); blocksize = vorbis_packet_blocksize(&pv->vi, &op); buf->frametype = HB_FRAME_AUDIO; buf->start = (int64_t)(vorbis_granule_time(&pv->vd, op.granulepos) * 90000); buf->stop = (int64_t)(vorbis_granule_time(&pv->vd, (pv->prev_blocksize + blocksize)/4 + op.granulepos) * 90000); /* The stop time isn't accurate for the first ~3 packets, as the actual blocksize depends on the previous _and_ current packets. */ pv->prev_blocksize = blocksize; return buf; } } return NULL; }
static int tc_frame_audio_add_ogg_packet(VorbisPrivateData *pd, TCFrameAudio *f, ogg_packet *op) { int16_t *pkt_num = (int16_t*)f->audio_buf; double ts = vorbis_granule_time(&(pd->vd), op->granulepos); int needed = sizeof(*op) + op->bytes; int avail = f->audio_size - f->audio_len; TC_FRAME_SET_TIMESTAMP_DOUBLE(f, ts); if (avail < needed) { tc_log_error(__FILE__, "(%s) no buffer in frame: (avail=%i|needed=%i)", __func__, avail, needed); return TC_ERROR; } ac_memcpy(f->audio_buf + f->audio_len, op, sizeof(*op)); f->audio_len += sizeof(*op); ac_memcpy(f->audio_buf + f->audio_len, op->packet, op->bytes); f->audio_len += op->bytes; *pkt_num += 1; if (op->e_o_s) { f->attributes |= TC_FRAME_IS_END_OF_STREAM; // useless? } return TC_OK; }
float VideoClip_Theora::_decodeAudio() { if (this->restarted) { return -1.0f; } ogg_packet opVorbis; float** pcm; int length = 0; float timeStamp = -1.0f; bool readPastTimestamp = false; float factor = 1.0f / this->audioFrequency; float videoTime = (float)this->lastDecodedFrameNumber / this->fps; float min = this->frameQueue->getSize() / this->fps + 1.0f; float audioTime = 0.0f; while (true) { length = vorbis_synthesis_pcmout(&this->info.VorbisDSPState, &pcm); if (length == 0) { if (ogg_stream_packetout(&this->info.VorbisStreamState, &opVorbis) > 0) { if (vorbis_synthesis(&this->info.VorbisBlock, &opVorbis) == 0) { if (timeStamp < 0 && opVorbis.granulepos >= 0) { timeStamp = (float)vorbis_granule_time(&this->info.VorbisDSPState, opVorbis.granulepos); } else if (timeStamp >= 0) { readPastTimestamp = true; } vorbis_synthesis_blockin(&this->info.VorbisDSPState, &this->info.VorbisBlock); } continue; } audioTime = this->readAudioSamples * factor; // always buffer up of audio ahead of the frames if (audioTime - videoTime >= min || !this->_readData()) { break; } } if (length > 0) { this->addAudioPacket(pcm, length, this->audioGain); this->readAudioSamples += length; if (readPastTimestamp) { timeStamp += (float)length / this->info.VorbisInfo.rate; } vorbis_synthesis_read(&this->info.VorbisDSPState, length); // tell vorbis we read a number of samples } } return timeStamp; }
static double encode_vorbis( FILE *f, vorbis_dsp_state *vd, ogg_stream_state *vo, vorbis_block *vb, unsigned int audio_rate, double audiopos, signed char *buf, int n_samples ) { float **vorbis_buffer; const int audio_ch = 2; int i,j; int count = 0; ogg_page og; int got_page = 0; vorbis_buffer = vorbis_analysis_buffer( vd, n_samples ); for( i = 0; i < n_samples; i++ ) { for( j = 0; j < audio_ch; j++ ) { vorbis_buffer[j][i] = ( ( buf[count+1] << 8 ) | ( buf[count] & 0xff ) ) / 32768.0f; count += 2; } } vorbis_analysis_wrote( vd, n_samples ); while( vorbis_analysis_blockout( vd, vb ) == 1 ) { ogg_packet op; vorbis_analysis( vb, NULL ); vorbis_bitrate_addblock( vb ); while( vorbis_bitrate_flushpacket( vd, &op ) ) { ogg_stream_packetin( vo, &op ); } } while( ogg_stream_pageout( vo, &og ) ) { got_page = 1; audiopos = vorbis_granule_time( vd, ogg_page_granulepos( &og ) ); /* printf( "VORBIS: %f\n", audiopos ); */ fwrite( og.header, og.header_len, 1, f ); fwrite( og.body, og.body_len, 1, f ); } if( !got_page ) { double t; t = ( ( 1.0 * n_samples ) / audio_rate ); audiopos += t; } return audiopos; }
bool VideoClip_Theora::_readData() { int audioEos = 0; int serno = 0; float audioTime = 0; float time = this->timer->getTime(); if (this->restarted) { time = 0.0f; } char* buffer = NULL; int bytesRead = 0; ogg_int64_t granule = 0; do { buffer = ogg_sync_buffer(&this->info.OggSyncState, BUFFER_SIZE); bytesRead = this->stream->read(buffer, BUFFER_SIZE); ogg_sync_wrote(&this->info.OggSyncState, bytesRead); if (bytesRead == 0) { if (!this->autoRestart) { this->endOfFile = true; log(this->name + " finished playing"); } return false; } // when we fill the stream with enough pages, it'll start spitting out packets // which contain key frames, delta frames or audio data while (ogg_sync_pageout(&this->info.OggSyncState, &this->info.OggPage) > 0) { serno = ogg_page_serialno(&this->info.OggPage); if (serno == this->info.TheoraStreamState.serialno) { ogg_stream_pagein(&this->info.TheoraStreamState, &this->info.OggPage); } if (this->audioInterface != NULL && serno == this->info.VorbisStreamState.serialno) { granule = ogg_page_granulepos(&this->info.OggPage); audioTime = (float)vorbis_granule_time(&this->info.VorbisDSPState, granule); audioEos = ogg_page_eos(&this->info.OggPage); ogg_stream_pagein(&this->info.VorbisStreamState, &this->info.OggPage); } } } while (this->audioInterface != NULL && audioEos == 0 && audioTime < time + 1.0f); return true; }
int main(int argc,char *argv[]){ int c,long_option_index,ret; ogg_stream_state to; /* take physical pages, weld into a logical stream of packets */ ogg_stream_state vo; /* 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 */ theora_state td; theora_info ti; theora_comment tc; vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ vorbis_comment vc; /* struct that stores all the user comments */ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ vorbis_block vb; /* local working space for packet->PCM decode */ int audioflag=0; int videoflag=0; int akbps=0; int vkbps=0; ogg_int64_t audio_bytesout=0; ogg_int64_t video_bytesout=0; double timebase; FILE* outfile = stdout; #ifdef _WIN32 # ifdef THEORA_PERF_DATA LARGE_INTEGER start_time; LARGE_INTEGER final_time; LONGLONG elapsed_ticks; LARGE_INTEGER ticks_per_second; LONGLONG elapsed_secs; LONGLONG elapsed_sec_mod; double elapsed_secs_dbl ; # endif /* We need to set stdin/stdout to binary mode. Damn windows. */ /* if we were reading/writing a file, it would also need to in binary mode, eg, fopen("file.wav","wb"); */ /* 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 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;; case 'a': audio_q=atof(optarg)*.099; if(audio_q<-.1 || audio_q>1){ fprintf(stderr,"Illegal audio quality (choose -1 through 10)\n"); exit(1); } audio_r=-1; break; case 'v': video_q=rint(atof(optarg)*6.3); if(video_q<0 || video_q>63){ fprintf(stderr,"Illegal video quality (choose 0 through 10)\n"); exit(1); } video_r=0; break; case 'A': audio_r=atof(optarg)*1000; if(audio_q<0){ fprintf(stderr,"Illegal audio quality (choose > 0 please)\n"); exit(1); } audio_q=-99; break; case 'V': video_r=rint(atof(optarg)*1000); if(video_r<45000 || video_r>2000000){ fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n"); exit(1); } video_q=0; break; case 's': video_an=rint(atof(optarg)); break; case 'S': video_ad=rint(atof(optarg)); break; case 'f': video_hzn=rint(atof(optarg)); break; case 'F': video_hzd=rint(atof(optarg)); break; default: usage(); } } while(optind<argc){ /* assume that anything following the options must be a filename */ id_file(argv[optind]); optind++; } #ifdef THEORA_PERF_DATA # ifdef WIN32 QueryPerformanceCounter(&start_time); # endif #endif /* yayness. Set up Ogg output stream */ srand(time(NULL)); { /* need two inequal serial numbers */ int serial1, serial2; serial1 = rand(); serial2 = rand(); if (serial1 == serial2) serial2++; ogg_stream_init(&to,serial1); ogg_stream_init(&vo,serial2); } /* Set up Theora encoder */ if(!video){ fprintf(stderr,"No video files submitted for compression?\n"); exit(1); } /* Theora has a divisible-by-sixteen restriction for the encoded video size */ /* scale the frame size up to the nearest /16 and calculate offsets */ video_x=((frame_x + 15) >>4)<<4; video_y=((frame_y + 15) >>4)<<4; /* We force the offset to be even. This ensures that the chroma samples align properly with the luma samples. */ frame_x_offset=((video_x-frame_x)/2)&~1; frame_y_offset=((video_y-frame_y)/2)&~1; theora_info_init(&ti); ti.width=video_x; ti.height=video_y; ti.frame_width=frame_x; ti.frame_height=frame_y; ti.offset_x=frame_x_offset; ti.offset_y=frame_y_offset; ti.fps_numerator=video_hzn; ti.fps_denominator=video_hzd; ti.aspect_numerator=video_an; ti.aspect_denominator=video_ad; ti.colorspace=OC_CS_UNSPECIFIED; ti.pixelformat=OC_PF_420; ti.target_bitrate=video_r; ti.quality=video_q; ti.dropframes_p=0; ti.quick_p=1; ti.keyframe_auto_p=1; ti.keyframe_frequency=64; ti.keyframe_frequency_force=64; ti.keyframe_data_target_bitrate=video_r*1.5; ti.keyframe_auto_threshold=80; ti.keyframe_mindistance=8; ti.noise_sensitivity=1; theora_encode_init(&td,&ti); theora_info_clear(&ti); /* initialize Vorbis too, assuming we have audio to compress. */ if(audio){ vorbis_info_init(&vi); if(audio_q>-99) ret = vorbis_encode_init_vbr(&vi,audio_ch,audio_hz,audio_q); else ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,audio_r,-1); if(ret){ fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n" "the requested quality or bitrate.\n\n"); exit(1); } vorbis_comment_init(&vc); vorbis_analysis_init(&vd,&vi); vorbis_block_init(&vd,&vb); } /* write the bitstream header packets with proper page interleave */ /* first packet will get its own page automatically */ theora_encode_header(&td,&op); ogg_stream_packetin(&to,&op); if(ogg_stream_pageout(&to,&og)!=1){ fprintf(stderr,"Internal Ogg library error.\n"); exit(1); } fwrite(og.header,1,og.header_len,outfile); fwrite(og.body,1,og.body_len,outfile); /* create the remaining theora headers */ theora_comment_init(&tc); theora_encode_comment(&tc,&op); ogg_stream_packetin(&to,&op); /*theora_encode_comment() doesn't take a theora_state parameter, so it has to allocate its own buffer to pass back the packet data. If we don't free it here, we'll leak. libogg2 makes this much cleaner: the stream owns the buffer after you call packetin in libogg2, but this is not true in libogg1.*/ free(op.packet); theora_encode_tables(&td,&op); ogg_stream_packetin(&to,&op); if(audio){ ogg_packet header; ogg_packet header_comm; ogg_packet header_code; vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code); ogg_stream_packetin(&vo,&header); /* automatically placed in its own page */ if(ogg_stream_pageout(&vo,&og)!=1){ fprintf(stderr,"Internal Ogg library error.\n"); exit(1); } fwrite(og.header,1,og.header_len,outfile); fwrite(og.body,1,og.body_len,outfile); /* remaining vorbis header packets */ ogg_stream_packetin(&vo,&header_comm); ogg_stream_packetin(&vo,&header_code); } /* Flush the rest of our headers. This ensures the actual data in each stream will start on a new page, as per spec. */ while(1){ int result = ogg_stream_flush(&to,&og); if(result<0){ /* can't get here */ fprintf(stderr,"Internal Ogg library error.\n"); exit(1); } if(result==0)break; fwrite(og.header,1,og.header_len,outfile); fwrite(og.body,1,og.body_len,outfile); } if(audio){ while(1){ int result=ogg_stream_flush(&vo,&og); if(result<0){ /* can't get here */ fprintf(stderr,"Internal Ogg library error.\n"); exit(1); } if(result==0)break; fwrite(og.header,1,og.header_len,outfile); fwrite(og.body,1,og.body_len,outfile); } } /* setup complete. Raw processing loop */ fprintf(stderr,"Compressing....\n"); while(1){ ogg_page audiopage; ogg_page videopage; /* is there an audio page flushed? If not, fetch one if possible */ audioflag=fetch_and_process_audio(audio,&audiopage,&vo,&vd,&vb,audioflag); /* is there a video page flushed? If not, fetch one if possible */ videoflag=fetch_and_process_video(video,&videopage,&to,&td,videoflag); /* no pages of either? Must be end of stream. */ if(!audioflag && !videoflag)break; /* which is earlier; the end of the audio page or the end of the video page? Flush the earlier to stream */ { int audio_or_video=-1; double audiotime= audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1; double videotime= videoflag?theora_granule_time(&td,ogg_page_granulepos(&videopage)):-1; if(!audioflag){ audio_or_video=1; } else if(!videoflag) { audio_or_video=0; } else { if(audiotime<videotime) audio_or_video=0; else audio_or_video=1; } if(audio_or_video==1){ /* flush a video page */ video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile); video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile); videoflag=0; timebase=videotime; }else{ /* flush an audio page */ audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,outfile); audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,outfile); audioflag=0; timebase=audiotime; } { int hundredths=timebase*100-(long)timebase*100; int seconds=(long)timebase%60; int minutes=((long)timebase/60)%60; int hours=(long)timebase/3600; if(audio_or_video) vkbps=rint(video_bytesout*8./timebase*.001); else akbps=rint(audio_bytesout*8./timebase*.001); fprintf(stderr, "\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps ", hours,minutes,seconds,hundredths,akbps,vkbps); } } } /* clear out state */ if(audio){ ogg_stream_clear(&vo); vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); } if(video){ ogg_stream_clear(&to); theora_clear(&td); } if(outfile && outfile!=stdout)fclose(outfile); fprintf(stderr,"\r \ndone.\n\n"); #ifdef THEORA_PERF_DATA # ifdef WIN32 QueryPerformanceCounter(&final_time); elapsed_ticks = final_time.QuadPart - start_time.QuadPart; ticks_per_second; QueryPerformanceFrequency(&ticks_per_second); elapsed_secs = elapsed_ticks / ticks_per_second.QuadPart; elapsed_sec_mod = elapsed_ticks % ticks_per_second.QuadPart; elapsed_secs_dbl = elapsed_secs; elapsed_secs_dbl += ((double)elapsed_sec_mod / (double)ticks_per_second.QuadPart); printf("Encode time = %lld ticks\n", elapsed_ticks); printf("~%lld and %lld / %lld seconds\n", elapsed_secs, elapsed_sec_mod, ticks_per_second.QuadPart); printf("~%Lf seconds\n", elapsed_secs_dbl); # endif #endif return(0); }