Skip to content

ffmpeg_obs #16

@jinleileiking

Description

@jinleileiking

FFMPEG

命令

-hls_list_size 0 录hls解决序号删除问题

compile

https://trac.ffmpeg.org/wiki/CompilationGuide/Centos

265

https://www.jianshu.com/p/cba823dddbfe

https://github.com/ksvc/FFmpeg

https://github.com/videolan/x265

./configure --enable-static --enable-pic \
        --disable-encoders --enable-encoder=aac --enable-encoder=libx264 --enable-gpl --enable-libx264 --enable-encoder=libx265  --enable-libx265 \
        --disable-decoders --enable-decoder=aac --enable-decoder=h264 --enable-decoder=hevc  \
        --disable-demuxers --enable-demuxer=aac  --enable-demuxer=mpegts --enable-demuxer=flv --enable-demuxer=h264 --enable-demuxer=hevc --enable-demuxer=hls  \
        --disable-muxers --enable-muxer=h264  --enable-muxer=flv   --enable-muxer=mp4 --enable-muxer=null \
        --disable-doc --extra-cflags="-fno-stack-check"   --pkg-config-flags="--static"

./configure --list-muxers

FAQ

整体流程

input_thread
  while(1)
  av_read_frame
    read_frame_internal
      while (!got_packet && !s->internal->parse_queue)
      ff_read_packet
        ret = s->iformat->read_packet(s, pkt)        ---- demux

