FFMPEG音视频同步-音视频实时采集编码封装

//-------------------------------------------------------------------------------------------------
参考链接1、https://blog.csdn.net/leixiaohua1020/article/details/39702113
参考链接2、https://blog.csdn.net/li_wen01/article/details/67631687

//-------------------------------------------------------------------------------------------------
音视频同步录制相关文章
//-------------------------------------------------------------------------------------------------
1、ffmpeg-摄像头采集保存
2、ffmpeg-摄像头采集编码封装
3、ffmpeg-音频正弦产生并编码封装
4、ffmpeg-音频实时采集保存
5、 ffmpeg-音频实时采集编码封装
6、ffmpeg-音视频实时采集编码封装
7、ffmpeg音视频同步-音视频实时采集编码推流
8、ffmpeg音视频同步-音视频实时采集编码推流-优化版本
//---------------------------------------------------------------

系统环境:
系统版本:lubuntu 16.04
Ffmpge版本:ffmpeg version N-93527-g1125277
摄像头:1.3M HD WebCan
虚拟机:Oracle VM VirtualBox 5.2.22

指令查看设备 ffmpeg -devices

本章文档基于《ffmpeg-摄像头采集编码封装》和《ffmpeg-音频实时采集编码封装》。在同一进程中,判断其产生的time=pts*time_base,根据其视频的帧率,以及音频产生的采样率等,来比较当前帧时间time,来写入音视频。

1.简介

FFmpeg中有一个和多媒体设备交互的类库:Libavdevice。使用这个库可以读取电脑(或者其他设备上)的多媒体设备的数据,或者输出数据到指定的多媒体设备上。

1.1数据流程图

FFMPEG音视频同步-音视频实时采集编码封装-LMLPHP

1.2 代码流程图

FFMPEG音视频同步-音视频实时采集编码封装-LMLPHP

1.3 队列传输流程图

FFMPEG音视频同步-音视频实时采集编码封装-LMLPHP

2.源码

最简单的基于Libavdevice的音频采集口数据读取一帧帧pcm数据,经过音频重采样获取目标AAC的音频源数据参数,同时基于Libavdevice的视频采集口,获取yuv420数据,再经过编码,封装等,保存成FLV文件。
程序主要是参考/doc/example/muxing.c源码的音视频同步方法。

2.1音频初始化

