extern int mp4_split(struct mp4_context_t* mp4_context,
                     unsigned int* trak_sample_start,
                     unsigned int* trak_sample_end,
                     mp4_split_options_t const* options)
{
  int result;

  float start_time = options->start;
  float end_time = options->end;

  moov_build_index(mp4_context, mp4_context->moov);

  {
    struct moov_t const* moov = mp4_context->moov;
    long moov_time_scale = moov->mvhd_->timescale_;
    unsigned int start = (unsigned int)(start_time * moov_time_scale + 0.5f);
    unsigned int end = (unsigned int)(end_time * moov_time_scale + 0.5f);

    // for every trak, convert seconds to sample (time-to-sample).
    // adjust sample to keyframe
    result = get_aligned_start_and_end(mp4_context, start, end,
                                       trak_sample_start, trak_sample_end);

    if (options->exact){
      // now we need to find the audio track and RESET *its* trak_sample_start
      // time to the exact start time we want, regardless of keyframes
      unsigned int i=0;
      for(i=0; i != moov->tracks_; ++i){
        struct trak_t* trak = moov->traks_[i];
        if (trak->mdia_->hdlr_->handler_type_ == FOURCC('s','o','u','n')){
          // the FOURCC is soun(d) AKA audio track
          long trak_time_scale = trak->mdia_->mdhd_->timescale_;
          struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_;
          unsigned int start_exact_time_sample = stts_get_sample(stts, moov_time_to_trak_time((options->start * moov_time_scale), moov_time_scale, trak_time_scale));
          MP4_WARNING("FFGOP: AUDIO REWRITING trak_sample_start[%i]: %u => %u\n", i, trak_sample_start[i], start_exact_time_sample);
          trak_sample_start[i] = start_exact_time_sample;
        }
      }
    }
  }

  return result;
}
static void trak_fast_forward_first_partial_GOP(struct mp4_context_t const* mp4_context, 
                                                struct mp4_split_options_t* options, 
                                                struct trak_t *trak, 
                                                unsigned int start_sample)
{
  if (!trak->mdia_->minf_->stbl_->stts_){
    MP4_WARNING("FFGOP: NO STTS FOR THIS TRACK -- CANNOT ADJUST THIS TRACK\n","");
    return;
  }
  // NOTE: STTS atom = "time to sample" atom, which is what we use
  //  (and STSS atom = "sync samples" atom, which is list of keyframes)
  struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_;
  
    

  // find the sample frame location of the exact desired time we wanted to 
  // start at (regardless of keyframes!)
  struct moov_t* moov = mp4_context->moov;
  float moov_time_scale = moov->mvhd_->timescale_;
  float trak_time_scale = trak->mdia_->mdhd_->timescale_;
  unsigned int start_exact_time_sample = stts_get_sample(stts, moov_time_to_trak_time((options->start * moov_time_scale), moov_time_scale, trak_time_scale));

  if (start_exact_time_sample == start_sample)
    return; // starting at wanted time already, nothing to do!

  MP4_INFO("FFGOP: start: %fs;  sample start exact time:%u;  sample keyframe just before:%u\n", 
           options->start, start_exact_time_sample, start_sample);
  MP4_INFO("FFGOP: moov_time_scale = %f, trak_time_scale = %f\n", moov_time_scale, trak_time_scale);

  
  

  // In practice, IA videos seem to always have stts->entries_ == 1 8-)
  // That's the starting number / table setup.
  // The STTS atom will be rewritten by caller, expanding to more entries since we dropping durations!
  unsigned int s=0, i=0, j=0, nRewritten=0;
  for (j=0; j < stts->entries_; j++){ 
    for (i=0; i < stts->table_[j].sample_count_; i++){
      // NOTE: begin time-shifting at "start_sample" bec. mod_h264_streaming 
      // finds the keyframe (sample time) before the exact start time, and *then*
      // decrements by one.  so those samples "go out the door" -- and thus we
      // need to rewrite them, too
      if (s >= start_sample  &&  s < start_exact_time_sample){
        /* see mp4_io.h for samples_t (pts_/size_/pos_/cto_/is_ss_/is_smooth_ss_) */
        samples_t sample = trak->samples_[s];
        // let's change current PTS to something fractionally *just* less than
        // the PTS of the first frame we want to see fully.  each frame we dont want
        // to see is 1 fraction earlier PTS than the next frame PTS.
        uint64_t pts  = sample.pts_;
        uint64_t pts2 = trak->samples_[start_exact_time_sample].pts_ - (start_exact_time_sample-s);
        //uint64_t pts2 = trak->samples_[start_exact_time_sample].pts_ + (s <= (start_sample+1) ? -2 : -1);
        trak->samples_[s].pts_ = pts2;
        MP4_INFO("FFGOP: stts[%d] samples_[%d].pts_ = %lu (%0.3fsec)  REWRITING TO %lu (%0.3fsec)\n", 
                 j, s, pts, ((float)pts / trak_time_scale), pts2, ((float)pts2 / trak_time_scale));
        nRewritten++;
      }
      s++;
    }
  }

  if (nRewritten){
    MP4_WARNING("FFGOP: ==============>  %u FRAMES GOT FAST-FORWARDED (APPROXIMATELY %2.1f SECONDS ASSUMING 29.97 fps, YMMV)\n\n", nRewritten, nRewritten/29.97);
  }
}
// reported by everwanna:
// av out of sync because: 
// audio track 0 without stss, seek to the exact time. 
// video track 1 with stss, seek to the nearest key frame time.
//
// fixed:
// first pass we get the new aligned times for traks with an stss present
// second pass is for traks without an stss
static int get_aligned_start_and_end(struct mp4_context_t const* mp4_context,
                                     unsigned int start, unsigned int end,
                                     unsigned int* trak_sample_start,
                                     unsigned int* trak_sample_end)
{
  unsigned int pass;
  struct moov_t* moov = mp4_context->moov;
  long moov_time_scale = moov->mvhd_->timescale_;

  for(pass = 0; pass != 2; ++pass)
  {
    unsigned int i;
    for(i = 0; i != moov->tracks_; ++i)
    {
      struct trak_t* trak = moov->traks_[i];
      struct stbl_t* stbl = trak->mdia_->minf_->stbl_;
      long trak_time_scale = trak->mdia_->mdhd_->timescale_;

      // 1st pass: stss present, 2nd pass: no stss present
      if(pass == 0 && !stbl->stss_)
        continue;
      if(pass == 1 && stbl->stss_)
        continue;

      // get start
      if(start == 0)
      {
        trak_sample_start[i] = start;
      }
      else
      {
        start = stts_get_sample(stbl->stts_,
          moov_time_to_trak_time(start, moov_time_scale, trak_time_scale));

        MP4_INFO("start=%u (trac time)\n", start);
        MP4_INFO("start=%.2f (seconds)\n",
          stts_get_time(stbl->stts_, start) / (float)trak_time_scale);

        start = stbl_get_nearest_keyframe(stbl, start + 1) - 1;
        MP4_INFO("start=%u (zero based keyframe)\n", start);
        trak_sample_start[i] = start;
        start = (unsigned int)(trak_time_to_moov_time(
          stts_get_time(stbl->stts_, start), moov_time_scale, trak_time_scale));
        MP4_INFO("start=%u (moov time)\n", start);
        MP4_INFO("start=%.2f (seconds)\n", start / (float)moov_time_scale);
      }

      // get end
      if(end == 0)
      {
        // The default is till-the-end of the track
        trak_sample_end[i] = trak->samples_size_;
      }
      else
      {
        end = stts_get_sample(stbl->stts_,
          moov_time_to_trak_time(end, moov_time_scale, trak_time_scale));
        MP4_INFO("end=%u (trac time)\n", end);
        MP4_INFO("end=%.2f (seconds)\n",
          stts_get_time(stbl->stts_, end) / (float)trak_time_scale);

        if(end >= trak->samples_size_)
        {
          end = trak->samples_size_;
        }
        else
        {
          end = stbl_get_nearest_keyframe(stbl, end + 1) - 1;
        }
        MP4_INFO("end=%u (zero based keyframe)\n", end);
        trak_sample_end[i] = end;
//          MP4_INFO("endframe=%u, samples_size_=%u\n", end, trak->samples_size_);
        end = (unsigned int)trak_time_to_moov_time(
          stts_get_time(stbl->stts_, end), moov_time_scale, trak_time_scale);
        MP4_INFO("end=%u (moov time)\n", end);
        MP4_INFO("end=%.2f (seconds)\n", end / (float)moov_time_scale);
      }
    }
  }

  MP4_INFO("start=%u\n", start);
  MP4_INFO("end=%u\n", end);

  if(end && start >= end)
  {
    return 0;
  }

  return 1;
}
예제 #4
0
// reported by everwanna:
// av out of sync because: 
// audio track 0 without stss, seek to the exact time. 
// video track 1 with stss, seek to the nearest key frame time.
//
// fixed:
// first pass we get the new aligned times for traks with an stss present
// second pass is for traks without an stss
static int get_aligned_start_and_end(mp4_context_t const* mp4_context,
                                     int64_t start, int64_t end,
                                     unsigned int* trak_sample_start,
                                     unsigned int* trak_sample_end)
{
  unsigned int pass;
  moov_t const* moov = mp4_context->moov;
  long moov_time_scale = moov->mvhd_->timescale_;

  for(pass = 0; pass != 2; ++pass)
  {
    unsigned int i;
    for(i = 0; i != moov->tracks_; ++i)
    {
      trak_t const* trak = moov->traks_[i];
      long trak_time_scale = trak->mdia_->mdhd_->timescale_;

      if(trak->samples_size_ == 0)
      {
        trak_sample_start[i] = 0;
        trak_sample_end[i] = 0;
        continue;
      }

      // get start
      {
        unsigned int sample_index = time_to_sample(trak,
            moov_time_to_trak_time(start, moov_time_scale, trak_time_scale));

        // backtrack to nearest keyframe
        if(!trak->samples_[sample_index].is_ss_)
        {
          while(sample_index && !trak->samples_[sample_index].is_ss_)
          {
            --sample_index;
          }
          start = trak_time_to_moov_time(trak->samples_[sample_index].pts_,
                                         moov_time_scale, trak_time_scale);
        }
        trak_sample_start[i] = sample_index;
//        MP4_INFO("ts=%"PRId64" (moov time)\n", start);
        MP4_INFO("ts=%.2f (seconds)\n",
                 trak->samples_[sample_index].pts_ / (float)trak_time_scale);
      }

      // get end
      {
        unsigned int sample_index;
        if(end == 0)
        {
          // The default is till-the-end of the track
          sample_index = trak->samples_size_;
        }
        else
        {
          sample_index = time_to_sample(trak,
              moov_time_to_trak_time(end, moov_time_scale, trak_time_scale));
        }

        // backtrack to nearest keyframe
        if(!trak->samples_[sample_index].is_ss_)
        {
          while(sample_index && !trak->samples_[sample_index].is_ss_)
          {
            --sample_index;
          }
          end = trak_time_to_moov_time(trak->samples_[sample_index].pts_,
                                       moov_time_scale, trak_time_scale);
        }
        trak_sample_end[i] = sample_index;
//        MP4_INFO("te=%"PRId64" (moov time)\n", end);
        MP4_INFO("te=%.2f (seconds)\n",
                 trak->samples_[sample_index].pts_ / (float)trak_time_scale);
      }
    }
  }

  MP4_INFO("final start=%"PRId64"\n", start);
  MP4_INFO("final end=%"PRId64"\n", end);

  if(end && start >= end)
  {
    return 0;
  }

  return 1;
}