ffmpeg RGB raw数据H264编码写mp4

本文详细介绍了如何在使用FFmpeg进行H264视频编码时,正确设置时间戳(PTS)和适应不同帧率,特别强调了采样频率和时间戳增量的概念。通过实例展示了如何设置时间戳基线和调整GOP大小以实现低延迟编码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间戳基本概念

时间戳:计算的单位不是秒,时间戳的单位采用的是采样频率的倒数,这样做的目的就是为了时间戳单位更精准。比如说一个音频的采样频率为8000Hz,那么我们可以把时间戳单位设为1 / 8000。

时间戳增量:相邻两帧之间的时间差(以时间戳单位为基准)。ORTP库中根据负载类型直接给定了时间戳的单位(音频负载1/8000,视频负载1/90000)。如果采样频率为90000Hz,则由上面讨论可知,时间戳单位为1/90000,我们就假设1秒钟被划分了90000个时间块,那么,如果每秒发送25帧,那么,每一个帧的发送占多少个时间块呢?当然是 90000/25 = 3600。因此,我们根据定义“时间戳增量是发送第二个RTP包相距发送第一个RTP包时的时间间隔”,故时间戳增量应该为3600。如果fps=50,那么pts增量就是1800。本人此处踩坑,设置fps=50,h264编码始终是25.

采样频率: 也叫时钟频率,即每秒钟抽取样本的次数,例如音频的采样率8000Hz,48k Hz等。

code:

extern "C"
{
   #include <libavformat/avformat.h>
   #include <libswscale/swscale.h>
}
 
#include <iostream>
using namespace std;
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")
 
int main()
{
    char infile[] = "out.rgb";
    char outfile[] = "rgb.mp4";
 
    //新版ffmpeg 不用注册
 
    FILE *fp = fopen(infile,"rb");
    if (!fp)
    {
        cout << infile << " open failed!" << endl;
        getchar();
        return -1;
    }
    int width = 848;
    int height = 480;
    int fps = 25;
 
    //1 创建编码器 
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
        getchar();
        return -1;
    }
    //编码器上下文
    AVCodecContext *c = avcodec_alloc_context3(codec);
    if (!c)
    {
        cout << " avcodec_alloc_context3  failed!" << endl;
        getchar();
        return -1;
    }
    //设置视频编码相关参数
    //比特率
    c->bit_rate = 400000000;
    c->width = width;
    c->height = height;
    //把1秒钟分成fps个单位 
    c->time_base = { 1,fps };
    c->framerate = { fps,1 };
    c->gop_size = 50;
    c->max_b_frames = 0;
    c->pix_fmt = AV_PIX_FMT_YUV420P;
    c->codec_id = AV_CODEC_ID_H264;
    c->thread_count = 8;
 
    //全局的编码信息
    c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  //h264编码器
        AVDictionary* param = 0;
        if (avcodec_context->codec_id == AV_CODEC_ID_H264)
        {
            av_dict_set(&param, "preset", "fast", 0);
            av_dict_set(&param, "tune", "zerolatency", 0);    
        }


    //打开编码器
    int ret = avcodec_open2(c,codec,NULL);
    if (ret < 0)
    {
        cout << " avcodec_open2  failed!" << endl;
        getchar();
        return -1;
    }
    cout << "avcodec_open2 success!" << endl;
 
    //2 create out context
    AVFormatContext *oc = NULL;
    avformat_alloc_output_context2(&oc, 0, 0, outfile);
 
    //3 add video stream
    AVStream *st = avformat_new_stream(oc,NULL);
    st->id = 0;
    st->codecpar->codec_tag = 0;
    avcodec_parameters_from_context(st->codecpar,c);
 
    cout << "===============================================" << endl;
    av_dump_format(oc, 0, outfile, 1);
    cout << "===============================================" << endl;
 
    //4 rgb to yuv
    //改变视频尺寸
    SwsContext *ctx= NULL;
    ctx = sws_getCachedContext(ctx,
        width,height,AV_PIX_FMT_BGRA,
        width,height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,
        NULL,NULL,NULL);
    //输入空间
    unsigned char *rgb = new unsigned char[width*height*4];
 
 
    //输出空间
    AVFrame *yuv = av_frame_alloc();
    yuv->format = AV_PIX_FMT_YUV420P;
    yuv->width = width;
    yuv->height = height;
    //分配空间
    ret = av_frame_get_buffer(yuv,32);
 
    if (ret < 0)
    {
        cout << " av_frame_get_buffer  failed!" << endl;
        getchar();
        return -1;
    }
 
    //5 write mp4 head
    ret = avio_open(&oc->pb,outfile,AVIO_FLAG_WRITE);
    if (ret < 0)
    {
        cout << " avio_open  failed!" << endl;
        getchar();
        return -1;
    }
    ret = avformat_write_header(oc, NULL);
    if (ret < 0)
    {
        cout << " avformat_write_header  failed!" << endl;
        getchar();
        return -1;
    }
    int p = 0;
    for (;;)
    {
        //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
        //ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
        //size -- 这是要读取的每个元素的大小,以字节为单位。
        //nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
        //stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
        int len = fread(rgb,1,width*height*4,fp);
        if (len<=0)
        {
            break;
        }
        uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
        indata[0] = rgb;
        int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
        inlinesize[0] = width * 4;
 
        int h = sws_scale(ctx,indata,inlinesize,0,height,yuv->data,yuv->linesize);
        if (h<=0)
        {
            break;
        }
        //6 encode frame
        yuv->pts = p;
        p = p + 3600;
        //发送到编码器
        ret = avcodec_send_frame(c,yuv);
        if (ret != 0)
        {
            continue;
        }
        AVPacket pkt;
        av_init_packet(&pkt);
        //接收编码结果
        ret = avcodec_receive_packet(c,&pkt);
        if (ret != 0)
            continue;
        //将编码后的帧写入文件
        av_interleaved_write_frame(oc,&pkt);
        cout << "<" << pkt.size << ">";
    }
 
    //写文件尾
    av_write_trailer(oc);
 
    //关闭视频输出IO
    avio_close(oc->pb);
 
    //清理封装输出上下文
    avformat_free_context(oc);
 
    //关闭编码器
    avcodec_close(c);
 
    //清理编码器上下文
    avcodec_free_context(&c);
 
    //清理视频重采样上下文
    sws_freeContext(ctx);
    cout << "======================end=========================" << endl;
    delete rgb;
    getchar();
    return 0;
}

