由于之前偶尔接触ffmpeg,但都没有深入的研究ffmpeg,所以打算一步步学习ffmpeg,写出我的学习过程,也希望各位高手能给于指点,给我提供更加便捷的学习之路。有一起学习的朋友,希望能多多交流。
1. ffmpeg编译(Ubuntu或者centos平台)
这里不多说,百度到处都是;
2. ffmpeg入门很难,各种资料很多,但一开始就各种专业函数其实很难理解,我在网上参考参考别人的代码做了一个读取264文件的例子。(frame没用到,我没删掉。)
//
//g++ read264.cpp -lavcodec
#include <stdio.h>
#include <string.h>
#include "Read.h"
#include <iostream>
using namespace std;
#define BUFFLEN 1024
#define in_buffer_size 4096
extern "C"
{
#include "libavcodec/avcodec.h"
AVCodec *pCodec;
AVCodecContext *pCodecCtx= NULL;
AVCodecParserContext *pCodecParserCtx=NULL;
AVFrame *pFrame;
FILE *fp_in;
uint8_t in_buffer[in_buffer_size + FF_INPUT_BUFFER_PADDING_SIZE];
int cur_size;
uint8_t *cur_ptr;
AVPacket packet;
enum AVCodecID codec_id=AV_CODEC_ID_H264;
int first_time = 1;
struct SwsContext *img_convert_ctx;
int Read264(char* filepath_in )
{
int ret;
avcodec_register_all();
pCodec = avcodec_find_decoder(codec_id);
if (!pCodec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx)
{
printf("Could not allocate video codec context\n");
return -1;
}
pCodecParserCtx=av_parser_init(codec_id);
if (!pCodecParserCtx){
printf("Could not allocate video parser context\n");
return -1;
}
if(pCodec->capabilities&CODEC_CAP_TRUNCATED)
pCodecCtx->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("Could not open codec\n");
return -1;
}
//Input File
fp_in = fopen(filepath_in, "rb");
if (!fp_in)
{
printf("Could not open input stream\n");
return -1;
}
pFrame = av_frame_alloc();
av_init_packet(&packet);
int frameNum = 0;
while(1)
{
cur_size = fread(in_buffer, 1, in_buffer_size, fp_in);
if (cur_size == 0)
break;
cur_ptr = in_buffer;
while (cur_size>0)
{
int len = <strong>av_parser_parse2</strong>(pCodecParserCtx, pCodecCtx, &packet.data, &packet.size, cur_ptr , cur_size , AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
cur_ptr += len;
cur_size -= len;
if(packet.size==0)
continue;
//for one frame
printf("frame: %d ", frameNum++);
//Some Info from AVCodecParserContext
printf("Packet Size:%6d\t",packet.size);
switch(pCodecParserCtx->pict_type)
{
case AV_PICTURE_TYPE_I: printf("Type: I\t");break;
case AV_PICTURE_TYPE_P: printf("Type: P\t");break;
case AV_PICTURE_TYPE_B: printf("Type: B\t");break;
default: printf("Type: Other\t");break;
}
cout<<endl;
}
}
fclose(fp_in);
av_parser_close(pCodecParserCtx);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
av_free(pCodecCtx);
return 0;
}
}
int main(int argc, char** argv)
{
int ret;
char filepath_in[] = "test.264";
ret = Read264(filepath_in);
if(ret < 0)
{
printf("Read error!");
return -1;
}
return 0;
}
这个例子用于读取264码流文件,并解析出帧类型I/P/B,计算帧数。
解析函数是
int av_parser_parse2 (AVCodecParserContext *s, AVCodecContext *avctx, uint8_t **poutbuf, int *poutbuf_size, const uint8_t *buf, int buf_size, int64_t pts, int64_t dts, int64_t pos)
程序调用时为:
int len = av_parser_parse2(pCodecParserCtx, pCodecCtx, &packet.data, &packet.size, cur_ptr , cur_size , AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
如果要查看函数的话,我建议去官网上找到avcodec.h文件查看,因为浏览网页的方式很方便,一目了然
http://ffmpeg.org/doxygen/2.6/libavcodec_2avcodec_8h.html
找到后也不需要点进去看函数内容,只需要看参数就可以了。如果要仔细研究,雷神博客:
http://blog.csdn.net/leixiaohua1020/article/details/12679719
(1) pCodecParserCtx
//定义
AVCodecParserContext *pCodecParserCtx=NULL;
//初始化
pCodecParserCtx=av_parser_init(codec_id);
if (!pCodecParserCtx){
printf("Could not allocate video parser context\n");
return -1;
}
</pre>
(2)初始化时的参数
<pre name="code" class="html">codec_id
//定义
enum AVCodecID codec_id=AV_CODEC_ID_H264;
如果是其他格式的,可查看AVCodecID枚举类型,找到对应的类型。
(3) 参数2
<pre name="code" class="html">pCodecCtx
//定义:
AVCodecContext *pCodecCtx= NULL;
//初始化
pCodecCtx = avcodec_alloc_context3(pCodec); if (!pCodecCtx) { printf("Could not allocate video codec context\n"); return -1; }
初始化的参数pCodec:(编解码器)
pCodec = avcodec_find_decoder(codec_id);
if (!pCodec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
(4) 参数3和:4:&packet.data; &packet.size 输出buffer
AVPacket packet;
//初始化
av_init_packet(&packet);
(5) 参数5和6: cur_ptr(当前指针)、cur_size(内容长度)
int cur_size;
uint8_t *cur_ptr;
cur_ptr = in_buffer;
inbuffer:
cur_size = fread(in_buffer, 1, in_buffer_size, fp_in);
然后调用 av_parser_parse2函数,完成码流解析。
解析后的帧类型在第一个参数中:
pCodecParserCtx->pict_type
如此,该程序顺利完成。