1.	 int open_audio_capture()
2.	{
3.
4.	        printf("open_audio_capture\n");
5.
6.	//********add alsa read***********//  
7.	    AVCodecContext  *pCodecCtx;
8.	    AVCodec         *pCodec;
9.	       AVFormatContext *a_ifmtCtx;
10.	    int i,ret;
11.	//Register Device  
12.	    avdevice_register_all();
13.
14.	    a_ifmtCtx = avformat_alloc_context();
15.
16.
17.	     //Linux  
18.	    AVInputFormat *ifmt=av_find_input_format("alsa");
19.	    if(avformat_open_input(&a_ifmtCtx,"default",ifmt,NULL)!=0){
20.	        printf("Couldn't open input stream.default\n");
21.	        return -1;
22.	    }
23.
24.
25.	    if(avformat_find_stream_info(a_ifmtCtx,NULL)<0)
26.	    {
27.	        printf("Couldn't find stream information.\n");
28.	        return -1;
29.	    }
30.
31.	    int audioindex=-1;
32.	    for(i=0; i<a_ifmtCtx->nb_streams; i++)
33.	    if(a_ifmtCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
34.	    {
35.	        audioindex=i;
36.	        break;
37.	    }
38.	    if(audioindex==-1)
39.	    {
40.	        printf("Couldn't find a video stream.\n");
41.	        return -1;
42.	    }
43.
44.	    pCodecCtx=a_ifmtCtx->streams[audioindex]->codec;
45.	    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
46.	    if(pCodec==NULL)
47.	    {
48.	        printf("Codec not found.\n");
49.	        return -1;
50.	    }
51.	    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
52.	    {
53.	        printf("Could not open codec.\n");
54.	        return -1;
55.	    }
56.
57.	    AVPacket *in_packet=(AVPacket *)av_malloc(sizeof(AVPacket));
58.
59.	    AVFrame *pAudioFrame=av_frame_alloc();
60.	    if(NULL==pAudioFrame)
61.	    {
62.	        printf("could not alloc pAudioFrame\n");
63.	        return -1;
64.	    }
65.
66.	    //audio output paramter //resample   
67.	    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
68.	    int out_sample_fmt = AV_SAMPLE_FMT_S16;
69.	    int out_nb_samples =1024; //pCodecCtx->frame_size;  
70.	    int out_sample_rate = 48000;
71.	    int out_nb_channels = av_get_channel_layout_nb_channels(out_channel_layout);
72.	    int out_buffer_size = av_samples_get_buffer_size(NULL, out_nb_channels, out_nb_samples, out_sample_fmt, 1);
73.	    uint8_t *dst_buffer=NULL;
74.	    dst_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE);
75.	    int64_t in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
76.
77.
78.	    printf("audio sample_fmt=%d size=%d channel=%d  sample_rate=%d in_channel_layout=%s\n",
79.	        pCodecCtx->sample_fmt, pCodecCtx->frame_size,
80.	        pCodecCtx->channels,pCodecCtx->sample_rate,av_ts2str(in_channel_layout));
81.
82.	    struct SwrContext   *audio_convert_ctx = NULL;
83.	    audio_convert_ctx = swr_alloc();
84.	    if (audio_convert_ctx == NULL)
85.	    {
86.	        printf("Could not allocate SwrContext\n");
87.	        return -1;
88.	    }
89.
90.	      /* set options */
91.	        av_opt_set_int       (audio_convert_ctx, "in_channel_count",   pCodecCtx->channels,       0);
92.	        av_opt_set_int       (audio_convert_ctx, "in_sample_rate",     pCodecCtx->sample_rate,    0);
93.	        av_opt_set_sample_fmt(audio_convert_ctx, "in_sample_fmt",      pCodecCtx->sample_fmt, 0);
94.	        av_opt_set_int       (audio_convert_ctx, "out_channel_count",  out_nb_channels,       0);
95.	        av_opt_set_int       (audio_convert_ctx, "out_sample_rate",   out_sample_rate,    0);
96.	        av_opt_set_sample_fmt(audio_convert_ctx, "out_sample_fmt",     out_sample_fmt,     0);
97.
98.	        /* initialize the resampling context */
99.	        if ((ret = swr_init(audio_convert_ctx)) < 0) {
100.	            fprintf(stderr, "Failed to initialize the resampling context\n");
101.	            exit(1);
102.	        }
103.
104.
105.	    alsa_input.in_packet=in_packet;
106.	    alsa_input.pCodecCtx=pCodecCtx;
107.	    alsa_input.pCodec=pCodec;
108.	       alsa_input.a_ifmtCtx=a_ifmtCtx;
109.	    alsa_input.audioindex=audioindex;
110.	    alsa_input.pAudioFrame=pAudioFrame;
111.	    alsa_input.audio_convert_ctx=audio_convert_ctx;
112.	    alsa_input.dst_buffer=dst_buffer;
113.	    alsa_input.out_buffer_size=out_buffer_size;
114.	    alsa_input.bCap=1;
115.
116.	//******************************//  
117.	}

2.2 视频初始化

