FFmpeg YUV编码为H264

使用FFmpeg库把YUV420P文件编码为H264文件,FFmpeg版本为4.4.2-0。

需要yuv测试文件的,可以从我上传的MP4文件中用ffmpeg提取,命令如下:

bash 复制代码
ffmpeg -i <输入MP4文件名> -pix_fmt yuv420p <输出YUV文件名>

例如:
ffmpeg -i input.mp4 -pix_fmt yuv420p output.yuv

代码如下:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/mem.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>

int main(int argc, char *argv[])
{
    int ret = -1;
    int frame_index = 0;
    const char *input_file = argv[1];
    const char *output_file = argv[2];
    FILE *input_yuv = NULL;
    AVFormatContext *format_context = NULL;
    AVCodecContext *codec_context = NULL;
    AVStream *stream = NULL;
    AVCodec *codec = NULL;
    AVFrame *frame = NULL;
    AVPacket *packet = NULL;
    const AVOutputFormat *ofmt = NULL;

    int in_width = 1280;                                // 输入YUV文件的宽度
    int in_height = 720;                                // 输入YUV文件的高度
    enum AVPixelFormat in_pix_fmt = AV_PIX_FMT_YUV420P; // 输入YUV文件的像素格式
    int frame_rate = 24;                                // 输出视频帧率

    if (argc < 3)
    {
        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
        return -1;
    }

    input_yuv = fopen(input_file, "rb");
    if (!input_yuv)
    {
        fprintf(stderr, "open input file failed\n");
        goto end;
    }

    // 分配输出格式上下文
    avformat_alloc_output_context2(&format_context, NULL, NULL, output_file);
    if (!format_context)
    {
        fprintf(stderr, "avformat_alloc_output_context2 failed\n");
        goto end;
    }
    ofmt = format_context->oformat;

    // 查找编码器
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        fprintf(stderr, "Codec not found\n");
        goto end;
    }
    printf("codec name: %s\n", codec->name);

    // 创建新的视频流
    stream = avformat_new_stream(format_context, NULL);
    if (!stream)
    {
        fprintf(stderr, "avformat_new_stream failed\n");
        goto end;
    }

    // 分配编码器上下文
    codec_context = avcodec_alloc_context3(codec);
    if (!codec_context)
    {
        fprintf(stderr, "avcodec_alloc_context3 failed\n");
        goto end;
    }

    /* 设置编码器参数 */
    codec_context->codec_id = AV_CODEC_ID_H264;
    codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
    codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
    codec_context->width = in_width;
    codec_context->height = in_height;
    codec_context->time_base = (AVRational){1, frame_rate};     // 设置时间基
    codec_context->framerate = (AVRational){frame_rate, 1};     // 设置帧率
    codec_context->bit_rate = 1456 * 1000;                      // 设置比特率
    codec_context->gop_size = frame_rate;                       // 设置GOP大小
    codec_context->max_b_frames = 1;                            // 设置最大B帧数,不需要B帧时设置为0
    av_opt_set(codec_context->priv_data, "profile", "main", 0); // 设置h264画质级别

    // 打开编码器
    if (avcodec_open2(codec_context, codec, NULL) < 0)
    {
        fprintf(stderr, "avcodec_open2 failed\n");
        goto end;
    }

    // 将编码器参数复制到流
    int result = avcodec_parameters_from_context(stream->codecpar, codec_context);
    if (result < 0)
    {
        fprintf(stderr, "avcodec_parameters_from_context failed\n");
        goto end;
    }

    // 打开输出文件
    if (!(ofmt->flags & AVFMT_NOFILE)) // 检查输出格式是否需要文件存储
    {
        result = avio_open(&format_context->pb, output_file, AVIO_FLAG_WRITE);
        if (result < 0)
        {
            fprintf(stderr, "open output file failed\n");
            goto end;
        }
    }

    // 写文件头
    result = avformat_write_header(format_context, NULL);
    if (result < 0)
    {
        fprintf(stderr, "avformat_write_header failed\n");
        goto end;
    }

    frame = av_frame_alloc();
    packet = av_packet_alloc();
    if (!frame || !packet)
    {
        fprintf(stderr, "allocate frame or packet failed\n");
        goto end;
    }

    // 设置帧参数
    frame->format = codec_context->pix_fmt;
    frame->width = codec_context->width;
    frame->height = codec_context->height;

    // 分配帧数据缓冲区
    result = av_frame_get_buffer(frame, 0);
    if (result < 0)
    {
        fprintf(stderr, "av_frame_get_buffer failed\n");
        goto end;
    }

    // 计算输入缓冲区大小
    int input_buffer_size = av_image_get_buffer_size(in_pix_fmt, in_width, in_height, 1);
    uint8_t *input_buffer = (uint8_t *)av_malloc(input_buffer_size);
    if (!input_buffer)
    {
        fprintf(stderr, "av_malloc failed\n");
        goto end;
    }

    // 编码循环
    while (1)
    {
        // 读取YUV数据到缓冲区
        result = fread(input_buffer, 1, input_buffer_size, input_yuv);
        if (result <= 0)
        {
            break;
        }

        // 填充帧数据
        av_image_fill_arrays(frame->data, frame->linesize, input_buffer, in_pix_fmt, in_width, in_height, 1);

        frame->pts = frame_index;
        frame_index++;

        // 发送帧到编码器
        result = avcodec_send_frame(codec_context, frame);
        if (result < 0)
        {
            fprintf(stderr, "avcodec_send_frame error (errmsg '%s')\n", av_err2str(result));
            goto end;
        }

        // 接收编码后的数据包
        while (result >= 0)
        {
            result = avcodec_receive_packet(codec_context, packet);
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                break;
            }
            else if (result < 0)
            {
                fprintf(stderr, "avcodec_receive_packet error (errmsg '%s')\n", av_err2str(result));
                goto end;
            }

            packet->stream_index = stream->index;
            // 将时间戳从编码器时间基转换到流时间基
            av_packet_rescale_ts(packet, codec_context->time_base, stream->time_base);
            // 写数据包到输出文件
            result = av_interleaved_write_frame(format_context, packet);
            if (result < 0)
            {
                fprintf(stderr, "av_interleaved_write_frame failed\n");
                av_packet_unref(packet);
                goto end;
            }

            av_packet_unref(packet);
        }
    }
    av_free(input_buffer);

    // 发送NULL帧到编码器,刷新编码器内部缓冲区
    result = avcodec_send_frame(codec_context, NULL);
    while (result >= 0)
    {
        result = avcodec_receive_packet(codec_context, packet);
        if (result == AVERROR_EOF)
        {
            break;
        }
        else if (result < 0)
        {
            fprintf(stderr, "avcodec_receive_packet error (errmsg '%s')\n", av_err2str(result));
            goto end;
        }

        packet->stream_index = stream->index;
        av_packet_rescale_ts(packet, codec_context->time_base, stream->time_base);
        result = av_interleaved_write_frame(format_context, packet);
        if (result < 0)
        {
            fprintf(stderr, "av_interleaved_write_frame failed\n");
            av_packet_unref(packet);
            goto end;
        }

        av_packet_unref(packet);
    }

    // 写文件尾
    av_write_trailer(format_context);
    ret = 0;

