FFMPEG音视频同步-音视频实时采集并编码推流

版权声明:引用请注明出处 https://blog.csdn.net/quange_style/article/details/90213300

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数据流程图

在这里插入图片描述

1.2 代码流程图

在这里插入图片描述

1.3 队列传输流程图

在这里插入图片描述

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.	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.	//check ouput file   
15.	    char push_stream = 0;    
16.	    char *ofmt_name = NULL;    
17.	    if (strstr(filename, "rtmp://") != NULL)    
18.	    {    
19.	        push_stream = 1;    
20.	        ofmt_name = "flv";    
21.	    }    
22.	    else if (strstr(filename, "udp://") != NULL)    
23.	    {    
24.	        push_stream = 1;    
25.	        ofmt_name = "mpegts";    
26.	    }    
27.	    else    
28.	    {    
29.	        push_stream = 0;    
30.	        ofmt_name = NULL;    
31.	    }    
32.	
33.	       /* allocate the output media context */  
34.	    avformat_alloc_output_context2(&oc, NULL, ofmt_name, filename);  
35.	    if (!oc) {  
36.	        printf("Could not deduce output format from file extension: using MPEG.\n");  
37.	        avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);  
38.	    }  
39.	    if (!oc)  
40.	        return 1;  
41.	  
42.	     fmt = oc->oformat;  
43.	  
44.	    /* Add the audio and video streams using the default format codecs 
45.	     * and initialize the codecs. */  
46.	    if (fmt->video_codec != AV_CODEC_ID_NONE) {  
47.	        add_stream(&video_st, oc, &video_codec, fmt->video_codec);  
48.	        have_video = 1;  
49.	        encode_video = 1;  
50.	    }  
51.	    if (fmt->audio_codec != AV_CODEC_ID_NONE) {  
52.	        add_stream(&audio_st, oc, &audio_codec, AV_CODEC_ID_AAC);//fmt->audio_codec);  
53.	        have_audio = 1;  
54.	        encode_audio = 1;  
55.	    }  
56.	  
57.	    /* Now that all the parameters are set, we can open the audio and 
58.	     * video codecs and allocate the necessary encode buffers. */  
59.	    if (have_video)  
60.	        open_video(oc, video_codec, &video_st, opt);  
61.	  
62.	    if (have_audio)  
63.	        open_audio(oc, audio_codec, &audio_st, opt);  
64.	  
65.	    av_dump_format(oc, 0, filename, 1);  
66.	  
67.	    /* open the output file, if needed */  
68.	    if (!(fmt->flags & AVFMT_NOFILE)) {  
69.	        ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);  
70.	        if (ret < 0) {  
71.	            fprintf(stderr, "Could not open '%s': %s\n", filename,  
72.	                    av_err2str(ret));  
73.	            return 1;  
74.	        }  
75.	    }  
76.	  
77.	    /* Write the stream header, if any. */  
78.	    ret = avformat_write_header(oc, &opt);  
79.	    if (ret < 0) {  
80.	        fprintf(stderr, "Error occurred when opening output file: %s\n",  
81.	                av_err2str(ret));  
82.	        return 1;  
83.	    }  
84.	  
85.	  
86.	    output_dev.encode_audio=encode_audio;  
87.	    output_dev.encode_video=encode_video;  
88.	    output_dev.oc=oc;  
89.	    output_dev.have_audio=have_audio;  
90.	    output_dev.have_video=have_video;  
91.	    output_dev.video_st=&video_st;  
92.	    output_dev.audio_st=&audio_st;  
93.	  
94.	}  

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结果

使用软件打开test.flv,可以看到录制的实时音视频,音视频延时维持在200ms以内,更精确的有待测试优化。
./av_record rtmp://127.0.0.1:1935/live/test1

在这里插入图片描述

3.3存在的问题

1、测试发现,音视频在录制过程中,会出现音频超前几秒,同步还待优化。
猜测导致的原因是:本程序采用的是固定帧率计算,手动生成frame的pts,但是实际上会出现视频帧率不稳定,音频采样率也不稳定,这可能会导致音频生成的ptstime_base比视频生成的ptstime_base快的越来越多(即同个时刻生成的音频ptstime_base比视频快ptstime_base)。
优化的方法:在获取到音视频数据是,实时获取当前的时间戳,并记录写入。在封装时,比较写入的时间戳,读取pkt数据,解码,编码,写入封装。
经过测试:测试了音频实时效果,你实时效果是可以的,但是视频录制效果出了问题。在录制输出文件,视频的编码器帧率设为了20帧,同样条件,使用ffmpeg指令ffmpeg -f video4linux2 -r 20 -s 640480 -i /dev/video0 -f flv ffmpeg_test.flv,指定20帧率进行录制时,也会出现录制了50s的文件实际上是过来1:02分钟。但是不指定帧率录制,则正常ffmpeg -f video4linux2 -s 640480 -i /dev/video0 -f flv ffmpeg_test.flv。所以问题还是出现在输出文件的帧率上。

https://blog.csdn.net/zhoubotong2012/article/details/79338093
参考该链接,有相关内容。

3.6 思考

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

猜你喜欢

转载自blog.csdn.net/quange_style/article/details/90213300