// 1: Use H.264 Bitstream Filter
@interface MediaFileSeparation(){
AVFormatContext *ifmtCtx;
AVFormatContext *ofmtCtxAudio;
AVFormatContext *ofmtCtxVideo;
//AVCodecContext *codecCtxAudio;
//AVCodecContext *codecCtxVideo;
AVPacket packet;
//AVBitStreamFilterContext *h264bsfc;
AVBSFContext *bsfCtx;
int audioIndex;
int videoIndex;
int ret;
}
@end
@implementation MediaFileSeparation
- (int)init:(const char *)inputFilePath videoFilePath:(char *)videoOutputFilePath audioFilePath:(char *) audioOutputFilePath{
ifmtCtx = NULL;
ofmtCtxAudio = NULL;
ofmtCtxVideo = NULL;
//h264bsfc = NULL;
bsfCtx = NULL;
audioIndex = -1;
videoIndex = -1;
ret = 0;
// 声明
// 作用:分离某些封装格式的H264的时候,首先需要写入SPS和PPS,否则导致分离出来的数据没有SPS和PPS,而无法播放。
// h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
// 声明 h264_mp4toannexb hevc_mp4toannexb
const AVBitStreamFilter *pfilter = av_bsf_get_by_name("h264_mp4toannexb");
av_bsf_alloc(pfilter, &bsfCtx);
// 注册设备
//avdevice_register_all();
// 打开输入流
ret = avformat_open_input(&ifmtCtx, inputFilePath, 0, 0);
if(ret < 0){
fprintf(stderr, "avformat_open_input function failed. \n");
return ret;
}
// 获取输入流信息
ret = avformat_find_stream_info(ifmtCtx, 0);
if(ret < 0){
fprintf(stderr, "avformat_find_stream_info function failed. \n");
return ret;
}
ret = [self openCodecParameters: &videoIndex withOutFilePath: &videoOutputFilePath withIfmtCtx: ifmtCtx withOfmtCtx: &ofmtCtxVideo withType: AVMEDIA_TYPE_VIDEO];
if(ret < 0){
fprintf(stderr, "openCodecContext function failed video can't copy parameters. \n");
return ret;
}
ret = [self openCodecParameters: &audioIndex withOutFilePath: &audioOutputFilePath withIfmtCtx: ifmtCtx withOfmtCtx: &ofmtCtxAudio withType: AVMEDIA_TYPE_AUDIO];
if(ret < 0){
fprintf(stderr, "openCodecContext function failed audio can't copy parameters. \n");
return ret;
}
ret = [self openCodecContext:ifmtCtx];
if(ret < 0){
fprintf(stderr, "openCodecContext function failed. \n");
return ret;
}
// 打开输出文件: 视频
if(!(ofmtCtxVideo->oformat->flags & AVFMT_NOFILE)){
ret = avio_open(&ofmtCtxVideo->pb, videoOutputFilePath, AVIO_FLAG_WRITE);
if(ret < 0){
fprintf(stderr, "avio_open function failed can't open output file: %s. \n", videoOutputFilePath);
return ret;
}
}
// 打开输出文件: 音频
if(!(ofmtCtxAudio->oformat->flags & AVFMT_NOFILE)){
ret = avio_open(&ofmtCtxAudio->pb, audioOutputFilePath, AVIO_FLAG_WRITE);
if(ret < 0){
fprintf(stderr, "avio_open function failed can't open output file: %s. \n", audioOutputFilePath);
return ret;
}
}
// 写头文件: 视频
ret = avformat_write_header(ofmtCtxVideo, NULL);
if(ret < 0){
fprintf(stderr, "avformat_write_header function failed error opening video write header");
return ret;
}
// 写头文集: 音频
ret = avformat_write_header(ofmtCtxAudio, NULL);
if(ret < 0){
fprintf(stderr, "avformat_write_header function failed error opening audio write header");
return ret;
}
// DumpFormat 转存储格式
// av_dump_format: 是一个手工调试函数,能让我们看到 ifmtCtx->streams 里面的内容
printf("\nInput Video: \n");
av_dump_format(ifmtCtx, 0, inputFilePath, 0);
printf("\nOutput Video: \n");
av_dump_format(ofmtCtxVideo, 0, videoOutputFilePath, 1);
printf("\nOutput Audio: \n");
av_dump_format(ofmtCtxAudio, 0, audioOutputFilePath, 1);
printf("\n");
printf("MediaFileSeparation init success. \n");
return ret;
}
/// 拆分转换 Mp4 数据
- (void)convertFrames{
while (true) {
AVFormatContext *ofmtCtx;
AVStream *inStream, *outStream;
//读取输入帧数据
int ret = av_read_frame(ifmtCtx, &packet);
if(ret < 0){
break;
}
//获取输入流
inStream = ifmtCtx->streams[packet.stream_index];
//判断是否为视频流下标
if(packet.stream_index == videoIndex){
outStream = ofmtCtxVideo->streams[0];
ofmtCtx = ofmtCtxVideo;
// 使用
// 每个AVPacket的数据有如下变化:
// 每个Packet的data增加了h264的NALU起始码{0,0,0,1}
// 每个IDR帧数据前面增加了SPS和PPS.
//av_bitstream_filter_filter(h264bsfc, inStream->codec, NULL, &, &, , , 0);
av_bsf_send_packet(bsfCtx, &packet);
av_bsf_receive_packet(bsfCtx, &packet);
}else if(packet.stream_index == audioIndex){
//判断是否为音频流下标
outStream = ofmtCtxAudio->streams[0];
ofmtCtx = ofmtCtxAudio;
}else{
continue;
}
// Convert PTS/DTS 转换
= av_rescale_q_rnd(, inStream->time_base, outStream->time_base, (enum AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
= av_rescale_q_rnd(, inStream->time_base, outStream->time_base, (enum AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
= av_rescale_q(, inStream->time_base, outStream->time_base);
// 写数据
= -1;
packet.stream_index = 0;
ret = av_interleaved_write_frame(ofmtCtx, &packet);
if(ret < 0){
fprintf(stderr, "av_interleaved_write_frame function failed muxing packet. \n");
continue;
}
// 释放数据
av_packet_unref(&packet);
}
}
/// 从输入格式中获取参数赋值给输出格式参数
/// - Parameters:
/// - streanIndex: 流下标
/// - ifmtCtx: 输入格式
/// - ofmtCtx: 输出格式
/// - type: 类型
- (int)openCodecParameters:(int *)streamIndex withOutFilePath:(char **)outFilePath withIfmtCtx:(AVFormatContext *)ifmtCtx withOfmtCtx:(AVFormatContext **)ofmtCtx withType:(enum AVMediaType)type{
// 输入输出流
AVStream *outStream = NULL, *inStream = NULL;
// 获取流索引
int index = av_find_best_stream(ifmtCtx, type, -1, -1, NULL, 0);
if(index < 0){
fprintf(stderr, "av_find_best_stream can't find %s stream in input file. \n", av_get_media_type_string(type));
return -1;
}
// 获取到指定的输入流
inStream = ifmtCtx->streams[index];
// 获取文件格式(后缀名) hevc/h264 aac
const char *formatName = avcodec_get_name(inStream->codecpar->codec_id);
// 拼接文件格式,回传
// *outFilePath = [*outFilePath stringByAppendingPathExtension:[[NSString alloc] initWithUTF8String:formatName]];
strcat(*outFilePath, ".");
strcat(*outFilePath, formatName);
// 创建输出上下文 &ofmtCtx
int ret = avformat_alloc_output_context2(ofmtCtx, NULL, NULL, *outFilePath);
if(ret < 0){
fprintf(stderr, "avformat_alloc_output_context2 function failed. \n");
}
// 创建指定的输出流 ofmtCtx
outStream = avformat_new_stream(*ofmtCtx, NULL);
if(!outStream){
fprintf(stderr , "avformat_new_stream function allocate output stream failed. \n ");
return -1;
}
// 输入参数拷贝到输出参数中
ret = avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
if(ret < 0){
fprintf(stderr, "avcodec_parameters_copy function copy codec parameters failed. \n");
return ret;
}
// outStream->codec->codec_tag
outStream->codecpar->codec_tag = 0;
// ofmtCtx
if((*ofmtCtx)->oformat->flags & AVFMT_GLOBALHEADER){
// outStream->codec->flags
outStream->parser->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
// 流下标回传
*streamIndex = index;
return ret;
}
/// 根据输入流中的 Codec 拷贝到 输出流中
/// - Parameter ifmtCtx: 输入流格式
- (int)openCodecContext: (AVFormatContext *)ifmtCtx{
int ret = -1;
for (int i = 0; i < ifmtCtx->nb_streams; i++) {
AVFormatContext *ofmtCtx;
AVStream *inStream = ifmtCtx->streams[i];
AVStream *outStream = NULL;
//inStream->codec->codec_type
if(inStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
videoIndex = i;
//AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL);
//avcodec_parameters_to_context(pCodecCtx, inStream->codecpar);
//outStream = avformat_new_stream(ofmtCtxVideo, pCodecCtx->codec);
outStream = avformat_new_stream(ofmtCtxVideo, inStream->codec->codec);
ofmtCtx = ofmtCtxVideo;
}else if(inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
audioIndex = i;
outStream = avformat_new_stream(ofmtCtxAudio, inStream->codec->codec);
ofmtCtx = ofmtCtxAudio;
}else{
break;
}
// 输出流为空
if(!outStream){
fprintf(stderr, "avformat_new_stream function failed to allocate output stream. \n");
return ret;
}
if(avcodec_copy_context(outStream->codec, inStream->codec) < 0){
fprintf(stderr, "avcodec_copy_context function failed to copy context form input to output stream codec context. \n");
return ret;
}
// outStream->codec->codec_tag
outStream->codecpar->codec_tag = 0;
if(ofmtCtx->oformat->flags & AVFMT_GLOBALHEADER){
//outStream->codec->flags
outStream->parser->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
return 0;
}
/// 停止
- (void)stop{
// 释放关闭
//av_bitstream_filter_close(h264bsfc);
av_bsf_free(&bsfCtx);
// 写入文件结束标识 write file trailer
av_write_trailer(ofmtCtxVideo);
av_write_trailer(ofmtCtxAudio);
}
/// 关闭
- (void)close{
// 关闭输入流
if(ifmtCtx){
avformat_close_input(&ifmtCtx);
ifmtCtx = NULL;
}
// 关闭视频文件
if(ofmtCtxVideo && !(ofmtCtxVideo->oformat->flags & AVFMT_NOFILE)){
avio_close(ofmtCtxVideo->pb);
}
// 关闭音频文件
if(ofmtCtxAudio && !(ofmtCtxAudio->oformat->flags & AVFMT_NOFILE)){
avio_close(ofmtCtxAudio->pb);
}
// 释放视频内容
if(ofmtCtxVideo){
avformat_free_context(ofmtCtxVideo);
ofmtCtxVideo = NULL;
}
// 释放音频内容
if(ofmtCtxAudio){
avformat_free_context(ofmtCtxAudio);
ofmtCtxAudio = NULL;
}
printf("MediaFileSeparation close");
}
/// 销毁
- (void)dealloc{
[self close];
}
@end