c++ - Decoding and playing audio with ffmpeg and XAudio2 - frequency raito wrong -


i'm using ffmpeg decode audio , output using xaudio2 api, works , plays synced video output using pts. it's high pitched (i.e. sounds chipmunks).

setting breakpoints can see has sets correct sample rate audio codec in createsourcevoice. i'm stumped.

any appreciated.

#include "dvdaudiodevice.h"  handle m_hbufferendevent;  cdvdaudio::cdvdaudio() {     m_pxaudio2 = null;     m_pmasteringvoice = null;     m_psourcevoice = null;     m_pwfx  = null;      m_voicecallback = null;      m_hbufferendevent = createevent(null, false, false, "buffer end event"); }  cdvdaudio::~cdvdaudio() {     m_pxaudio2 = null;     m_pmasteringvoice = null;     m_psourcevoice = null;     m_pwfx  = null;      m_voicecallback = null;      closehandle(m_hbufferendevent);     m_hbufferendevent = null; }  bool cdvdaudio::create(int ichannels, int ibitrate, int ibitspersample, bool bpasstrough) {     coinitializeex(null, coinit_multithreaded);      hresult hr = xaudio2create( &m_pxaudio2, 0, xaudio2_default_processor);      if (succeeded(hr))     {         m_pxaudio2->createmasteringvoice( &m_pmasteringvoice );     }      // create source voice     waveformatextensible wfx;     memset(&wfx, 0, sizeof(waveformatextensible));      wfx.format.wformattag           = wave_format_pcm;     wfx.format.nsamplespersec       = ibitrate;//pffmpegdata->paudiocodecctx->sample_rate;//48000 default     wfx.format.nchannels            = ichannels;//pffmpegdata->paudiocodecctx->channels;     wfx.format.wbitspersample       = 16;     wfx.format.nblockalign          = wfx.format.nchannels*16/8;     wfx.format.navgbytespersec      = wfx.format.nsamplespersec * wfx.format.nblockalign;     wfx.format.cbsize               = sizeof(waveformatextensible)-sizeof(waveformatex);     wfx.samples.wvalidbitspersample = wfx.format.wbitspersample;      if(wfx.format.nchannels == 1)     {         wfx.dwchannelmask = speaker_mono;     }     else if(wfx.format.nchannels == 2)     {         wfx.dwchannelmask = speaker_stereo;     }     else if(wfx.format.nchannels == 5)     {         wfx.dwchannelmask = speaker_5point1;     }      wfx.subformat = ksdataformat_subtype_pcm;      unsigned int flags = 0;//xaudio2_voice_nosrc;// | xaudio2_voice_nopitch;      //source voice      m_voicecallback = new streamingvoicecallback(this);      hr = m_pxaudio2->createsourcevoice(&m_psourcevoice,(waveformatex*)&wfx, 0 , 1.0f, m_voicecallback);      if(!succeeded(hr))         return false;      // start sound     hr = m_psourcevoice->start(0);      if(!succeeded(hr))         return false;      return true; }  dword cdvdaudio::addpackets(unsigned char* data, dword len) {           memset(&m_soundbuffer,0,sizeof(xaudio2_buffer));          m_soundbuffer.audiobytes = len;         m_soundbuffer.paudiodata = data;         m_soundbuffer.pcontext = null;//(void*)data;          xaudio2_voice_state state;          while(m_psourcevoice->getstate( &state ), state.buffersqueued > 60)         {             waitforsingleobject( m_hbufferendevent, infinite );         }          m_psourcevoice->submitsourcebuffer( &m_soundbuffer );      return 0; }  void cdvdaudio::destroy() {     m_pmasteringvoice->destroyvoice();     m_pxaudio2->release();      m_psourcevoice->destroyvoice();      delete m_voicecallback;     m_voicecallback = null; } 

