开发介绍
- libavcodec/avcodec.h
- 常用的数据结构
- AVCodec 编码器结构体
- AVCodecContext 编码器上下文
- AVFrame 解码后的帧
- 结构体内存的分配和释放
- av_frame_alloc 申请
- av_frame_free() 释放
- avcodec_alloc_context3() 创建编码器上下文
- avcodec_free_context() 释放编码器上下文
- 解码步骤
- avcodec_find_decoder 查找解码器
- avcodec_open2 打开解码器
- avcodec_decode_video2 解码
FFMpegH264编码
- avcodec_find_encoder_by_name 查找编码器
- avcodec_open3 设置编码参数(分辨率、高、宽),并打开编码器,(解码的时候直接拷贝对应参数即可,无需再次设置)
- avcodec_encode_video2 编码
- 真正执行编码的由第三方库进行
- 如libx264 libopenh264
- 注意
- 通过ID 查找编/解码器
- 通过Name 查找编/解码器
- 参考链接:FFmpeg h264编码 - 简书
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
extern "C" {
#include<libavutil/opt.h>
#include<libavutil/imgutils.h>
#include<libavcodec/avcodec.h>
}
//对每一帧数据进行编码
static void encode(AVCodecContext *enc_ctx,AVFrame *frame,AVPacket *pkt,FILE *outfile){
int ret = 0;
//send the frame to the encoder
if (frame){
printf("Send frame %3"PRId64"\n",frame->pts);
}
ret = avcodec_send_frame(enc_ctx,frame);
if (ret < 0){
fprintf(stderr,"Error sending a frame for encoding\n");
exit(1);
}
while (ret >= 0){
ret = avcodec_receive_packet(enc_ctx,pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
return;
} else if (ret < 0){
fprintf(stderr,"Error during encoding\n");
exit(1);
}
printf("Write packet %3"PRId64" (size=%5d)\n",pkt->pts,pkt->size);
fwrite(pkt->data,1,pkt->size,outfile);
av_packet_unref(pkt);
}
}
int main(int argc,char** argv){
const char *file_name,*codec_name;//输出文件路径和编码器名字,由运行程序时传入参数(要编码的内容是从摄像头获取的)
const AVCodec *codec; //编码器
AVCodecContext *codec_context = nullptr;//编码上下文环境
int i,ret,x,y,got_output; //got_output用于标记一帧是否压缩成功
FILE *file;
AVFrame *frame; //原始帧(未压缩的数据)
AVPacket pkt;
uint8_t endcode[]={0,0,1,0xb7};
if (argc <= 2){
fprintf(stderr,"Usage: %s <output file> <codec name>\n",argv[0]);
exit(0);
}
file_name = argv[1];
codec_name = argv[2]; //h264编码器名字是libx264
//avcodec_register_all() //delete
//通过名字查找编码器
codec = avcodec_find_encoder_by_name(codec_name);
if (!codec){
fprintf(stderr,"Codec not found\n");
exit(1);
}
//生成编码上下文环境
codec_context = avcodec_alloc_context3(codec);
if (!codec_context){
fprintf(stderr,"Could not allocate video codec context\n");
exit(1);
}
// 设置码率
codec_context->bit_rate = 400000;
// 设置视频宽高
codec_context->width = 352;
codec_context->height = 288;
// 设置时间基、帧率(时间基根据帧率而变化)
codec_context->time_base = (AVRational){1,25}; //一秒钟25帧,刻度就是1/25
codec_context->framerate = (AVRational){25,1}; //时间基根据帧率进行变化
// 设置多少帧产生一个关键帧,也就是一组帧是多少帧
// 如果同一个镜头没有变化,只需要设定一个关键帧,一组帧以这个关键帧作为参照,从而降低数据存储
codec_context->gop_size = 10;
// 设置b帧(前后参考帧)
// P帧 向前参考帧
codec_context->max_b_frames = 1;
// 要编码的原始数据的YUV格式
codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
// 如果编码器id是h264
if (codec->id == AV_CODEC_ID_H264){
// preset表示采用一个预先设定好的参数集,级别是slow
// slow表示压缩速度是慢的,慢的可以保证视频质量,用快的会降低视频质量
av_opt_set(codec_context->priv_data,"preset","slow",0);
}
// 打开编码器
if (avcodec_open2(codec_context,codec,NULL) < 0){
fprintf(stderr,"Could not open codec\n");
exit(1);
}
file = fopen(file_name,"wb");
if (!file){
fprintf(stderr,"Could not open %s\n",file_name);
exit(1);
}
// 初始化帧并设置帧的YUV格式和分辨率
frame = av_frame_alloc();
if (!frame){
fprintf(stderr,"Could not allocate video frame\n");
exit(1);
}
frame->format = codec_context->pix_fmt;
frame->width = codec_context->width;
frame->height = codec_context->height;
ret = av_frame_get_buffer(frame,32);
if (ret < 0){
fprintf(stderr,"Could not allocate the video frame data\n");
exit(1);
}
// 这里是人工添加数据模拟生成1秒钟(25帧)的视频(真实应用中是从摄像头获取的原始数据,摄像头拿到数据后会传给编码器,然后编码器进行编码形成一帧帧数据。)
for (i = 0; i < 25; i++) {
av_init_packet(&pkt);//packet data will be allocated by the encoder
pkt.data = NULL;
pkt.size = 0;
// 强制输出写入文件
fflush(stdout);
/* make sure the frame data is writable */
ret = av_frame_make_writable(frame);
if (ret < 0){
exit(1);
}
// 下面2个循环是人工往frame里面添的数据
/* Y */
for (int y = 0; y < codec_context->height; y++) {
for (int x = 0; x < codec_context->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i*3;
}
}
/* Cb and Cr */
for (int y = 0; y < codec_context->height/2; y++) {
for (int x = 0; x < codec_context->width/2; x++) {
frame->data[1][y * frame->linesize[1] +x] = 128 + y + i*2;
frame->data[2][y * frame->linesize[2] +x] = 64 + x + i*5;
}
}
frame->pts = i;
// 进行编码压缩
encode(codec_context,frame,&pkt,file);
}
/* flush the encoder */
encode(codec_context,NULL,&pkt,file);
/* add sequence end code to have a real MPEG file */
fwrite(endcode,1,sizeof (endcode),file);
fclose(file);
avcodec_free_context(&codec_context);
av_frame_free(&frame);
return 0;
}
- build不出现错误之后,点击run,弹出提示信息,需要输入指定的参数
- 进入终端页面,进入cmake-build-debug文件夹下
- 使用如下命令进行数据编码 ./learn_ffmpeg 1.h264 libx264
- 生成1.h264文件
- 使用ffplay 1.h264 进行播放
- 本人使用开源软件 PotPlayer进行视频播放
文章来源:https://www.toymoban.com/news/detail-430369.html
- ffplay 1.h264进行视频播放,输出数据的相关描述信息
- Stream #0:0: 流的ID
- 视频流是 h264 high
- 数据先前的格式是 yuv420p 分辨率是353x288 帧率是25 时间基是25 流的时间基是1200 编码的时间基是 50
文章来源地址https://www.toymoban.com/news/detail-430369.html
到了这里,关于音视频处理 ffmpeg中级开发 H264编码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!