jinleileiking / learning-notes

Notes for my learning.
GNU General Public License v3.0
1 stars 2 forks source link

ffmpeg_obs #16

Open jinleileiking opened 1 year ago

jinleileiking commented 1 year ago

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
  * 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++)
* 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

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的打印统计,客户如果使用这个参数,可以使用这个指标来提高系统的可观测性:

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"

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

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