Audacity  3.0.3
ODDecodeFFmpegTask.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4  Audacity(R) is copyright (c) 1999-2012 Audacity Team.
5  License: GPL v2. See License.txt.
6 
7  ODDecodeFFmpegTask.cpp
8  Michael Chinen
9 
10 ******************************************************************/
11 
12 #include "../Audacity.h" // needed before FFmpeg.h // for USE_* macros
13 #include "../blockfile/ODDecodeBlockFile.h" // base class
14 #include "ODDecodeFFmpegTask.h"
15 
16 #include <wx/crt.h>
17 #include <wx/wxprec.h>
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #ifndef WX_PRECOMP
20 // Include your minimal set of headers here, or wx.h
21 #include <wx/window.h>
22 #endif
23 
24 #ifdef USE_FFMPEG
25 #ifdef EXPERIMENTAL_OD_FFMPEG
26 
27 #include <algorithm>
28 #include <functional>
29 
30 #include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
31 
32 
33 #define ODFFMPEG_SEEKING_TEST_UNKNOWN 0
34 #define ODFFMPEG_SEEKING_TEST_FAILED -1
35 #define ODFFMPEG_SEEKING_TEST_SUCCESS 1
36 
37 #define kMaxSamplesInCache 4410000
38 
39 //struct for caching the decoded samples to be used over multiple blockfiles
40 struct FFMpegDecodeCache
41 {
42  ArrayOf<uint8_t> samplePtr;//interleaved samples
43  sampleCount start;
44  size_t len;
45  unsigned numChannels;
46  AVSampleFormat samplefmt; // input (from libav) sample format
47 
48 };
49 
50 
51 //------ ODFFmpegDecoder declaration and defs - here because we strip dependencies from .h files
52 
54 class ODFFmpegDecoder final : public ODFileDecoder
55 {
56 public:
58  ODFFmpegDecoder(const wxString & fileName,
59  const ScsPtr &scs,
60  ODDecodeFFmpegTask::Streams &&channels,
61  const std::shared_ptr<FFmpegContext> &formatContext,
62  int streamIndex);
63  virtual ~ODFFmpegDecoder();
64 
72  int Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, size_t len, unsigned int channel) override;
73 
76  bool ReadHeader() override {return true;}
77 
78  bool SeekingAllowed() override;
79 
80 private:
81  void InsertCache(std::unique_ptr<FFMpegDecodeCache> &&cache);
82 
83  //puts the actual audio samples into the blockfile's data array
84  int FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount & start, size_t& len, unsigned int channel);
88  streamContext* ReadNextFrame();
89 
94  int DecodeFrame(streamContext *sc, bool flushing);
95 
96  ScsPtr mScs;
97  ODDecodeFFmpegTask::Streams mChannels;
98  std::shared_ptr<FFmpegContext> mContext;
99  std::vector<std::unique_ptr<FFMpegDecodeCache>> mDecodeCache;
100  int mNumSamplesInCache;
101  sampleCount mCurrentPos; //the index of the next sample to be decoded
102  size_t mCurrentLen; //length of the last packet decoded
103 
104  bool mSeekingAllowedStatus;
105  int mStreamIndex;
106 };
107 
108 auto ODDecodeFFmpegTask::FromList( const TrackHolders &channels ) -> Streams
109 {
110  // Convert array of array of unique_ptr to array of array of bare pointers
111  return transform_container<Streams>( channels,
112  [](const NewChannelGroup &holders) {
113  return transform_container<Channels>( holders,
114  std::mem_fn(&NewChannelGroup::value_type::get) );
115  }
116  );
117 }
118 
119 //------ ODDecodeFFmpegTask definitions
120 ODDecodeFFmpegTask::ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, const std::shared_ptr<FFmpegContext> &context, int streamIndex)
121  : mChannels(std::move(channels))
122 {
123  mScs=scs;
124  mContext = context;
125  mStreamIndex = streamIndex;
126 }
127 ODDecodeFFmpegTask::~ODDecodeFFmpegTask()
128 {
129 }
130 
131 
132 std::unique_ptr<ODTask> ODDecodeFFmpegTask::Clone() const
133 {
134  auto clone = std::make_unique<ODDecodeFFmpegTask>(mScs, Streams{ mChannels }, mContext, mStreamIndex);
135  clone->mDemandSample=GetDemandSample();
136 
137  //the decoders and blockfiles should not be copied. They are created as the task runs.
138  // This std::move is needed to "upcast" the pointer type
139  return std::move(clone);
140 }
141 
143 //
144 //compare to FLACImportPlugin::Open(wxString filename)
145 ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName)
146 {
147  // Open the file for import
148  auto decoder =
149  std::make_unique<ODFFmpegDecoder>(fileName, mScs, ODDecodeFFmpegTask::Streams{ mChannels },
150  mContext, mStreamIndex);
151 
152  mDecoders.push_back(std::move(decoder));
153  return mDecoders.back().get();
154 
155 }
156 
159 bool ODFFmpegDecoder::SeekingAllowed()
160 {
161  return false;
162  /*
163  if(ODFFMPEG_SEEKING_TEST_UNKNOWN != mSeekingAllowedStatus)
164  return mSeekingAllowedStatus == ODFFMPEG_SEEKING_TEST_SUCCESS;
165 
166  //we can seek if the following checks pass:
167  //-sample rate is less than the reciprocal of the time_base of the seeking stream.
168  //-a seek test has been made and dts updates as expected.
169  //we want to clone this to run a seek test.
170  AVFormatContext* ic = (AVFormatContext*)mFormatContext;
171  bool audioStreamExists = false;
172  AVStream* st;
173 
174  //test the audio stream(s)
175  for (unsigned int i = 0; i < ic->nb_streams; i++)
176  {
177  if (ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
178  {
179  audioStreamExists = true;
180  st = ic->streams[i];
181  if(st->duration <= 0 || st->codec->sample_rate <= 0)
182  goto test_failed;
183 
184  //if the time base reciprocal is less than the sample rate it means we can't accurately represent a sample with the timestamp in av.
185  float time_base_inv = ((float)st->time_base.den/st->time_base.num);
186  if(time_base_inv < st->codec->sample_rate)
187  goto test_failed;
188 
189  }
190  }
191 
192  if(!audioStreamExists)
193  goto test_failed;
194  //TODO: now try a seek and see if dts/pts (decode/presentation timestamp) is updated as we expected it to be.
195  //This should be done using a NEW AVFormatContext clone so that we don't ruin the file pointer if we fail.
196 // url_fseek(mFormatContext->pb,0,SEEK_SET);
197 
198 
199  std::unique_ptr<FFmpegContext> tempMpegContext;
200  AVFormatContext* tempContext;
201  int err;
202  err = ufile_fopen_input(tempMpegContext, mFName);
203  if (err < 0)
204  {
205  goto test_failed;
206  }
207  tempContext = tempMpegContext->ic_ptr;
208 
209  err = avformat_find_stream_info(tempContext, NULL);
210  if (err < 0)
211  {
212  goto test_failed;
213  }
214 
215  if(av_seek_frame(tempContext, st->index, 0, 0) >= 0) {
216  mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_SUCCESS;
217  return SeekingAllowed();
218  }
219 
220 test_failed:
221  mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_FAILED;
222  return SeekingAllowed();
223  */
224 }
225 
226 
227 //------ ODDecodeFFmpegFileDecoder
228 ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName,
229  const ScsPtr &scs,
230  ODDecodeFFmpegTask::Streams &&channels,
231  const std::shared_ptr<FFmpegContext> &context,
232 int streamIndex)
233 :ODFileDecoder(fileName),
234 //mSamplesDone(0),
235 mScs(scs),
236 mContext(context),
237 mNumSamplesInCache(0),
238 mCurrentLen(0),
239 mSeekingAllowedStatus(ODFFMPEG_SEEKING_TEST_UNKNOWN),
240 mStreamIndex(streamIndex)
241 {
242  PickFFmpegLibs();
243 
244  //do a shallow copy of the 2d array.
245  mChannels = std::move(channels);
246 
247  // get the current stream start time.
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;
253  }
254  mCurrentPos = sampleCount{ double(stream_delay) / AV_TIME_BASE };
255 
256  //TODO: add a ref counter to scs? This will be necessary if we want to allow copy and paste of not-yet decoded
257  //ODDecodeBlockFiles that point to FFmpeg files.
258 }
259 
260 //we have taken ownership, so DELETE the ffmpeg stuff allocated in ImportFFmpeg that was given to us.
261 ODFFmpegDecoder::~ODFFmpegDecoder()
262 {
263  // Do this before unloading libraries
264  mContext.reset();
265 
266  //DELETE our caches.
267  while(mDecodeCache.size())
268  mDecodeCache.erase(mDecodeCache.begin());
269 
270  DropFFmpegLibs();
271 }
272 
273 //we read the file from left to right, so in some cases it makes more sense not to seek and just carry on the decode if the gap is small enough.
274 //this value controls this amount. this should be a value that is much larger than the payload for a single packet, and around block file size around 1-10 secs.
275 #define kDecodeSampleAllowance 400000
276 //number of jump backwards seeks
277 #define kMaxSeekRewindAttempts 8
278 int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, size_t len, unsigned int channel)
279 {
280  auto mFormatContext = mContext->ic_ptr;
281 
282  auto scs = mScs->get();
283  auto sci = scs[mStreamIndex].get();
284  format = sci->m_osamplefmt;
285 
286  data.Allocate(len, format);
287  samplePtr bufStart = data.ptr();
288  streamContext* sc = NULL;
289 
290  // wxPrintf("start %llu len %lu\n", start, len);
291  //TODO update this to work with seek - this only works linearly now.
292  if(mCurrentPos > start && mCurrentPos <= start+len + kDecodeSampleAllowance)
293  {
294  //this next call takes data, start and len as reference variables and updates them to reflect the NEW area that is needed.
295  FillDataFromCache(bufStart, format, start,len,channel);
296  }
297 
298  bool seeking = false;
299  //look at the decoding timestamp and see if the next sample that will be decoded is not the next sample we need.
300  if(len && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start ) && SeekingAllowed()) {
301  sc = sci;
302  AVStream* st = sc->m_stream;
303  int stindex = -1;
304  uint64_t targetts;
305 
306  //wxPrintf("attempting seek to %llu\n", start);
307  //we have to find the index for this stream.
308  for (unsigned int i = 0; i < mFormatContext->nb_streams; i++) {
309  if (mFormatContext->streams[i] == sc->m_stream )
310  stindex =i;
311  }
312 
313  if(stindex >=0) {
314  int numAttempts = 0;
315  //reset mCurrentPos to a bogus value
316  mCurrentPos = start+len +1;
317  while(numAttempts++ < kMaxSeekRewindAttempts && mCurrentPos > start) {
318  //we want to move slightly before the start of the block file, but not too far ahead
319  targetts = std::max( 0.0,
320  (start - kDecodeSampleAllowance * numAttempts / kMaxSeekRewindAttempts)
321  .as_long_long() *
322  ((double)st->time_base.den/(st->time_base.num * st->codec->sample_rate ))
323  );
324 
325  //wxPrintf("attempting seek to %llu, attempts %d\n", targetts, numAttempts);
326  if(av_seek_frame(mFormatContext,stindex,targetts,0) >= 0){
327  //find out the dts we've seekd to.
328  sampleCount actualDecodeStart { 0.5 + st->codec->sample_rate * st->cur_dts * ((double)st->time_base.num/st->time_base.den) }; //this is mostly safe because den is usually 1 or low number but check for high values.
329 
330  mCurrentPos = actualDecodeStart;
331  seeking = true;
332 
333  //if the seek was past our desired position, rewind a bit.
334  //wxPrintf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble);
335  } else {
336  wxPrintf("seek failed");
337  break;
338  }
339  }
340  if(mCurrentPos>start){
341  mSeekingAllowedStatus = (bool)ODFFMPEG_SEEKING_TEST_FAILED;
342  // url_fseek(mFormatContext->pb,sc->m_pkt.pos,SEEK_SET);
343  wxPrintf("seek fail, reverting to previous pos\n");
344  return -1;
345  }
346  }
347  }
348  bool firstpass = true;
349 
350  //we decode up to the end of the blockfile
351  while (len>0 && (mCurrentPos < start+len) && (sc = ReadNextFrame()) != NULL)
352  {
353  // ReadNextFrame returns 1 if stream is not to be imported
354  if (sc != (streamContext*)1)
355  {
356  //find out the dts we've seekd to. can't use the stream->cur_dts because it is faulty. also note that until we do the first seek, pkt.dts can be false and will change for the same samples after the initial seek.
357  auto actualDecodeStart = mCurrentPos;
358 
359  // we need adjacent samples, so don't use dts most of the time which will leave gaps between frames
360  // for some formats
361  // The only other case for inserting silence is for initial offset and ImportFFmpeg.cpp does this for us
362  if (seeking) {
363  actualDecodeStart = sampleCount{ 0.52 + (sc->m_stream->codec->sample_rate * sc->m_pkt->dts
364  * ((double)sc->m_stream->time_base.num / sc->m_stream->time_base.den)) };
365  //this is mostly safe because den is usually 1 or low number but check for high values.
366 
367  //hack to get rounding to work to neareset frame size since dts isn't exact
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;
370  }
371  // reset for the next one
372  seeking = false;
373  }
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);
376  //if we've skipped over some samples, fill the gap with silence. This could happen often in the beginning of the file.
377  if(actualDecodeStart>start && firstpass) {
378  // find the number of samples for the leading silence
379 
380  // UNSAFE_SAMPLE_COUNT_TRUNCATION
381  // -- but used only experimentally as of this writing
382  // Is there a proof size_t will not overflow size_t?
383  // Result is surely nonnegative.
384  auto amt = (actualDecodeStart - start).as_size_t();
385  auto cache = std::make_unique<FFMpegDecodeCache>();
386 
387  //wxPrintf("skipping/zeroing %i samples. - now:%llu (%f), last:%llu, lastlen:%lu, start %llu, len %lu\n",amt,actualDecodeStart, actualDecodeStartdouble, mCurrentPos, mCurrentLen, start, len);
388 
389  //put it in the cache so the other channels can use it.
390  // wxASSERT(sc->m_stream->codec->channels > 0);
391  cache->numChannels = std::max<unsigned>(0, sc->m_stream->codec->channels);
392  cache->len = amt;
393  cache->start=start;
394  // 8 bit and 16 bit audio output from ffmpeg means
395  // 16 bit int out.
396  // 32 bit int, float, double mean float out.
397  if (format == int16Sample)
398  cache->samplefmt = AV_SAMPLE_FMT_S16;
399  else
400  cache->samplefmt = AV_SAMPLE_FMT_FLT;
401 
402  cache->samplePtr.reinit(amt * cache->numChannels * SAMPLE_SIZE(format), true);
403 
404  InsertCache(std::move(cache));
405  }
406  firstpass=false;
407  mCurrentPos = actualDecodeStart;
408  //decode the entire packet (unused bits get saved in cache, so as long as cache size limit is bigger than the
409  //largest packet size, we're ok.
410  while (sc->m_pktRemainingSiz > 0)
411  //Fill the cache with decoded samples
412  if (DecodeFrame(sc,false) < 0)
413  break;
414 
415  // Cleanup after frame decoding
416  sc->m_pkt.reset();
417  }
418  }
419 
420  // Flush the decoders if we're done.
421  if((!sc || sc == (streamContext*) 1)&& len>0)
422  {
423  for (int i = 0; i < mChannels.size(); i++)
424  {
425  sc = scs[i].get();
426  sc->m_pkt.emplace();
427  if (DecodeFrame(sc, true) == 0)
428  {
429  sc->m_pkt.reset();
430  }
431  }
432  }
433 
434  //this next call takes data, start and len as reference variables and updates them to reflect the NEW area that is needed.
435  FillDataFromCache(bufStart, format, start, len, channel);
436 
437  // CHECK: not sure if we need this. In any case it has to be updated for the NEW float case (not just int16)
438  //if for some reason we couldn't get the samples, fill them with silence
439  /*
440  int16_t* outBuf = (int16_t*) bufStart;
441  for(int i=0;i<len;i++)
442  outBuf[i]=0;
443  */
444  return 1;
445 }
446 
447 
448 //puts the actual audio samples into the blockfile's data array
449 // the minimum amount of cache entries necessary to warrant a binary search.
450 #define kODFFmpegSearchThreshold 10
451 //also updates data and len to reflect NEW unfilled area - start is unmodified.
453 int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount &start, size_t& len, unsigned int channel)
454 {
455  if(mDecodeCache.size() <= 0)
456  return 0;
457  int samplesFilled=0;
458 
459  //do a search for the best position to start at.
460  //Guess that the array is evenly spaced from end to end - (dictionary sort)
461  //assumes the array is sorted.
462  //all we need for this to work is a location in the cache array
463  //that has a start time of less than our start sample, but try to get closer with binary search
464  int searchStart = 0;
465  int searchEnd = mDecodeCache.size();
466  int guess;
467  if(searchEnd>kODFFmpegSearchThreshold)
468  {
469  //first just guess that the cache is contiguous and we can just use math to figure it out like a dictionary
470  //by guessing where our hit will be.
471  while(searchStart+1<searchEnd)
472  {
473  guess = (searchStart+searchEnd)/2;//find a midpoint. //searchStart+ (searchEnd-searchStart)* ((float)start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
474 
475  //we want guess to point at the first index that hits even if there are duplicate start times (which can happen)
476  if(mDecodeCache[guess]->start+mDecodeCache[guess]->len >= start)
477  searchEnd = --guess;
478  else
479  searchStart = guess;
480  }
481  }
482 
483  //this is a sorted array
484  for(int i=searchStart; i < (int)mDecodeCache.size(); i++)
485  {
486  //check for a cache hit - be careful to include the first/last sample an nothing more.
487  //we only accept cache hits that touch either end - no piecing out of the middle.
488  //this way the amount to be decoded remains set.
489  if(start < mDecodeCache[i]->start+mDecodeCache[i]->len &&
490  start + len > mDecodeCache[i]->start)
491  {
492  uint8_t* outBuf;
493  outBuf = (uint8_t*)data;
494  //reject buffers that would split us into two pieces because we don't have
495  //a method of dealing with this yet, and it won't happen very often.
496  if(start<mDecodeCache[i]->start && start+len > mDecodeCache[i]->start+mDecodeCache[i]->len)
497  continue;
498 
499  auto nChannels = mDecodeCache[i]->numChannels;
500  auto samplesHit = (
501  // Proof that the result is never negative: consider four cases
502  // of FFMIN and FFMAX choices, and use the if-condition enclosing.
503  // The result is not more than len.
504  FFMIN(start+len,mDecodeCache[i]->start+mDecodeCache[i]->len)
505  - FFMAX(mDecodeCache[i]->start, start)
506  ).as_size_t();
507  //find the start of the hit relative to the cache buffer start.
508  // UNSAFE_SAMPLE_COUNT_TRUNCATION
509  // -- but used only experimentally as of this writing
510  // Is there a proof size_t will not overflow?
511  const auto hitStartInCache =
512  // result is less than mDecodeCache[i]->len:
513  FFMAX(sampleCount{0},start-mDecodeCache[i]->start).as_size_t();
514  //we also need to find out which end was hit - if it is the tail only we need to update from a later index.
515  const auto hitStartInRequest = start < mDecodeCache[i]->start
516  ? len - samplesHit : 0;
517  for(decltype(samplesHit) j = 0; j < samplesHit; j++)
518  {
519  const auto outIndex = hitStartInRequest + j;
520  const auto inIndex = (hitStartInCache + j) * nChannels + channel;
521  switch (mDecodeCache[i]->samplefmt)
522  {
523  case AV_SAMPLE_FMT_U8:
524  //wxPrintf("u8 in %lu out %lu cachelen %lu outLen %lu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
525  ((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr.get())[inIndex] - 0x80) << 8;
526  break;
527 
528  case AV_SAMPLE_FMT_S16:
529  //wxPrintf("u16 in %lu out %lu cachelen %lu outLen % lu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
530  ((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->samplePtr.get())[inIndex];
531  break;
532 
533  case AV_SAMPLE_FMT_S32:
534  //wxPrintf("s32 in %lu out %lu cachelen %lu outLen %lu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
535  ((float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr.get())[inIndex] * (1.0 / (1u << 31));
536  break;
537 
538  case AV_SAMPLE_FMT_FLT:
539  //wxPrintf("f in %lu out %lu cachelen %lu outLen %lu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
540  ((float *)outBuf)[outIndex] = (float) ((float*)mDecodeCache[i]->samplePtr.get())[inIndex];
541  break;
542 
543  case AV_SAMPLE_FMT_DBL:
544  //wxPrintf("dbl in %lu out %lu cachelen %lu outLen %lu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
545  ((float *)outBuf)[outIndex] = (float) ((double*)mDecodeCache[i]->samplePtr.get())[inIndex];
546  break;
547 
548  default:
549  wxPrintf("ODDecodeFFMPEG TASK unrecognized sample format\n");
550  return 1;
551  break;
552  }
553  }
554  //update the cursor
555  samplesFilled += samplesHit;
556 
557  //update the input start/len params - if the end was hit we can take off just len.
558  //otherwise, we can assume only the front of the request buffer was hit since we don't allow it to be split.
559  if(start < mDecodeCache[i]->start)
560  len-=samplesHit;
561  else
562  {
563  //we update data pointer too- but it is a typedef'd char* so be careful with the pointer math
564  data+= samplesHit * (SAMPLE_SIZE(outFormat) / sizeof(*data));
565  start+=samplesHit;
566  len -=samplesHit;
567  }
568  }
569  //if we've had our fill, leave. if we've passed the point which can have hits, leave.
570  if(len<=0 || mDecodeCache[i]->start > start+len)
571  break;
572  }
573  return samplesFilled;
574 }
575 
576 
577 //these next few look highly refactorable.
578 //get the right stream pointer.
579 streamContext* ODFFmpegDecoder::ReadNextFrame()
580 {
581  // Get pointer to array of contiguous unique_ptrs
582  auto scs = mScs->get();
583  // This reinterpret_cast to array of plain pointers is innocent
585  (mContext->ic_ptr, reinterpret_cast<streamContext**>(scs), mChannels.size());
586 }
587 
588 
589 int ODFFmpegDecoder::DecodeFrame(streamContext *sc, bool flushing)
590 {
591  int ret = import_ffmpeg_decode_frame(sc, flushing);
592 
593  if (ret == 0 && sc->m_frameValid) {
594  //stick it in the cache.
595  //TODO- consider growing/unioning a few cache buffers like WaveCache does.
596  //however we can't use wavecache as it isn't going to handle our stereo interleaved part, and isn't for samples
597  //However if other ODDecode tasks need this, we should do a NEW class for caching.
598  auto cache = std::make_unique<FFMpegDecodeCache>();
599  //len is number of samples per channel
600  // wxASSERT(sc->m_stream->codec->channels > 0);
601  cache->numChannels = std::max<unsigned>(0, sc->m_stream->codec->channels);
602 
603  cache->len = (sc->m_decodedAudioSamplesValidSiz / sc->m_samplesize) / cache->numChannels;
604  cache->start = mCurrentPos;
605  cache->samplePtr.reinit(sc->m_decodedAudioSamplesValidSiz);
606  cache->samplefmt = sc->m_samplefmt;
607  memcpy(cache->samplePtr.get(), sc->m_decodedAudioSamples.get(), sc->m_decodedAudioSamplesValidSiz);
608 
609  InsertCache(std::move(cache));
610  }
611  return ret;
612 }
613 
614 void ODFFmpegDecoder::InsertCache(std::unique_ptr<FFMpegDecodeCache> &&cache) {
615  int searchStart = 0;
616  int searchEnd = mDecodeCache.size(); //size() is also a valid insert index.
617  int guess = 0;
618  //first just guess that the cache is contiguous and we can just use math to figure it out like a dictionary
619  //by guessing where our hit will be.
620 
621 // wxPrintf("inserting cache start %llu, mCurrentPos %llu\n", cache->start, mCurrentPos);
622  while(searchStart<searchEnd)
623  {
624  guess = (searchStart+searchEnd)/2;//searchStart+ (searchEnd-searchStart)* ((float)cache->start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
625  //check greater than OR equals because we want to insert infront of old dupes.
626  if(mDecodeCache[guess]->start>= cache->start) {
627 // if(mDecodeCache[guess]->start == cache->start) {
628 // wxPrintf("dupe! start cache %llu start NEW cache %llu, mCurrentPos %llu\n",mDecodeCache[guess]->start, cache->start, mCurrentPos);
629 // }
630  searchEnd = guess;
631  }
632  else
633  searchStart = ++guess;
634  }
635  mCurrentLen = cache->len;
636  mCurrentPos=cache->start+cache->len;
637  mDecodeCache.insert(mDecodeCache.begin()+guess, std::move(cache));
638  // mDecodeCache.push_back(cache);
639 
640  mNumSamplesInCache+=cache->len;
641 
642  //if the cache is too big, drop some.
643  while(mNumSamplesInCache>kMaxSamplesInCache)
644  {
645  int dropindex;
646  //drop which ever index is further from our newly added one.
647  dropindex = (guess > (int)mDecodeCache.size()/2) ? 0 : (mDecodeCache.size()-1);
648  mNumSamplesInCache-=mDecodeCache[dropindex]->len;
649  mDecodeCache.erase(mDecodeCache.begin()+dropindex);
650  }
651 }
652 
653 #endif //EXPERIMENTAL_OD_FFMPEG
654 #endif //USE_FFMPEG
streamContext::m_frameValid
int m_frameValid
Definition: FFmpeg.h:1019
streamContext::m_pktRemainingSiz
int m_pktRemainingSiz
Definition: FFmpeg.h:1014
streamContext::m_pkt
Optional< AVPacketEx > m_pkt
Definition: FFmpeg.h:1012
Optional::emplace
X & emplace(Args &&... args)
Definition: MemoryX.h:193
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:84
streamContext
Definition: FFmpeg.h:1007
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
ODFileDecoder::SeekingAllowed
virtual bool SeekingAllowed()
Definition: ODDecodeBlockFile.h:205
streamContext::m_samplesize
size_t m_samplesize
Definition: FFmpeg.h:1025
ScsPtr
std::shared_ptr< Scs > ScsPtr
Definition: FFmpeg.h:1038
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
PickFFmpegLibs
FFmpegLibs * PickFFmpegLibs()
Definition: FFmpeg.cpp:52
streamContext::m_stream
AVStream * m_stream
Definition: FFmpeg.h:1009
Optional::reset
void reset()
Definition: MemoryX.h:221
streamContext::m_samplefmt
AVSampleFormat m_samplefmt
Definition: FFmpeg.h:1026
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
streamContext::m_decodedAudioSamplesValidSiz
size_t m_decodedAudioSamplesValidSiz
Definition: FFmpeg.h:1022
format
int format
Definition: ExportPCM.cpp:56
ODFileDecoder::Decode
virtual int Decode(SampleBuffer &data, sampleFormat &format, sampleCount start, size_t len, unsigned int channel)=0
import_ffmpeg_read_next_frame
streamContext * import_ffmpeg_read_next_frame(AVFormatContext *formatContext, streamContext **streams, unsigned int numStreams)
Definition: FFmpeg.cpp:316
streamContext::m_decodedAudioSamples
AVMallocHolder< uint8_t > m_decodedAudioSamples
Definition: FFmpeg.h:1020
SampleBuffer
Definition: SampleFormat.h:69
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
ODDecodeFFmpegTask.h
ODFileDecoder
class to decode a particular file (one per file). Saves info such as filename and length (after the h...
Definition: ODDecodeBlockFile.h:194
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
ODFileDecoder::ReadHeader
virtual bool ReadHeader()=0
DropFFmpegLibs
void DropFFmpegLibs()
Definition: FFmpeg.cpp:62
import_ffmpeg_decode_frame
int import_ffmpeg_decode_frame(streamContext *sc, bool flushing)
Definition: FFmpeg.cpp:351
NewChannelGroup
std::vector< std::shared_ptr< WaveTrack > > NewChannelGroup
Definition: Import.cpp:62
ArrayOf< uint8_t >
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98