yuv420转h264

注意视频的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(&param, "preset", "fast", 0);
        // 通过--tune的参数值指定片子的类型,是和视觉优化的参数,或有特别的情况。
        // zerolatency: 零延迟,用在需要非常低的延迟的情况下,比如电视电话会议的编码
        av_dict_set(&param, "tune", "zerolatency", 0);

    }
    /* open the codec */
    if (avcodec_open2(codec_context, codec, &param) < 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;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值