ffmpeg4.2.2 yuv编码成h264(修改官方encode_video.c)

纯属学习记录,有问题请评论指点

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>

static int ptsIndex = 0;

void encode(AVFormatContext* outFormatCtx,AVFrame* frame,AVPacket* packet,AVCodecContext* codecCtx,FILE* outFile);

int main(int argc,char *argv[])
{
    int ret = 0;
    AVCodec *codec = NULL;
    AVCodecContext* codecCtx = NULL;
    AVFormatContext* outFormatCtx = NULL;
    AVStream*  outStream = NULL;

    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    codecCtx = avcodec_alloc_context3(codec);
    if (!codecCtx) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    /*  设置码率为400000b/s,大概为390kb/s */
    codecCtx->bit_rate = 400000;
    /* 分辨率必须为偶数 */
    codecCtx->width = 176;
    codecCtx->height = 144;
    /* AVRational表示一个分数,前面是分子,后面是分母 
       time_base为时间基,用来计算时间,例如PTS*timebase,计算显示时间*/
    codecCtx->time_base = (AVRational){1,25};
    /* 帧率,表示一秒多少帧,一般与time_base互为倒数 */
    codecCtx->framerate = (AVRational){25,1};
    /* 设置gop为12,表示两个关键帧的距离 */
    codecCtx->gop_size = 12;
    /* 表示两个非B帧之间允许存在最大数目的B帧 */
    codecCtx->max_b_frames = 0;
    /* Pixel format 像素格式 */
    codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

    /* 特定的文件大小或恒定的比特率,使用较慢的预设获得更好的质量。同样,对于
       恒定质量的编码,您只需选择较慢的预设即可节省比特率 */
    if(codec->id == AV_CODEC_ID_H264)
        av_opt_set(codecCtx->priv_data,"preset","slow",0);

    /* 初始化AVCodecContext,各种分配内存和检查,假如是H264的,编码器就会初始化为libx264 */
    ret = avcodec_open2(codecCtx,codec,NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
        exit(1);
    }


    AVPacket* packet = av_packet_alloc();
    AVFrame* frame = av_frame_alloc();
    frame->format = codecCtx->pix_fmt;
    frame->height = codecCtx->height;
    frame->width = codecCtx->width;

    /*  一开始第二个参数align(对齐)也和官方demo一样设为32,结果输出的画面是花屏,
    大家可以参考下这篇博客:https://blog.csdn.net/grafx/article/details/29185147 */
    ret = av_frame_get_buffer(frame,1);
    if( ret != 0)
    {
        printf("frame get buffer fail\n");
        goto end;
    }

    const char* inputFileName = "source/akiyo_qcif.yuv";
    const char* outFileName = "source/akiyo_qcif.h264";

    /* 分配内存,根据格式名(第三个参数)或者文件名(第四个参数)获取输出格式 */
    avformat_alloc_output_context2(&outFormatCtx,NULL,NULL,outFileName);
    if(outFormatCtx == NULL)
    {
        fprintf(stderr," alloc output contex fail,exit!!\n");
        exit(1);
    }

    outStream = avformat_new_stream(outFormatCtx,codec);

#if 0  /* 老版函数,试过也是可以的,不过会打印警告 */
    if (avcodec_copy_context(outStream->codec, codecCtx) < 0) {
        printf("Failed to copy context from input to output stream codec context\n");
        return -1;
    }
    outStream->codec->codec_tag = 0;
    if (outFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
        outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
    if(avcodec_parameters_from_context(outStream->codecpar,codecCtx) < 0)
    {
        fprintf(stderr,"copy parameters fail!\n");
        goto end;
    }
#endif
    /* AVFMT_NOFILE跟是否是一个真实的文件无关 */
    if(!(outFormatCtx->oformat->flags & AVFMT_NOFILE))
    {
        if(avio_open2(&outFormatCtx->pb,"source/akiyo_qcif.h264",AVIO_FLAG_WRITE,NULL,NULL))
        {
            printf("avio open fail\n");
            goto end;
        }
    }

    FILE* inputFile = fopen(inputFileName,"rb");
    FILE* outFile = fopen(outFileName,"wb+");


    /*  针对不同的编码格式,写不同的文件头*/
    if(avformat_write_header(outFormatCtx,NULL) < 0)
    {
         fprintf(stderr, "Error occurred when opening output file: %s\n",av_err2str(ret));
         goto end;
    }

    while (!feof(inputFile))
    {
        /* 确保frame是可写的,例如编码器要缓存此帧,下面函数就会分配新的缓冲区,并复制参数 */
        ret = av_frame_make_writable(frame);
        if(ret < 0)
        {
            printf("%s %d ret:%d\n",__FUNCTION__,__LINE__,ret);        
            goto end;
        }

        fread(frame->data[0],1,frame->width*frame->height,inputFile); //Y数据
        fread(frame->data[1],1,frame->width*frame->height/4,inputFile); //U数据
        fread(frame->data[2],1,frame->width*frame->height/4,inputFile); //V数据

        encode(outFormatCtx,frame,packet,codecCtx,outFile);
    }

    /* 发送一个空帧,可以防止编码器中数据未处理完 */
    encode(outFormatCtx,NULL,packet,codecCtx,outFile);

     /*  针对不同的编码格式,写不同的文件尾*/   
    if(av_write_trailer(outFormatCtx) != 0)
        printf("write file trail fail\n");

end:
    avcodec_free_context(&codecCtx);
    avformat_free_context(outFormatCtx);
    if(inputFile)
        fclose(inputFile);
    if(outFile)
        fclose(outFile);
    av_frame_free(&frame);
    av_packet_free(&packet);

    return 0;
}


void encode(AVFormatContext* outFormatCtx,AVFrame* frame,AVPacket* packet,AVCodecContext* codecCtx,FILE* outFile)
{
    int ret = 0;
    if(frame)
        frame->pts = ptsIndex++;

    ret = avcodec_send_frame(codecCtx,frame);
    if (ret < 0) 
    {
        fprintf(stderr, "Error sending the frame to the encoder\n");
        exit(1);
    }

    while(ret >= 0)
    {
        ret = avcodec_receive_packet(codecCtx,packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }

        packet->stream_index = 0;
#if 1   /* 两种写数据的方式都是可以的 */
        if(av_interleaved_write_frame(outFormatCtx,packet) != 0)
        {
            printf("write frame fail\n");
            continue;
        }
#else
        fwrite(packet->data,1,packet->size,outFile);
#endif
        av_packet_unref(packet);
    }
}

在MediaPipe中,你可以使用FFmpeg进行YUV编码为H.264,并将其输出到文件或其他目标。以下是一个使用C++语言在MediaPipe中将YUV编码为H.264的示例代码: ```cpp #include <mediapipe/framework/calculator_framework.h> #include <mediapipe/framework/formats/image_frame.h> #include <mediapipe/framework/formats/image_frame_opencv.h> void EncodeYuvToH264() { // 初始化MediaPipe图 mediapipe::CalculatorGraphConfig config; mediapipe::CalculatorGraph graph; MP_RETURN_IF_ERROR(graph.Initialize(config)); // 创建输入流 mediapipe::CalculatorGraph::InputStreamPoller poller = graph.AddInputStreamPoller("input_yuv"); // 设置YUV图像参数 mediapipe::Packet video_header_packet; video_header_packet.Set<std::unique_ptr<mediapipe::ImageFrame>>( mediapipe::MakePacket<mediapipe::ImageFormat::Format>( mediapipe::ImageFormat::YUV420P) .At(mediapipe::Timestamp(0))); MP_RETURN_IF_ERROR( graph.AddPacketToInputStream("input_yuv_header", video_header_packet)); // 设置输出H.264文件路径 std::string output_file_path = "output.h264"; // 创建输出流 std::ofstream output_file(output_file_path, std::ios::binary); if (!output_file) { return; } // 运行MediaPipe图 ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller, graph.AddOutputStreamPoller("output_h264")); MP_RETURN_IF_ERROR(graph.StartRun({})); mediapipe::Packet packet; while (poller.Next(&packet)) { // 获取H.264数据 std::string h264_data = packet.Get<std::string>(); // 写入H.264数据到输出文件 output_file.write(h264_data.data(), h264_data.size()); } // 结束MediaPipe图 MP_RETURN_IF_ERROR(graph.CloseInputStream("input_yuv")); MP_RETURN_IF_ERROR(graph.WaitUntilDone()); // 关闭输出文件 output_file.close(); } ``` 在该示例代码中,你需要将YUV图像数据逐帧传递给输入流`input_yuv`。MediaPipe图会将YUV图像数据编码为H.264,并将编码后的数据通过输出流`output_h264`输出。你可以将输出的H.264数据写入到指定的输出文件中。 请注意,示例代码中的输入流和输出流名称以及数据类型可能需要根据你的MediaPipe图的配置进行适当的调整。此外,你还需要根据具体需求设置YUV图像参数和输出文件路径。 希望这可以帮助到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值