从零开始学习音视频编程技术(十九) 录屏软件开发之YUV AAC合成MP4

原文地址:http://blog.yundiantech.com/?log=blog&id=26


我们已经获取到了屏幕的图像和麦克风的声音,现在就将它们合并一个普通的视频文件。


所谓视频,其实就是一种容器,里面以某种固定的格式放置了视频和音频数据(比如我们前面生成的H264和AAC)。


先来感受一下FLV的格式:


也就是说只需要按上面规定的方式把图像和音频数据写入文件,最后生成的文件就是一个FLV视频文件了。


当然上面只是FLV规范中的一小块而已,完整的规范还有几页。。

我们只是想写个视频文件而已,就要去看如此可怕的规范,实在是说不过去啊。。

而且换个别的格式规范又不一样,又是几页的规范,这还看得过来么。。

所以让我去看这些规范,我是拒绝的。


好在FFMPEG已经把一些主流的视频格式都封装好了,只需要直接调用API就可以了。

使用ffmpeg生成视频可以参考ffmpeg自带的例子output-example.c。这个文件是在ffmpeg第一版中的doc/example里面。


点击下载output-example.c


output-example.c使用的都是第一版的API,因此这个文件是无法在2.5.2里面编译通过的。

所以我没有提供完整的工程项目,想运行起来看看效果的,可以自行到ffmpeg官网下载第一版的ffmpeg,参考之前搭建ffmpeg开发环境的方法导入库,然后加上output-example.c的代码编译运行。


output-example.c实现的功能就是凭空造出一个视频文件,完全是无中生有,所以强烈建议初学时按照上面说的方法,运行起来感受一下。


虽然output-example.c里面用了第一版的API,但是整体的思路是值得参考的,只需要简单修改一些API就可以在2.5.2中使用了,因此我们生成视频文件的方式就根据这个例子来改。

首先我们就先来了解下output-example.c的流程:


1.初始化libavcodec库,并注册所有的编解码器和格式。

1
av_register_all();


2.根据文件名来获取输出文件格式

