时间戳基本概念
时间戳:计算的单位不是秒,时间戳的单位采用的是采样频率的倒数,这样做的目的就是为了时间戳单位更精准。比如说一个音频的采样频率为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)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