FFmpeg入门(3)-An ffmpeg and SDL Tutorial

7 篇文章 0 订阅
4 篇文章 0 订阅

虽然已经实现了 H264 到 TS流的转码,但是里面FFmpeg的具体过程还是不名所以,研究了这么久也没找到一个比较系统的教材,官网上的API介绍可以说空无一物啊。找了好久找到一个系统一点的说明,发现竟然还是“已经有一点过时”的官网上介绍的An FFmpeg and SDL Tutorial。旧就旧吧,好多人已经研究过这个资料了进行了修改,不过他们的资料又老了,我再帮他们更新下。

这是2011年的资料,我在他的基础上改。

ffmpeg学习(1)----下载及安装中遇到的问题

http://blog.csdn.net/gavinr/article/details/6973316

根据资料,安装完成后会生成以下两个可执行文件:
ffmpeg,用于转码及文件格式转换、录像、抓屏等等
ffplay,一个命令行播放器程序
但是,我发现成功生成了ffmpeg,但是却没有ffplay,why?

2.安装SDL
经过一翻google,原来ffplay依赖于SDL,所以要先安装SDL。安装SDL有点麻烦,主要要安装好几个附加包,安装命令如下:
sudo apt-get install libsdl1.2-dev(比较大,10M左右)
附加包:
sudo apt-get install libsdl-image1.2-dev
sudo apt-get install libsdl-mixer1.2-dev
sudo apt-get install libsdl-ttf2.0-dev
sudo apt-get install libsdl-gfx1.2-dev

我发现这些库都还没变,直接全部安装

sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev  libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsdl-gfx1.2-dev
安装完成之后,在目录/usr/include中,就会有个SDL文件夹,里面包含了所需要的头文件。
接着重新编译安装ffmpeg,成功生成了ffplay

ffmpeg学习(2)--An ffmpeg and SDL Tutorial

http://blog.csdn.net/gavinr/article/details/6973704


安装好ffmpeg后,就开始学习如何应用了,主要也就是熟悉一些常用API。
同事推荐了一个ffmpeg的自学教程--An ffmpeg and SDL Tutorial,觉得不错,就开始照敲代码了。这个教程的网址为:http://dranger.com/ffmpeg/tutorial01.html

学习过程中敲的代码及编译makefile文件 ,已上传至http://download.csdn.net/detail/gavinr/3798699


1.教程中的内容
教程中提供8个例程:
Tutorial 01: Making Screencaps 读取一个文件解码,并保存成*.ppm格式的图片,*.ppm其实就是RGB数据添加一个简单的文件头(一个魔数、宽、高等)组成。
Tutorial 02: Outputting to the Screen  主要是在Tutorial 01基础上,将解码后的图像通过SDL输出到屏幕,
Tutorial 03: Playing Sound 增加了SDL播放声音
Tutorial 04: Spawning Threads 调整程序结构,使用了多线程机制,方便后续扩展
Tutorial 05: Synching Video 音频同步
Tutorial 06: Synching Audio 视频同步
Tutorial 07: Seeking 定位
Tutorial 08: Software Scaling 使用libswscale库进行,图像的格式转换及缩放操作
每个例程都是在前一个的基础上完成的,所以需要从第一个看起。不过有一个例外,Tutorial 08最好提前看。前面的例子中图像格式转换时,均使用了img_convert函数,但是在新版本的ffmpeg中已经不再支持,必需使用例程8讲述的方式。


2.SDL的作用
SDL是一个跨平台媒体库,似乎在游戏编程中有大量应用,例中大量使用了SDL中的函数,主要用到其以下功能:
1)视频渲染
2)音频播放
3)事件机制,可以响应键盘及自定义事件
4)线程机制,SDL提供了线程创建函数,及线程同步机制

3.编译例程遇到的问题
前5个例程,偶都敲进去编译运行了一下,遇到的问题不算太多,主要修改几个已经不再支持的宏及函数调用,主要有以下几个地方
1).宏CODEC_TYPE_VIDEO需要改为AVMEDIA_TYPE_VIDEO

2).音频解码函数