#include "dvdaudiocodecffmpeg.h" #include "log.h"  cdvdaudiocodecffmpeg::cdvdaudiocodecffmpeg() : cdvdaudiocodec() {     m_ibuffersize = 0;     m_pcodeccontext = null;     m_bopenedcodec = false; }  cdvdaudiocodecffmpeg::~cdvdaudiocodecffmpeg() {     dispose(); }  bool cdvdaudiocodecffmpeg::open(avcodecid codecid, int ichannels, int isamplerate) {     avcodec* pcodec;     m_bopenedcodec = false;      av_register_all();      pcodec = avcodec_find_decoder(codecid);      m_pcodeccontext = avcodec_alloc_context3(pcodec);//avcodec_alloc_context();     avcodec_get_context_defaults3(m_pcodeccontext, pcodec);      if (!pcodec)     {         clog::log(logerror, "cdvdaudiocodecffmpeg::open() unable find codec");         return false;     }      m_pcodeccontext->debug_mv = 0;     m_pcodeccontext->debug = 0;     m_pcodeccontext->workaround_bugs = 1;      if (pcodec->capabilities & codec_cap_truncated)         m_pcodeccontext->flags |= codec_flag_truncated;      m_pcodeccontext->channels = ichannels;     m_pcodeccontext->sample_rate = isamplerate;     //m_pcodeccontext->bits_per_sample = 24;  /* //fixme brent   if( extradata && extrasize > 0 )   {     m_pcodeccontext->extradata_size = extrasize;     m_pcodeccontext->extradata = m_dllavcodec.av_mallocz(extrasize + ff_input_buffer_padding_size);     memcpy(m_pcodeccontext->extradata, extradata, extrasize);   } */     // set acceleration     //m_pcodeccontext->dsp_mask = ff_mm_force | ff_mm_mmx | ff_mm_mmxext | ff_mm_sse; //brent      if (avcodec_open2(m_pcodeccontext, pcodec, null) < 0)     {         clog::log(logerror, "cdvdaudiocodecffmpeg::open() unable open codec");         dispose();         return false;     }      m_bopenedcodec = true;     return true; }  void cdvdaudiocodecffmpeg::dispose() {     if (m_pcodeccontext)     {         if (m_bopenedcodec) avcodec_close(m_pcodeccontext);         m_bopenedcodec = false;         av_free(m_pcodeccontext);         m_pcodeccontext = null;     }     m_ibuffersize = 0; }  int cdvdaudiocodecffmpeg::decode(byte* pdata, int isize) {     int ibytesused;     if (!m_pcodeccontext) return -1;      //copy ffmpeg avpacket again     avpacket packet;     av_init_packet(&packet);      packet.data=pdata;     packet.size=isize;      int ioutputsize = avcodec_max_audio_frame_size; //brent      ibytesused = avcodec_decode_audio3(m_pcodeccontext, (int16_t *)m_buffer, &ioutputsize/*m_ibuffersize*/, &packet);      m_ibuffersize = ioutputsize;//brent      return ibytesused; }  int cdvdaudiocodecffmpeg::getdata(byte** dst) {     *dst = m_buffer;     return m_ibuffersize; }  void cdvdaudiocodecffmpeg::reset() {     if (m_pcodeccontext) avcodec_flush_buffers(m_pcodeccontext); }  int cdvdaudiocodecffmpeg::getchannels() {     if (m_pcodeccontext) return m_pcodeccontext->channels;     return 0; }  int cdvdaudiocodecffmpeg::getsamplerate() {     if (m_pcodeccontext) return m_pcodeccontext->sample_rate;     return 0; }  int cdvdaudiocodecffmpeg::getbitspersample() {     if (m_pcodeccontext) return 16;     return 0; } 

