diff options
Diffstat (limited to 'Src/nsv/nsvplay/nsvdecode.cpp')
-rw-r--r-- | Src/nsv/nsvplay/nsvdecode.cpp | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/Src/nsv/nsvplay/nsvdecode.cpp b/Src/nsv/nsvplay/nsvdecode.cpp new file mode 100644 index 00000000..8d8bbcb7 --- /dev/null +++ b/Src/nsv/nsvplay/nsvdecode.cpp @@ -0,0 +1,999 @@ +#include <bfc/platform/platform.h> +#include <bfc/platform/strcmp.h> +#include <stdio.h> +#include <locale.h> +#include "audiostub.h" +#include "api.h" +#include "main.h" +#include "subtitles.h" +#include "../in_nsv/resource.h" + +extern int config_subtitles; +#ifdef __APPLE__ +#include <sys/time.h> +#include <unistd.h> +uint32_t GetTickCount() +{ + struct timeval newtime; + + gettimeofday(&newtime, 0); + return newtime.tv_sec*1000 + newtime.tv_usec/1000; +} + +int MulDiv(int a, int b, int d) +{ + return int ((int64_t)a * (int64_t)b) / (int64_t)d; +} +#endif +class NullAudioOutput : public IAudioOutput +{ + public: + NullAudioOutput() { m_pause=0; m_pause_t=0; m_start_t=GetTickCount64(); } + ~NullAudioOutput(){} + int canwrite() { return m_pause ? 0 : 65536; } // returns bytes writeable + void write(void *buf, int len) { } + ULONGLONG getpos() { return m_pause?m_pause_t:(GetTickCount64()-m_start_t); } + int isplaying(void) { return -1; } + ULONGLONG getwritepos() { return getpos(); } + void flush(unsigned int newtime) + { + if (m_pause) m_pause_t=newtime; + else m_start_t = GetTickCount64()-newtime; + } + void pause(int pause) + { + if (pause && !m_pause) + { + m_pause_t=GetTickCount64()-m_start_t; + m_pause=1; + } + else if (!pause && m_pause) + { + m_start_t = GetTickCount64()-m_pause_t; + m_pause=0; + m_pause_t=0; + } + } + + private: + int m_pause; + ULONGLONG m_pause_t; + ULONGLONG m_start_t; +}; + +void NSVDecoder::pause(int pause) +{ + if (aud_output) aud_output->pause(pause); + m_paused=pause; +} + +ULONGLONG NSVDecoder::getpos() +{ + return aud_output ? aud_output->getpos() : 0; +} + +unsigned int NSVDecoder::getlen() +{ + if (!fileheader.header_size || fileheader.file_lenms == 0xFFFFFFFF) + { + if (!file_reader || !avg_framesize_cnt || !avg_framesize_tot) return 0xFFFFFFFF; + int s=(int)file_reader->getsize(); + if (s == 0xFFFFFFFF) return 0xFFFFFFFF; + + int bytespersec=(int)((double)avg_framesize_tot/(double)avg_framesize_cnt*getFrameRate()); + + if (!bytespersec) return 0xFFFFFFFF; + return MulDiv(s,1000,bytespersec); + } + return fileheader.file_lenms; +} + +void NSVDecoder::seek(unsigned int newpos) +{ + m_need_seek=newpos; + m_again=0; +} + +int NSVDecoder::canseek() +{ + return file_reader ? file_reader->canseek() : 0; +} + +char *NSVDecoder::getStatus() +{ + if (m_prebuffer) return "Buffering"; + return NULL; +} + +void NSVDecoder::SetBuffering(int total_ms, int initial_ms, int after_underrun) +{ + if (m_prebuffer) m_prebuffer=initial_ms; + m_pb_init=initial_ms; + m_pb_init_ur=after_underrun; + m_buffer_total=total_ms; +} + + +int64_t pdReadResolution(void) +{ + int64_t myfeq; +#ifdef _WIN32 + LARGE_INTEGER feq; + + QueryPerformanceFrequency( &feq); + myfeq = feq.QuadPart; +#else + myfeq = 1000000; +#endif + + return myfeq; +} + +int64_t pdReadTimer(void) +{ + int64_t mynow; + +#ifdef _WIN32 + LARGE_INTEGER now; + + QueryPerformanceCounter( &now ); + mynow = now.QuadPart; +#else + struct timeval newtime; + + gettimeofday(&newtime,NULL); + mynow = (newtime.tv_sec * 1000000) + newtime.tv_usec ; +#endif + + return mynow; +} + +NSVDecoder::NSVDecoder(const char *url, IVideoOutput *output, char *subtitleurl) +{ + m_precise_seeking=1; + pcm_samplerate=0; + seek_dumpframes=0; + seek_dumpaudiosamples=0; + m_avresync_time=0; + nsvbitstream_search=0; + m_paused=0; + m_bufstate=0; + hack_l_curpos=-1; + hack_l_curpos_ot=0; + vid_decoder_isnotnull=1; + aud_decoder_isnotnull=1; + video_frames_avail=0; + audio_frames_avail=0; + m_buffer_total=1000; // bufferahead + m_pb_init=m_prebuffer=1500; // initial prebuffer + m_pb_init_ur=1500; // after underrun + m_buf_memlimit=32*1024*1024; + m_pan=0; + m_volume=192; + m_title=0; + m_audio_type[0]=m_video_type[0]=0; + m_out_opened=0; + m_out=output; + vidout_ready=0; + vidout=0; + vidout_type=0; + vidout_time=0; + vidout_codec_width=0; + vidout_codec_height=0; + m_url=_strdup(url); + m_need_seek=~0; + needkf=1; + aspect=1.0; + framerate_override=0.0; + use_framerate_override=0; + vid_decoder=NULL; + aud_decoder=NULL; + aud_output=NULL; + framecnt=0; + hdrsearched=0; + file_reader=CreateReader(url); + if (!file_reader) m_err="Error opening input"; + else m_err=NULL; + + m_enable_subtitles=1; + m_subs_fontsize=0; + m_cur_subtitle=0; + if (subtitleurl && *subtitleurl) insertSubtitlesItem("From subtitle file",subtitleurl); + + memset(&fileheader,0,sizeof(fileheader)); + + avg_framesize_tot=0; + avg_framesize_cnt=0; + avg_framesize_tot_v=0; + avg_framesize_cnt_v=0; + avg_framesize_tot_a=0; + avg_framesize_cnt_a=0; + + unpacket.setAudioOut(&audiobs); + unpacket.setVideoOut(&videobs); + unpacket.setAuxOut(&auxbs); + + proTimerStart = 1; + proTimerEnd = 0; + profiletime = 0.00; + prostart = 0.00; + proend = 0.00; + timeref = (float)pdReadResolution(); + + + m_again = 0; +} + +NSVDecoder::~NSVDecoder() +{ + if (m_out_opened) m_out->close(); + if (WASABI_API_MEMMGR) + WASABI_API_MEMMGR->Delete(vid_decoder); + else + delete vid_decoder; + if (WASABI_API_MEMMGR) + WASABI_API_MEMMGR->Delete(aud_decoder); + else + delete aud_decoder; + delete aud_output; + delete file_reader; + free(fileheader.metadata); + free(fileheader.toc); + free(m_url); + free(m_title); +} + +char *NSVDecoder::get_error() +{ + return m_err; +} + +char *NSVDecoder::get_status() +{ + return NULL; +} + +char *NSVDecoder::getFromMeta(char *name) +{ + if (file_reader) + { + char *t=(char*)malloc(strlen(name)+8); + if (t) + { + strcpy(t,"x-nsv-"); + strcat(t,name); + char *v=file_reader->getheader(t); + free(t); + if (v) return _strdup(v); + } + } + return nsv_getmetadata(fileheader.metadata,name); +} + + +unsigned int NSVDecoder::getFileSize() +{ + if (file_reader) return (unsigned int)file_reader->getsize(); + return ~0; +} + +int NSVDecoder::getBitrate() +{ + if (!fileheader.header_size || !fileheader.file_lenms || !fileheader.file_lenbytes || + fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF) + { + if (!avg_framesize_cnt) return 0; + return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot / (double)avg_framesize_cnt); + } + return MulDiv(fileheader.file_lenbytes,8000,fileheader.file_lenms); +} + +const char *NSVDecoder::getServerHeader(char *name) +{ + return file_reader?file_reader->getheader(name):NULL; +} + +void NSVDecoder::getAudioDesc(char *buf) +{ + char *t=getAudioType(); + if (t && *t) + { + if (strcmp(t, "VLB")==0) + sprintf(buf,"Dolby AAC "); // special case; make sure the user never sees "VLB" name + else if (strcmp(t, "AACP")==0) + sprintf(buf,"HE-AAC "); + else if (strcmp(t, "AAC")==0) + sprintf(buf,"AAC LC "); + else + sprintf(buf,"%s ",t); + + char *p=buf+strlen(buf); + if (aud_output) aud_output->getdescstr(p); + if (!*p) *--p=0; + + int a=(getAudioBitrate()+500)/1000; + if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS)); + } + else *buf=0; +} + +void NSVDecoder::getVideoDesc(char *buf) +{ + char *t=getVideoType(); + if (t && *t) + { + int fr=(int)(getFrameRate()*100.0); + sprintf(buf,"%s %dx%d@%d.%02d%s",t,getWidth(),getHeight(),fr/100,fr%100,WASABI_API_LNGSTRING(IDS_FPS)); + + int a=(getVideoBitrate()+500)/1000; + if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS)); + } + else *buf=0; +} + + +int NSVDecoder::getAudioBitrate() +{ + if (!avg_framesize_cnt_a) return 0; + return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_a / (double)avg_framesize_cnt_a); +} + +int NSVDecoder::getVideoBitrate() +{ + if (!avg_framesize_cnt_v) return 0; + return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_v / (double)avg_framesize_cnt_v); +} + +char *NSVDecoder::getTitle() +{ + char *v=getFromMeta("TITLE"); + if (v) + { + if (!m_title || strcmp(v,m_title)) + { + free(m_title); + m_title=v; + } + else free(v); + return m_title; + } + if (file_reader) + { + v=file_reader->gettitle(); + if (v) return v; + } + + if (!m_url) return ""; + + v=m_url+strlen(m_url); + while (v >= m_url && *v != '/' && *v != '\\' && *v != '=') v--; + return ++v; +} + +void NSVDecoder::ProcessSubtitleBlock(void *data, int len) +{ + unsigned char *dataptr=(unsigned char *)data; + while (len > 2) + { + int len_block=(dataptr[0] | ((unsigned short)dataptr[1] << 8)); + dataptr += 2; + len -= 2; + if (len_block > len) break; + + SUBTITLE_INFO sti={0,}; + sti.language = (char *)dataptr; + while (len_block > 0 && *dataptr) + { + dataptr++; + len_block--; + len--; + } + dataptr++; + len_block--; + len--; + if (len_block < 1) break; + sti.utf8_text = (char *)dataptr; + while (len_block > 0 && *dataptr) + { + dataptr++; + len_block--; + len--; + } + dataptr++; + len_block--; + len--; + if (len_block < 6) break; + + sti.start_frame = framecnt + video_frames_avail + (dataptr[0] | ((int)dataptr[1] << 8)); + dataptr+=2; + len-=2; + len_block-=2; + + sti.end_frame = sti.start_frame + (dataptr[0] | ((int)dataptr[1] << 8) | ((int)dataptr[2] << 16) | ((int)dataptr[3] << 24)); + dataptr+=4; + len-=4; + len_block-=4; + + // set defaults for color/position + sti.xPos=128; + sti.yPos=255; + sti.colorRed=255; + sti.colorGreen=255; + sti.colorBlue=255; + sti.fontSize=0; + + if (len_block >= 2) + { + sti.xPos = *dataptr++; + sti.yPos = *dataptr++; + len-=2; + len_block-=2; + if (len_block >= 3) + { + sti.colorRed=*dataptr++; + sti.colorGreen=*dataptr++; + sti.colorBlue=*dataptr++; + len-=3; + len_block-=3; + if (len_block > 0) + { + sti.fontSize=(signed char) *dataptr++; + len--; + len_block--; + } + + if (len_block > 0) + { + sti.extraData=dataptr; + sti.extraDataSize=len_block; + } + + } + } + + Subtitles *sub=findSubtitles(sti.language); + if(!sub) sub=insertSubtitlesItem(sti.language,NULL); + sub->addSubtitlePacket(&sti); + + if (len_block > 0) + { + len-=len_block; + dataptr+=len_block; + } + + } +} + +int NSVDecoder::run(int * volatile quit) // returns -1 on error, 0 if no frames processed, 1 if frames processed, 2 if underrun +{ + int retval=0; + + + if (!file_reader) return -1; + + if (!aud_decoder_isnotnull && !vid_decoder_isnotnull && vid_decoder && aud_decoder) + { + m_err="Codec(s) not found"; + return -1; + } + + if (aud_output && vidout_ready) + { + ULONGLONG curpos=aud_output->getpos(); + if (!m_paused) + { + if (!audio_frames_avail && curpos == hack_l_curpos && unpacket.getEof()) + { + hack_l_curpos=curpos; + curpos += GetTickCount64() - hack_l_curpos_ot; + } + else + { + hack_l_curpos_ot=GetTickCount64(); + hack_l_curpos=curpos; + } + } + + if (curpos >= vidout_time && !m_prebuffer) + { + if (vidout) + { + // send this to get the flip state updated so when toggled on-the-fly it will work with nsv + m_out->extended(0x1002/*VIDUSER_SET_VFLIP*/,vid_flip,0); + m_out->draw(vidout); + } + + if (m_enable_subtitles) + { + SubtitlesItem *it=m_subtitles.get(m_cur_subtitle); + if(it && config_subtitles) { + it->m_subs->setFontSizeModifier(m_subs_fontsize); + m_out->drawSubtitle(it->m_subs->getSubtitle((unsigned int)aud_output->getpos(),framecnt)); + } + if ( it && !config_subtitles ) + { + m_out->drawSubtitle(NULL); + } + } + vidout=0; + vidout_ready=0; + } + } + + if (hdrsearched && m_need_seek!=~0 && aud_output) + { + unsigned int newpos=m_need_seek; + m_need_seek=~0; + seek_dumpaudiosamples=0; + seek_dumpframes=0; + if (file_reader->canseek() && file_reader->getsize() != 0xFFFFFFFF) + { + int nbpos; + + if (!fileheader.toc_size || !fileheader.toc || !fileheader.file_lenms || + !fileheader.file_lenbytes || + fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF) + { + int avg_framesize=avg_framesize_tot/avg_framesize_cnt; + int pos_frames=(int)(newpos*getFrameRate()); + nbpos=fileheader.header_size+MulDiv(pos_frames,avg_framesize,1000); + } + else // calculate offset using TOC + { + if (fileheader.toc_ex) // use extended toc, find next earliest time closest + { + int x; + double scale; + if (unpacket.isValid() && getFrameRate() > 0.0001) + scale=1000.0 / getFrameRate(); + else scale = 1000.0 / 30.0; + for (x = 0; x < (int)fileheader.toc_size; x ++) + { + if (newpos < (unsigned int) (fileheader.toc_ex[x]*scale)) break; + } + unsigned int hdr[2]={0,0}; + if (--x >= 0) + { + hdr[0]=fileheader.toc[x]; + hdr[1]=(unsigned int) (fileheader.toc_ex[x] * scale); + } + //hdr[1] is the time of that keyframe + + if (m_precise_seeking) // precise seek + { + int timediff = newpos - hdr[1]; + double fr; + if (unpacket.isValid() && getFrameRate() >= 0.0001) fr = getFrameRate() / 1000.0; + else fr = 30.0 / 1000.0; + + seek_dumpframes=(int) (timediff * fr); + seek_dumpaudiosamples=MulDiv(timediff,pcm_samplerate,1000); + } + else + { + newpos=hdr[1]; + } + + nbpos = fileheader.header_size + hdr[0]; + } + else + { + double tocpos=(newpos*(double)fileheader.toc_size)/(double)fileheader.file_lenms; + unsigned int tocidx=(unsigned int)tocpos; + if (tocidx > fileheader.toc_size) + { + nbpos=fileheader.header_size + fileheader.file_lenbytes; + } + else + { + unsigned int a,b; + if (tocidx<0) tocidx=0; + a = fileheader.toc[tocidx]; + if (tocidx < fileheader.toc_size-1) + b = fileheader.toc[tocidx+1]; + else b=fileheader.file_lenbytes; + double frac=tocpos-tocidx; + + nbpos = fileheader.header_size + (int) (a * (1.0 - frac) + b * frac); + } + } + } + if (!file_reader->seek(nbpos)) + { + framecnt=(int)(newpos*getFrameRate()/1000.0); + unpacket.reset(0); + unpacket.setEof(0); + hdrsearched=1; + needkf=1; + m_avresync_time=0; + vidout=0; + vidout_ready=0; + video_frames_avail=0; + audio_frames_avail=0; + m_prebuffer=m_pb_init; + inbs.clear(); + audiobs.clear(); + videobs.clear(); + auxbs.clear(); + if (aud_output) aud_output->flush(newpos); + if (aud_decoder) aud_decoder->flush(); + if (vid_decoder) vid_decoder->flush(); + } + } + } // end of seeking + + if (!hdrsearched) // search for header + { +readagain_header: + if (quit && *quit) return 0; + int ret=nsv_readheader(inbs,&fileheader); + if (ret <= 0) + { + hdrsearched++; + if (!ret) + { + _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale(); + char *v=getFromMeta("ASPECT"); + if (v) + { + aspect=_atof_l(v,C_locale); + } + free(v); + v=getFromMeta("FRAMERATE"); + if (v) + { + framerate_override=_atof_l(v,C_locale); + if (framerate_override >= 0.01) use_framerate_override=1; + free(v); + } + } + } + else + { + char buf[8192] = {0}; + size_t ret2=file_reader->read(buf,sizeof(buf)); + + if (file_reader->iseof()) unpacket.setEof(1); + + if (file_reader->geterror()) + { + m_err=file_reader->geterror(); + return -1; + } + if (ret2>0) + { + inbs.add(buf, (int)ret2); + goto readagain_header; + } + } + } + if (!hdrsearched) return 0; + // end of header search + + // read from source + if (m_prebuffer || + (((!unpacket.isValid() || audio_frames_avail < (int)((getFrameRate() * m_buffer_total)/1000.0)) || !videobs.avail()) && + (!m_buf_memlimit || ((int)(audiobs.avail()+videobs.avail()) < m_buf_memlimit*8)))) + { +readagain_stream: + if (quit && *quit) return 0; + int lavail= (int)inbs.avail(); + + int ret=unpacket.unpacket(inbs); + + inbs.compact(); + + if (ret) + { + if (!unpacket.isValid() && nsvbitstream_search > 8*1024*1024) // only scan for 8mb max + { + m_err="Error synching to NSV stream"; + return -1; + } + if (unpacket.getEof()) + { + if (!videobs.avail() && !video_frames_avail && (!aud_output || aud_output->isplaying() <= 0)) retval=-1; + // do nothing + } + else + { + char buf[8192] = {0}; + + size_t ret2=file_reader->read(buf,sizeof(buf)); + + if (ret2 == 0 && file_reader->iseof()) + { + m_prebuffer=0; + unpacket.setEof(1); + } + + if (file_reader->geterror()) + { + m_err=file_reader->geterror(); + return -1; + } + + if (ret2 > 0) + { + nsvbitstream_search+= (int)ret2; + inbs.add(buf, (int)ret2); + goto readagain_stream; + } + } + } + else + { + nsvbitstream_search=0; + video_frames_avail++; + audio_frames_avail++; + nsv_type_to_string(unpacket.getAudFmt(),m_audio_type); + nsv_type_to_string(unpacket.getVidFmt(),m_video_type); + + avg_framesize_cnt++; + avg_framesize_tot+=lavail- (int)((inbs.avail())/8); + if (avg_framesize_tot > 0x80000000) + { + avg_framesize_tot/=2; + avg_framesize_cnt/=2; + } + + if (1) + { + //parse aux packets + while (auxbs.avail() >= 8*8) + { + size_t thislen = auxbs.getbits(32); + unsigned int thistype = auxbs.getbits(32); + if (thislen > auxbs.avail()/8) break; + + if (thistype == NSV_MAKETYPE('S','U','B','T') ) ProcessSubtitleBlock(auxbs.getcurbyteptr(), (int)thislen); + else if (thistype == NSV_MAKETYPE('A','S','Y','N')) + { + //if (thislen == 0) // resyncpoint + { + audiobs.addint(-1); + audiobs.addint(framecnt+video_frames_avail); + } + //else // handle other form [todo] + //{ + //} + } + + auxbs.seek(thislen*8); + } + } + auxbs.clear(); + } + } + + if (unpacket.isValid() && !m_prebuffer && !audiobs.avail() && aud_output) + { + if (aud_output->isplaying()) aud_output->write(0,0); + else if (!unpacket.getEof()) + { + m_prebuffer=m_pb_init_ur+(int)((video_frames_avail/getFrameRate())*1000.0); + if (m_prebuffer < m_pb_init_ur) m_prebuffer=m_pb_init_ur; + if (m_prebuffer > m_pb_init_ur*3) m_prebuffer=m_pb_init_ur*3; + retval=2; + } + } + + if (video_frames_avail > (int)((m_prebuffer*getFrameRate())/1000.0)) + { + m_bufstate=256; + m_prebuffer=0; + } + else + { + int a = (int) (video_frames_avail * 256.0 / ((m_prebuffer*getFrameRate())/1000.0)); + if (a > 255) a=255; + if (a < 0) a=0; + m_bufstate=a; + if (m_out) m_out->notifyBufferState(a); + } + + if (unpacket.isValid() && !vidout_ready && videobs.avail() && !m_prebuffer) // decode frame + { + if (!vid_decoder) + { + vid_flip=0; + vid_decoder = CreateVideoDecoder(unpacket.getWidth(),unpacket.getHeight(), + getFrameRate(),unpacket.getVidFmt(),&vid_flip,&vid_decoder_isnotnull); + } + + int kf=0; + int l=videobs.getbits(32); + unsigned int local_vidout_type[3] = { 1, 0, 0 }; + int ret=vid_decoder->decode(needkf,videobs.getcurbyteptr(),l,&vidout,local_vidout_type,&kf); + vidout_type = local_vidout_type[0]; + if (ret == 0) + { + + vidout_codec_width = local_vidout_type[1]; + vidout_codec_height = local_vidout_type[2]; + } + + if (kf) needkf=0; + video_frames_avail--; + videobs.seek(l*8); + videobs.compact(); + + avg_framesize_cnt_v++; + avg_framesize_tot_v+=l; + + if (needkf || seek_dumpframes > 0) + { + vidout=NULL; + if (seek_dumpframes>0) seek_dumpframes--; + } + else + { + if (!m_out_opened && vid_decoder_isnotnull) + { + m_out->extended(0x1008/*VIDUSER_SET_THREAD_SAFE*/, 1, 0); + unsigned int vidwidth = vidout_codec_width, vidheight = vidout_codec_height; + if (vidwidth && vidheight) + { + /* if the codec provided a width/height to use */ + double aspect_adjust = (double)vidwidth / (double)vidheight / ((double)unpacket.getWidth() / (double)unpacket.getHeight()); + if (m_out->open(vidwidth,vidheight,vid_flip,aspect * aspect_adjust,vidout_type)) + { + m_err="Error opening video output"; + return -1; + } + } + else + { + if (m_out->open(unpacket.getWidth(),unpacket.getHeight(),vid_flip,aspect,vidout_type)) + { + m_err="Error opening video output"; + return -1; + } + } + m_out_opened=1; + } + + vidout_ready=1; + vidout_time = (unsigned int)(framecnt++ / getFrameRate() * 1000.0) + (unsigned int)m_avresync_time; + int offs=unpacket.getSyncOffset() + m_out->get_latency(); + + if ((int)vidout_time <= offs) vidout_time=0; + else vidout_time-=offs; + + //drop the image if we're late + if(aud_output && vidout_time<aud_output->getpos()) { + vidout=0; + vidout_ready=0; + } + } + + retval=1; + } + + if ( ( audiobs.avail() && !m_prebuffer ) || m_again ) + { + if (needkf) + { + int l=audiobs.getbits(32); + if (l == -1) audiobs.seek(32); // skip over 32 bits of our audiobs info + else + { + audiobs.seek(l*8); + audio_frames_avail--; + } + + audiobs.compact(); + } + else // decode some audio + { + char pcmbuf[16384] = {0}; + int outbufl=512; + if (!aud_decoder) + { + aud_decoder=CreateAudioDecoder(unpacket.getAudFmt(),&aud_decoder_isnotnull,&aud_output); + if (aud_output && m_paused) aud_output->pause(1); + } + + if (aud_output) outbufl=aud_output->canwrite(); + + if (outbufl) + { + unsigned int outfmt[8] = {0}; + + retval=1; + if (outbufl > sizeof(pcmbuf)) outbufl=sizeof(pcmbuf); + + int inl=audiobs.getbits(32); + + if (inl == -1) + { + int vidframe=audiobs.getbits(32); + if (aud_output) + { + ULONGLONG audpos = aud_output->getwritepos(); + int vidpos = (int)(vidframe / getFrameRate() * 1000.0); + + m_avresync_time = audpos-vidpos; + } + } + else + { + int ret=aud_decoder->decode(audiobs.getcurbyteptr(), inl, pcmbuf, &outbufl, outfmt); + + if ( !outbufl ) m_again = 0; + else m_again =1; + + if (ret < 1) + { + avg_framesize_cnt_a++; + avg_framesize_tot_a+=inl; + audiobs.seek(inl*8); + audiobs.compact(); + audio_frames_avail--; + } + else audiobs.seek(-32); + + if (outbufl || outfmt[0]) + { + if (!aud_output) + { + aud_output=PCMOUT_CREATE(outfmt); + pcm_samplerate=outfmt[1]; + if (!aud_output) aud_output = new NullAudioOutput; + aud_output->setvolume(m_volume); + aud_output->setpan(m_pan); + if (aud_output && m_paused) aud_output->pause(1); + } + if (outbufl) + { + if (seek_dumpaudiosamples) + { + int nch=outfmt[2]; + int bps=outfmt[3]; + int dump=seek_dumpaudiosamples*nch*(bps/8); + if (dump >= outbufl) + { + seek_dumpaudiosamples -= outbufl/nch/(bps/8); + } + else // partial write + { + aud_output->write(pcmbuf+dump,outbufl-dump); + seek_dumpaudiosamples=0; + } + } + else aud_output->write(pcmbuf,outbufl); + } + } + } + } // if can write + } //end of decode + } // audiobs avail + + if (m_prebuffer) +#ifdef _WIN32 + Sleep(10); +#else + usleep(10000); +#endif + + return retval; +} + +Subtitles *NSVDecoder::insertSubtitlesItem(const char *language, const char *subfile) { + Subtitles *sub=new Subtitles(subfile); + m_subtitles.put(new SubtitlesItem(language, sub)); + return sub; +} + +Subtitles *NSVDecoder::findSubtitles(const char *language) { + for(int i=0;i<m_subtitles.getlen();i++) { + SubtitlesItem *s=m_subtitles.get(i); + if(!_stricmp(language,s->m_language) ) return s->m_subs; + } + return NULL; +} + +const char *NSVDecoder::getSubLanguage(int index) { + if(index>=m_subtitles.getlen()) return NULL; + return m_subtitles.get(index)->m_language; +}
\ No newline at end of file |