• ffmpeg ts 关于av_seek_frame


    1 ffmpeg命令

    一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现,比如

    ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4

     其中 -ss 放置较前  开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 (此描述可能不精确)

    ffmpeg根据此命令是如何裁剪呢?

    1.1 命令行入口

    执行代码位置

    fftools/ffmpeg.c 

    1. int main(int argc, char **argv)
    2. {
    3. int i, ret;
    4. BenchmarkTimeStamps ti;
    5. init_dynload();
    6. register_exit(ffmpeg_cleanup);
    7. setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
    8. av_log_set_flags(AV_LOG_SKIP_REPEATED);
    9. parse_loglevel(argc, argv, options);
    10. if(argc>1 && !strcmp(argv[1], "-d")){
    11. run_as_daemon=1;
    12. av_log_set_callback(log_callback_null);
    13. argc--;
    14. argv++;
    15. }
    16. #if CONFIG_AVDEVICE
    17. avdevice_register_all();
    18. #endif
    19. avformat_network_init();
    20. show_banner(argc, argv, options);
    21. /* parse options and open all input/output files */
    22. ret = ffmpeg_parse_options(argc, argv);
    23. if (ret < 0)
    24. exit_program(1);
    25. if (nb_output_files <= 0 && nb_input_files == 0) {
    26. show_usage();
    27. av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
    28. exit_program(1);
    29. }
    30. /* file converter / grab */
    31. if (nb_output_files <= 0) {
    32. av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
    33. exit_program(1);
    34. }
    35. for (i = 0; i < nb_output_files; i++) {
    36. if (strcmp(output_files[i]->ctx->oformat->name, "rtp"))
    37. want_sdp = 0;
    38. }
    39. current_time = ti = get_benchmark_time_stamps();
    40. if (transcode() < 0)
    41. exit_program(1);
    42. if (do_benchmark) {
    43. int64_t utime, stime, rtime;
    44. current_time = get_benchmark_time_stamps();
    45. utime = current_time.user_usec - ti.user_usec;
    46. stime = current_time.sys_usec - ti.sys_usec;
    47. rtime = current_time.real_usec - ti.real_usec;
    48. av_log(NULL, AV_LOG_INFO,
    49. "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n",
    50. utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0);
    51. }
    52. av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
    53. decode_error_stat[0], decode_error_stat[1]);
    54. if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
    55. exit_program(69);
    56. exit_program(received_nb_signals ? 255 : main_return_code);
    57. return main_return_code;
    58. }

    1.2 执行流程

    代码流程比较清晰 主要流程如下:

    1.3 输入上下文设置 

    ffmpeg将解析的参数存放到OptionParseContext中包含输入及输出参数设置,然后进入输入上下文

    根据之前解析的数据OptionPareseContext作为参数传入,确定好seek点,然后调用 avformat_seek_file 

    1. static int open_input_file(OptionsContext *o, const char *filename)
    2. {
    3. InputFile *f;
    4. AVFormatContext *ic;
    5. AVInputFormat *file_iformat = NULL;
    6. int err, i, ret;
    7. int64_t timestamp;
    8. AVDictionary *unused_opts = NULL;
    9. AVDictionaryEntry *e = NULL;
    10. char * video_codec_name = NULL;
    11. char * audio_codec_name = NULL;
    12. char *subtitle_codec_name = NULL;
    13. char * data_codec_name = NULL;
    14. int scan_all_pmts_set = 0;
    15. if (o->stop_time != INT64_MAX && o->recording_time != INT64_MAX) {
    16. o->stop_time = INT64_MAX;
    17. av_log(NULL, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n");
    18. }
    19. if (o->stop_time != INT64_MAX && o->recording_time == INT64_MAX) {
    20. int64_t start_time = o->start_time == AV_NOPTS_VALUE ? 0 : o->start_time;
    21. if (o->stop_time <= start_time) {
    22. av_log(NULL, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n");
    23. exit_program(1);
    24. } else {
    25. o->recording_time = o->stop_time - start_time;
    26. }
    27. }
    28. if (o->format) {
    29. if (!(file_iformat = av_find_input_format(o->format))) {
    30. av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format);
    31. exit_program(1);
    32. }
    33. }
    34. if (!strcmp(filename, "-"))
    35. filename = "pipe:";
    36. stdin_interaction &= strncmp(filename, "pipe:", 5) &&
    37. strcmp(filename, "/dev/stdin");
    38. /* get default parameters from command line */
    39. ic = avformat_alloc_context();
    40. if (!ic) {
    41. print_error(filename, AVERROR(ENOMEM));
    42. exit_program(1);
    43. }
    44. if (o->nb_audio_sample_rate) {
    45. av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i, 0);
    46. }
    47. if (o->nb_audio_channels) {
    48. /* because we set audio_channels based on both the "ac" and
    49. * "channel_layout" options, we need to check that the specified
    50. * demuxer actually has the "channels" option before setting it */
    51. if (file_iformat && file_iformat->priv_class &&
    52. av_opt_find(&file_iformat->priv_class, "channels", NULL, 0,
    53. AV_OPT_SEARCH_FAKE_OBJ)) {
    54. av_dict_set_int(&o->g->format_opts, "channels", o->audio_channels[o->nb_audio_channels - 1].u.i, 0);
    55. }
    56. }
    57. if (o->nb_frame_rates) {
    58. /* set the format-level framerate option;
    59. * this is important for video grabbers, e.g. x11 */
    60. if (file_iformat && file_iformat->priv_class &&
    61. av_opt_find(&file_iformat->priv_class, "framerate", NULL, 0,
    62. AV_OPT_SEARCH_FAKE_OBJ)) {
    63. av_dict_set(&o->g->format_opts, "framerate",
    64. o->frame_rates[o->nb_frame_rates - 1].u.str, 0);
    65. }
    66. }
    67. if (o->nb_frame_sizes) {
    68. av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0);
    69. }
    70. if (o->nb_frame_pix_fmts)
    71. av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0);
    72. MATCH_PER_TYPE_OPT(codec_names, str, video_codec_name, ic, "v");
    73. MATCH_PER_TYPE_OPT(codec_names, str, audio_codec_name, ic, "a");
    74. MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, ic, "s");
    75. MATCH_PER_TYPE_OPT(codec_names, str, data_codec_name, ic, "d");
    76. if (video_codec_name)
    77. ic->video_codec = find_codec_or_die(video_codec_name , AVMEDIA_TYPE_VIDEO , 0);
    78. if (audio_codec_name)
    79. ic->audio_codec = find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0);
    80. if (subtitle_codec_name)
    81. ic->subtitle_codec = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0);
    82. if (data_codec_name)
    83. ic->data_codec = find_codec_or_die(data_codec_name , AVMEDIA_TYPE_DATA , 0);
    84. ic->video_codec_id = video_codec_name ? ic->video_codec->id : AV_CODEC_ID_NONE;
    85. ic->audio_codec_id = audio_codec_name ? ic->audio_codec->id : AV_CODEC_ID_NONE;
    86. ic->subtitle_codec_id = subtitle_codec_name ? ic->subtitle_codec->id : AV_CODEC_ID_NONE;
    87. ic->data_codec_id = data_codec_name ? ic->data_codec->id : AV_CODEC_ID_NONE;
    88. ic->flags |= AVFMT_FLAG_NONBLOCK;
    89. if (o->bitexact)
    90. ic->flags |= AVFMT_FLAG_BITEXACT;
    91. ic->interrupt_callback = int_cb;
    92. if (!av_dict_get(o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
    93. av_dict_set(&o->g->format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
    94. scan_all_pmts_set = 1;
    95. }
    96. /* open the input file with generic avformat function */
    97. err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts);
    98. if (err < 0) {
    99. print_error(filename, err);
    100. if (err == AVERROR_PROTOCOL_NOT_FOUND)
    101. av_log(NULL, AV_LOG_ERROR, "Did you mean file:%s?\n", filename);
    102. exit_program(1);
    103. }
    104. if (scan_all_pmts_set)
    105. av_dict_set(&o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
    106. remove_avoptions(&o->g->format_opts, o->g->codec_opts);
    107. assert_avoptions(o->g->format_opts);
    108. /* apply forced codec ids */
    109. for (i = 0; i < ic->nb_streams; i++)
    110. choose_decoder(o, ic, ic->streams[i]);
    111. if (find_stream_info) {
    112. AVDictionary **opts = setup_find_stream_info_opts(ic, o->g->codec_opts);
    113. int orig_nb_streams = ic->nb_streams;
    114. /* If not enough info to get the stream parameters, we decode the
    115. first frames to get it. (used in mpeg case for example) */
    116. ret = avformat_find_stream_info(ic, opts);
    117. for (i = 0; i < orig_nb_streams; i++)
    118. av_dict_free(&opts[i]);
    119. av_freep(&opts);
    120. if (ret < 0) {
    121. av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename);
    122. if (ic->nb_streams == 0) {
    123. avformat_close_input(&ic);
    124. exit_program(1);
    125. }
    126. }
    127. }
    128. if (o->start_time != AV_NOPTS_VALUE && o->start_time_eof != AV_NOPTS_VALUE) {
    129. av_log(NULL, AV_LOG_WARNING, "Cannot use -ss and -sseof both, using -ss for %s\n", filename);
    130. o->start_time_eof = AV_NOPTS_VALUE;
    131. }
    132. if (o->start_time_eof != AV_NOPTS_VALUE) {
    133. if (o->start_time_eof >= 0) {
    134. av_log(NULL, AV_LOG_ERROR, "-sseof value must be negative; aborting\n");
    135. exit_program(1);
    136. }
    137. if (ic->duration > 0) {
    138. o->start_time = o->start_time_eof + ic->duration;
    139. if (o->start_time < 0) {
    140. av_log(NULL, AV_LOG_WARNING, "-sseof value seeks to before start of file %s; ignored\n", filename);
    141. o->start_time = AV_NOPTS_VALUE;
    142. }
    143. } else
    144. av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename);
    145. }
    146. timestamp = (o->start_time == AV_NOPTS_VALUE) ? 0 : o->start_time;
    147. /* add the stream start time */
    148. if (!o->seek_timestamp && ic->start_time != AV_NOPTS_VALUE)
    149. timestamp += ic->start_time;
    150. /* if seeking requested, we execute it */
    151. if (o->start_time != AV_NOPTS_VALUE) {
    152. int64_t seek_timestamp = timestamp;
    153. if (!(ic->iformat->flags & AVFMT_SEEK_TO_PTS)) {
    154. int dts_heuristic = 0;
    155. for (i=0; inb_streams; i++) {
    156. const AVCodecParameters *par = ic->streams[i]->codecpar;
    157. if (par->video_delay) {
    158. dts_heuristic = 1;
    159. break;
    160. }
    161. }
    162. if (dts_heuristic) {
    163. seek_timestamp -= 3*AV_TIME_BASE / 23;
    164. }
    165. }
    166. ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0);
    167. if (ret < 0) {
    168. av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
    169. filename, (double)timestamp / AV_TIME_BASE);
    170. }
    171. }
    172. /* update the current parameters so that they match the one of the input stream */
    173. add_input_streams(o, ic);
    174. /* dump the file content */
    175. av_dump_format(ic, nb_input_files, filename, 0);
    176. GROW_ARRAY(input_files, nb_input_files);
    177. f = av_mallocz(sizeof(*f));
    178. if (!f)
    179. exit_program(1);
    180. input_files[nb_input_files - 1] = f;
    181. f->ctx = ic;
    182. f->ist_index = nb_input_streams - ic->nb_streams;
    183. f->start_time = o->start_time;
    184. f->recording_time = o->recording_time;
    185. f->input_ts_offset = o->input_ts_offset;
    186. f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
    187. f->nb_streams = ic->nb_streams;
    188. f->rate_emu = o->rate_emu;
    189. f->accurate_seek = o->accurate_seek;
    190. f->loop = o->loop;
    191. f->duration = 0;
    192. f->time_base = (AVRational){ 1, 1 };
    193. #if HAVE_THREADS
    194. f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8;
    195. #endif
    196. /* check if all codec options have been used */
    197. unused_opts = strip_specifiers(o->g->codec_opts);
    198. for (i = f->ist_index; i < nb_input_streams; i++) {
    199. e = NULL;
    200. while ((e = av_dict_get(input_streams[i]->decoder_opts, "", e,
    201. AV_DICT_IGNORE_SUFFIX)))
    202. av_dict_set(&unused_opts, e->key, NULL, 0);
    203. }
    204. e = NULL;
    205. while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
    206. const AVClass *class = avcodec_get_class();
    207. const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
    208. AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
    209. const AVClass *fclass = avformat_get_class();
    210. const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
    211. AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
    212. if (!option || foption)
    213. continue;
    214. if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) {
    215. av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
    216. "input file #%d (%s) is not a decoding option.\n", e->key,
    217. option->help ? option->help : "", nb_input_files - 1,
    218. filename);
    219. exit_program(1);
    220. }
    221. av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
    222. "input file #%d (%s) has not been used for any stream. The most "
    223. "likely reason is either wrong type (e.g. a video option with "
    224. "no video streams) or that it is a private option of some decoder "
    225. "which was not actually used for any stream.\n", e->key,
    226. option->help ? option->help : "", nb_input_files - 1, filename);
    227. }
    228. av_dict_free(&unused_opts);
    229. for (i = 0; i < o->nb_dump_attachment; i++) {
    230. int j;
    231. for (j = 0; j < ic->nb_streams; j++) {
    232. AVStream *st = ic->streams[j];
    233. if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1)
    234. dump_attachment(st, o->dump_attachment[i].u.str);
    235. }
    236. }
    237. input_stream_potentially_available = 1;
    238. return 0;
    239. }

    1.4 avformat_seek_file

    1. int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts,
    2. int64_t ts, int64_t max_ts, int flags)
    3. {
    4. if (min_ts > ts || max_ts < ts)
    5. return -1;
    6. if (stream_index < -1 || stream_index >= (int)s->nb_streams)
    7. return AVERROR(EINVAL);
    8. if (s->seek2any>0)
    9. flags |= AVSEEK_FLAG_ANY;
    10. flags &= ~AVSEEK_FLAG_BACKWARD;
    11. if (s->iformat->read_seek2) {
    12. int ret;
    13. ff_read_frame_flush(s);
    14. if (stream_index == -1 && s->nb_streams == 1) {
    15. AVRational time_base = s->streams[0]->time_base;
    16. ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
    17. min_ts = av_rescale_rnd(min_ts, time_base.den,
    18. time_base.num * (int64_t)AV_TIME_BASE,
    19. AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
    20. max_ts = av_rescale_rnd(max_ts, time_base.den,
    21. time_base.num * (int64_t)AV_TIME_BASE,
    22. AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
    23. stream_index = 0;
    24. }
    25. ret = s->iformat->read_seek2(s, stream_index, min_ts,
    26. ts, max_ts, flags);
    27. if (ret >= 0)
    28. ret = avformat_queue_attached_pictures(s);
    29. return ret;
    30. }
    31. if (s->iformat->read_timestamp) {
    32. // try to seek via read_timestamp()
    33. }
    34. // Fall back on old API if new is not implemented but old is.
    35. // Note the old API has somewhat different semantics.
    36. if (s->iformat->read_seek || 1) {
    37. int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0);
    38. int ret = av_seek_frame(s, stream_index, ts, flags | dir);
    39. if (ret<0 && ts != min_ts && max_ts != ts) {
    40. ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir);
    41. if (ret >= 0)
    42. ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD));
    43. }
    44. return ret;
    45. }
    46. // try some generic seek like seek_frame_generic() but with new ts semantics
    47. return -1; //unreachable
    48. }

    ffmpeg中大部分fromat都定义实现了read_seek ,一般情况下都会调用av_seek_frame,因为输入文件是mp4格式 .format对应 mov ,

    av_ssek_frame->seek_frame_internal-> read_seek ->mov_read_seek

    1. AVInputFormat ff_mov_demuxer = {
    2. .name = "mov,mp4,m4a,3gp,3g2,mj2",
    3. .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
    4. .priv_class = &mov_class,
    5. .priv_data_size = sizeof(MOVContext),
    6. .extensions = "mov,mp4,m4a,3gp,3g2,mj2",
    7. .read_probe = mov_probe,
    8. .read_header = mov_read_header,
    9. .read_packet = mov_read_packet,
    10. .read_close = mov_read_close,
    11. .read_seek = mov_read_seek,
    12. .flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS,
    13. };

    真正确定seek位置代码函数 ff_index_search_timestamp flags不同 查询规则有所不同

    1. int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries,
    2. int64_t wanted_timestamp, int flags)
    3. {
    4. int a, b, m;
    5. int64_t timestamp;
    6. a = -1;
    7. b = nb_entries;
    8. // Optimize appending index entries at the end.
    9. if (b && entries[b - 1].timestamp < wanted_timestamp)
    10. a = b - 1;
    11. while (b - a > 1) {
    12. m = (a + b) >> 1;
    13. // Search for the next non-discarded packet.
    14. while ((entries[m].flags & AVINDEX_DISCARD_FRAME) && m < b && m < nb_entries - 1) {
    15. m++;
    16. if (m == b && entries[m].timestamp >= wanted_timestamp) {
    17. m = b - 1;
    18. break;
    19. }
    20. }
    21. timestamp = entries[m].timestamp;
    22. if (timestamp >= wanted_timestamp)
    23. b = m;
    24. if (timestamp <= wanted_timestamp)
    25. a = m;
    26. }
    27. m = (flags & AVSEEK_FLAG_BACKWARD) ? a : b;
    28. if (!(flags & AVSEEK_FLAG_ANY))
    29. while (m >= 0 && m < nb_entries &&
    30. !(entries[m].flags & AVINDEX_KEYFRAME))
    31. m += (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1;
    32. if (m == nb_entries)
    33. return -1;
    34. return m;
    35. }

    flags:

    #define AVSEEK_FLAG_BACKGROUND 1              ///<

    #define AVSEEK_FALG_BYTE         ///<< 变成一个 byte, 按照文件的大小位置跳到那个位置

    #define AVSEEK_FLAG_ANY         ///<<

    #define AVSEEK_FLAG_FRAME        ///<<

    2  TS 

    对于ts切割格式采用同样的方式分析  mpegts.c , 并未定义 read_seek

    1. AVInputFormat ff_mpegts_demuxer = {
    2. .name = "mpegts",
    3. .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
    4. .priv_data_size = sizeof(MpegTSContext),
    5. .read_probe = mpegts_probe,
    6. .read_header = mpegts_read_header,
    7. .read_packet = mpegts_read_packet,
    8. .read_close = mpegts_read_close,
    9. .read_timestamp = mpegts_get_dts,
    10. .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
    11. .priv_class = &mpegts_class,
    12. };

    从而进入 ff_seek_frame_binary 方法,由于是实时流没有index_entry 调用ff_gen_search 生成seek pos  gen_seek

    1. int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
    2. int64_t target_ts, int flags)
    3. {
    4. const AVInputFormat *avif = s->iformat;
    5. int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit;
    6. int64_t ts_min, ts_max, ts;
    7. int index;
    8. int64_t ret;
    9. AVStream *st;
    10. if (stream_index < 0)
    11. return -1;
    12. av_log(s, AV_LOG_TRACE, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts));
    13. ts_max =
    14. ts_min = AV_NOPTS_VALUE;
    15. pos_limit = -1; // GCC falsely says it may be uninitialized.
    16. st = s->streams[stream_index];
    17. if (st->index_entries) {
    18. AVIndexEntry *e;
    19. /* FIXME: Whole function must be checked for non-keyframe entries in
    20. * index case, especially read_timestamp(). */
    21. index = av_index_search_timestamp(st, target_ts,
    22. flags | AVSEEK_FLAG_BACKWARD);
    23. index = FFMAX(index, 0);
    24. e = &st->index_entries[index];
    25. if (e->timestamp <= target_ts || e->pos == e->min_distance) {
    26. pos_min = e->pos;
    27. ts_min = e->timestamp;
    28. av_log(s, AV_LOG_TRACE, "using cached pos_min=0x%"PRIx64" dts_min=%s\n",
    29. pos_min, av_ts2str(ts_min));
    30. } else {
    31. av_assert1(index == 0);
    32. }
    33. index = av_index_search_timestamp(st, target_ts,
    34. flags & ~AVSEEK_FLAG_BACKWARD);
    35. av_assert0(index < st->nb_index_entries);
    36. if (index >= 0) {
    37. e = &st->index_entries[index];
    38. av_assert1(e->timestamp >= target_ts);
    39. pos_max = e->pos;
    40. ts_max = e->timestamp;
    41. pos_limit = pos_max - e->min_distance;
    42. av_log(s, AV_LOG_TRACE, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64
    43. " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max));
    44. }
    45. }
    46. pos = ff_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit,
    47. ts_min, ts_max, flags, &ts, avif->read_timestamp);
    48. if (pos < 0)
    49. return -1;
    50. /* do the seek */
    51. if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0)
    52. return ret;
    53. ff_read_frame_flush(s);
    54. ff_update_cur_dts(s, st, ts);
    55. return 0;
    56. }

    注意avid->read_timestamap  mpegts 将通过mpegts_get_dts 通过时间戳查询pos

    1. AVInputFormat ff_mpegts_demuxer = {
    2. .name = "mpegts",
    3. .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
    4. .priv_data_size = sizeof(MpegTSContext),
    5. .read_probe = mpegts_probe,
    6. .read_header = mpegts_read_header,
    7. .read_packet = mpegts_read_packet,
    8. .read_close = mpegts_read_close,
    9. .read_timestamp = mpegts_get_dts,
    10. .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
    11. .priv_class = &mpegts_class,
    12. };
    1. static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index,
    2. int64_t *ppos, int64_t pos_limit)
    3. {
    4. MpegTSContext *ts = s->priv_data;
    5. int64_t pos;
    6. int pos47 = ts->pos47_full % ts->raw_packet_size;
    7. pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47;
    8. ff_read_frame_flush(s);
    9. if (avio_seek(s->pb, pos, SEEK_SET) < 0)
    10. return AV_NOPTS_VALUE;
    11. while(pos < pos_limit) {
    12. int ret;
    13. AVPacket pkt;
    14. av_init_packet(&pkt);
    15. ret = av_read_frame(s, &pkt);
    16. if (ret < 0)
    17. return AV_NOPTS_VALUE;
    18. if (pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0) {
    19. ff_reduce_index(s, pkt.stream_index);
    20. av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */);
    21. if (pkt.stream_index == stream_index && pkt.pos >= *ppos) {
    22. int64_t dts = pkt.dts;
    23. *ppos = pkt.pos;
    24. av_packet_unref(&pkt);
    25. return dts;
    26. }
    27. }
    28. pos = pkt.pos;
    29. av_packet_unref(&pkt);
    30. }
    31. return AV_NOPTS_VALUE;
    32. }

    未完待续

  • 相关阅读:
    二十四节气-白露 | 白露至,秋实美
    算法题java
    为什么那么多人选择找app外包公司,而不是自己组建团队开发app呢?
    【软件测试】8年资深测试说出来我们的心声......
    为什么反射慢?
    TensorFlow实战教程(二十八)-Keras实现BiLSTM微博情感分类和LDA主题挖掘分析
    FLOPS的计算
    长安链大规模数据存储及数据膨胀分析
    计算某个整数含有几位数
    linux文件的隐藏属性、特殊属性和ACL权限
  • 原文地址:https://blog.csdn.net/TyearLin/article/details/132733039