end:
    if (frame)
        av_frame_free(&frame);
    if (packet)
        av_packet_free(&packet);
    if (codec_context)
        avcodec_free_context(&codec_context);
    if (ofmt && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(format_context->pb);
    if (format_context)
        avformat_free_context(format_context);
    if (input_yuv)
        fclose(input_yuv);

    return ret;
}
相关推荐
runing_an_min2 小时前
ffmpeg视频滤镜:提取缩略图-framestep
ffmpeg·音视频·framestep
小曲曲3 小时前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
EasyNTS5 小时前
H.264/H.265播放器EasyPlayer.js网页全终端安防视频流媒体播放器关于iOS不能系统全屏
h.265·h.264
EasyNTS5 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
安静读书5 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
佑华硬盘拷贝机5 小时前
音频档案批量拷贝:专业SD拷贝机解决方案
音视频
EasyNVR5 小时前
NVR管理平台EasyNVR多个NVR同时管理:全方位安防监控视频融合云平台方案
安全·音视频·监控·视频监控
xcLeigh13 小时前
HTML5超酷响应式视频背景动画特效(六种风格,附源码)
前端·音视频·html5
韩曙亮14 小时前
【FFmpeg】FFmpeg 内存结构 ③ ( AVPacket 函数简介 | av_packet_ref 函数 | av_packet_clone 函数 )
ffmpeg·音视频·avpacket·av_packet_clone·av_packet_ref·ffmpeg内存结构
9527华安18 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网