ffmpeg RGB raw数据H264编码写mp4

本文介绍了时间戳在音视频编码中的基本概念,包括时间戳单位、时间戳增量和采样频率。时间戳单位基于采样频率的倒数来确保精度,而时间戳增量则表示相邻帧之间的时间差。在实际编码过程中,如H264编码,正确设置fps和时间戳增量对于保证视频质量至关重要。通过示例代码展示了如何使用FFmpeg进行RGB数据到H264编码并写入MP4文件的过程。
摘要由CSDN通过智能技术生成

时间戳基本概念

时间戳:计算的单位不是秒,时间戳的单位采用的是采样频率的倒数,这样做的目的就是为了时间戳单位更精准。比如说一个音频的采样频率为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:

时间戳基本概念
时间戳:计算的单位不是秒,时间戳的单位采用的是采样频率的倒数,这样做的目的就是为了时间戳单位更精准。比如说一个音频的采样频率为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(¶m, "preset", "fast", 0);
            av_dict_set(¶m, "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 RGB raw数据H264编码写mp4 - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓ 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值