ffmpeg frame reset retry to push design limits
[goodguy/history.git] / cinelerra-5.1 / cinelerra / ffmpeg.C
index 8e7e62eef0cca6af322fe7a9f75e40c9a1dfcde4..dcbb49c63d4eeee18333df5bbacac178112e0d39 100644 (file)
@@ -148,10 +148,13 @@ void FFAudioStream::reset_history()
 {
        inp = outp = bfr;
        hpos = 0;
+       memset(bfr, 0, lmt-bfr);
 }
 
 void FFAudioStream::iseek(int64_t ofs)
 {
+       if( ofs > hpos ) ofs = hpos;
+       if( ofs > sz ) ofs = sz;
        outp = inp - ofs*nch;
        if( outp < bfr ) outp += sz*nch;
 }
@@ -392,12 +395,11 @@ int FFStream::decode(AVFrame *frame)
 
 int FFStream::load_filter(AVFrame *frame)
 {
+       av_frame_unref(frame);
        int ret = av_buffersrc_add_frame_flags(buffersrc_ctx,
                        frame, AV_BUFFERSRC_FLAG_KEEP_REF);
-       if( ret < 0 ) {
-               av_frame_unref(frame);
+       if( ret < 0 )
                eprintf(_("av_buffersrc_add_frame_flags failed\n"));
-       }
        return ret;
 }
 
@@ -415,6 +417,7 @@ int FFStream::read_filter(AVFrame *frame)
 
 int FFStream::read_frame(AVFrame *frame)
 {
+       av_frame_unref(frame);
        if( !filter_graph || !buffersrc_ctx || !buffersink_ctx )
                return decode(frame);
        if( !fframe && !(fframe=av_frame_alloc()) ) {
@@ -488,7 +491,6 @@ int FFStream::flush()
 
 int FFStream::seek(int64_t no, double rate)
 {
-       int64_t tstmp = -INT64_MAX+1;
 // default ffmpeg native seek
        int npkts = 1;
        int64_t pos = no, pkt_pos = -1;
@@ -505,11 +507,25 @@ int FFStream::seek(int64_t no, double rate)
                        npkts = MAX_RETRY;
                }
        }
-       if( pos > 0 && st->time_base.num > 0 ) {
-               double secs = pos / rate;
-               tstmp = secs * st->time_base.den / st->time_base.num;
-               if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
-       }
+       if( pos == curr_pos ) return 0;
+       double secs = pos < 0 ? 0. : pos / rate;
+       AVRational time_base = st->time_base;
+       int64_t tstmp = time_base.num > 0 ? secs * time_base.den/time_base.num : 0;
+       if( !tstmp ) {
+               if( st->nb_index_entries > 0 ) tstmp = st->index_entries[0].timestamp;
+               else if( st->start_time != AV_NOPTS_VALUE ) tstmp = st->start_time;
+               else if( st->first_dts != AV_NOPTS_VALUE ) tstmp = st->first_dts;
+               else tstmp = INT64_MIN+1;
+       }
+       else if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
+       int idx = st->index;
+#if 0
+// seek all streams using the default timebase.
+//   this is how ffmpeg and ffplay work.  stream seeks are less tested.
+       tstmp = av_rescale_q(tstmp, time_base, AV_TIME_BASE_Q);
+       idx = -1;
+#endif
+
        avcodec_flush_buffers(avctx);
        avformat_flush(fmt_ctx);
 #if 0
@@ -519,9 +535,11 @@ int FFStream::seek(int64_t no, double rate)
                seek = pkt_pos;
                flags = AVSEEK_FLAG_BYTE;
        }
-        int ret = avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, seek, INT64_MAX, flags);
+       int ret = avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, seek, INT64_MAX, flags);
 #else
-        int ret = av_seek_frame(fmt_ctx, st->index, tstmp, AVSEEK_FLAG_ANY);
+// finds the first index frame below the target time
+       int flags = AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY;
+       int ret = av_seek_frame(fmt_ctx, idx, tstmp, flags);
 #endif
        int retry = MAX_RETRY;
        while( ret >= 0 ) {
@@ -553,9 +571,9 @@ int FFStream::seek(int64_t no, double rate)
                }
        }
        if( ret < 0 ) {
-printf("** seek fail %ld, %ld\n", pos, tstmp);
+printf("** seek fail %jd, %jd\n", pos, tstmp);
                seeked = need_packet = 0;
-               st_eof(flushed=1);
+               st_eof(flushed=1);
                return -1;
        }
 //printf("seeked pos = %ld, %ld\n", pos, tstmp);
@@ -721,6 +739,7 @@ int FFAudioStream::load(int64_t pos, int len)
                        init_swr(frame->channels, frame->format, frame->sample_rate);
                        load_history(&frame->extended_data[0], frame->nb_samples);
                        curr_pos += frame->nb_samples;
+                       i = 0;
                }
        }
        if( end_pos > curr_pos ) {
@@ -787,6 +806,11 @@ int FFAudioStream::encode_frame(AVFrame *frame)
        return FFStream::encode_frame(frame);
 }
 
+int FFAudioStream::write_packet(FFPacket &pkt)
+{
+       return FFStream::write_packet(pkt);
+}
+
 void FFAudioStream::load_markers()
 {
        IndexState *index_state = ffmpeg->file_base->asset->index_state;
@@ -845,7 +869,7 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos)
        }
        for( int i=0; ret>=0 && !flushed && curr_pos<=pos && i<MAX_RETRY; ++i ) {
                ret = read_frame(frame);
-               if( ret > 0 ) ++curr_pos;
+               if( ret > 0 ) { ++curr_pos;  i = 0; }
        }
        if( frame->format == AV_PIX_FMT_NONE || frame->width <= 0 || frame->height <= 0 )
                ret = -1;
@@ -917,6 +941,13 @@ int FFVideoStream::encode_frame(AVFrame *frame)
        return FFStream::encode_frame(frame);
 }
 
+int FFVideoStream::write_packet(FFPacket &pkt)
+{
+       if( !(ffmpeg->fmt_ctx->oformat->flags & AVFMT_VARIABLE_FPS) )
+               pkt->duration = 1;
+       return FFStream::write_packet(pkt);
+}
+
 AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model)
 {
        switch( color_model ) {