#include "dvdplayeraudio.h" #include "dvddemuxutils.h" #include "log.h" #include <assert.h> #include "dvdaudiocodecffmpeg.h" //fixme move codec factory!! cdvdplayeraudio::cdvdplayeraudio(cdvdclock* pclock) : cthread() { m_pclock = pclock; m_paudiocodec = null; m_binitializedoutputdevice = false; m_isourcechannels = 0; m_audioclock = 0; // m_currentptsitem.pts = dvd_nopts_value; // m_currentptsitem.timestamp = 0; setspeed(dvd_playspeed_normal); initializecriticalsection(&m_critcodecsection); m_messagequeue.setmaxdatasize(10 * 16 * 1024); // g_dvdperformancecounter.enableaudioqueue(&m_packetqueue); } cdvdplayeraudio::~cdvdplayeraudio() { // g_dvdperformancecounter.disableaudioqueue(); // close stream, , don't wait audio finished closestream(true); deletecriticalsection(&m_critcodecsection); } bool cdvdplayeraudio::openstream( cdemuxstreamaudio *pdemuxstream ) { // should alway's null!!!!, crash anyway when deleting m_paudiocodec here. if (m_paudiocodec) { clog::log(logfatal, "cdvdplayeraudio::openstream() m_paudiocodec != null"); return false; } avcodecid codecid = pdemuxstream->codec; clog::log(lognotice, "finding audio codec for: %i", codecid); //m_paudiocodec = cdvdfactorycodec::createaudiocodec( pdemuxstream ); m_paudiocodec = new cdvdaudiocodecffmpeg; //fixme brent codec factory needed! if (!m_paudiocodec->open(pdemuxstream->codec, pdemuxstream->ichannels, pdemuxstream->isamplerate)) { m_paudiocodec->dispose(); delete m_paudiocodec; m_paudiocodec = null; return false; } if( !m_paudiocodec ) { clog::log(logerror, "unsupported audio codec"); return false; } m_codec = pdemuxstream->codec; m_isourcechannels = pdemuxstream->ichannels; m_messagequeue.init(); clog::log(lognotice, "creating audio thread"); create(); return true; } void cdvdplayeraudio::closestream(bool bwaitforbuffers) { // wait until buffers empty if (bwaitforbuffers) m_messagequeue.waituntilempty(); // send abort message audio queue m_messagequeue.abort(); clog::log(lognotice, "waiting audio thread exit"); // shut down adio_decode thread , wait stopthread(); // set this->m_bstop true this->waitforthreadexit(infinite); // uninit queue m_messagequeue.end(); clog::log(lognotice, "deleting audio codec"); if (m_paudiocodec) { m_paudiocodec->dispose(); delete m_paudiocodec; m_paudiocodec = null; } // flush remaining pts values //flushptsqueue(); //fixme brent } void cdvdplayeraudio::onstartup() { cthread::setname("cdvdplayeraudio"); paudiopacket = null; m_audioclock = 0; audio_pkt_data = null; audio_pkt_size = 0; // g_dvdperformancecounter.enableaudiodecodeperformance(threadhandle()); } void cdvdplayeraudio::process() { clog::log(lognotice, "running thread: cdvdplayeraudio::process()"); int result; // silence data byte silence[1024]; memset(silence, 0, 1024); dvdaudioframe audioframe; __int64 iclockdiff=0; while (!m_bstop) { //don't let mess our global variables entercriticalsection(&m_critcodecsection); result = decodeframe(audioframe, m_speed != dvd_playspeed_normal); // blocks if no audio available, leaves critical section before doing leavecriticalsection(&m_critcodecsection); if( result & decode_flag_error ) { clog::log(logerror, "cdvdplayeraudio::process - decode error. skipping audio frame"); continue; } if( result & decode_flag_abort ) { clog::log(logdebug, "cdvdplayeraudio::process - abort recieved, exiting thread"); break; } if( result & decode_flag_drop ) //fixme brent { /* //frame should dropped. don't let audio move ahead of current time thou //we need able start playing @ time //when playing backwords, try keep small buffers possible // set time @ delay addptsqueue(audioframe.pts, m_dvdaudio.getdelay()); */ if (m_speed > 0) { __int64 timestamp = m_pclock->getabsoluteclock() + (audioframe.duration * dvd_playspeed_normal) / m_speed; while( !m_bstop && timestamp > m_pclock->getabsoluteclock() ) sleep(1); } continue; } if( audioframe.size > 0 ) { // have succesfully decoded audio frame, openup audio device if not done if (!m_binitializedoutputdevice) { m_binitializedoutputdevice = initializeoutputdevice(); } //add packets play m_dvdaudio.addpackets(audioframe.data, audioframe.size); // store delay pts value can calculate current playing //addptsqueue(audioframe.pts, m_dvdaudio.getdelay() - audioframe.duration);//brent } // if asked resync on packet, here if( result & decode_flag_resync ) { clog::log(logdebug, "cdvdplayeraudio::process - resync recieved."); //while (!m_bstop && (unsigned int)m_dvdaudio.getdelay() > audioframe.duration ) sleep(5); //brent m_pclock->discontinuity(clock_disc_normal, audioframe.pts); } #ifdef useoldsync //clock should calculated after packets have been added m_audioclock points //time after have been played const __int64 icurrdiff = (m_audioclock - m_dvdaudio.getdelay()) - m_pclock->getclock(); const __int64 iavdiff = (iclockdiff + icurrdiff)/2; //check discontinuity in stream, use moving average //eliminate highfreq fluctuations of large packet sizes if( abs(iavdiff) > 5000 ) // sync clock if average diff bigger 5 msec { //wait untill new audio frame wich triggered discontinuity left //then set disc state while (!m_bstop && (unsigned int)m_dvdaudio.getbytesinbuffer() > audioframe.size ) sleep(5); m_pclock->discontinuity(clock_disc_normal, m_audioclock - m_dvdaudio.getdelay()); clog::("cdvdplayer:: detected audio discontinuity, syncing clock. diff was: %i64d, %i64d, av: %i64d", iclockdiff, icurrdiff, iavdiff); iclockdiff = 0; } else { //do gradual adjustments (not working yet) //m_pclock->adjustspeedtomatch(iclock + iavdiff); iclockdiff = icurrdiff; } #endif } } void cdvdplayeraudio::onexit() { //g_dvdperformancecounter.disableaudiodecodeperformance(); // destroy audio device clog::log(lognotice, "closing audio device"); m_dvdaudio.destroy(); m_binitializedoutputdevice = false; clog::log(lognotice, "thread end: cdvdplayeraudio::onexit()"); } // decode 1 audio frame , returns uncompressed size int cdvdplayeraudio::decodeframe(dvdaudioframe &audioframe, bool bdroppacket) { cdvddemux::demuxpacket* ppacket = paudiopacket; int n=48000*2*16/8, len; //store amount left @ point, , last pts unsigned __int64 first_pkt_pts = 0; int first_pkt_size = 0; int first_pkt_used = 0; int result = 0; // make sure sent frame clean memset(&audioframe, 0, sizeof(dvdaudioframe)); if (ppacket) { first_pkt_pts = ppacket->pts; first_pkt_size = ppacket->isize; first_pkt_used = first_pkt_size - audio_pkt_size; } (;;) { /* note: audio packet can contain several frames */ while (audio_pkt_size > 0) { len = m_paudiocodec->decode(audio_pkt_data, audio_pkt_size); if (len < 0) { /* if error, skip frame */ audio_pkt_size=0; m_paudiocodec->reset(); break; } // fix fucked decoders //fixme brent if( len > audio_pkt_size ) { clog::log(logerror, "cdvdplayeraudio:decodeframe - codec tried consume more data available. potential memory corruption"); audio_pkt_size=0; m_paudiocodec->reset(); assert(0); } // decoded data , size of audioframe.size = m_paudiocodec->getdata(&audioframe.data); audio_pkt_data += len; audio_pkt_size -= len; if (audioframe.size <= 0) continue; audioframe.pts = m_audioclock; // compute duration. n = m_paudiocodec->getchannels() * m_paudiocodec->getbitspersample() / 8 * m_paudiocodec->getsamplerate(); if (n > 0) { // safety check, if channels == 0, n result in 0, , result in nice devide exception audioframe.duration = (unsigned int)(((__int64)audioframe.size * dvd_time_base) / n); // increase audioclock after packet m_audioclock += audioframe.duration; } //if asked drop packet, return size of zero. won't played //we still decode audio.. needed since still need know it's //duration make sure clock updated correctly. if( bdroppacket ) { result |= decode_flag_drop; } return result; } // free current packet if (ppacket) { cdvddemuxutils::freedemuxpacket(ppacket); //brent fixme ppacket = null; paudiopacket = null; } if (m_messagequeue.recievedabortrequest()) return decode_flag_abort; // read next packet , return -1 on error leavecriticalsection(&m_critcodecsection); //leave here might stall while cdvdmsg* pmsg; msgqueuereturncode ret = m_messagequeue.get(&pmsg, infinite); entercriticalsection(&m_critcodecsection); if (msgq_is_error(ret) || ret == msgq_abort) return decode_flag_abort; if (pmsg->istype(cdvdmsg::demuxer_packet)) { cdvdmsgdemuxerpacket* pmsgdemuxerpacket = (cdvdmsgdemuxerpacket*)pmsg; ppacket = pmsgdemuxerpacket->getpacket(); pmsgdemuxerpacket->m_ppacket = null; // xxx, test paudiopacket = ppacket; audio_pkt_data = ppacket->pdata; audio_pkt_size = ppacket->isize; } else { // other data not used here, free if // msg still available pmsg->release(); } // if update audio clock pts if (pmsg->istype(cdvdmsg::demuxer_packet) || pmsg->istype(cdvdmsg::general_resync)) { if (pmsg->istype(cdvdmsg::general_resync)) { //player asked sync on package cdvdmsggeneralresync* pmsggeneralresync = (cdvdmsggeneralresync*)pmsg; result |= decode_flag_resync; m_audioclock = pmsggeneralresync->getpts(); } else if (ppacket->pts != dvd_nopts_value) // cdvdmsg::demuxer_packet, ppacket set above { if (first_pkt_size == 0) { //first package m_audioclock = ppacket->pts; } else if (first_pkt_pts > ppacket->pts) { //okey first packet in continous stream, make sure use time here m_audioclock = ppacket->pts; } else if((unsigned __int64)m_audioclock < ppacket->pts || (unsigned __int64)m_audioclock > ppacket->pts) { //crap, moved outsided correct pts //use pts current packet, untill find better value it. //should ok after couple of frames, starts clean on packet m_audioclock = ppacket->pts; } else if(first_pkt_size == first_pkt_used) { //nice starting freshly on start of packet, use pts m_audioclock = ppacket->pts; } } } pmsg->release(); } } void cdvdplayeraudio::setspeed(int speed) { m_speed = speed; //if (m_speed == dvd_playspeed_pause) m_dvdaudio.pause(); //brent fixme //else m_dvdaudio.resume(); } bool cdvdplayeraudio::initializeoutputdevice() { int ichannels = m_paudiocodec->getchannels(); int isamplerate = m_paudiocodec->getsamplerate(); int ibitspersample = m_paudiocodec->getbitspersample(); //bool bpasstrough = m_paudiocodec->needpasstrough(); //brent if (ichannels == 0 || isamplerate == 0 || ibitspersample == 0) { clog::log(logerror, "unable create audio device, (ichannels == 0 || isamplerate == 0 || ibitspersample == 0)"); return false; } clog::log(lognotice, "creating audio device codec id: %i, channels: %i, sample rate: %i", m_codec, ichannels, isamplerate); if (m_dvdaudio.create(ichannels, isamplerate, ibitspersample, /*bpasstrough*/0)) // 16 bit ffmpeg ? //brent passthrough needed? { return true; } clog::log(logerror, "failed creating audio device codec id: %i, channels: %i, sample rate: %i", m_codec, ichannels, isamplerate); return false; }


Comments

Popular posts from this blog

sql - invalid in the select list because it is not contained in either an aggregate function -

Angularjs unit testing - ng-disabled not working when adding text to textarea -

How to start daemon on android by adb -