7

FFmpeg的API使用篇(三):提取纯音频/纯画面

 3 years ago
source link: http://kevinnan.org.cn/index.php/archives/587/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文将在上一篇文章FFmpeg的API使用篇(二):转封装(不涉及转码)的基础上,拓展从视频中提取纯音频或者纯视频的功能。依旧不涉及转码操作,仅仅是将音频流提取出来,然后封装成对应的格式。比如从mp4格式的视频中提取音频,并保存为aac格式。或者提取纯视频转为mp4格式。

2、提取流程

提取音频流的过程和视频转封装的过程大部分都类似。只不过,我们需要的只是音频或者只是视频而已。因此,与转封装不同的是:首先,在复制编码参数(AVCodecParameters)时,我们只需要复制视频或者音频。而在使用AVPacket读取帧并写入容器时,我们也只需要音频流或视频流。

其中代码的关键部分在这里指出,看的时候可以多多关注。

//获取音频索引
    this->audio_stream_index = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
//逐帧读取音频
    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);
         }
     }
  • 提取纯音频/纯视频头文件(extractaudioorvideo.h)
// Copyright (c) 2021 LucasNan <[email protected]> <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)
// Copyright (c) 2021 LucasNan <[email protected]> <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
// Copyright (c) 2021 LucasNan <[email protected]> <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();
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK