FFmpeg-- c++实现:音频流aac和视频流h264封装
文章目录
- 流程
- api
- 核心代码
- muxer.h
- muxer.cpp
aac 和 h264 封装为视频流,封装为c++的Muxter类
流程
-
分配视频文件上下文
int Init(const char *url); -
创建流,赋值给视频的音频流和视频流
int AddStream(AVCodecContext *codec_ctx); -
写视频流的head
int SendHeader(); -
写视频流的packet,需要转换packet的pts和dts , 值为 原有pts * 编码时间基/ 视频流的时间基
int SendPacket(AVPacket *packet) -
写视频流的trail
int SendTrailer(); -
释放资源
void DeInit();
api
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const
// 时间基转换函数 , 计算结果为 a * bq / cq
核心代码
muxer.h
#ifndef MUXER_H
#define MUXER_H
#include <iostream>
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
class Muxer
{
public:
Muxer();
~Muxer();
// 输出文件 返回<0值异常
// 初始化
int Init(const char *url);
// 资源释放
void DeInit();
// 创建流
int AddStream(AVCodecContext *codec_ctx);
// 写流
int SendHeader();
int SendPacket(AVPacket *packet);
int SendTrailer();
int Open(); // avio open
private:
AVFormatContext *fmt_ctx_ = NULL;
std::string url_ = "";
// 编码器上下文
AVCodecContext *aud_codec_ctx_= NULL;
AVStream *aud_stream_ = NULL;
AVCodecContext *vid_codec_ctx_= NULL;
AVStream *vid_stream_ = NULL;
int audio_index_ = -1;
int video_index_ = -1;
};
#endif // MUXER_H
muxer.cpp
#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", ret);
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", ret);
return -1;
}
return 0;
}
int Muxer::SendPacket(AVPacket *packet)
{
int stream_index = packet->stream_index;
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;
}
// 时间基转换
// int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;
// 时间基转换函数 a * bq / cq
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;
//写packet
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("avformat_write_header failed:%s\n", ret);
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", ret);
return -1;
}
return 0;
}