系列音视频开发
文章目录
前言
音视频文件转封装操作就是把一种格式转换为另外一种格式,例如从 flv 转到 MP4,或者把流地址数据转换为MP4。
一、mp4、flv格式
Mp4文件格式
MP4文件中的所有数据都装在box(QuickTime中为atom)中,也就是说MP4文件由若干个box组成,每个box有类型和长度,可以将box理解为一个数据对象块。box中可以包含另一个box,这种box称为container box。
一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;
之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;
MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。
Flv文件格式
Flv由文件头(File Header)和 文件体(File Body)组成。
Flv Body由一系列的Tag组成,每个Tag又有一个preTagSize字段,标记着前面一个Tag的大小。
二、mp4 转封装flv
FLV只支持 H264+ AAC,通常aac采样率48000,如果mp4文件不是H264+ AAC格式,那么需要解码并编码为H264+ AAC,然后封装成FLV。
源码实现
mp42flv.h
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/timestamp.h>
#ifdef __cplusplus
};
#endif
#include <string>
class Mp4ToFlv
{
public:
static Mp4ToFlv &instance();
~Mp4ToFlv(){};
int transform(const std::string &inFile,const std::string &outFile);
private:
Mp4ToFlv(){};
};
mp42flv.cpp
#include "mp42flv.h"
#include <string>
Mp4ToFlv &Mp4ToFlv::instance()
{
static Mp4ToFlv inst;
return inst;
}
int Mp4ToFlv::transform(const std::string &inFile,const std::string &outFile)
{
AVFormatContext *ifmt_ctx = NULL;
AVFormatContext *ofmt_ctx = NULL;
std::string in_filename;
std::string out_filename;
int ret = 0;
int stream_mapping_size = 0;
int *stream_mapping = NULL;
AVOutputFormat *ofmt = NULL;
unsigned int i = 0;
int stream_index = 0;
AVPacket pkt;
av_log_set_level(AV_LOG_INFO);
in_filename = inFile;
out_filename = outFile;
ret = avformat_open_input(&ifmt_ctx,in_filename.c_str(),NULL,NULL);
if (ret < 0){
goto ERROR;
}
ret = avformat_find_stream_info(ifmt_ctx, NULL);
if (ret < 0){
av_log(NULL, AV_LOG_ERROR, "Failed to retrieve input stream information");
goto ERROR;
}
av_dump_format(ifmt_ctx, 0, in_filename.c_str(), 0);
//second parameter can be "mpegts","mpeg","flv"
avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,out_filename.c_str()); //now is determined by out_filename
if (!ofmt_ctx){
av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto ERROR;
}
//stream_mapping记录视频、音频、字幕流的索引
stream_mapping_size = ifmt_ctx->nb_streams;
stream_mapping = static_cast<int*>(av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping)));
if (!stream_mapping){
ret = AVERROR(ENOMEM);
goto ERROR;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++){
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE){
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index++; //key is pkg.index , value as video=0,audio=1,subtitle=2
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream){
av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto ERROR;
}
//because mp4 and flv has same stream and audio format,so just demutex is OK
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0){
av_log(NULL, AV_LOG_ERROR, "Failed to copy codec parameters\n");
goto ERROR;
}
out_stream->codecpar->codec_tag = 0;
}
av_dump_format(ofmt_ctx, 0, out_filename.c_str(), 1);
av_init_packet(&pkt);
pkt.size = 0;
pkt.data = NULL;
if (!(ofmt->flags & AVFMT_NOFILE)){
ret = avio_open(&ofmt_ctx->pb, out_filename.c_str(), AVIO_FLAG_WRITE);
if (ret < 0){
av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", out_filename.c_str());
goto ERROR;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
while (1){
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
//Only store video,audio,metadata
if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0){
av_packet_unref(&pkt);
continue;
}
pkt.stream_index = stream_mapping[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
//rescale pts dts duration
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
//ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
ret = av_write_frame(ofmt_ctx, &pkt);
av_packet_unref(&pkt);
if (ret < 0){
av_log(NULL, AV_LOG_ERROR, "Error muxing packet\n");
break;
}
}
av_write_trailer(ofmt_ctx);
av_log(NULL, AV_LOG_ERROR, "remux success \n");
av_dump_format(ofmt_ctx, 0, out_filename.c_str(), 1);
ERROR:
if (ifmt_ctx){
avformat_close_input(&ifmt_ctx);
avformat_free_context(ifmt_ctx);
}
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)){
avio_closep(&ofmt_ctx->pb); //close pb
}
if (ofmt_ctx){
avformat_free_context(ofmt_ctx);
}
if (stream_mapping){
av_freep(&stream_mapping);
}
return ret;
}
转封装,mp4与flv的音视频格式都保持不变,只是将MP4格式的封装重新封装成了新的FLV视频文件格式。
转换过程:
mp4原始视频:
flv转码后视频:
总结
本文含有隐藏内容,请 开通VIP 后查看