yuv420转h264

来源:互联网 发布:淘宝管理软件 编辑:程序博客网 时间:2024/05/18 14:15

注意视频的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;}
原创粉丝点击