登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: h264 ffmpeg FFVCL ccavc libavcodec.dll     [回主站]     [分站链接]
标题
h264 ffmpeg FFVCL ccavc libavcodec.dll
clq
浏览(0) + 2010-10-21 11:10:20 发表 编辑

关键字:

寻找 delphi 的 h264 控件,意外发现有人把 ffmpeg 封装为控件卖钱.
 http://bbs.2ccc.com/topic.asp?topicid=315446

关于 ffmpeg 的直接使用,在
http://www.91linux.com/html/article/program/cpp/20090512/16790.html
有一篇翻译的文章,写得非常好.

clq
2010-10-21 11:13:00 发表 编辑

http://apt-blog.net/using_ffmpeg_api

ffmpeg API 笔记:使用libavcodec/libavformat/libswscale

十二月 11, 2009

Update 2010.1.5: 其实研究ffmpeg不用找什么教程,第一步应该是下载ffmpeg的源码包。下面提到的An FFmpeg and SDL Tutorial确 实有讲解,但是教程总是跟不上代码的变化的,所以直接看可工作代码最好;ffmpeg的结构很分明,后台是几个库:libxxx,前台是三个程序 ffmpeg, ffplay, ffserver,那篇教程说的就是ffplay的实现。一个播放器,其实重点不是解码,解码的东西是lib去做的,主要是做声音视频的时钟同步。 ffplay的代码可以说是一个可用播放器最简单的实现了,源码里面有个output_example.c,可以说是最基本的api示范吧。ffmpeg 是转换编码解码转换程序,因为涉及重新采样等等,所以代码量也不少的。


这两天"调研"了下ffmpeg的API,不得不承认被雷倒:ffmpeg又是一个很geek的项目,纯社区开发,基于逆向,功能强大,但是文档极度有限,想了解API?看源码去…… 网上关于ffmpeg API的资料,无非是ffmpeg文档里面的两个链接,Using libavformat and libavcodec by
Martin Böhme(以及其Update,介绍了新引入的API)跟An FFmpeg and SDL Tutorial
by Stephen Dranger;两个tutorial基于ffmpeg 0.4.8,现在ffmpeg发布的版本是0.5.0,好像数值相差不大,不过0.4.8是5年前的了(相比之下wine用了15年版本号才到达1.0, 有过之余无不及),两个教程里面的代码在0.5.0下一编译,哇,一堆错误,可不仅有些api函数变了,有些结构成员压根就没了,头文件的位置更是不一样 (各个库分家了)……所以我调试了好几个小时,终于把例子的代码弄好(其实Martin Böhme那篇有一段09年加入的更新说明,链接了有相关的解决办法,我一开始没注意,几个小时自己解决,不过也有收获)。

最后我调试好的代码流程:打开一个视频文件,抓取前5帧保存为文件;
基于Stephen Dranger的Tutorial1源码在此:GoogleCode

av_register_all();//初始化ffmpeg库,如果系统里面的ffmpeg没配置好这里会出错
av_open_input_file();
av_find_stream_info();//查找文件的流信息
dump_format();//dump只是个调试函数,输出文件的音、视频流的基本信息了,帧率、分辨率、音频采样等等
for(...);//遍历文件的各个流,找到第一个视频流,并记录该流的编码信息
sws_getContext();//根据编码信息设置渲染格式
avcodec_find_decoder();//在库里面查找支持该格式的解码器
avcodec_open();//打开解码器
pFrame=avcodec_alloc_frame();//分配一个帧指针,指向解码后的原始帧
pFrameRGB=avcodec_alloc_frame();//分配一个帧指针,指向存放转换成RGB后的帧
avpicture_fill(pFrameRGB);//给pFrameRGB帧加上分配的内存;
while true{
     av_read_frame();//读取一个帧(到最后帧则break)
     avcodec_decode_video();//解码该帧
     sws_scale();//把该帧转换(渲染)成RGB
     SaveFrame();//对前5帧保存成ppm图形文件(这个是自定义函数,非API)
     av_free_packet();//释放本次读取的帧内存
}
avcodec_close();
av_close_input_file();

