9

FFmpeg的API使用篇(二):转封装(不涉及转码)

 3 years ago
source link: http://kevinnan.org.cn/index.php/archives/583/
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.

本文不涉及音频和视频的编解码操作,仅仅是转换视频的封装格式,比如mp4转flv,mp4转mkv等,也可以认为仅仅是为文件重命名,更改了文件后缀而已。

由于不涉及音视频的转码操作,因此此程序处理速度极快。需要知道的是,音视频编解码的算法非常复杂,占用了很多的CPU,这部分也消耗了是视频转码的绝大部分时间。关于视频转码的API使用方法将在之后的博客中详细介绍,本文只关注于转封装。

只转封装的工作原理如下图所示:

2、转封装流程

FFmpeg转封装API

2.1 打开媒体文件,读取音视频流信息

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);

2.2 创建输出对象

//给AVFormatContext分配动态内存
    avformat_alloc_output_context2(&ofmt_ctx_, NULL, NULL, output_file_.c_str());
    if(!ofmt_ctx_){
        printf("can not create output");
        return;
    }

    //得到AVOutputFormat对象
    ofmt = ofmt_ctx_->oformat;

2.3 数据拷贝

2.3.1 读取音视频流,复制参数信息

//读取音视频流
    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_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;        
    }

2.3.2 初始化内存

//初始化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;
        }
    }

2.3.3 拷贝数据

//写入媒体头文件
    ret = avformat_write_header(ofmt_ctx_, NULL);
    if(ret < 0){
        printf("can not write");
        return;
    }

    while(1){
        AVStream *in_stream, *out_stream;
        //读出每一帧数据
        ret = av_read_frame(ifmt_ctx_, pkt);
        if(ret < 0){
            printf("over");
            break;
        }

        in_stream = ifmt_ctx_->streams[pkt->stream_index];
        out_stream = ofmt_ctx_->streams[pkt->stream_index];

        //copy packet
        //转换PTS/DTS(Convert PTS/DTS)
        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);
        //数据在媒体流中的为止,未知设为-1
        pkt->pos = -1;

        //将包写入输出媒体文件
        ret = av_interleaved_write_frame(ofmt_ctx_, pkt);        
        if(ret < 0){
            printf("erroe muxing packet");
            break;
        }
        //减少引用计数,避免内存泄漏
        av_packet_unref(pkt);        
    }
    //写入尾部信息
    av_write_trailer(ofmt_ctx_);

2.4 释放内存

//释放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);

    //在析构函数中释放内存

3、完整代码

  • 类头文件代码(remuxing.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 REMUXING_H
#define REMUXING_H

#include <iostream>
#include <string>

extern "C"{

    #include "libavformat/avformat.h"
    #include "libavcodec/avcodec.h"
}


class ReMuxing
{
public:
    ReMuxing();

    ~ReMuxing();

    //@brief: 得到输入文件
    //@param: input_file: 输入文件
    //@ret  : void
    //@birth: created by LucasNan on 20210220
    void getInputFile(std::string input_file){
        this->input_file_ = input_file;
    }

    //@brief: 得到输出文件
    //@param: output_file: 输出文件
    //@ret  : void
    //@birth: created by LucasNan on 20210220
    void getOutputFile(std::string output_file){
        this->output_file_ = output_file;
    }

    //@brief: 视频转封装
    //@param: void
    //@ret  : void
    //@birth: created by LucasNan on 20210220
    void reMuxing();

private:
    //输出文件AVFormatContext
    AVFormatContext* ifmt_ctx_;
    //输出文件AVFormatContext
    AVFormatContext* ofmt_ctx_;

    std::string input_file_;
    std::string output_file_;

};

#endif // REMUXING_H
  • 类实现代码(remuxing.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 "remuxing.h"

ReMuxing::ReMuxing()
{
    ifmt_ctx_ = avformat_alloc_context();
    ofmt_ctx_ = avformat_alloc_context();
}

ReMuxing::~ReMuxing(){
    avformat_free_context(ifmt_ctx_);
    avformat_free_context(ofmt_ctx_);

}


void ReMuxing::reMuxing(){
    AVOutputFormat* ofmt;
    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());
    if(!ofmt_ctx_){
        printf("can not create output");
        return;
    }

    //得到AVOutputFormat对象
    ofmt = ofmt_ctx_->oformat;

    //读取音视频流
    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_AUDIO &&
           in_codepar->codec_type != AVMEDIA_TYPE_SUBTITLE){
            continue;
        }

        std::cout<<"one"<<std::endl;

        //新建输出流
        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;        
    }

    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;
        }
    }

    //写入媒体头文件
    ret = avformat_write_header(ofmt_ctx_, NULL);
    if(ret < 0){
        printf("can not write");
        return;
    }

    while(1){
        AVStream *in_stream, *out_stream;
        //读出每一帧数据
        ret = av_read_frame(ifmt_ctx_, pkt);
        if(ret < 0){
            printf("over");
            break;
        }

        in_stream = ifmt_ctx_->streams[pkt->stream_index];
        out_stream = ofmt_ctx_->streams[pkt->stream_index];

        //copy packet
        //转换PTS/DTS(Convert PTS/DTS)
        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);
        //数据在媒体流中的为止,未知设为-1
        pkt->pos = -1;

        //将包写入输出媒体文件
        ret = av_interleaved_write_frame(ofmt_ctx_, pkt);        
        if(ret < 0){
            printf("erroe muxing packet");
            break;
        }
        //减少引用计数,避免内存泄漏
        av_packet_unref(pkt);        
    }
    //写入尾部信息
    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);

    //在析构函数中释放内存

}
  • 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 "remuxing.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);  

    const char* file = "F:/google/durant.mp4";

    ReMuxing* re = new ReMuxing();
    re->getInputFile(file);
    re->getOutputFile("./output.flv");
    re->reMuxing();

    return a.exec();
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK