源码github
//ffmpeg -i sound_in_sync_test.mp4 -pix_fmt yuv420p 720x576_yuv420p.yuv
//ffmpeg -i sound_in_sync_test.mp4 -vn -ar 44100 -ac 2 -f s16le 44100_2_s16le.pcm
1 in_pcm 2 In_yuv 3 out_mp4
if(argc != 4) {
printf("usage -> exe in.yuv in.pcm out.mp4");
return -1;
}
// 1. 打开yuv pcm文件
char *in_yuv_name = argv[1];
char *in_pcm_name = argv[2];
char *out_mp4_name = argv[3];
FILE *in_yuv_fd = NULL;
FILE *in_pcm_fd = NULL;
//1. 打开测试文件
// 打开YUV文件
in_yuv_fd = fopen(in_yuv_name, "rb");
if( !in_yuv_fd )
{
printf("Failed to open %s file\n", in_yuv_name);
return -1;
}
// 打开PCM文件
in_pcm_fd = fopen(in_pcm_name, "rb");
if( !in_pcm_fd )
{
printf("Failed to open %s file\n", in_pcm_name);
return -1;
}
初始化编码器
初始化视频编码器
int ret = 0;
// 2. 初始化编码器,包括视频、音频编码器, 分配yuv、pcm的帧buffer
// 2.1 初始化video
// 初始化编码器
int yuv_width = YUV_WIDTH;
int yuv_height = YUV_HEIGHT;
int yuv_fps = YUV_FPS;
int video_bit_rate = VIDEO_BIT_RATE;
VideoEncoder video_encoder;
ret = video_encoder.InitH264(yuv_width, yuv_height, yuv_fps, video_bit_rate);
if(ret < 0)
{
printf("video_encoder.InitH264 failed\n");
return -1;
}
// 分配yuv buf
int y_frame_size = yuv_width * yuv_height;
int u_frame_size = yuv_width * yuv_height /4;
int v_frame_size = yuv_width * yuv_height /4;
int yuv_frame_size = y_frame_size + u_frame_size + v_frame_size;
uint8_t *yuv_frame_buf = (uint8_t *)malloc(yuv_frame_size);
if(!yuv_frame_buf)
{
printf("malloc(yuv_frame_size)\n");
return -1;
}
初始化音频编码器
// 2.2 初始化audio
// 初始化音频编码器
int pcm_channels= PCM_CHANNELS;
int pcm_sample_rate = PCM_SAMPLE_RATE;
int pcm_sample_format = PCM_SAMPLE_FORMAT;
int audio_bit_rate = AUDIO_BIT_RATE;
AudioEncoder audio_encoder;
ret = audio_encoder.InitAAC(pcm_channels, pcm_sample_rate, audio_bit_rate);
if(ret < 0)
{
printf("audio_encoder.InitAAC failed\n");
return -1;
}
// 分配pcm buf
// pcm_frame_size = 单个采样点占用的字节 * 通道数量 * 每个通道有多少给采用点
int pcm_frame_size = av_get_bytes_per_sample((AVSampleFormat)pcm_sample_format)
*pcm_channels * audio_encoder.GetFrameSize();
if(pcm_frame_size <= 0) {
printf("pcm_frame_size <= 0\n");
return -1;
}
uint8_t *pcm_frame_buf = (uint8_t *)malloc(pcm_frame_size);
if(!pcm_frame_buf)
{
printf("malloc(pcm_frame_size)\n");
return -1;
}
初始化重采样器
// 初始化重采样
AudioResampler audio_resampler;
ret = audio_resampler.InitFromS16ToFLTP(pcm_channels, pcm_sample_rate,
audio_encoder.GetChannels(), audio_encoder.GetSampleRate());
if(ret < 0)
{
printf("audio_resampler.InitFromS16ToFLTP failed\n");
return -1;
}
Muxer初始化和创建新流
// 3. mp4初始化 包括新建流,open io, send header
Muxer mp4_muxer;
ret = mp4_muxer.Init(out_mp4_name);
if(ret < 0)
{
printf("mp4_muxer.Init failed\n");
return -1;
}
ret = mp4_muxer.AddStream(video_encoder.GetCodecContext());
if(ret < 0)
{
printf("mp4_muxer.AddStream video failed\n");
return -1;
}
ret = mp4_muxer.AddStream(audio_encoder.GetCodecContext());
if(ret < 0)
{
printf("mp4_muxer.AddStream audio failed\n");
return -1;
}
ret = mp4_muxer.Open();
if(ret < 0)
{
printf("mp4_muxer.Open failed\n");
return -1;
}
ret = mp4_muxer.SendHeader();
if(ret < 0)
{
printf("mp4_muxer.SendHeader failed\n");
return -1;
}
编码
// 4. 在while循环读取yuv、pcm进行编码然后发送给MP4 muxer 以及 PTS 计算
// 4.1 时间戳相关
int64_t audio_time_base = AUDIO_TIME_BASE;
int64_t video_time_base = VIDEO_TIME_BASE;
double audio_pts = 0;
double video_pts = 0;
double audio_frame_duration = 1.0 * audio_encoder.GetFrameSize()/pcm_sample_rate*audio_time_base;
double video_frame_duration = 1.0/yuv_fps * video_time_base;
int audio_finish = 0; // 两者都为0的时候才结束while循环
int video_finish = 0;
size_t read_len = 0;
AVPacket *packet = NULL;
std::vector<AVPacket*> packets;
int audio_index = mp4_muxer.GetAudioStreamIndex();
int video_index = mp4_muxer.GetVideoStreamIndex();
while (1) {
if(audio_finish && video_finish) {
break;
}
printf("apts:%0.0lf vpts:%0.0lf\n", audio_pts/1000, video_pts/1000);
if((video_finish != 1 && audio_pts > video_pts) // audio和vidoe都还有数据,优先audio(audio_pts > video_pts)
|| (video_finish != 1 && audio_finish == 1)) {
// 读取 从 yuv 文件中读取一帧文件
read_len = fread(yuv_frame_buf, 1, yuv_frame_size, in_yuv_fd);
if(read_len < yuv_frame_size) {
video_finish = 1;
printf("fread yuv_frame_buf finish\n");
}
if(video_finish != 1) {
// packet = video_encoder.Encode(yuv_frame_buf, yuv_frame_size, video_index,
// video_pts, video_time_base);
// 也是在这里绑定了 packet 和 新创建的流
ret = video_encoder.Encode(yuv_frame_buf, yuv_frame_size,
video_index, video_pts, video_time_base,
packets);
}else {
// packet = video_encoder.Encode(NULL, 0, video_index,
// video_pts, video_time_base);
printf("flush video encoder\n");
//传入空就是冲刷编码器
ret = video_encoder.Encode(NULL, 0,
video_index, video_pts, video_time_base,
packets);
}
video_pts += video_frame_duration; // 叠加pts
if(ret >= 0) {
for(int i = 0; i < packets.size(); i++) {
ret = mp4_muxer.SendPacket(packets[i]);
printf("123");
}
}
packets.clear();
} else if(audio_finish != 1) {
read_len = fread(pcm_frame_buf, 1, pcm_frame_size, in_pcm_fd);
if(read_len < pcm_frame_size) {
audio_finish = 1;
printf("fread pcm_frame_buf finish\n");
}
if(audio_finish != 1) {
AVFrame *fltp_frame = AllocFltpPcmFrame(pcm_channels, audio_encoder.GetFrameSize());
ret = audio_resampler.ResampleFromS16ToFLTP(pcm_frame_buf, fltp_frame);
if(ret < 0)
printf("ResampleFromS16ToFLTP error\n");
// packet = audio_encoder.Encode(fltp_frame, audio_index,
// audio_pts, audio_time_base);
ret = audio_encoder.Encode(fltp_frame,audio_index,audio_pts,audio_time_base,packets);
FreePcmFrame(fltp_frame);
}else {
printf("flush audio encoder\n");
// packet = audio_encoder.Encode(NULL,video_index,
// audio_pts, audio_time_base);
ret = audio_encoder.Encode(NULL,audio_index,audio_pts,audio_time_base,packets);
}
audio_pts += audio_frame_duration; // 叠加pts
if(ret >= 0) {
for(int i = 0; i < packets.size(); i++) {
ret = mp4_muxer.SendPacket(packets[i]);
}
}
packets.clear();
}
}
ret = mp4_muxer.SendTrailer();
if(ret < 0)
{
printf("mp4_muxer.SendTrailer failed\n");
}
printf("write mp4 finish\n");
if(yuv_frame_buf)
free(yuv_frame_buf);
if(pcm_frame_buf)
free(pcm_frame_buf);
if(in_yuv_fd)
fclose(in_yuv_fd);
if(in_pcm_fd)
fclose(in_pcm_fd);
return 0;
}
全部代码
//
// Created by zyc on 2022/4/8.
//
#include "Muxer.h"
Muxer::Muxer()
{
}
Muxer::~Muxer()
{
}
int Muxer::Init(const char *url)
{
int ret = avformat_alloc_output_context2(&fmt_ctx_, NULL, NULL,url);
if(ret < 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avformat_alloc_output_context2 failed:%s\n", errbuf);
return -1;
}
url_ = url;
return 0;
}
void Muxer::DeInit()
{
if(fmt_ctx_) {
avformat_close_input(&fmt_ctx_);
}
url_ = "";
aud_codec_ctx_ = NULL;
aud_stream_ = NULL;
audio_index_ = -1;
vid_codec_ctx_ = NULL;
vid_stream_ = NULL;
video_index_ = -1;
}
int Muxer::AddStream(AVCodecContext *codec_ctx)
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
if(!codec_ctx) {
printf("codec ctx is NULL\n");
return -1;
}
AVStream *st = avformat_new_stream(fmt_ctx_, NULL);
if(!st) {
printf("avformat_new_stream failed\n");
return -1;
}
// st->codecpar->codec_tag = 0;
// 从编码器上下文复制
avcodec_parameters_from_context(st->codecpar, codec_ctx);
av_dump_format(fmt_ctx_, 0, url_.c_str(), 1);
// 判断当前的是视频流还是音频流
if(codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
aud_codec_ctx_ = codec_ctx;
aud_stream_ = st;
audio_index_ = st->index;
} else if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
vid_codec_ctx_ = codec_ctx;
vid_stream_ = st;
video_index_ = st->index;
}
return 0;
}
int Muxer::SendHeader()
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
int ret = avformat_write_header(fmt_ctx_, NULL);
if(ret < 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avformat_write_header failed:%s\n", errbuf);
return -1;
}
return 0;
}
int Muxer::SendPacket(AVPacket *packet)
{
int stream_index = packet->stream_index;
printf("index:%d, pts:%lld\n", stream_index, packet->pts);
if(!packet || packet->size <= 0 || !packet->data) {
printf("packet is null\n");
if(packet)
av_packet_free(&packet);
return -1;
}
AVRational src_time_base; // 编码后的包
AVRational dst_time_base; // mp4输出文件对应流的time_base
if(vid_stream_ && vid_codec_ctx_ && stream_index == video_index_) {
src_time_base = vid_codec_ctx_->time_base;
dst_time_base = vid_stream_->time_base;
} else if(aud_stream_ && aud_codec_ctx_ && stream_index == audio_index_) {
src_time_base = aud_codec_ctx_->time_base;
dst_time_base = aud_stream_->time_base;
}
// 时间基转换
packet->pts = av_rescale_q(packet->pts, src_time_base, dst_time_base);
packet->dts = av_rescale_q(packet->dts, src_time_base, dst_time_base);
packet->duration = av_rescale_q(packet->duration, src_time_base, dst_time_base);
int ret = 0;
ret = av_interleaved_write_frame(fmt_ctx_, packet); // 不是立即写入文件,内部缓存,主要是对pts进行排序
// ret = av_write_frame(fmt_ctx_, packet);
av_packet_free(&packet);
if(ret == 0) {
return 0;
} else {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("av_write_frame failed:%s\n", errbuf);
return -1;
}
}
int Muxer::SendTrailer()
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
int ret = av_write_trailer(fmt_ctx_);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("av_write_trailer failed:%s\n", errbuf);
return -1;
}
return 0;
}
int Muxer::Open()
{
int ret = avio_open(&fmt_ctx_->pb, url_.c_str(), AVIO_FLAG_WRITE);
if(ret < 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avio_open %s failed:%s\n",url_.c_str(), errbuf);
return -1;
}
return 0;
}
int Muxer::GetAudioStreamIndex()
{
return audio_index_;
}
int Muxer::GetVideoStreamIndex()
{
return video_index_;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)