用到的API就这么多,当然实际代码稍复杂一点;ppm图像是类似BMP的非压缩格式,SaveFrame就是相当于把pFrameRGB的内存拷贝进文件,写文件并不复杂;

调试过程的问题,首先是头文件,ffmpeg 0.5.0的API已经拆分成好几个独立的库,用pacman -Ql ffmpeg看了下文件分布,在include下好几个目录都是它的,看名字可以大概猜出他们的功能:

libavcodec:CODEC其实是Coder/Decoder的缩写,也就是编码解码器;
libavdevice:对输出输入设备的支持;
libavformat:对音频视频格式的解析
libavutil:集项工具;
libpostproc:后期效果处理;
libswscale:视频场景比例缩放、色彩映射转换;

修改好头文件包含,终于少了些not declared错误;

Martin Böhme那篇教程的代码是使用g++编译的,虽然代码是C风格;在我修改了头文件以及一些错误之后,居然链接出错,av_register_all什么 的函数统统undefined reference,想到ffmpeg是纯C实现,以及以前用g++编译GTK出现回呼函数找不到的经历,相信又是g++的function mangling搞的,Google了一下,解决方法是把几个头文件包含在exterc "C"里面

代码里面的错误还涉及一些结构成员的变化,比如

pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO

就有个类型错误,因为0.5.0里面的codec已经是指针,而不是结构了,要把.换成->,而相应地获得解码器指针,不再需要&:

pCodecCtx=&pFormatCtx->streams[videoStream]->codec;

原教程提到一个视频码率的rate的Hack:

    // Hack to correct wrong frame rates that seem to be generated by some codecs<br />    if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)<br />        pCodecCtx->frame_rate_base=1000;

好吧现在frame_rate跟frame_rate_base压根就没了,去掉算了;

最麻烦的变化还是原代码里面的img_convert,就是解码出一个帧的数据后,需要转换成RGB格式才能写入文件,然而这个函数在0.5.0里 面彻底没了。尝试把img_convert完全注释掉,把原始帧img_convert写入文件,还算可喜的是能够看到图形,只是被分成三个画面的通道图 形罢了;

Google了一番,还是回到 Stephen Dranger的An FFmpeg and SDL Tutorial第八节,介绍了swscale的接口,虽然里面的例子是转换成SDL所用的YUV,而不是RGB;注意到其使用的参数PIX_FMT_YUV420P,跟img_convert所用的PIX_FMT_RGB24有相同前序,就试试照样花虎了;
给 sws_getContext传入源格式的H/W,格式,输出格式的H/W,PIX_FMT_RGB24格式,其中有个参数flags的解释是 specify which algorithm and options to use for rescaling,是选择在缩放过程中是使用线性还是双立方等算法(参数文档没说,要找看源码去),这里照抄了例子里面的SWS_BICUBIC。获得 这个SwsContext,类似python的re.compile,再用这个转换器去转换每一个帧,所以后面每次解码了帧后,调用sws_scale, 跟原来的img_convert倒是挺像;

也就是说,以后如果需要对视频进行4:3跟16:9的转换,就是在sws_getContext的参数里面做设置了;

最后整个程序正常,会把视频的前5帧抓成图像了,只是会有个小警告:

[swscaler @ 0x1d8f670]No accelerated colorspace conversion found.

估计是转换成RGB,swscale里面没有特别优化的算法?

目前发现程序运行的时候CPU占用很高,是因为程序还没有控制帧速的时钟,只会用尽CPU的性能不断的读取解码;

PT修改过可编译通过(ffmpeg 5.0/gcc 4.4.2/ubuntu 9.10)的前三个Tutorial代码可在Google Code查看下载。(编译方法请看各个文件头部的注释说明)

tags: , , , ,
posted in Programming by BOYPT



clq
2010-10-21 15:14:01 发表 编辑

ffmpeg 是有编译好的下载的,好象叫 FFMPEG SDK, 在华军上就有一个 FFMPEG FULL SDK 下载
http://www.newhua.com/soft/70420.htm
其来自一些国内的开发者 http://www.ffmpeg.com.cn