/* 
  len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *) audio_buf, &data_size, audio_pkt_data, 
 audio_pkt_size); 
 */  
  
len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *) audio_buf,  
        &data_size, &pkt);  


3).视频解码函数
/* 
 avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size); 
 */  
  
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);  


4).图像格式转换,就是Tutorial 08中提到的
/* 
                *此函数已经不用 
                img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); 
                */  
  
  static struct SwsContext *img_convert_ctx;  
...  
   // Convert the image into YUV format that SDL uses  
   if(img_convert_ctx == NULL) {  
     int w = is->video_st->codec->width;  
     int h = is->video_st->codec->height;  
     img_convert_ctx = sws_getContext(w, h,   
                       is->video_st->codec->pix_fmt,   
                       w, h, dst_pix_fmt, SWS_BICUBIC,   
                       NULL, NULL, NULL);  
     if(img_convert_ctx == NULL) {  
fprintf(stderr, "Cannot initialize the conversion context!\n");  
exit(1);  
     }  
   }  
   sws_scale(img_convert_ctx, pFrame->data,   
             pFrame->linesize, 0,   
             is->video_st->codec->height,   
             pict.data, pict.linesize);  


4.其它一些问题

1) 编译的程序播放音频时,会比较卡,这应该是代码本身的问题。例程中的音频解码,是在回调函数中进行的,这浪费了一定的时间,如果将解码过程放到其它线程中,应该能解决这个问题。

2)关于音视频同步这块也是就tutorial 05与tutorial06的内容还没有完全弄清楚



先来第一个例子

从一个视频里截图生成 .PPM文件

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
    FILE *pFile;
    char szFilename[32];
    int y;

    sprintf(szFilename, "frame%d.ppm", iFrame);
    pFile = fopen(szFilename, "wb");

    if (pFile == NULL)
        return;

    fprintf(pFile, "P6\n%d %d\n255\n", width, height);

    for (y = 0; y < height; y++) {
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
    }

    fclose(pFile);
}
//slq
static AVFormatContext *pFormatCtx = NULL;
int main(int argc, char *argv[]) {
//    AVFormatContext *pFormatCtx;
//    static AVFormatContext *pFormatCtx = NULL;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVPacket packet;
    AVFrame *pFrame;
    AVFrame *pFrameRGB;

    int frameFinished;
    uint8_t *buffer;
    int numBytes;
    int i;

    av_register_all();

    //slq
	//reads the file header and stores information about the file format in the AVFormatContext structure pFormatCtx
    if (avformat_open_input( &pFormatCtx, argv[1], NULL, NULL) < 0)
    //if (avformat_open_input( &input_fmt_ctx, input_file, NULL, NULL) < 0) {
    //if (av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL) != 0)
        return -1;

	// Retrieve stream information
    if (av_find_stream_info(pFormatCtx) < 0)
        return -1;

	// Dump information about file onto standard error
    //    dump_format(pFormatCtx, 0, argv[1], 0);
    av_dump_format(pFormatCtx, 0, argv[1], 0);


	// Find the first video stream
    int videoStream = -1;
    for (i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    }

	// Get a pointer to the codec context for the video stream
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    // Find the decoder for the video stream
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        fprintf(stderr, "Unsupported codec@!\n");
    }

    // Open codec
    //slq
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    //if (avcodec_open(pCodecCtx, pCodec) < 0)
        return -1;

    // Allocate video frame (native format)
    pFrame = avcodec_alloc_frame();

    // Allocate an AVFrame structure ( RGB )
    pFrameRGB = avcodec_alloc_frame();
    if (pFrameRGB == NULL)
        return -1;

    // we still need a place to put the raw data when we convert it.
    //We use avpicture_get_size to get the size we need,
    //and allocate the space manually:
    // Determine required buffer size and allocate buffer
    numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
            pCodecCtx->height);
    //av_malloc is ffmpeg's malloc that is just a simple wrapper around malloc that makes sure the memory addresses are aligned and such.
    buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    // Assign appropriate parts of buffer to image planes n. 图像平面;映像平面 in pFrameRGB
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,
            pCodecCtx->width, pCodecCtx->height);

    i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {
            /*
             avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);
             */

            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
            // Did we get a video frame?
            if (frameFinished) {
                /*
                 *此函数已经不用
                 img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
                 */
                 // Convert the image into YUV format that SDL uses  
                static struct SwsContext *img_convert_ctx;
                img_convert_ctx = sws_getContext(pCodecCtx->width,
                        pCodecCtx->height, pCodecCtx->pix_fmt,
                        pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24,
                        SWS_BICUBIC, NULL, NULL, NULL);

                if(img_convert_ctx == NULL) {  
                    fprintf(stderr, "Cannot initialize the conversion context!\n");  
                    exit(1);  
                }  
              
            
                sws_scale(img_convert_ctx,
                        (const uint8_t* const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height,
                        pFrameRGB->data, pFrameRGB->linesize);

                // Save the frame to disk, first 5 frames
                if (++i <= 5)
                    SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);

            }
        }
        // Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }

    av_free(buffer);
    av_free(pFrameRGB);
    av_free(pFrame);

    avcodec_close(pCodecCtx);

    av_close_input_file(pFormatCtx);

    return 0;
}

    修改了两处 error, warning 没改