你可以使用 FFmpeg 的 C++ 接口来实现 RGB 转 H.264 的多线程编码。下面是一个简单的示例代码,它将一系列 RGB 图像转换为 H.264 视频: ```cpp #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <vector> #include <sstream> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> } using namespace std; // 定义互斥量和条件变量,用于多线程同步 mutex mtx; condition_variable cv; // 定义全局变量,用于存储编码后的视频数据 vector<uint8_t> video_buffer; // 定义编码器上下文和帧对象 AVCodecContext *encoder_ctx = NULL; AVFrame *frame = NULL; // 定义编码器线程 void encode_thread() { AVPacket pkt; int ret; while (true) { unique_lock<mutex> lock(mtx); cv.wait(lock, [] { return !video_buffer.empty(); }); // 从全局变量中取出一帧 RGB 图像 vector<uint8_t> rgb_image = video_buffer.back(); video_buffer.pop_back(); // 把 RGB 图像转换为编码器所需的格式 av_image_fill_arrays(frame->data, frame->linesize, rgb_image.data(), encoder_ctx->pix_fmt, encoder_ctx->width, encoder_ctx->height, 1); // 发送图像数据编码器 ret = avcodec_send_frame(encoder_ctx, frame); if (ret < 0) { cerr << "Error sending a frame to the encoder: " << av_err2str(ret) << endl; continue; } // 接收编码后的数据包 while (ret >= 0) { ret = avcodec_receive_packet(encoder_ctx, &amp;pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { cerr << "Error encoding a frame: " << av_err2str(ret) << endl; continue; } // 把编码后的数据包存储到全局变量中 video_buffer.insert(video_buffer.begin(), pkt.data, pkt.data + pkt.size); av_packet_unref(&amp;pkt); } } } int main() { // 初始化 FFmpeg 库 av_register_all(); avformat_network_init(); // 打开编码器并设置编码参数 AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!codec) { cerr << "Codec not found" << endl; return -1; } encoder_ctx = avcodec_alloc_context3(codec); if (!encoder_ctx) { cerr << "Could not allocate video codec context" << endl; return -1; } encoder_ctx->bit_rate = 400000; encoder_ctx->width = 640; encoder_ctx->height = 480; encoder_ctx->time_base = {1, 25}; encoder_ctx->gop_size = 10; encoder_ctx->max_b_frames = 1; encoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P; if (avcodec_open2(encoder_ctx, codec, NULL) < 0) { cerr << "Could not open codec" << endl; return -1; } // 创建帧对象并分配内存 frame = av_frame_alloc(); if (!frame) { cerr << "Could not allocate video frame" << endl; return -1; } frame->format = encoder_ctx->pix_fmt; frame->width = encoder_ctx->width; frame->height = encoder_ctx->height; if (av_frame_get_buffer(frame, 0) < 0) { cerr << "Could not allocate the video frame data" << endl; return -1; } // 创建编码器线程 thread t(encode_thread); // 读入 RGB 图像并发送到编码器线程进行编码 for (int i = 1; i <= 100; i++) { stringstream ss; ss << "rgb" << i << ".raw"; FILE *f = fopen(ss.str().c_str(), "rb"); if (!f) { cerr << "Could not open file: " << ss.str() << endl; return -1; } int size = encoder_ctx->width * encoder_ctx->height * 3; vector<uint8_t> rgb_image(size); fread(rgb_image.data(), 1, size, f); fclose(f); { lock_guard<mutex> lock(mtx); video_buffer.push_back(rgb_image); } cv.notify_one(); } // 等待编码器线程退出 t.join(); // 释放资源 avcodec_free_context(&amp;encoder_ctx); av_frame_free(&amp;frame); return 0; } ``` 在这个示例代码中,我们使用了一个互斥量和一个条件变量来实现多线程同步。编码器线程等待条件变量的通知,然后从全局变量中取出一帧 RGB 图像,把它转换为编码器所需的格式,并发送到编码器进行编码编码器线程把编码后的数据包存储到全局变量中,然后再次等待条件变量的通知。主线程读入一系列 RGB 图像,并把它们存储到全局变量中,然后通过条件变量通知编码器线程进行编码。最后,主线程等待编码器线程退出,并释放资源。 注意,这个示例代码中只使用了一个编码器线程。如果你想进一步提高编码速度,可以使用多个编码器线程,并且把 RGB 图像分割成多块,让每个编码器线程处理一块图像。你也可以使用 OpenMP 或其他线程库来实现多线程编码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步基

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值