#include "stdafx.h"
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/avutil.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/parseutils.h>
#include <libavutil/avutil.h>
#ifdef __cplusplus
};
#endif
#endif
void encoder()
{
const char *pOutFile = "ds.h264";
FILE*pFile = fopen("ds_480x272.yuv", "rb");
if (!pFile){
return;
}
// 注册 ffmpeg 中的所有的封装、解封装 和 协议等
av_register_all();
// 用作之后写入视频帧并编码成 h264,贯穿整个工程当中
AVFormatContext *pFormatContext = avformat_alloc_context();
// 通过这个函数可以获取输出文件的编码格式, 那么这里我们的 fmt 为 h264 格式(AVOutputFormat *)
AVOutputFormat *pOutputFormat = av_guess_format(NULL, pOutFile, NULL);
pFormatContext->oformat = pOutputFormat;
//将输出文件中的数据读入到程序的 buffer 当中,方便之后的数据写入,也可以说缓存数据写入
avio_open(&pFormatContext->pb, pOutFile, AVIO_FLAG_READ_WRITE);
// 通过媒体文件控制者获取输出文件的流媒体数据,这里 AVCodec * 写 0 , 默认会为我们计算出合适的编码格式
AVStream *pStream = avformat_new_stream(pFormatContext, 0);
if (!pStream){
return;
}
// 设置 25 帧每秒 ,也就是 fps 为 25。(其实不设置也可以)
pStream->time_base.num = 1;
pStream->time_base.den = 25;
// 用户存储编码所需的参数格式等等
AVCodecContext*pCodecContext = NULL;
// 从媒体流中获取到编码结构体,他们是一一对应的关系,一个 AVStream 对应一个 AVCodecContext
pCodecContext = pStream->codec;
// 设置编码器的 id,每一个编码器都对应着自己的 id,例如 h264 的编码 id 就是 AV_CODEC_ID_H264
pCodecContext->codec_id = pOutputFormat->video_codec;
// 设置编码类型为 视频编码
pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
// 设置像素格式为 yuv 格式
pCodecContext->pix_fmt = PIX_FMT_YUVJ420P;
// 设置视频的宽高
pCodecContext->width = 480;
pCodecContext->height = 272;
// 设置 25 帧每秒 ,也就是 fps 为 25
pCodecContext->time_base.num = 1;
pCodecContext->time_base.den = 25;
// 设置比特率,每秒传输多少比特数 bit,比特率越高,传送速度越快,也可以称作码率,
// 视频中的比特是指由模拟信号转换为数字信号后,单位时间内的二进制数据量。
pCodecContext->bit_rate = 400000;
// 设置图像组层的大小。
// 图像组层是在 MPEG 编码器中存在的概念,图像组包 若干幅图像, 组头包 起始码、GOP 标志等,如视频磁带记录器时间、控制码、B 帧处理码等;
pCodecContext->gop_size = 250;
pCodecContext->qmin = 10;
pCodecContext->qmax = 51;
pCodecContext->qcompress = 1;
// 设置 B 帧最大的数量,B帧为视频图片空间的前后预测帧, B 帧相对于 I、P 帧来说,压缩率比较大,也就是说相同码率的情况下,
// 越多 B 帧的视频,越清晰,现在很多打视频网站的高清视频,就是采用多编码 B 帧去提高清晰度,
// 但同时对于编解码的复杂度比较高,比较消耗性能与时间
pCodecContext->max_b_frames = 3;
AVDictionary *param = 0;
if(pCodecContext->codec_id == AV_CODEC_ID_H264) {// 可选设置
// 通过--preset的参数调节编码速度和质量的平衡。
av_dict_set(¶m, "preset", "slow", 0);
// 通过--tune的参数值指定片子的类型,是和视觉优化的参数,或有特别的情况。
// zerolatency: 零延迟,用在需要非常低的延迟的情况下,比如电视电话会议的编码
av_dict_set(¶m, "tune", "zerolatency", 0);
}
// 通过 codec_id 找到对应的编码器
AVCodec *pCodec = avcodec_find_encoder(pCodecContext->codec_id);
if (!pCodec){
return;
}
// 打开编码器,并设置参数 param
avcodec_open2(pCodecContext, pCodec, ¶m);
AVFrame *pFrame = av_frame_alloc();
// 通过像素格式(这里为 YUV)获取图片的真实大小,例如将 480 * 720 转换成 int 类型
int nPicSize = avpicture_get_size(pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height);
// 将 picture_size 转换成字节数据,byte
uint8_t *buf = (uint8_t*)av_malloc(nPicSize);
// 设置原始数据 AVFrame 的每一个frame 的图片大小,AVFrame 这里存储着 YUV 非压缩数据
avpicture_fill((AVPicture*)pFrame, buf, pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height);
// 编写 h264 封装格式的文件头部,基本上每种编码都有着自己的格式的头部,想看具体实现的同学可以看看 h264 的具体实现
avformat_write_header(pFormatContext, NULL);
//创建编码后的数据 AVPacket 结构体来存储 AVFrame 编码后生成的数据
AVPacket pkt;
av_new_packet(&pkt, nPicSize);
int i = 0;
int y_size = pCodecContext->width * pCodecContext->height;
while (true){
if (fread(buf, 1, y_size*3/2, pFile) <= 0){
printf("Could not read input file.");
break;
}
else if(feof(pFile)){
break;
}
pFrame->data[0] = buf; // 亮度Y
pFrame->data[1] = buf+ y_size; // U
pFrame->data[2] = buf+ y_size*5/4; // V
pFrame->pts = i++;
//Encode
int got_picture = 0;
int ret = avcodec_encode_video2(pCodecContext, &pkt, pFrame, &got_picture);
if(ret < 0){
printf("Encode Error.\n");
break;
}
if (got_picture == 1){// 编码成功后写入 AVPacket 到 输入输出数据操作着 pFormatCtx 中,当然,记得释放内存
pkt.stream_index = pStream->index;
ret = av_write_frame(pFormatContext, &pkt);
av_free_packet(&pkt);
}
}
av_free_packet(&pkt);
// 写入数据流尾部到输出文件当中,并释放文件的私有数据
av_write_trailer(pFormatContext);
if (pStream){
avcodec_close(pStream->codec);// 关闭编码器
}
av_free(pFrame);
av_free(buf);
avio_close(pFormatContext->pb);
avformat_free_context(pFormatContext);// 关闭输入数据的缓存
fclose(pFile);
return ;
}
int _tmain(int argc, _TCHAR* argv[])
{
encoder();
return 0;
}
http://download.csdn.net/detail/sz76211822/9698824
ffmpeg 将yuv转换成H264
最新推荐文章于 2024-07-14 11:36:02 发布