+static int seek_video( producer_avformat self, mlt_position position,
+ int64_t req_position, int must_decode, int use_new_seek, int *ignore )
+{
+ mlt_producer producer = self->parent;
+ int paused = 0;
+
+ if ( self->seekable && ( position != self->video_expected || self->last_position < 0 ) )
+ {
+ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+ // Fetch the video format context
+ AVFormatContext *context = self->video_format;
+
+ // Get the video stream
+ AVStream *stream = context->streams[ self->video_index ];
+
+ // Get codec context
+ AVCodecContext *codec_context = stream->codec;
+
+ // We may want to use the source fps if available
+ double source_fps = mlt_properties_get_double( properties, "meta.media.frame_rate_num" ) /
+ mlt_properties_get_double( properties, "meta.media.frame_rate_den" );
+
+ if ( self->av_frame && position + 1 == self->video_expected )
+ {
+ // We're paused - use last image
+ paused = 1;
+ }
+ else if ( !self->seekable && position > self->video_expected && ( position - self->video_expected ) < 250 )
+ {
+ // Fast forward - seeking is inefficient for small distances - just ignore following frames
+ *ignore = ( int )( ( position - self->video_expected ) / mlt_producer_get_fps( producer ) * source_fps );
+ codec_context->skip_loop_filter = AVDISCARD_NONREF;
+ }
+ else if ( self->seekable && ( position < self->video_expected || position - self->video_expected >= 12 || self->last_position < 0 ) )
+ {
+ if ( use_new_seek && self->last_position == POSITION_INITIAL )
+ {
+ // find first key frame
+ int ret = 0;
+ int toscan = 100;
+ AVPacket pkt;
+
+ while ( ret >= 0 && toscan-- > 0 )
+ {
+ ret = av_read_frame( context, &pkt );
+ if ( ret >= 0 && ( pkt.flags & PKT_FLAG_KEY ) && pkt.stream_index == self->video_index )
+ {
+ mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "first_pts %"PRId64" dts %"PRId64" pts_dts_delta %d\n", pkt.pts, pkt.dts, (int)(pkt.pts - pkt.dts) );
+ self->first_pts = pkt.pts;
+ toscan = 0;
+ }
+ av_free_packet( &pkt );
+ }
+ // Rewind
+ av_seek_frame( context, -1, 0, AVSEEK_FLAG_BACKWARD );
+ }
+
+ // Calculate the timestamp for the requested frame
+ int64_t timestamp;
+ if ( use_new_seek )
+ {
+ timestamp = ( req_position - 0.1 / source_fps ) /
+ ( av_q2d( stream->time_base ) * source_fps );
+ mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pos %"PRId64" pts %"PRId64"\n", req_position, timestamp );
+ if ( self->first_pts > 0 )
+ timestamp += self->first_pts;
+ else if ( context->start_time != AV_NOPTS_VALUE )
+ timestamp += context->start_time;
+ }
+ else
+ {
+ timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 );
+ if ( context->start_time != AV_NOPTS_VALUE )
+ timestamp += context->start_time;
+ }
+ if ( must_decode )
+ timestamp -= AV_TIME_BASE;
+ if ( timestamp < 0 )
+ timestamp = 0;
+ mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "seeking timestamp %"PRId64" position %d expected %d last_pos %"PRId64"\n",
+ timestamp, position, self->video_expected, self->last_position );
+
+ // Seek to the timestamp
+ if ( use_new_seek )
+ {
+ codec_context->skip_loop_filter = AVDISCARD_NONREF;
+ av_seek_frame( context, self->video_index, timestamp, AVSEEK_FLAG_BACKWARD );
+ }
+ else if ( req_position > 0 || self->last_position <= 0 )
+ {
+ av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD );
+ }
+ else
+ {
+ // Re-open video stream when rewinding to beginning from somewhere else.
+ // This is rather ugly, and I prefer not to do it this way, but ffmpeg is
+ // not reliably seeking to the first frame across formats.
+ reopen_video( self, producer );
+ }
+
+ // Remove the cached info relating to the previous position
+ self->current_position = POSITION_INVALID;
+ self->last_position = POSITION_INVALID;
+ av_freep( &self->av_frame );
+
+ if ( use_new_seek )
+ {
+ // flush any pictures still in decode buffer
+ avcodec_flush_buffers( codec_context );
+ }
+ }
+ }
+ return paused;
+}
+