void ff_restore_parser_state(AVFormatContext *s, AVParserState *state) { int i; AVStream *st; AVParserStreamState *ss; ff_read_frame_flush(s); if (!state) return; avio_seek(s->pb, state->fpos, SEEK_SET); // copy context structures s->packet_buffer = state->packet_buffer; s->parse_queue = state->parse_queue; s->raw_packet_buffer = state->raw_packet_buffer; s->raw_packet_buffer_remaining_size = state->raw_packet_buffer_remaining_size; // copy stream structures for (i = 0; i < state->nb_streams; i++) { st = s->streams[i]; ss = &state->stream_states[i]; st->parser = ss->parser; st->last_IP_pts = ss->last_IP_pts; st->cur_dts = ss->cur_dts; st->probe_packets = ss->probe_packets; } av_freep(&state->stream_states); av_freep(&state); }
/** * Partial search for keyframes in multiple streams. * * This routine searches in each stream for the next lower and the next higher * timestamp compared to the given target timestamp. The search starts at the current * file position and ends at the file position, where all streams have already been * examined (or when all higher key frames are found in the first iteration). * * This routine is called iteratively with an exponential backoff to find the lower * timestamp. * * @param s format context * @param timestamp target timestamp (or position, if AVSEEK_FLAG_BYTE) * @param timebase time base for timestamps * @param flags seeking flags * @param sync array with information per stream * @param keyframes_to_find count of keyframes to find in total * @param found_lo ptr to the count of already found low timestamp keyframes * @param found_hi ptr to the count of already found high timestamp keyframes * @param first_iter flag for first iteration */ static void search_hi_lo_keyframes(AVFormatContext *s, int64_t timestamp, AVRational timebase, int flags, AVSyncPoint *sync, int keyframes_to_find, int *found_lo, int *found_hi, int first_iter) { AVPacket pkt; AVSyncPoint *sp; AVStream *st; int idx; int flg; int terminated_count = 0; int64_t pos; int64_t pts, dts; // PTS/DTS from stream int64_t ts; // PTS in stream-local time base or position for byte seeking AVRational ts_tb; // Time base of the stream or 1:1 for byte seeking for (;;) { if (av_read_frame(s, &pkt) < 0) { // EOF or error, make sure high flags are set for (idx = 0; idx < s->nb_streams; ++idx) { if (s->streams[idx]->discard < AVDISCARD_ALL) { sp = &sync[idx]; if (sp->pos_hi == INT64_MAX) { // no high frame exists for this stream (*found_hi)++; sp->ts_hi = INT64_MAX; sp->pos_hi = INT64_MAX - 1; } } } break; } idx = pkt.stream_index; st = s->streams[idx]; if (st->discard >= AVDISCARD_ALL) // this stream is not active, skip packet continue; sp = &sync[idx]; flg = pkt.flags; pos = pkt.pos; pts = pkt.pts; dts = pkt.dts; if (pts == AV_NOPTS_VALUE) // some formats don't provide PTS, only DTS pts = dts; av_free_packet(&pkt); // Multi-frame packets only return position for the very first frame. // Other frames are read with position == -1. Therefore, we note down // last known position of a frame and use it if a frame without // position arrives. In this way, it's possible to seek to proper // position. Additionally, for parsers not providing position at all, // an approximation will be used (starting position of this iteration). if (pos < 0) pos = sp->last_pos; else sp->last_pos = pos; // Evaluate key frames with known TS (or any frames, if AVSEEK_FLAG_ANY set). if (pts != AV_NOPTS_VALUE && ((flg & AV_PKT_FLAG_KEY) || (flags & AVSEEK_FLAG_ANY))) { if (flags & AVSEEK_FLAG_BYTE) { // for byte seeking, use position as timestamp ts = pos; ts_tb.num = 1; ts_tb.den = 1; } else { // otherwise, get stream time_base ts = pts; ts_tb = st->time_base; } if (sp->first_ts == AV_NOPTS_VALUE) { // Note down termination timestamp for the next iteration - when // we encounter a packet with the same timestamp, we will ignore // any further packets for this stream in next iteration (as they // are already evaluated). sp->first_ts = ts; sp->first_ts_tb = ts_tb; } if (sp->term_ts != AV_NOPTS_VALUE && av_compare_ts(ts, ts_tb, sp->term_ts, sp->term_ts_tb) > 0) { // past the end position from last iteration, ignore packet if (!sp->terminated) { sp->terminated = 1; ++terminated_count; if (sp->pos_hi == INT64_MAX) { // no high frame exists for this stream (*found_hi)++; sp->ts_hi = INT64_MAX; sp->pos_hi = INT64_MAX - 1; } if (terminated_count == keyframes_to_find) break; // all terminated, iteration done } continue; } if (av_compare_ts(ts, ts_tb, timestamp, timebase) <= 0) { // keyframe found before target timestamp if (sp->pos_lo == INT64_MAX) { // found first keyframe lower than target timestamp (*found_lo)++; sp->ts_lo = ts; sp->pos_lo = pos; } else if (sp->ts_lo < ts) { // found a better match (closer to target timestamp) sp->ts_lo = ts; sp->pos_lo = pos; } } if (av_compare_ts(ts, ts_tb, timestamp, timebase) >= 0) { // keyframe found after target timestamp if (sp->pos_hi == INT64_MAX) { // found first keyframe higher than target timestamp (*found_hi)++; sp->ts_hi = ts; sp->pos_hi = pos; if (*found_hi >= keyframes_to_find && first_iter) { // We found high frame for all. They may get updated // to TS closer to target TS in later iterations (which // will stop at start position of previous iteration). break; } } else if (sp->ts_hi > ts) { // found a better match (actually, shouldn't happen) sp->ts_hi = ts; sp->pos_hi = pos; } } } } // Clean up the parser. ff_read_frame_flush(s); }