main
  transcode
    init_input_threads
      init_input_thread
        av_thread_message_queue_alloc
        pthread_create(input_thread)
    ...
    while (!received_sigterm)
    transcode_step
      process_input
        get_input_packet
        process_input_packet
          while (ist->decoding_needed) {  ---   一个packet有多个frame在这循环
          decode_video      ---- decode
            decode
            send_frame_to_filters           ---- filter
              ifilter_send_frame
          if (!got_output); break
          
      reap_filters
        for (i = 0; i < nb_output_streams; i++)
        见下

write hls

hls_write_packet
  hlsenc_io_open
  flush_dynbuf
    av_write_frame
      s->oformat->write_packet = mov_write_packet
        mov_flush_fragment
      flush_if_needed
        avio_flush
          flush_buffer
    avio_write
    avio_flush
      flush_buffer
        writeout
          s->write_packet = ffurl_write
            retry_transfer_wrapper
              transfer_func = file_write
                write ---> 落盘m4s, m3u8
  hlsenc_io_close
    ff_format_io_close
      s->io_close = io_close_default
        avio_close
          avio_flush

  • 打开 fmp4
  * frame #0: 0x0000000100514228 ffmpeg_g`io_open_default(s=0x0000000103809e00, pb=0x000000010392ea30, url="test0.m4s", flags=2, options=0x000000016fdfdce8) at options.c:174:17
    frame #1: 0x0000000100445d90 ffmpeg_g`hlsenc_io_open(s=0x0000000103809e00, pb=0x000000010392ea30, filename="test0.m4s", options=0x000000016fdfdce8) at hlsenc.c:324:15
    frame #2: 0x0000000100443498 ffmpeg_g`hls_write_packet(s=0x0000000103809e00, pkt=0x000000016fdfdee8) at hlsenc.c:2755:23
    frame #3: 0x00000001004e3f34 ffmpeg_g`write_packet(s=0x0000000103809e00, pkt=0x000000016fdfdee8) at mux.c:749:15
    frame #4: 0x00000001004e21f0 ffmpeg_g`interleaved_write_packet(s=0x0000000103809e00, pkt=0x0000000000000000, flush=0) at mux.c:1124:15
    frame #5: 0x00000001004e305c ffmpeg_g`write_packet_common(s=0x0000000103809e00, st=0x0000000103005170, pkt=0x000000010303a7a0, interleaved=1) at mux.c:1151:16
    frame #6: 0x00000001004e20f8 ffmpeg_g`write_packets_common(s=0x0000000103809e00, pkt=0x000000010303a7a0, interleaved=1) at mux.c:1208:16
    frame #7: 0x00000001004e2138 ffmpeg_g`av_interleaved_write_frame(s=0x0000000103809e00, pkt=0x000000010303a7a0) at mux.c:1264:15
    frame #8: 0x000000010002c0b0 ffmpeg_g`write_packet(of=0x0000000103106990, pkt=0x000000010303a7a0, ost=0x0000000103005610, unqueue=0) at ffmpeg.c:895:15
    frame #9: 0x00000001000314a0 ffmpeg_g`output_packet(of=0x0000000103106990, pkt=0x000000010303a7a0, ost=0x0000000103005610, eof=0) at ffmpeg.c:974:9
    frame #10: 0x000000010003074c ffmpeg_g`do_video_out(of=0x0000000103106990, ost=0x0000000103005610, next_picture=0x000000010310b2f0) at ffmpeg.c:1509:13
    frame #11: 0x000000010002e864 ffmpeg_g`reap_filters(flush=0) at ffmpeg.c:1669:17
    frame #12: 0x00000001000266cc ffmpeg_g`transcode_step at ffmpeg.c:4928:12
    frame #13: 0x0000000100024918 ffmpeg_g`transcode at ffmpeg.c:4972:15
    frame #14: 0x0000000100023eb4 ffmpeg_g`main(argc=9, argv=0x000000016fdfeb80) at ffmpeg.c:5177:9
    frame #15: 0x00000001022b90f4 dyld`start + 520
  • 写 fmp4 hls_write_packet
  • mux.c: ret = s->oformat->write_packet(s, pkt);¬
  * frame #0: 0x00000001004a649c ffmpeg_g`ff_mov_write_packet(s=0x000000010288fe00, pkt=0x000000010300fef0) at movenc.c:5885:12
    frame #1: 0x00000001004bc024 ffmpeg_g`mov_write_single_packet(s=0x000000010288fe00, pkt=0x000000010300fef0) at movenc.c:5970:12
    frame #2: 0x00000001004a7fcc ffmpeg_g`mov_write_packet(s=0x000000010288fe00, pkt=0x000000010300fef0) at movenc.c:6090:16
    frame #3: 0x00000001004e3f34 ffmpeg_g`write_packet(s=0x000000010288fe00, pkt=0x000000010300fef0) at mux.c:749:15
    frame #4: 0x00000001004e3070 ffmpeg_g`write_packet_common(s=0x000000010288fe00, st=0x000000010301fea0, pkt=0x000000010300fef0, interleaved=0) at mux.c:1153:16
    frame #5: 0x00000001004e20f8 ffmpeg_g`write_packets_common(s=0x000000010288fe00, pkt=0x000000010300fef0, interleaved=0) at mux.c:1208:16
    frame #6: 0x00000001004e1fe4 ffmpeg_g`av_write_frame(s=0x000000010288fe00, in=0x000000016fdfdbe0) at mux.c:1251:11
    frame #7: 0x00000001004e2774 ffmpeg_g`ff_write_chained(dst=0x000000010288fe00, dst_stream=0, pkt=0x000000016fdfdee8, src=0x000000010481a000, interleave=0) at mux.c:1342:27
    frame #8: 0x0000000100443aa4 ffmpeg_g`hls_write_packet(s=0x000000010481a000, pkt=0x000000016fdfdee8) at hlsenc.c:2886:15
    frame #9: 0x00000001004e3f34 ffmpeg_g`write_packet(s=0x000000010481a000, pkt=0x000000016fdfdee8) at mux.c:749:15
    frame #10: 0x00000001004e21f0 ffmpeg_g`interleaved_write_packet(s=0x000000010481a000, pkt=0x0000000000000000, flush=0) at mux.c:1124:15
    frame #11: 0x00000001004e305c ffmpeg_g`write_packet_common(s=0x000000010481a000, st=0x0000000103206de0, pkt=0x000000010323cc70, interleaved=1) at mux.c:1151:16
    frame #12: 0x00000001004e20f8 ffmpeg_g`write_packets_common(s=0x000000010481a000, pkt=0x000000010323cc70, interleaved=1) at mux.c:1208:16
    frame #13: 0x00000001004e2138 ffmpeg_g`av_interleaved_write_frame(s=0x000000010481a000, pkt=0x000000010323cc70) at mux.c:1264:15
    frame #14: 0x000000010002c0b0 ffmpeg_g`write_packet(of=0x0000000103207cb0, pkt=0x000000010323cc70, ost=0x00000001032072f0, unqueue=0) at ffmpeg.c:895:15
    frame #15: 0x00000001000314a0 ffmpeg_g`output_packet(of=0x0000000103207cb0, pkt=0x000000010323cc70, ost=0x00000001032072f0, eof=0) at ffmpeg.c:974:9
    frame #16: 0x000000010003074c ffmpeg_g`do_video_out(of=0x0000000103207cb0, ost=0x00000001032072f0, next_picture=0x0000000103244e70) at ffmpeg.c:1509:13
    frame #17: 0x000000010002e864 ffmpeg_g`reap_filters(flush=0) at ffmpeg.c:1669:17
    frame #18: 0x00000001000266cc ffmpeg_g`transcode_step at ffmpeg.c:4928:12
    frame #19: 0x0000000100024918 ffmpeg_g`transcode at ffmpeg.c:4972:15
    frame #20: 0x0000000100023eb4 ffmpeg_g`main(argc=9, argv=0x000000016fdfeb80) at ffmpeg.c:5177:9
    frame #21: 0x00000001022b90f4 dyld`start + 520

  • 多码率
reap_filters
  for (i = 0; i < nb_output_streams; i++)
  • init
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x00000001002f9828 ffmpeg_g`io_open_default(s=0x000000010300c200, pb=0x0000000103098420, url="init.mp4", flags=2, options=0x000000016fdf7688) at options.c:174:25 [opt]
    frame #1: 0x000000010026064c ffmpeg_g`hls_init [inlined] hls_mux_init(s=0x000000010300c200, vs=0x0000000103098400) at hlsenc.c:0 [opt]
    frame #2: 0x0000000100260364 ffmpeg_g`hls_init(s=<unavailable>) at hlsenc.c:3099:20 [opt]
    frame #3: 0x00000001002cf508 ffmpeg_g`avformat_init_output [inlined] init_muxer(s=0x000000010300c200, options=0x00000001023046b8) at mux.c:408:20 [opt]
    frame #4: 0x00000001002cef5c ffmpeg_g`avformat_init_output(s=0x000000010300c200, options=<unavailable>) at mux.c:490:16 [opt]
    frame #5: 0x00000001002cf798 ffmpeg_g`avformat_write_header(s=0x000000010300c200, options=<unavailable>) at mux.c:513:20 [opt]
    frame #6: 0x000000010001f294 ffmpeg_g`check_init_output_file(of=0x00000001023046b0, file_index=0) at ffmpeg.c:3150:11 [opt]
    frame #7: 0x000000010001f0e4 ffmpeg_g`init_output_stream_wrapper [inlined] init_output_stream(ost=0x0000000102305150, frame=<unavailable>, error="", error_len=1024) at ffmpeg.c:3824:11 [opt]
    frame #8: 0x000000010001ef08 ffmpeg_g`init_output_stream_wrapper(ost=0x0000000102305150, frame=<unavailable>, fatal=<unavailable>) at ffmpeg.c:1054:11 [opt]
    frame #9: 0x00000001000209c4 ffmpeg_g`do_video_out(of=0x00000001023046b0, ost=0x0000000102305150, next_picture=0x0000000102206920) at ffmpeg.c:1223:5 [opt]
    frame #10: 0x0000000100020368 ffmpeg_g`reap_filters(flush=0) at ffmpeg.c:1669:17 [opt]
    frame #11: 0x000000010001a48c ffmpeg_g`transcode [inlined] transcode_step at ffmpeg.c:4928:12 [opt]
    frame #12: 0x0000000100018be4 ffmpeg_g`transcode at ffmpeg.c:4972:15 [opt]
    frame #13: 0x0000000100016724 ffmpeg_g`main(argc=<unavailable>, argv=<unavailable>) at ffmpeg.c:5177:9 [opt]
    frame #14: 0x0000000101c390f4 dyld`start + 520

input

main
  transcode
    init_input_threads
      init_input_thread
        av_thread_message_queue_alloc
        pthread_create(input_thread)
    ...
    while (!received_sigterm)
    transcode_step
      process_input
        get_input_packet
          get_input_packet_mt      
      

input_thread
  av_read_frame
  av_thread_message_queue_send
 

改变pts

process_input_packet
  pkt->pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base)
  decode_video