1.	int open_video_capture()
2.	{
3.	    int i,ret;
4.	    printf("open_video_capture\n");
5.
6.	//********add camera read***********//  
7.	    AVCodecContext  *pCodecCtx;
8.	    AVCodec         *pCodec;
9.	       AVFormatContext *v_ifmtCtx;
10.
11.	//Register Device  
12.	    avdevice_register_all();
13.
14.	    v_ifmtCtx = avformat_alloc_context();
15.
16.
17.	     //Linux  
18.	    AVInputFormat *ifmt=av_find_input_format("video4linux2");
19.	    if(avformat_open_input(&v_ifmtCtx,"/dev/video0",ifmt,NULL)!=0){
20.	        printf("Couldn't open input stream./dev/video0\n");
21.	        return -1;
22.	    }
23.
24.
25.	    if(avformat_find_stream_info(v_ifmtCtx,NULL)<0)
26.	    {
27.	        printf("Couldn't find stream information.\n");
28.	        return -1;
29.	    }
30.
31.	    int videoindex=-1;
32.	    for(i=0; i<v_ifmtCtx->nb_streams; i++)
33.	    if(v_ifmtCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
34.	    {
35.	        videoindex=i;
36.	        break;
37.	    }
38.	    if(videoindex==-1)
39.	    {
40.	        printf("Couldn't find a video stream.\n");
41.	        return -1;
42.	    }
43.
44.	    pCodecCtx=v_ifmtCtx->streams[videoindex]->codec;
45.	    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
46.	    if(pCodec==NULL)
47.	    {
48.	        printf("Codec not found.\n");
49.	        return -1;
50.	    }
51.	    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
52.	    {
53.	        printf("Could not open codec.\n");
54.	        return -1;
55.	    }
56.
57.	    AVFrame *pFrame,*pFrameYUV;
58.	    pFrame=av_frame_alloc();
59.	    pFrameYUV=av_frame_alloc();
60.	    unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
61.	    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
62.
63.	    printf("camera width=%d height=%d \n",pCodecCtx->width, pCodecCtx->height);
64.
65.
66.	    struct SwsContext *img_convert_ctx;
67.	    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
68.	    AVPacket *in_packet=(AVPacket *)av_malloc(sizeof(AVPacket));
69.
70.
71.	    video_input.img_convert_ctx=img_convert_ctx;
72.	    video_input.in_packet=in_packet;
73.
74.	    video_input.pCodecCtx=pCodecCtx;
75.	    video_input.pCodec=pCodec;
76.	       video_input.v_ifmtCtx=v_ifmtCtx;
77.	    video_input.videoindex=videoindex;
78.	    video_input.pFrame=pFrame;
79.	    video_input.pFrameYUV=pFrameYUV;
80.	    video_input.bCap=1;
81.
82.	//******************************//  
83.	}

2.3输出初始化

1.	int open_output(    const char *filename,AVDictionary *opt)
2.	{
3.
4.	    printf("open_output\n");
5.	    static  OutputStream video_st = { 0 }, audio_st = { 0 };
6.
7.	    AVOutputFormat *fmt;
8.	    AVFormatContext *oc;
9.	    AVCodec *audio_codec, *video_codec;
10.	    int ret;
11.	    int have_video = 0, have_audio = 0;
12.	    int encode_video = 0, encode_audio = 0;
13.
14.
15.	       /* allocate the output media context */
16.	    avformat_alloc_output_context2(&oc, NULL, NULL, filename);
17.	    if (!oc) {
18.	        printf("Could not deduce output format from file extension: using MPEG.\n");
19.	        avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
20.	    }
21.	    if (!oc)
22.	        return 1;
23.
24.	     fmt = oc->oformat;
25.
26.	    /* Add the audio and video streams using the default format codecs
27.	     * and initialize the codecs. */
28.	    if (fmt->video_codec != AV_CODEC_ID_NONE) {
29.	        add_stream(&video_st, oc, &video_codec, fmt->video_codec);
30.	        have_video = 1;
31.	        encode_video = 1;
32.	    }
33.	    if (fmt->audio_codec != AV_CODEC_ID_NONE) {
34.	        add_stream(&audio_st, oc, &audio_codec, AV_CODEC_ID_AAC);//fmt->audio_codec);  
35.	        have_audio = 1;
36.	        encode_audio = 1;
37.	    }
38.
39.	    /* Now that all the parameters are set, we can open the audio and
40.	     * video codecs and allocate the necessary encode buffers. */
41.	    if (have_video)
42.	        open_video(oc, video_codec, &video_st, opt);
43.
44.	    if (have_audio)
45.	        open_audio(oc, audio_codec, &audio_st, opt);
46.
47.	    av_dump_format(oc, 0, filename, 1);
48.
49.	    /* open the output file, if needed */
50.	    if (!(fmt->flags & AVFMT_NOFILE)) {
51.	        ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
52.	        if (ret < 0) {
53.	            fprintf(stderr, "Could not open '%s': %s\n", filename,
54.	                    av_err2str(ret));
55.	            return 1;
56.	        }
57.	    }
58.
59.	    /* Write the stream header, if any. */
60.	    ret = avformat_write_header(oc, &opt);
61.	    if (ret < 0) {
62.	        fprintf(stderr, "Error occurred when opening output file: %s\n",
63.	                av_err2str(ret));
64.	        return 1;
65.	    }
66.
67.
68.	    output_dev.encode_audio=encode_audio;
69.	    output_dev.encode_video=encode_video;
70.	    output_dev.oc=oc;
71.	    output_dev.have_audio=have_audio;
72.	    output_dev.have_video=have_video;
73.	    output_dev.video_st=&video_st;
74.	    output_dev.audio_st=&audio_st;
75.
76.	}

2.4音频采集线程

1.	int audioThreadProc(void *arg)
2.	{
3.	    int got_pic;
4.	    while(alsa_input.bCap)
5.	    {
6.
7.	        //printf("audioThreadProc running\n");  
8.
9.	        AVPacket *pkt=get_audio_pkt(output_dev.audio_st,&alsa_input);
10.	        if(pkt==NULL)  //从alsa中获取pkt音频源数据包
11.	        {
12.	            alsa_input.bCap =0;
13.
14.	        }
15.	        else
16.	        {
17.	            packet_queue_put(&output_dev.audioq,pkt,output_dev.audio_st->next_pts);  //将获取的数据包发送到传输队列当中
18.	        }
19.
20.
21.	    }
22.
23.	    printf("videoThreadProc exit\n");
24.	    usleep(1000000);
25.	    return 0;
26.
27.	}

2.5视频采集线程

1.	int videoThreadProc(void *arg)
2.	{
3.	    int got_pic;
4.	    while(video_input.bCap)
5.	    {
6.
7.
8.	        AVPacket * pkt=get_video_pkt(output_dev.video_st,&video_input);
9.	  		//从V4L中获取视频源数据包pkt
10.	        if(pkt==NULL)
11.	        {
12.	            //packet_queue_put_nullpacket(&output_dev.videoq,0);  
13.	            video_input.bCap =0;
14.
15.	        }
16.	        else
17.	        {
18.	            packet_queue_put(&output_dev.videoq,pkt,output_dev.video_st->next_pts);  //将获取到的数据包发送到视频传输队列中
19.	        }
20.
21.
22.
23.	    }
24.
25.	    printf("videoThreadProc exit\n");
26.	    usleep(1000000);
27.	    return 0;
28.
29.	}

2.6主进程

1.	 while (output_dev.encode_video || output_dev.encode_audio) { //判断进程是否退出  
2.	    if (output_dev.encode_video &&
3.	        (!output_dev.encode_audio || av_compare_ts(frame_pts, output_dev.video_st->enc->time_base,
4.	                frame_audio_pts, output_dev.audio_st->enc->time_base) <= 0))   //比较音频视频产生是的pts* time_base大小,以音频pts*times_base为基准,若视频的pts*time_base小于音频,则写入视频帧,否则写入音频帧 
5.	           {
6.	    if(packet_queue_get(&output_dev.videoq,&pkt,0,&frame_pts)<0)  //获取队列中的视频pkt
7.	            {
8.	                printf("packet_queue_get Error.\n");
9.	                break;
10.	            }
11.
12.	            if(flush_pkt.data== pkt.data)
13.	            {
14.	                printf("get pkt flush_pkt\n");
15.	                continue;
16.	            }
17.
18.
19.	              vframe=get_video_pkt2Frame(output_dev.video_st,&video_input,&pkt,&got_pic,frame_pts);  //解码pkt 成frame
20.	            if(!got_pic)
21.	            {
22.	                av_free_packet(&pkt);
23.	                printf("get_video_pkt2Frame error\n");
24.	                usleep(10000);
25.	                continue;
26.	            }
27.	            av_free_packet(&pkt);
28.
29.	WRITE_FRAME:
30.	            output_dev.encode_video = !write_video_frame1(output_dev.oc, output_dev.video_st,vframe);  //编码frame成pkt,并且写入封装
31.	            //usleep(300000);  
32.	        }
33.	        else//audio  
34.	        {if(packet_queue_get(&output_dev.audioq,&audio_pkt,0,&frame_audio_pts)<0)  //获取队列中的音频pkt
35.
36.	            {
37.	                printf("packet_queue_get Error.\n");
38.	                break;
39.	            }
40.
41.	            if(flush_pkt.data== audio_pkt.data)
42.	            {
43.	                printf("get pkt flush_pkt\n");
44.	                continue;
45.	            }
46.	            //av_free_packet(&audio_pkt);             
47.
48.	#if 1
49.
50.
51.	              aframe=get_audio_pkt2Frame(output_dev.audio_st,&alsa_input,&audio_pkt,&got_pcm,frame_audio_pts);  //解码pkt 成frame
52.	            if(!got_pcm)
53.	            {
54.	                av_free_packet(&audio_pkt);
55.	                printf("get_video_pkt2Frame error\n");
56.	                usleep(10000);
57.	                continue;
58.	            }
59.	            av_free_packet(&audio_pkt);
60.
61.	WRITE_AUDIO_FRAME:
62.	            output_dev.encode_audio = !write_audio_frame1(output_dev.oc, output_dev.audio_st,aframe);  //编码frame成pkt,并且写入封装
63.
64.	            //usleep(300000);  
65.	#endif
66.	    }
67.
68.
69.
70.
71.	    }

3.验证

3.1编译

1.	#!/bin/sh
2.	CC=gcc
3.	SRCS=$(wildcard *.c */*.c)
4.	OBJS=$(patsubst %.c, %.o, $(SRCS))
5.	FLAG=-g
6.	#LIB=-lavutil -lavformat -lavcodec -lavutil -lswscale -lswresample -lSDL2
7.
8.
9.
10.	LIB=-lSDL2 -lSDLmain  -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT -L/usr/lib/i386-linux-gnu -lSDL -I./\
11.	    -I/home/quange/ffmpeg_build/include -L/home/quange/ffmpeg_build/lib -L/usr/local/lib -L/home/quange/ffmpeg_build/lib -lavcodec -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -pthread -lm -lz -lfdk-aac -lm -lmp3lame -lm -lopus -lm -lvorbis -lm -logg -lvorbisenc -lvorbis -lm -logg -lx264 -lpthread -lm -ldl -lx265 -lstdc++ -lm -lrt -ldl -lnuma -lswresample -lm -lavutil -pthread -lm -lXv -lX11 -lXext \
12.	    -I/home/quange/ffmpeg_build/include -L/home/quange/ffmpeg_build/lib -L/usr/local/lib -L/home/quange/ffmpeg_build/lib -lavdevice -lm -lxcb -lXau -lXdmcp -lxcb-shape -lxcb -lXau -lXdmcp -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lXau -lXdmcp -lasound -lm -ldl -lpthread -lrt -lSDL2 -Wl,--no-undefined -lm -ldl -lasound -lm -ldl -lpthread -lpulse-simple -lpulse -lsndio -lX11 -lXext -lXcursor -lXinerama -lXi -lXrandr -lXss -lXxf86vm -lwayland-egl -lwayland-client -lwayland-cursor -lxkbcommon -lpthread -lrt -lsndio -lXv -lX11 -lXext -lavfilter -pthread -lm -lfreetype -lz -lpng12 -lz -lm -lswscale -lm -lpostproc -lm -lavformat -lm -lz -lavcodec -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -pthread -lm -lz -lfdk-aac -lm -lmp3lame -lm -lopus -lm -lvorbis -lm -logg -lvorbisenc -lvorbis -lm -logg -lx264 -lpthread -lm -ldl -lx265 -lstdc++ -lm -lrt -ldl -lnuma -lswresample -lm -lavutil -pthread -lm -lXv -lX11 -lXext \
13.	    -I/home/quange/ffmpeg_build/include -L/home/quange/ffmpeg_build/lib -L/usr/local/lib -L/home/quange/ffmpeg_build/lib -lavformat -lm -lz -lavcodec -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -pthread -lm -lz -lfdk-aac -lm -lmp3lame -lm -lopus -lm -lvorbis -lm -logg -lvorbisenc -lvorbis -lm -logg -lx264 -lpthread -lm -ldl -lx265 -lstdc++ -lm -lrt -ldl -lnuma -lswresample -lm -lavutil -pthread -lm -lXv -lX11 -lXext \
14.	    -I/home/quange/ffmpeg_build/include -L/home/quange/ffmpeg_build/lib -L/usr/local/lib -L/home/quange/ffmpeg_build/lib -lavformat -lm -lz -lavcodec -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -lvpx -lm -lpthread -pthread -lm -lz -lfdk-aac -lm -lmp3lame -lm -lopus -lm -lvorbis -lm -logg -lvorbisenc -lvorbis -lm -logg -lx264 -lpthread -lm -ldl -lx265 -lstdc++ -lm -lrt -ldl -lnuma -lswresample -lm -lavutil -pthread -lm -lXv -lX11 -lXext \
15.	    -I/home/quange/ffmpeg_build/include -L/home/quange/ffmpeg_build/lib -lswscale -lm -lavutil -pthread -lm -lXv -lX11 -lXext \
16.
17.
18.
19.
20.
21.	NAME=$(wildcard *.c)
22.	TARGET=av_record
23.
24.	$(TARGET):$(OBJS)
25.
26.	    $(CC) $(FLAG) -o $@ $^  $(LIB)
27.
28.
29.	%.o:%.c
30.	    $(CC) $(FLAG) -c -o $@ $^ $(LIB)
31.
32.
33.
34.	clean:
35.	    rm -rf $(TARGET) $(OBJS)

3.2结果

使用VLC打开test.flv,可以看到录制的实时音视频,音视频延时维持在200ms以内,更精确的有待测试优化。
FFMPEG音视频同步-音视频实时采集编码封装-LMLPHP

3.3存在的问题

3.6 思考

1、问:为啥不讲音视频数据的采集,编码,封装放在同一个进程里?
答:
1)、由于音视频的编码耗时比较久(特别是视频),所有操作都放在同一个进程里面,会影响到数据的采集,造成音频,视频数据采集丢失。
2)、由于音视频在写入封装时,需要比较音频与视频的pts*time_base,若视频的实时时间小于音频,则获取音频写入封装频,否则获取视频写入封装;这样的判断方法,或影响到音频和视频的采集,造成大部分数据丢失。

2、关于设备前端采集数据速度思考:
一开始,我是将前端数据采集,获取视频帧frame,视频帧编码成pkt等三个步骤放在采集线程里,然后再发送数据到队列,主进程再获取队列中的pkt,直接写入封装。但是,考虑到视频编码耗时较久,会影响到数据采集,所以还是直接单线程运行获取数据,最大限度保证数据的采集。

3、关于不同封装,编码数据的pts问题
这方面的相关在《ffmpeg-摄像头采集编码封装》中有详细的讲解。总的来说pts*time_base的值在flv,mp4,ts等容器中,值都是一样的。不同的表现在编码生成的pts与编码器时基(1/frame_rate),流时基有关。

4、关于摄像头实时采集帧率与编码器设定的帧率关系?
在本例中,摄像头采集的帧率为20帧,而由于将编码器时基设为(1/25),就会录制下来的视频要比实际上跑的快(ps:10s的视频从采集手机屏幕5s开始,播放结束后,显示20s)。主要是因为录制下来每帧间隔为(1/25),而实际摄像头是(1/20)。同样一秒的数据,写到封装里只有0.8s的数据。所以10s录制的视频,显示的是15s的时间。这问题,编码器时基改为实际帧率即可。
指令ffmpeg -f video4linux2 -s 640*480 -i /dev/video0 -f flv test.flv可以显示实时帧率

4.附件

5.参考资料

[1] ffmpeg之PCM转AAC
https://blog.csdn.net/mengzhengjie/article/details/78919067

[2]官方Encode pcm file to aac
http://ffmpeg.org/pipermail/ffmpeg-user/2014-January/019268.html

[3]PCM编码AAC,参考其普通PCM格式与AAC转格式差异 https://blog.csdn.net/mengzhengjie/article/details/78919067

[4]https://cloud.tencent.com/developer/article/1194003

05-19 03:04