+
+ /* find the corresponding dts */
+ if (sc && sc->sample_to_time_index < sc->stts_count && pkt) {
+ unsigned int count;
+ uint64_t dts, pts;
+ unsigned int duration = sc->stts_data[sc->sample_to_time_index].duration;
+ count = sc->stts_data[sc->sample_to_time_index].count;
+ if ((sc->sample_to_time_sample + count) < sc->current_sample) {
+ sc->sample_to_time_sample += count;
+ sc->sample_to_time_time += count*duration;
+ sc->sample_to_time_index ++;
+ duration = sc->stts_data[sc->sample_to_time_index].duration;
+ }
+ dts = sc->sample_to_time_time + (sc->current_sample-1 - sc->sample_to_time_sample) * (int64_t)duration;
+ /* find the corresponding pts */
+ if (sc->sample_to_ctime_index < sc->ctts_count) {
+ int duration = sc->ctts_data[sc->sample_to_ctime_index].duration;
+ int count = sc->ctts_data[sc->sample_to_ctime_index].count;
+
+ if ((sc->sample_to_ctime_sample + count) < sc->current_sample) {
+ sc->sample_to_ctime_sample += count;
+ sc->sample_to_ctime_index ++;
+ duration = sc->ctts_data[sc->sample_to_ctime_index].duration;
+ }
+ pts = dts + duration;
+ }else
+ pts = dts;
+ pkt->pts = pts;
+ pkt->dts = dts;
+#ifdef DEBUG
+ av_log(NULL, AV_LOG_DEBUG, "stream #%d smp #%ld dts = %lld pts = %lld (smp:%ld time:%lld idx:%d ent:%d count:%d dur:%d)\n"
+ , pkt->stream_index, sc->current_sample-1, pkt->dts, pkt->pts
+ , sc->sample_to_time_sample
+ , sc->sample_to_time_time
+ , sc->sample_to_time_index
+ , sc->stts_count
+ , count
+ , duration);
+#endif
+ }
+
+ return 0;
+}
+
+#if defined(MOV_SPLIT_CHUNKS) && defined(MOV_SEEK)
+/**
+ * Seek method based on the one described in the Appendix C of QTFileFormat.pdf
+ */
+static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
+{
+ MOVContext* mov = (MOVContext *) s->priv_data;
+ MOVStreamContext* sc;
+ int32_t i, a, b, m;
+ int64_t start_time;
+ int32_t seek_sample, sample;
+ int32_t duration;
+ int32_t count;
+ int32_t chunk;
+ int32_t left_in_chunk;
+ int64_t chunk_file_offset;
+ int64_t sample_file_offset;
+ int32_t first_chunk_sample;
+ int32_t sample_to_chunk_idx;
+ int sample_to_time_index;
+ long sample_to_time_sample = 0;
+ uint64_t sample_to_time_time = 0;
+ int mov_idx;
+
+ // Find the corresponding mov stream
+ for (mov_idx = 0; mov_idx < mov->total_streams; mov_idx++)
+ if (mov->streams[mov_idx]->ffindex == stream_index)
+ break;
+ if (mov_idx == mov->total_streams) {
+ av_log(s, AV_LOG_ERROR, "mov: requested stream was not found in mov streams (idx=%i)\n", stream_index);
+ return -1;
+ }
+ sc = mov->streams[mov_idx];
+
+ // Step 1. Find the edit that contains the requested time (elst)
+ if (sc->edit_count) {
+ // FIXME should handle edit list
+ av_log(s, AV_LOG_ERROR, "mov: does not handle seeking in files that contain edit list (c:%d)\n", sc->edit_count);
+ return -1;
+ }
+
+ // Step 2. Find the corresponding sample using the Time-to-sample atom (stts) */
+#ifdef DEBUG
+ av_log(s, AV_LOG_DEBUG, "Searching for time %li in stream #%i (time_scale=%i)\n", (long)timestamp, mov_idx, sc->time_scale);
+#endif
+ start_time = 0; // FIXME use elst atom
+ sample = 1; // sample are 0 based in table
+#ifdef DEBUG
+ av_log(s, AV_LOG_DEBUG, "Searching for sample_time %li \n", (long)sample_time);
+#endif
+ for (i = 0; i < sc->stts_count; i++) {
+ count = sc->stts_data[i].count;
+ duration = sc->stts_data[i].duration;
+//av_log(s, AV_LOG_DEBUG, "> sample_time %lli \n", (long)sample_time);
+//av_log(s, AV_LOG_DEBUG, "> count=%i duration=%i\n", count, duration);
+ if ((start_time + count*duration) > sample_time) {
+ sample_to_time_time = start_time;
+ sample_to_time_index = i;
+ sample_to_time_sample = sample;
+ sample += (sample_time - start_time) / duration;
+ break;
+ }
+ sample += count;
+ start_time += count * duration;
+ }
+ sample_to_time_time = start_time;
+ sample_to_time_index = i;
+ /* NOTE: despite what qt doc say, the dt value (Display Time in qt vocabulary) computed with the stts atom
+ is a decoding time stamp (dts) not a presentation time stamp. And as usual dts != pts for stream with b frames */
+
+#ifdef DEBUG
+ av_log(s, AV_LOG_DEBUG, "Found time %li at sample #%u\n", (long)sample_time, sample);
+#endif
+ if (sample > sc->sample_count) {
+ av_log(s, AV_LOG_ERROR, "mov: sample pos is too high, unable to seek (req. sample=%i, sample count=%ld)\n", sample, sc->sample_count);
+ return -1;
+ }
+
+ // Step 3. Find the prior sync. sample using the Sync sample atom (stss)
+ if (sc->keyframes) {
+ a = 0;
+ b = sc->keyframe_count - 1;
+ while (a < b) {
+ m = (a + b + 1) >> 1;
+ if (sc->keyframes[m] > sample) {
+ b = m - 1;
+ } else {
+ a = m;
+ }
+#ifdef DEBUG
+ // av_log(s, AV_LOG_DEBUG, "a=%i (%i) b=%i (%i) m=%i (%i) stream #%i\n", a, sc->keyframes[a], b, sc->keyframes[b], m, sc->keyframes[m], mov_idx);
+#endif
+ }
+ // for low latency prob: always use the previous keyframe, just uncomment the next line
+ // if (a) a--;
+ seek_sample = sc->keyframes[a];
+ }
+ else
+ seek_sample = sample; // else all samples are key frames
+#ifdef DEBUG
+ av_log(s, AV_LOG_DEBUG, "Found nearest keyframe at sample #%i \n", seek_sample);
+#endif
+
+ // Step 4. Find the chunk of the sample using the Sample-to-chunk-atom (stsc)
+ for (first_chunk_sample = 1, i = 0; i < (sc->sample_to_chunk_sz - 1); i++) {
+ b = (sc->sample_to_chunk[i + 1].first - sc->sample_to_chunk[i].first) * sc->sample_to_chunk[i].count;
+ if (seek_sample >= first_chunk_sample && seek_sample < (first_chunk_sample + b))
+ break;
+ first_chunk_sample += b;
+ }
+ chunk = sc->sample_to_chunk[i].first + (seek_sample - first_chunk_sample) / sc->sample_to_chunk[i].count;
+ left_in_chunk = sc->sample_to_chunk[i].count - (seek_sample - first_chunk_sample) % sc->sample_to_chunk[i].count;
+ first_chunk_sample += ((seek_sample - first_chunk_sample) / sc->sample_to_chunk[i].count) * sc->sample_to_chunk[i].count;
+ sample_to_chunk_idx = i;
+#ifdef DEBUG
+ av_log(s, AV_LOG_DEBUG, "Sample was found in chunk #%i at sample offset %i (idx %i)\n", chunk, seek_sample - first_chunk_sample, sample_to_chunk_idx);
+#endif