ffmpeg cmd

  • -copyts ts不变,否则demux后就会改
  • -debug_ts 打印ts信息

input_thread_queue

不使用input_thread_queue: input -> demux -> decode -> encode -> remux -> output
使用: input | queue | demux -> encode -> remux -> output, 即input receive回建立一个线程,通过queue的方式供 demux进行处理
对于直播场景而言,使用input_thread_queue,这个 thread会不停的调用receive收取tcp数据包,由于直播发包是按照设置的码率来发的,所以只要后续处理跟的上,这个队列不会有积压。实际测试在40-0之间波动。如果后续处理慢,则这个queue会积压到满,ffmpeg也会打一个log进行告警。理论上到达配置值,则ffmpeg的后续处理就有问题了。
不使用input_thread_queue,直播是否正常的可观测指标为input tcp链接的tcp q size,这个指标不容易观察
wz_live分支已经有这个queue的打印统计,客户如果使用这个参数,可以使用这个指标来提高系统的可观测性:

  • 客户自己看这个值,发现queue满了,就异常了,需要处理
  • 我们上报这个值到云,我们监测,发现异常了,提醒客户有异常,提醒客户处理

ffprobe

  • 让ffprobe探流一会停:
-read_intervals will do the trick. e.g. %+2 is to read first 2 sec, %+#2 to read 2 frames.
For example: -read_intervals "%+2"
  • 看 bp

ffprobe ~/a1.flv -show_frames -print_format json -hide_banner -v quiet | jq '.frames[] | select(.media_type == "video")' | jq .pict_type

  • 打印pts

ffprobe -i 'rtmp://xxxb' -print_format json -show_frames -hide_banner -v quiet -select_streams v 2>&1 | jq -r -c --stream | grep pts | grep -v pts_time

ffplay

zmq

obs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions