ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【FFmpeg4.1.4】音视频分离器

2020-12-09 12:01:40  阅读:316  来源: 互联网

标签:fmt ctx 音视频 分离器 FFmpeg4.1 av bsf NULL codec


音视频分离器

一、MP4提取H265裸流无效

FFmpeg解封装得到的AVPacket只包含了视频压缩数据,没有相关的参数集信息(比如:h265的vps头信息,h264的sps、pps头信息,AAC的adts头信息),不能初始化解码器。

二、添加头信息

StartCodePrefix的两种方式: Annex B和HVCC。

  1. Annex B:在NALU前加0x000001或者0x00000001 ;
  2. HVCC:在NALU前加上指示其长度的前缀。

MP4编码方式,Nalu开始四个字节表示数据长度。在FFmpeg中,这些头信息(VPS、SPS、PPS)是保存在解码器上下文(AVCodecContext)的extradata中的,所以我们需要为每一种格式的视频添加相应的解码头信息,这样解码器(MediaCodec)才能正确解析每一个AVPacket里的视频数据。FFmpeg提供了AVBitStreamFilter类实现头信息添加的功能。

三、音视频分离步骤

  1. 获取AVPacket
av_read_frame(fmt_ctx, &pkt)
  1. 添加头信息
if (av_bsf_send_packet(bsf_ctx, &pkt) < 0) {
	std::cerr << "failed to push" << std::endl;
	exit(1);
}			
while (av_bsf_receive_packet(bsf_ctx, &pkt) == 0);

源码

#include <iostream>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

#ifdef __cplusplus
}
#endif // __cplusplus

int demux(std::string mediafile) {
	const AVBitStreamFilter *filter = NULL;
	AVFormatContext *fmt_ctx = NULL;
	AVBSFContext *bsf_ctx;
	AVPacket pkt;
	AVCodecID video_codec_id;
	AVCodecID audio_codec_id;
	FILE *fp_vides = NULL, *fp_audes = NULL;
	int i, vid_idx, aud_idx;

	std::string videofile;
	std::string audiofile;

	if (avformat_open_input(&fmt_ctx, mediafile.c_str(), NULL, NULL) != 0) {
		av_log(NULL, AV_LOG_ERROR, "Could not open input file");
		exit(1);
	}
	av_dump_format(fmt_ctx, 0, mediafile.c_str(), 0);

	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
		avformat_free_context(fmt_ctx);
		av_log(NULL, AV_LOG_ERROR, "Failed to retrieve input stream information");
		exit(1);
	}
	
	for (i = 0; i<fmt_ctx->nb_streams; i++)
	{
		//vidoe
		if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			vid_idx = i;
			video_codec_id = fmt_ctx->streams[i]->codecpar->codec_id;
		}
		//audio
		else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			audio_codec_id = fmt_ctx->streams[i]->codecpar->codec_id;
			aud_idx = i;
		}
		//such as subtitile
		else {
			//To DO
		}
	}
	
	switch (video_codec_id) {
	case AV_CODEC_ID_H264:
		filter = av_bsf_get_by_name("h264_mp4toannexb");
		videofile = "video_es.h264";
		break;
	case AV_CODEC_ID_HEVC:
		filter = av_bsf_get_by_name("hevc_mp4toannexb");
		videofile = "video_es.h265";
		break;
	default:
		av_log(NULL, AV_LOG_ERROR, "Unkonw Video AVCodecID");
		exit(1);
	}

	switch (audio_codec_id) {
	case AV_CODEC_ID_AAC:
		audiofile = "audio_es.aac";
		break;
	case AV_CODEC_ID_AC3:
		audiofile = "audio_es.aac";
		break;
	case AV_CODEC_ID_MP3:
		audiofile = "audio_es.mp3";
		break;
	default:
		av_log(NULL, AV_LOG_ERROR, "Unkonw Audio AVCodecID");
	}

	if (!filter)
	{
		av_log(NULL, AV_LOG_ERROR, "Unkonw bitstream filter");
		exit(1);
	}
	
	if (av_bsf_alloc(filter, &bsf_ctx) < 0) {
		av_log(NULL, AV_LOG_ERROR, "Failed to alloc filter");
		exit(1);
	}

	//拷贝参数集信息
	avcodec_parameters_copy(bsf_ctx->par_in, fmt_ctx->streams[vid_idx]->codecpar);
	//初始化过滤器上下文
	av_bsf_init(bsf_ctx);

	fp_vides = fopen(videofile.c_str(), "wb");
	if (!fp_vides) {
		std::cerr << "Could not open file" << videofile.c_str() << std::endl;
		exit(1);
	}

	fp_audes = fopen(audiofile.c_str(), "wb");
	if (!fp_audes) {
		std::cerr << "Could not open file" << audiofile.c_str() << std::endl;
		exit(1);
	}

	while (av_read_frame(fmt_ctx, &pkt) >= 0)
	{
		if (pkt.stream_index == vid_idx) {
			if (av_bsf_send_packet(bsf_ctx, &pkt) < 0) {
				std::cerr << "failed to push" << std::endl;
				exit(1);
			}			
			while (av_bsf_receive_packet(bsf_ctx, &pkt) == 0);
			fwrite(pkt.data, pkt.size, 1, fp_vides);
		}			
		else if (pkt.stream_index == aud_idx)
			fwrite(pkt.data, pkt.size, 1, fp_audes);
		else
			;// such as subtitile
		av_packet_unref(&pkt);
	}
	fclose(fp_vides);
	fclose(fp_audes);
	avformat_close_input(&fmt_ctx);
	av_bsf_free(&bsf_ctx);
	return 0;
}

int main() {
	std::string mediafile = "bbb_sunflower_1080p_30fps_normal.mp4";
	demux(mediafile);
}

四、命令行

FFmpeg命令,从MP4中提取h265视频流

ffmpeg -i input.mp4 -codec copy -bsf: hevc_mp4toannexb -f hevc out.h265

-i input.mp4: 是输入的MP4文件
-codec copy: 从mp4中拷贝
-bsf: hevc_mp4toannexb: 从mp4拷贝到annexB封装
-f hevc: 采用hevc格式
out.h265: 输出的文件

标签:fmt,ctx,音视频,分离器,FFmpeg4.1,av,bsf,NULL,codec
来源: https://blog.csdn.net/Joker_N/article/details/110915952

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有