clq
2010-10-21 17:15:28 发表 编辑


// t2Dlg.cpp : 实现文件
//

#include "stdafx.h"
#include "t2.h"
#include "t2Dlg.h"

extern "C"
{
    #include <avcodec.h>
    #include <avformat.h>
    #include <swscale.h>
}

#include <stdio.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// 对话框数据
    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// Ct2Dlg 对话框




Ct2Dlg::Ct2Dlg(CWnd* pParent /*=NULL*/)
    : CDialog(Ct2Dlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void Ct2Dlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(Ct2Dlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BUTTON1, &Ct2Dlg::OnBnClickedButton1)
END_MESSAGE_MAP()


// Ct2Dlg 消息处理程序

BOOL Ct2Dlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void Ct2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void Ct2Dlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 用于绘制的设备上下文

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使图标在工作区矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // 绘制图标
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR Ct2Dlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

//--------------------------------------------------
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
    FILE *pFile;
    char szFilename[32];
    int  y;

    // Open file
    sprintf(szFilename, "frame%d.ppm", iFrame);
    pFile=fopen(szFilename, "wb");
    if(pFile==NULL)
        return;

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

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

    // Close file
    fclose(pFile);
}

//int main2(int argc, char *argv[])
int main2(const char * fn)
{
    AVFormatContext *pFormatCtx;
    int             i, videoStream;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    AVFrame         *pFrame;
    AVFrame         *pFrameRGB;
    AVPacket        packet;
    int             frameFinished;
    int             numBytes;
    uint8_t         *buffer;
    struct SwsContext *img_convert_ctx;

    //if(argc < 2) {
    //    printf("Please provide a movie file\n");
    //    return -1;
    //}
    // Register all formats and codecs
    av_register_all();

    // Open video file
    //if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
    if(av_open_input_file(&pFormatCtx, fn, NULL, 0, NULL)!=0)
        return -1; // Couldn't open file

    // Retrieve stream information
    if(av_find_stream_info(pFormatCtx)<0)
        return -1; // Couldn't find stream information

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

    // Find the first video stream
    videoStream=-1;
    for(i=0; i<pFormatCtx->nb_streams; i++)
        if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
            videoStream=i;
            break;
        }
    if(videoStream==-1)
        return -1; // Didn't find a video stream

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

    // construct the scale context, conversing to PIX_FMT_RGB24
    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);
    }

    // Find the decoder for the video stream
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL) {
        fprintf(stderr, "Unsupported codec!\n");
        return -1; // Codec not found
    }
    // Open codec
    if(avcodec_open(pCodecCtx, pCodec)<0)
        return -1; // Could not open codec

    // Allocate video frame
    pFrame=avcodec_alloc_frame();

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

    // Determine required buffer size and allocate buffer
    numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
            pCodecCtx->height);
    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    // Assign appropriate parts of buffer to image planes 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);

    // Read frames and save first five frames to disk
    i=0;
    while(av_read_frame(pFormatCtx, &packet)>=0) {
        // Is this a packet from the video stream?
        if(packet.stream_index==videoStream) {
            // Decode video frame
            avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
                    packet.data, packet.size);

            // Did we get a video frame?
            if(frameFinished) {
                // Convert the image from its native format to RGB
                //    img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,
                //                    (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,
                //                    pCodecCtx->height);
                sws_scale(img_convert_ctx,
                        pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                        pFrameRGB->data, pFrameRGB->linesize);    
                // Save the frame to disk//clq 其实只保存了 5 张
                if(++i<=5)
                    SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
            }
        }

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

    // Free the RGB image
    av_free(buffer);
    av_free(pFrameRGB);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codec
    avcodec_close(pCodecCtx);

    // Close the video file
    av_close_input_file(pFormatCtx);

    return 0;
}


void Ct2Dlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here
    main2("d:\\1.avi");


}



总数:3 页次:1/1 首页 尾页  
总数:3 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1