1
2
3
4
5
6
     fmt = guess_format(NULL, filename, NULL);    
     if  (!fmt) {
         printf ("Could not deduce output format from file extension:  using  MPEG.
");
         fmt = guess_format( "mpeg" , NULL, NULL);
     }

只需要在这里指定文件名,ffmpeg便可以根据后缀名自动识别将要保存的视频格式,前面所说的几页的规范这里只需要一句话就搞定了。。


3.分配一个AVFormatContext

1
oc = avformat_alloc_context();


4.添加视频和音频流

1
2
3
4
5
6
     if  (fmt->video_codec != CODEC_ID_NONE) {
         video_st = add_video_stream(oc, fmt->video_codec);
     }
     if  (fmt->audio_codec != CODEC_ID_NONE) {
         audio_st = add_audio_stream(oc, fmt->audio_codec);
     }


这里做的操作其实就是指定了视频编码器和音频编码器的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
static  AVStream *add_video_stream(AVFormatContext *oc,  int  codec_id)
{
     AVCodecContext *c;
     AVStream *st;
 
     st = av_new_stream(oc, 0);
     if  (!st) {
         fprintf (stderr, "Could not alloc stream
");
         exit (1);
     }
 
     c = st->codec;
     c->codec_id = codec_id;
     c->codec_type = CODEC_TYPE_VIDEO;
 
     /* put sample parameters */
     c->bit_rate = 400000;
     /* resolution must be a multiple of two */
     c->width = 352;
     c->height = 288;
     /* time base: this is the fundamental unit of time (in seconds) in terms
        of which frame timestamps are represented. for fixed-fps content,
        timebase should be 1/framerate and timestamp increments should be
        identically 1. */
     c->time_base.den = STREAM_FRAME_RATE;
     c->time_base.num = 1;
     c->gop_size = 12;  /* emit one intra frame every twelve frames at most */
     c->pix_fmt = STREAM_PIX_FMT;
     if  (c->codec_id == CODEC_ID_MPEG2VIDEO) {
         /* just for testing, we also add B frames */
         c->max_b_frames = 2;
     }
     if  (c->codec_id == CODEC_ID_MPEG1VIDEO){
         /* Needed to avoid using macroblocks in which some coeffs overflow.
            This does not happen with normal video, it just happens here as
            the motion of the chroma plane does not match the luma plane. */
         c->mb_decision=2;
     }
     // some formats want stream headers to be separate
     if (oc->oformat->flags & AVFMT_GLOBALHEADER)
         c->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
     return  st;
}


仔细一看就可以知道这个和之前编码音视频的时候,设置编码参数是一样的。


5.打开音视频编码器

1
2
3
4
     if  (video_st)        
         open_video(oc, video_st);
     if  (audio_st)
         open_audio(oc, audio_st);


这里执行的操作就是打开编码器和分配一些需要的存储空间了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
static  void  open_video(AVFormatContext *oc, AVStream *st)
{
     AVCodec *codec;
     AVCodecContext *c;
 
     c = st->codec;
 
     /* find the video encoder */
     codec = avcodec_find_encoder(c->codec_id);
     if  (!codec) {
         fprintf (stderr, "codec not found
");
         exit (1);
     }
 
     /* open the codec */
     if  (avcodec_open(c, codec) < 0) {
         fprintf (stderr, "could not open codec
");
         exit (1);
     }
 
     video_outbuf = NULL;
     if  (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
         /* allocate output buffer */
         /* XXX: API change will be done */
         /* buffers passed into lav* can be allocated any way you prefer,
            as long as they're aligned enough for the architecture, and
            they're freed appropriately (such as using av_free for buffers
            allocated with av_malloc) */
         video_outbuf_size = 200000;
         video_outbuf = av_malloc(video_outbuf_size);
     }
 
     /* allocate the encoded raw picture */
     picture = alloc_picture(c->pix_fmt, c->width, c->height);
     if  (!picture) {
         fprintf (stderr, "Could not allocate picture
");
         exit (1);
     }
 
     /* if the output format is not YUV420P, then a temporary YUV420P
        picture is needed too. It is then converted to the required
        output format */
     tmp_picture = NULL;
     if  (c->pix_fmt != PIX_FMT_YUV420P) {
         tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);
         if  (!tmp_picture) {
             fprintf (stderr, "Could not allocate temporary picture
");
             exit (1);
         }
     }
}


6.写入视频头

1
av_write_header(oc);

这一步非常重要,不能漏。


7.写入一帧帧的音视频数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
     for (;;) 
     {        
         if  (audio_st)
             audio_pts = ( double )audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
         else
             audio_pts = 0.0;
 
         if  (video_st)
             video_pts = ( double )video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
         else
             video_pts = 0.0;
 
         if  ((!audio_st || audio_pts >= STREAM_DURATION) &&
             (!video_st || video_pts >= STREAM_DURATION))
             break ;
 
         /* write interleaved audio and video frames */
         if  (!video_st || (video_st && audio_st && audio_pts < video_pts)) {
             write_audio_frame(oc, audio_st);
         else  {
             write_video_frame(oc, video_st);
         }
     }

音视频的pts和dts信息就是这个时候写入的。


8.写视频尾

1
av_write_trailer(oc);

这一步也很重要,虽然不是所有的视频文件都有尾部信息,但是这一步依然不能漏。


整个output-example.c差不多就是这些内容了,ffmpeg保存生成视频还是比较简单的,以后只需要根据这个例子改下就OK了。


最后附上根据output-example.c修改的例子,实现用YUV和PCM文件合成视频文件。

代码就不做解释了,自行下载查看吧。


例子工程下载地址:http://download.csdn.net/download/qq214517703/9826315


音视频技术交流讨论欢迎加 QQ群 121376426



原文地址:http://blog.yundiantech.com/?log=blog&id=26



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一种跨平台的应用程序开发框架,它可以用于开发各种类型的软件。如果使用Qt编写录屏软件的话,可以实现抓屏和合成视频的功能。 首先,Qt提供了Qt Multimedia模块,该模块中包含了对音频和视频的控制和处理功能。通过使用Qt的媒体类,可以实现录制屏幕上的内容,并将其保存为视频文件。 其次,Qt还提供了Qt GUI模块,通过使用该模块中的图形界面组件,可以实现用户界面的设计和交互。在录屏软件中,可以使用Qt的图形界面来创建一个用户友好的界面,其中包含开始录制、暂停录制、停止录制等功能按钮。 在录制过程中,通过调用Qt的图像处理类,可以实现抓取屏幕上的图像,并将其保存在内存中或者直接保存为图像文件。同时,可以使用Qt的定时器功能,定时抓取屏幕上的图像,以保证录制得到的视频流畅度。 在合成视频方面,Qt提供了Qt Multimedia模块中的视频处理类,可以将抓取到的图像序列合成视频文件。通过设置视频的帧率、分辨率等参数,可以实现合成视频的定制化。 最后,结合Qt的文件操作类,可以将录制的视频文件保存到指定的位置,并进行管理和分享等操作。 总而言之,通过使用Qt开发录屏软件,可以方便快捷地实现抓屏和合成视频的功能,并且可以跨平台运行,使得用户能够在不同的操作系统上使用该软件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值