注意视频的AVCodecContext需要设置哪些参数,还有avcodec_encode_video2编码出来的AVPacket是不是空(部落格它为空,got_packet_ptr为0)和max_b_frames有关。另外视频的flush,和音频转码那篇文章同理。代码中也包含了pts的计算方法
#include <stdio.h>
#include <stdint.h>
#include <cstring>
#include <malloc.h>
#include <windows.h>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/audio_fifo.h"
#include "libavformat/avformat.h"
#include "libavutil/avstring.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
};
#define STREAM_FRAME_RATE 25
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P
const int width = 640, height = 360;
int framecount = 0;
void init_packet(AVPacket *packet){
av_init_packet(packet);
packet->data = NULL;
packet->size = 0;
}
/* add a video output stream */
AVStream *add_video_stream(AVFormatContext *out_format_context, AVCodecID codec_id)
{
AVCodecContext *out_codec_context;
AVStream *video_st;
video_st = avformat_new_stream(out_format_context, 0);//vido_st的time_base是1/90000 ,avg_frame_rate是0
if (!video_st) {
fprintf(stderr, "Could not alloc stream");
exit(1);
}
out_codec_context = video_st->codec;
out_codec_context->codec_id = codec_id;
out_codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
/* 设置比特率(码率),每秒传输多少bit */
out_codec_context->bit_rate = 400000;
/* resolution must be a multiple of two */
out_codec_context->width = width;
out_codec_context->height = height;
// 设置 25 帧每秒 ,也就是 fps 为 25
out_codec_context->time_base.den = STREAM_FRAME_RATE;
out_codec_context->time_base.num = 1;
out_codec_context->gop_size = 12; /* //每12帧插入1个I帧,I帧越少,视频越小 */
out_codec_context->pix_fmt = STREAM_PIX_FMT;
out_codec_context->qmin = 10;
out_codec_context->qmax = 51;
/*
最大B帧数,默认是0,它的影响:avcodec_encode_video2 丢失的也就是缓存的帧数(got_packet会为0).这个帧数是固定的并且由最大B帧数决定的.
B帧越多,文件越小
*/
out_codec_context->max_b_frames = 3;
// some formats want stream headers to be separate
if (out_format_context->oformat->flags & AVFMT_GLOBALHEADER)
out_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
return video_st;
}
void open_video(AVCodecContext* codec_context){
/* find the video encoder */
AVCodec* codec = avcodec_find_encoder(codec_context->codec_id);
if (!codec) {
fprintf(stderr, "codec not found\n");
exit(1);
}
AVDictionary *param = NULL;
//H.264
if (codec_context->codec_id == AV_CODEC_ID_H264) {
// 通过--preset的参数调节编码速度和质量的平衡。
av_dict_set(¶m, "preset", "fast", 0);
// 通过--tune的参数值指定片子的类型,是和视觉优化的参数,或有特别的情况。
// zerolatency: 零延迟,用在需要非常低的延迟的情况下,比如电视电话会议的编码
av_dict_set(¶m, "tune", "zerolatency", 0);
}
/* open the codec */
if (avcodec_open2(codec_context, codec, ¶m) < 0) {
fprintf(stderr, "could not open codec\n");
exit(1);
}
}
int encode_video_frame(AVFrame *frame,
AVFormatContext *out_format_context,
AVStream *video_st){
AVCodecContext* out_codec_context = video_st->codec;
int got_packet;
AVPacket enc_pkt;
init_packet(&enc_pkt);
int ret = avcodec_encode_video2(out_codec_context, &enc_pkt,
frame, &got_packet);
if (ret < 0 || !got_packet){ //在flush的时候,如果失败 ,说明丢失帧(缓存帧)已经空了
av_free_packet(&enc_pkt);
av_frame_free(&frame);
return 1;
}
if (enc_pkt.pts == AV_NOPTS_VALUE){
double duration_s = av_q2d(out_codec_context->time_base);//每帧有多少秒(标准时间)
double duration = duration_s / av_q2d(video_st->time_base);//AVStream时间基下的格子数
enc_pkt.pts = framecount*duration;//current_pts-pre_pts=current_duration,根据数学公式an=a1+(n-1)*d可得pts=n*d
enc_pkt.dts = enc_pkt.pts;
printf("pts:%lld\n", enc_pkt.pts);
}
enc_pkt.stream_index = video_st->index;
ret = av_write_frame(out_format_context, &enc_pkt);
av_free_packet(&enc_pkt);
av_frame_free(&frame);
if (ret < 0){
return 1;
}else{
framecount++;
}
return 0;
}
int main(int argc, char **argv){
av_register_all();
FILE* fp = fopen("sintel_640_360.yuv", "rb");
const char* out_file = "output.h264";
AVOutputFormat* fmt = av_guess_format(NULL, out_file, NULL);
if (!fmt) {
fprintf(stderr, "Could not find suitable output format");
exit(1);
}
AVFormatContext* out_format_context = avformat_alloc_context();
out_format_context->oformat = fmt;
AVStream *video_st = add_video_stream(out_format_context, fmt->video_codec);
//这里video_st的time_base是默认值 1/90000
AVCodecContext* out_codec_context = video_st->codec;
open_video(out_codec_context);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&(out_format_context->pb), out_file, AVIO_FLAG_WRITE) < 0) {
fprintf(stderr, "Could not open '%s'\n", out_file);
return 1;
}
}
/* write the stream header, if any */
avformat_write_header(out_format_context, NULL);
uint8_t *buffer = new uint8_t[avpicture_get_size(out_codec_context->pix_fmt, out_codec_context->width, out_codec_context->height)];
while (1){
int ret = fread(buffer, out_codec_context->width * out_codec_context->height * 3 / 2, 1, fp);
if (ret == 0){
break;
}
AVFrame* yuvFrame = av_frame_alloc();
avpicture_fill((AVPicture *)yuvFrame, buffer, out_codec_context->pix_fmt, out_codec_context->width, out_codec_context->height);
encode_video_frame(yuvFrame, out_format_context, video_st);
}
if (out_codec_context->codec->capabilities &CODEC_CAP_DELAY){
while (!encode_video_frame(NULL, out_format_context, video_st)){ ; }
}
av_write_trailer(out_format_context);
if (video_st){
avcodec_close(video_st->codec);
}
if (out_codec_context){
avcodec_close(out_codec_context);
}
if (out_format_context) {
avio_closep(&out_format_context->pb);
avformat_free_context(out_format_context);
}
return 0;
}