课程地址 http://edu.csdn.net/course/detail/2515/39428?auto_start=1
需要包含的头文件
#include <iostream>
#include <stdio.h>
#include "Error.h"
extern "C"
{
//#include<stdio.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include<libavutil/imgutils.h>
#include <libavutil/opt.h>
}
Error.h
就是定义几个出错类型就可以了
/*
* Error.h
*
* Created on: Aug 14, 2017
* Author: media
*/
#ifndef ERROR_H_
#define ERROR_H_
#define IO_FILE_ERROR_READ_FAILED -1
#define FF_ERROR_INIT_FAILED -10
#define FF_ERROR_ENCODE_FAILED -11
#endif /* ERROR_H_ */
定义相关的变量
注意以下均为全局变量
文件路径
const char *inputFileName = NULL;
const char *outputFileName = NULL;
FILE *pFin = NULL, *pFout = NULL;
视频信息
int frameWidth = 0 , frameHeight = 0;
int bitRate = 0, frameToEncode = 0;
ffmpeg中相关的数据结构
AVCodec *codec = NULL;
AVCodecContext *codecCtx = NULL;
AVFrame *frame = NULL;
AVPacket pkt;
处理命令行参数
argv分别为
- 输入文件路径
- 输出文件路径
- 视频宽度
- 视频高度
- 码率
- 需要编码的帧数
static int parse_input_paramaters(int argc, char **argv)
{
inputFileName=argv[1];
outputFileName=argv[2];
pFin=fopen(inputFileName, "rb+");
if(!pFin)
{
std::cout<<"Error in reading inputfile"<<std::endl;
return IO_FILE_ERROR_READ_FAILED;
}
pFout=fopen(outputFileName, "wb+");
if(!pFout)
{
std::cout<<"Error in reading outputfile"<<std::endl;
return IO_FILE_ERROR_READ_FAILED;
}
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
bitRate = atoi(argv[5]);
frameToEncode = atoi(argv[6]);
return 1;
}
读取YUV数据
static int read_yuv_data(int color)
{
/* color = 0 : Y
* color = 1 : U
* color = 2 : V
*/
int color_height = color == 0 ? frameHeight : frameHeight/2;
int color_width = color == 0 ? frameWidth : frameWidth/2;
//这里是YUV420P的格式
int color_size=color_height*color_width;
int color_stride=frame->linesize[color];
if(color_width == color_stride)
//这种情况相当于frame->data放满了
{
//fread_s(frame->data[color], color_size, 1, color_size, pFin);
//LINUX下没有fread_s
fread(frame->data[color], color_size, 1, pFin);
/*
fread
函数原型:size_t fread(void* buff,size_t size,size_t count,FILE* stream)
作用:从文件中读入数据到指定的地址中,ffpempeg里面的AVFrame->data[0,1,2]用于存储YUV数据,所以读到这里
参数:第一个参数为接收数据的指针(buff),也即数据存储的地址
第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节
第三个参数为元素个数,即要读取的数据大小为size的元素个素
第四个参数为提供数据的文件指针,该指针指向文件内部数据
*/
}
else
//这种情况还需要对齐
{
for(int row_idx=0; row_idx<color_height;row_idx++)
{
//fread_s(frame->data[color]+row_idx*color_stride,color_width,1,color_width,pFin);
fread(frame->data[color]+row_idx*color_stride,color_width,1,pFin);
}
}
return pkt.size;
}
main 函数
int main(int argv,char **argc)
{
int got_packet = 0;
if(parse_input_paramaters(argv,argc)>0)
{
std::cout<<"Input file:"<<inputFileName<<std::endl;
std::cout<<"Output file:"<<outputFileName<<std::endl;
std::cout<<"frameWidth:"<<frameWidth<<std::endl;
std::cout<<"frameHeight:"<<frameHeight<<std::endl;
std::cout<<"bitRate:"<<bitRate<<std::endl;
std::cout<<"frameToEncode:"<<frameToEncode<<std::endl;
}
else
{
std::cout<<"Error in cmd line!"<<std::endl;
return -1;
}
//处理argv完毕
{//这个大括号似乎是多余的
avcodec_register_all();
//寻找编码器
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if(!codec)
{
return FF_ERROR_INIT_FAILED;
}
//创建AVCodecContext 结构体,这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,参考http://blog.csdn.net/yuan892173701/article/details/8702333
codecCtx = avcodec_alloc_context3(codec);
if(!codecCtx)
{
return FF_ERROR_INIT_FAILED;
}
codecCtx->width = frameWidth;
codecCtx->height = frameHeight;
codecCtx->bit_rate = bitRate;
AVRational r = {1, 25};
//时间刻度是1/25
codecCtx->time_base = r;
codecCtx->gop_size = 12;
//the number of pictures in a group of pictures, or 0 for intra_only
codecCtx->max_b_frames = 1;
codecCtx->pix_fmt=AV_PIX_FMT_YUV420P;
av_opt_set(codecCtx->priv_data,"preset","slow",0);
//初始化一个视音频编解码器的AVCodecContext
if(avcodec_open2(codecCtx, codec, NULL)<0)
{
return FF_ERROR_INIT_FAILED;
}
//分配AVFrame
frame = av_frame_alloc();
if(!frame)
{
return FF_ERROR_INIT_FAILED;
}
frame->width = codecCtx->width;
frame->height = codecCtx->height;
frame->format = codecCtx->pix_fmt;
if(av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, (AVPixelFormat)frame->format, 32)<0)
{
return FF_ERROR_INIT_FAILED;
}
}
for(int frameIdx = 0; frameIdx <frameToEncode; frameIdx++)
{
//初始化AVPacket
av_init_packet(&pkt);
pkt.data=NULL;
pkt.size=0;
//把YUV数据写入frame->data里面
read_yuv_data(0);
read_yuv_data(1);
read_yuv_data(2);
frame->pts = frameIdx;
if(avcodec_encode_video2(codecCtx, &pkt, frame, &got_packet)<0)
//avcodec_encodec_video2: AVFrame to AVPacket
{
std::cout<<"Error in encoding"<<std::endl;
return FF_ERROR_ENCODE_FAILED;
}
if(got_packet)
{
std::cout<<"Write packet of frame "<<frameIdx<<",size "<<pkt.size<<std::endl;
fwrite(pkt.data, 1, pkt.size, pFout);
av_packet_unref(&pkt);
}
for(got_packet = 1;got_packet;)
{
if(avcodec_encode_video2(codecCtx, &pkt, NULL, &got_packet)<0)
{
std::cout<<"Error in encoding"<<std::endl;
return FF_ERROR_ENCODE_FAILED;
}
if(got_packet)
//有时候帧数据写完了AVPacket里面还有东西
{
if(avcodec_encode_video2(codecCtx, &pkt, frame, &got_packet)<0)
{
std::cout<<"Write packet of size "<<pkt.size<<std::endl;
fwrite(pkt.data, 1, pkt.size, pFout);
av_packet_unref(&pkt);
}
}
}
}
//
fclose(pFin);
fclose(pFout);
//释放内存
avcodec_close(codecCtx);
av_free(codecCtx);
av_freep(frame->data[0]);
av_frame_free(&frame);
std::cout<<"success!"<<std::endl;
return 0;
}