12 #include "../Audacity.h"
13 #include "../blockfile/ODDecodeBlockFile.h"
17 #include <wx/wxprec.h>
21 #include <wx/window.h>
25 #ifdef EXPERIMENTAL_OD_FFMPEG
30 #include "../FFmpeg.h"
33 #define ODFFMPEG_SEEKING_TEST_UNKNOWN 0
34 #define ODFFMPEG_SEEKING_TEST_FAILED -1
35 #define ODFFMPEG_SEEKING_TEST_SUCCESS 1
37 #define kMaxSamplesInCache 4410000
40 struct FFMpegDecodeCache
46 AVSampleFormat samplefmt;
58 ODFFmpegDecoder(
const wxString & fileName,
60 ODDecodeFFmpegTask::Streams &&channels,
61 const std::shared_ptr<FFmpegContext> &formatContext,
63 virtual ~ODFFmpegDecoder();
81 void InsertCache(std::unique_ptr<FFMpegDecodeCache> &&cache);
97 ODDecodeFFmpegTask::Streams mChannels;
98 std::shared_ptr<FFmpegContext> mContext;
99 std::vector<std::unique_ptr<FFMpegDecodeCache>> mDecodeCache;
100 int mNumSamplesInCache;
104 bool mSeekingAllowedStatus;
108 auto ODDecodeFFmpegTask::FromList(
const TrackHolders &channels ) -> Streams
111 return transform_container<Streams>( channels,
113 return transform_container<Channels>( holders,
114 std::mem_fn(&NewChannelGroup::value_type::get) );
120 ODDecodeFFmpegTask::ODDecodeFFmpegTask(
const ScsPtr &scs, Streams &&channels,
const std::shared_ptr<FFmpegContext> &context,
int streamIndex)
121 : mChannels(std::move(channels))
125 mStreamIndex = streamIndex;
127 ODDecodeFFmpegTask::~ODDecodeFFmpegTask()
132 std::unique_ptr<ODTask> ODDecodeFFmpegTask::Clone()
const
134 auto clone = std::make_unique<ODDecodeFFmpegTask>(mScs, Streams{ mChannels }, mContext, mStreamIndex);
135 clone->mDemandSample=GetDemandSample();
139 return std::move(clone);
145 ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(
const wxString & fileName)
149 std::make_unique<ODFFmpegDecoder>(fileName, mScs, ODDecodeFFmpegTask::Streams{ mChannels },
150 mContext, mStreamIndex);
152 mDecoders.push_back(std::move(decoder));
153 return mDecoders.back().get();
159 bool ODFFmpegDecoder::SeekingAllowed()
228 ODFFmpegDecoder::ODFFmpegDecoder(
const wxString & fileName,
230 ODDecodeFFmpegTask::Streams &&channels,
231 const std::shared_ptr<FFmpegContext> &context,
237 mNumSamplesInCache(0),
239 mSeekingAllowedStatus(ODFFMPEG_SEEKING_TEST_UNKNOWN),
240 mStreamIndex(streamIndex)
245 mChannels = std::move(channels);
248 int64_t stream_delay = 0;
249 const auto sc = mScs->get()[streamIndex].get();
250 if (sc->m_stream->start_time != int64_t(AV_NOPTS_VALUE) &&
251 sc->m_stream->start_time > 0) {
252 stream_delay = sc->m_stream->start_time;
254 mCurrentPos =
sampleCount{ double(stream_delay) / AV_TIME_BASE };
261 ODFFmpegDecoder::~ODFFmpegDecoder()
267 while(mDecodeCache.size())
268 mDecodeCache.erase(mDecodeCache.begin());
275 #define kDecodeSampleAllowance 400000
277 #define kMaxSeekRewindAttempts 8
280 auto mFormatContext = mContext->ic_ptr;
282 auto scs = mScs->get();
283 auto sci = scs[mStreamIndex].get();
284 format = sci->m_osamplefmt;
292 if(mCurrentPos > start && mCurrentPos <= start+len + kDecodeSampleAllowance)
295 FillDataFromCache(bufStart,
format, start,len,channel);
298 bool seeking =
false;
300 if(len && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start ) && SeekingAllowed()) {
308 for (
unsigned int i = 0; i < mFormatContext->nb_streams; i++) {
309 if (mFormatContext->streams[i] == sc->
m_stream )
316 mCurrentPos = start+len +1;
317 while(numAttempts++ < kMaxSeekRewindAttempts && mCurrentPos > start) {
319 targetts = std::max( 0.0,
320 (start - kDecodeSampleAllowance * numAttempts / kMaxSeekRewindAttempts)
322 ((
double)st->time_base.den/(st->time_base.num * st->codec->sample_rate ))
326 if(av_seek_frame(mFormatContext,stindex,targetts,0) >= 0){
328 sampleCount actualDecodeStart { 0.5 + st->codec->sample_rate * st->cur_dts * ((double)st->time_base.num/st->time_base.den) };
330 mCurrentPos = actualDecodeStart;
336 wxPrintf(
"seek failed");
340 if(mCurrentPos>start){
341 mSeekingAllowedStatus = (bool)ODFFMPEG_SEEKING_TEST_FAILED;
343 wxPrintf(
"seek fail, reverting to previous pos\n");
348 bool firstpass =
true;
351 while (len>0 && (mCurrentPos < start+len) && (sc = ReadNextFrame()) != NULL)
357 auto actualDecodeStart = mCurrentPos;
368 if (sc->
m_stream->codec->frame_size) {
369 actualDecodeStart = ((actualDecodeStart + sc->
m_stream->codec->frame_size/2) / sc->
m_stream->codec->frame_size) * sc->
m_stream->codec->frame_size;
374 if(actualDecodeStart != mCurrentPos)
375 wxPrintf(
"ts not matching - now:%llu , last:%llu, lastlen:%lu, start %llu, len %lu\n",actualDecodeStart.as_long_long(), mCurrentPos.as_long_long(), mCurrentLen, start.
as_long_long(), len);
377 if(actualDecodeStart>start && firstpass) {
384 auto amt = (actualDecodeStart - start).as_size_t();
385 auto cache = std::make_unique<FFMpegDecodeCache>();
391 cache->numChannels = std::max<unsigned>(0, sc->
m_stream->codec->channels);
398 cache->samplefmt = AV_SAMPLE_FMT_S16;
400 cache->samplefmt = AV_SAMPLE_FMT_FLT;
404 InsertCache(std::move(cache));
407 mCurrentPos = actualDecodeStart;
412 if (DecodeFrame(sc,
false) < 0)
423 for (
int i = 0; i < mChannels.size(); i++)
427 if (DecodeFrame(sc,
true) == 0)
435 FillDataFromCache(bufStart,
format, start, len, channel);
450 #define kODFFmpegSearchThreshold 10
455 if(mDecodeCache.size() <= 0)
465 int searchEnd = mDecodeCache.size();
467 if(searchEnd>kODFFmpegSearchThreshold)
471 while(searchStart+1<searchEnd)
473 guess = (searchStart+searchEnd)/2;
476 if(mDecodeCache[guess]->start+mDecodeCache[guess]->len >= start)
484 for(
int i=searchStart; i < (int)mDecodeCache.size(); i++)
489 if(start < mDecodeCache[i]->start+mDecodeCache[i]->len &&
490 start + len > mDecodeCache[i]->start)
493 outBuf = (uint8_t*)data;
496 if(start<mDecodeCache[i]->start && start+len > mDecodeCache[i]->start+mDecodeCache[i]->len)
499 auto nChannels = mDecodeCache[i]->numChannels;
504 FFMIN(start+len,mDecodeCache[i]->start+mDecodeCache[i]->len)
505 - FFMAX(mDecodeCache[i]->start, start)
511 const auto hitStartInCache =
513 FFMAX(
sampleCount{0},start-mDecodeCache[i]->start).as_size_t();
515 const auto hitStartInRequest = start < mDecodeCache[i]->start
516 ? len - samplesHit : 0;
517 for(decltype(samplesHit) j = 0; j < samplesHit; j++)
519 const auto outIndex = hitStartInRequest + j;
520 const auto inIndex = (hitStartInCache + j) * nChannels + channel;
521 switch (mDecodeCache[i]->samplefmt)
523 case AV_SAMPLE_FMT_U8:
525 ((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr.get())[inIndex] - 0x80) << 8;
528 case AV_SAMPLE_FMT_S16:
530 ((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->
samplePtr.get())[inIndex];
533 case AV_SAMPLE_FMT_S32:
535 ((
float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr.get())[inIndex] * (1.0 / (1u << 31));
538 case AV_SAMPLE_FMT_FLT:
540 ((
float *)outBuf)[outIndex] = (float) ((
float*)mDecodeCache[i]->samplePtr.get())[inIndex];
543 case AV_SAMPLE_FMT_DBL:
545 ((
float *)outBuf)[outIndex] = (float) ((
double*)mDecodeCache[i]->samplePtr.get())[inIndex];
549 wxPrintf(
"ODDecodeFFMPEG TASK unrecognized sample format\n");
555 samplesFilled += samplesHit;
559 if(start < mDecodeCache[i]->start)
564 data+= samplesHit * (
SAMPLE_SIZE(outFormat) /
sizeof(*data));
570 if(len<=0 || mDecodeCache[i]->start > start+len)
573 return samplesFilled;
582 auto scs = mScs->get();
585 (mContext->ic_ptr,
reinterpret_cast<streamContext**
>(scs), mChannels.size());
589 int ODFFmpegDecoder::DecodeFrame(
streamContext *sc,
bool flushing)
598 auto cache = std::make_unique<FFMpegDecodeCache>();
601 cache->numChannels = std::max<unsigned>(0, sc->
m_stream->codec->channels);
604 cache->start = mCurrentPos;
609 InsertCache(std::move(cache));
614 void ODFFmpegDecoder::InsertCache(std::unique_ptr<FFMpegDecodeCache> &&cache) {
616 int searchEnd = mDecodeCache.size();
622 while(searchStart<searchEnd)
624 guess = (searchStart+searchEnd)/2;
626 if(mDecodeCache[guess]->start>= cache->start) {
633 searchStart = ++guess;
635 mCurrentLen = cache->len;
636 mCurrentPos=cache->start+cache->len;
637 mDecodeCache.insert(mDecodeCache.begin()+guess, std::move(cache));
640 mNumSamplesInCache+=cache->len;
643 while(mNumSamplesInCache>kMaxSamplesInCache)
647 dropindex = (guess > (int)mDecodeCache.size()/2) ? 0 : (mDecodeCache.size()-1);
648 mNumSamplesInCache-=mDecodeCache[dropindex]->len;
649 mDecodeCache.erase(mDecodeCache.begin()+dropindex);
653 #endif //EXPERIMENTAL_OD_FFMPEG