Loading... ## 1、前言 本文将在上一篇文章[FFmpeg的API使用篇(二):转封装(不涉及转码)](http://kevinnan.org.cn/index.php/archives/583/)的基础上,拓展从视频中提取纯音频或者纯视频的功能。依旧不涉及转码操作,仅仅是将音频流提取出来,然后封装成对应的格式。比如从mp4格式的视频中提取音频,并保存为aac格式。或者提取纯视频转为mp4格式。 ## 2、提取流程 提取音频流的过程和视频转封装的过程大部分都类似。只不过,我们需要的只是音频或者只是视频而已。因此,与转封装不同的是:首先,在复制编码参数(AVCodecParameters)时,我们只需要复制视频或者音频。而在使用AVPacket读取帧并写入容器时,我们也只需要音频流或视频流。 其中代码的关键部分在这里指出,看的时候可以多多关注。 ```cpp //获取音频索引 this->audio_stream_index = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); ``` ```cpp //逐帧读取音频 while(av_read_frame(ifmt_ctx_, &packet) >=0 ){ AVStream *in_stream, *out_stream; in_stream = ifmt_ctx_->streams[pkt->stream_index]; out_stream = ofmt_ctx_->streams[pkt->stream_index]; if(packet.stream_index == this->audio_stream_index){ //时间基计算,音频pts和dts一致 packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet.dts = packet.pts; packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); packet.pos = -1; packet.stream_index = 0; //将包写到输出媒体文件 av_interleaved_write_frame(ofmt_ctx_, &packet); //减少引用计数,避免内存泄漏 av_packet_unref(&packet); } } ```  ### 3、代码 - 提取纯音频/纯视频头文件(extractaudioorvideo.h) ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #ifndef EXTRACTAUDIOORVIDEO_H #define EXTRACTAUDIOORVIDEO_H #include <iostream> #include <string> extern "C"{ #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" } class ExtractAudioORVideo { public: ExtractAudioORVideo(); ~ExtractAudioORVideo(); //@brief: 得到输入文件 //@param: input_file: 输入文件 //@ret : void //@birth: created by LucasNan on 20210221 void getInputFile(std::string input_file){ this->input_file_ = input_file; } //@brief: 得到输出文件 //@param: output_file: 输出文件 //@ret : void //@birth: created by LucasNan on 20210221 void getOutputFile(std::string output_file){ this->output_file_ = output_file; } //@brief: 提取纯音频流 //@param: void //@ret : void //@birth: created by LucasNan on 20210221 void extractAudio(); //@brief: 提取纯画面 //@param: void //@ret : void //@birth: created by LucasNan on 20210221 void extractVideo(); private: //输出文件AVFormatContext AVFormatContext* ifmt_ctx_; //输出文件AVFormatContext AVFormatContext* ofmt_ctx_; std::string input_file_; std::string output_file_; int audio_stream_index; int video_stream_index; }; #endif // EXTRACTAUDIOORVIDEO_H ``` - 类实现(extractaudioorvideo.cpp) ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #include "extractaudioorvideo.h" ExtractAudioORVideo::ExtractAudioORVideo(){ ifmt_ctx_ = avformat_alloc_context(); ofmt_ctx_ = avformat_alloc_context(); audio_stream_index = -1; video_stream_index = -1; } ExtractAudioORVideo::~ExtractAudioORVideo(){ avformat_free_context(ifmt_ctx_); avformat_free_context(ofmt_ctx_); } void ExtractAudioORVideo::extractAudio(){ AVOutputFormat* ofmt; AVPacket packet; av_init_packet(&packet); packet.data = NULL; packet.size = 0; AVPacket *pkt = av_packet_alloc(); int ret; //打开媒体文件 if(avformat_open_input(&ifmt_ctx_, this->input_file_.c_str(), NULL, NULL) < 0){ printf("file open error!"); return; } //读取音视频流信息 if(avformat_find_stream_info(ifmt_ctx_, 0) < 0){ printf("stream info error!"); return; } //打印视频信息 av_dump_format(ifmt_ctx_, 0, input_file_.c_str(), 0); //给AVFormatContext分配动态内存 avformat_alloc_output_context2(&ofmt_ctx_, NULL, NULL, output_file_.c_str()); //ofmt = av_guess_format(NULL, output_file_.c_str(), NULL); if(!ofmt_ctx_){ printf("can not create output"); return; } //得到AVOutputFormat对象 ofmt = ofmt_ctx_->oformat; //ofmt_ctx_->oformat = ofmt; //读取音频流 for(unsigned int i = 0; i < ifmt_ctx_->nb_streams; i++){ AVStream* out_stream = NULL; AVStream* in_stream = ifmt_ctx_->streams[i]; AVCodecParameters* in_codepar = in_stream->codecpar; if(in_codepar->codec_type == AVMEDIA_TYPE_VIDEO || in_codepar->codec_type == AVMEDIA_TYPE_SUBTITLE){ continue; } //新建输出流 out_stream = avformat_new_stream(ofmt_ctx_, NULL); if(!out_stream){ printf("Failed allocat output stream"); return; } //copy编码参数,上下文信息 ret = avcodec_parameters_copy(out_stream->codecpar, in_codepar); if(ret < 0){ printf("codec copy error!"); return; } out_stream->codecpar->codec_tag = 0; break; } av_dump_format(ofmt_ctx_, 0, output_file_.c_str(), 1); //如果flags没有设置为AVFMT_NOFILE,则使用pb //处初始化AVIOContext,文件操作由他完成 if(!(ofmt->flags & AVFMT_NOFILE)){ printf("avio_open\n"); ret = avio_open(&ofmt_ctx_->pb, output_file_.c_str(), AVIO_FLAG_WRITE); if(ret < 0){ printf("can not open file"); return; } } //获取音频索引 this->audio_stream_index = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if(this->audio_stream_index < 0){ printf("can not find audio stream!"); return; } //写入媒体头文件 ret = avformat_write_header(ofmt_ctx_, NULL); if(ret < 0){ printf("can not write"); return; } //逐帧读取音频 while(av_read_frame(ifmt_ctx_, &packet) >=0 ){ AVStream *in_stream, *out_stream; in_stream = ifmt_ctx_->streams[pkt->stream_index]; out_stream = ofmt_ctx_->streams[pkt->stream_index]; if(packet.stream_index == this->audio_stream_index){ //时间基计算,音频pts和dts一致 packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet.dts = packet.pts; packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); packet.pos = -1; packet.stream_index = 0; //将包写到输出媒体文件 av_interleaved_write_frame(ofmt_ctx_, &packet); //减少引用计数,避免内存泄漏 av_packet_unref(&packet); } } //写入尾部信息 av_write_trailer(ofmt_ctx_); //释放AVPacket av_packet_free(&pkt); //打印输出文件信息 av_dump_format(ofmt_ctx_, 0, output_file_.c_str(), 1); //关闭内存 if (ofmt_ctx_ && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx_->pb); //在析构函数中释放内存 printf("over"); } void ExtractAudioORVideo::extractVideo(){ AVOutputFormat* ofmt; AVPacket packet; av_init_packet(&packet); packet.data = NULL; packet.size = 0; AVPacket *pkt = av_packet_alloc(); int ret; //打开媒体文件 if(avformat_open_input(&ifmt_ctx_, this->input_file_.c_str(), NULL, NULL) < 0){ printf("file open error!"); return; } //读取音视频流信息 if(avformat_find_stream_info(ifmt_ctx_, 0) < 0){ printf("stream info error!"); return; } //打印视频信息 av_dump_format(ifmt_ctx_, 0, input_file_.c_str(), 0); //给AVFormatContext分配动态内存 avformat_alloc_output_context2(&ofmt_ctx_, NULL, NULL, output_file_.c_str()); //ofmt = av_guess_format(NULL, output_file_.c_str(), NULL); if(!ofmt_ctx_){ printf("can not create output"); return; } //得到AVOutputFormat对象 ofmt = ofmt_ctx_->oformat; //ofmt_ctx_->oformat = ofmt; //读取视频流 for(unsigned int i = 0; i < ifmt_ctx_->nb_streams; i++){ AVStream* out_stream = NULL; AVStream* in_stream = ifmt_ctx_->streams[i]; AVCodecParameters* in_codepar = in_stream->codecpar; if(in_codepar->codec_type == AVMEDIA_TYPE_AUDIO || in_codepar->codec_type == AVMEDIA_TYPE_SUBTITLE){ continue; } //新建输出流 out_stream = avformat_new_stream(ofmt_ctx_, NULL); if(!out_stream){ printf("Failed allocat output stream"); return; } //copy编码参数,上下文信息 ret = avcodec_parameters_copy(out_stream->codecpar, in_codepar); if(ret < 0){ printf("codec copy error!"); return; } out_stream->codecpar->codec_tag = 0; break; } av_dump_format(ofmt_ctx_, 0, output_file_.c_str(), 1); //如果flags没有设置为AVFMT_NOFILE,则使用pb //处初始化AVIOContext,文件操作由他完成 if(!(ofmt->flags & AVFMT_NOFILE)){ ret = avio_open(&ofmt_ctx_->pb, output_file_.c_str(), AVIO_FLAG_WRITE); if(ret < 0){ printf("can not open file"); return; } } video_stream_index = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if(video_stream_index < 0){ printf("can not find audio stream!"); return; } //写入媒体头文件 ret = avformat_write_header(ofmt_ctx_, NULL); if(ret < 0){ printf("can not write"); return; } while(av_read_frame(ifmt_ctx_, &packet) >=0 ){ AVStream *in_stream, *out_stream; in_stream = ifmt_ctx_->streams[pkt->stream_index]; out_stream = ofmt_ctx_->streams[pkt->stream_index]; if(packet.stream_index == this->video_stream_index){ //时间基计算,音频pts和dts一致 packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet.dts = packet.pts; packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); packet.pos = -1; packet.stream_index = 0; //将包写到输出媒体文件 av_interleaved_write_frame(ofmt_ctx_, &packet); //减少引用计数,避免内存泄漏 av_packet_unref(&packet); } } //写入尾部信息 av_write_trailer(ofmt_ctx_); //释放AVPacket av_packet_free(&pkt); //打印输出文件信息 av_dump_format(ofmt_ctx_, 0, output_file_.c_str(), 1); //关闭内存 if (ofmt_ctx_ && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx_->pb); //在析构函数中释放内存 printf("over"); } ``` - main.cpp ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #include <QCoreApplication> #include <iostream> #include "extractaudioorvideo.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); const char* file = "F:/google/durant.mp4"; ExtractAudioORVideo *ext = new ExtractAudioORVideo(); ext->getInputFile(file); ext->getOutputFile("./output_audio.aac"); ext->extractAudio(); //ext->extractVideo(); delete ext; return a.exec(); } ``` Last modification:June 15th, 2021 at 06:01 pm © 允许规范转载