1.  if (av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL) != 0)改为

 if (avformat_open_input( &pFormatCtx, argv[1], NULL, NULL) < 0)

2. if (avcodec_open(pCodecCtx, pCodec) < 0) 改为

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)    

编译

ls@ubuntu:/slq/local/src/ffmpeg-test/doc/examples/tutorial$ gcc -g -o tutorial01 tutorial01.1.c  -lavformat -lavcodec -lswscale -lavutil  -lz  -lm -lpthreadtutorial01.1.c: In function ‘main’:tutorial01.1.c:46:5: warning: ‘av_find_stream_info’ is deprecated (declared at /slq/local/ffmpeg/include/libavformat/avformat.h:1854) [-Wdeprecated-declarations]tutorial01.1.c:73:5: warning: ‘avcodec_alloc_frame’ is deprecated (declared at /slq/local/ffmpeg/include/libavcodec/avcodec.h:3444) [-Wdeprecated-declarations]tutorial01.1.c:75:5: warning: ‘avcodec_alloc_frame’ is deprecated (declared at /slq/local/ffmpeg/include/libavcodec/avcodec.h:3444) [-Wdeprecated-declarations]tutorial01.1.c:124:5: warning: ‘av_close_input_file’ is deprecated (declared at /slq/local/ffmpeg/include/libavformat/avformat.h:2034) [-Wdeprecated-declarations]

运行( .mp4, .ts, .h264 视频文件都能运行)

ls@ubuntu:/slq/local/src/ffmpeg-test/doc/examples/tutorial$ ./tutorial01 test2.mp4 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test2.mp4':  Metadata:    major_brand     : isom    minor_version   : 1    compatible_brands: isomavc1    creation_time   : 2013-09-06 05:02:55  Duration: 00:00:19.23, start: 0.000000, bitrate: 535 kb/s    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 576x432 [SAR 1:1 DAR 4:3], 489 kb/s, 24 fps, 24 tbr, 24k tbn, 48 tbc (default)    Metadata:      creation_time   : 2013-09-06 05:02:55      handler_name    : GPAC ISO Video Handler    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 47 kb/s (default)    Metadata:      creation_time   : 2013-09-06 05:02:55      handler_name    : GPAC ISO Audio Handler

1.

gcc -o bin/tutorial01 tutorial01.c  -lavformat -lavcodec -lswscale -lavutil  -lz  -lm -lpthread

gcc -g  -o tutorial01 tutorial01.c  -lavformat -lavcodec -lswscale -lavutil  -lz  -lm -lpthread

2. 程序2编译 运行

gcc -o tutorial02 tutorial02.1.c -lavutil -lavformat -lavcodec -lz -lm `sdl-config --cflags --libs`

gcc -g -o tutorial02 tutorial02.1.c -lavutil -lavformat -lavcodec -lswscale -lz -lm -lpthread `sdl-config --cflags --libs`




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值