c++基于ffmpeg实现mp4转flv

发布于:2022-12-05 ⋅ 阅读:(1590) ⋅ 点赞:(1)

系列音视频开发



前言

音视频文件转封装操作就是把一种格式转换为另外一种格式,例如从 flv 转到 MP4,或者把流地址数据转换为MP4。


一、mp4、flv格式

Mp4文件格式
MP4文件中的所有数据都装在box(QuickTime中为atom)中,也就是说MP4文件由若干个box组成,每个box有类型和长度,可以将box理解为一个数据对象块。box中可以包含另一个box,这种box称为container box。
一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;
之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;
MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。
在这里插入图片描述

Flv文件格式
Flv由文件头(File Header)和 文件体(File Body)组成。
Flv Body由一系列的Tag组成,每个Tag又有一个preTagSize字段,标记着前面一个Tag的大小。
在这里插入图片描述

二、mp4 转封装flv

FLV只支持 H264+ AAC,通常aac采样率48000,如果mp4文件不是H264+ AAC格式,那么需要解码并编码为H264+ AAC,然后封装成FLV。

源码实现

mp42flv.h

#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/timestamp.h>

#ifdef __cplusplus
};
#endif
#include <string>

class Mp4ToFlv
{
public:
    static Mp4ToFlv &instance();
    ~Mp4ToFlv(){};    
    int transform(const std::string &inFile,const std::string &outFile);
private:
    Mp4ToFlv(){};
};

mp42flv.cpp

#include "mp42flv.h"
#include <string>
 

Mp4ToFlv &Mp4ToFlv::instance()
{
    static Mp4ToFlv inst;
    return inst;
}
int Mp4ToFlv::transform(const std::string &inFile,const std::string &outFile)
{
    AVFormatContext *ifmt_ctx = NULL;
    AVFormatContext *ofmt_ctx = NULL;
    std::string in_filename;
    std::string out_filename;
    int ret = 0;
    int stream_mapping_size = 0;
    int *stream_mapping = NULL;
    AVOutputFormat *ofmt = NULL;
    unsigned int i = 0;
    int stream_index = 0;
    AVPacket pkt;
    av_log_set_level(AV_LOG_INFO);
 
    in_filename = inFile;
    out_filename = outFile;
 
    ret = avformat_open_input(&ifmt_ctx,in_filename.c_str(),NULL,NULL);
    if (ret < 0){
        goto ERROR;
    }
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0){
        av_log(NULL, AV_LOG_ERROR, "Failed to retrieve input stream information");
        goto ERROR;
    }
 
    av_dump_format(ifmt_ctx, 0, in_filename.c_str(), 0);
 
    //second parameter can be "mpegts","mpeg","flv"
    avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,out_filename.c_str());   //now is determined by out_filename

    if (!ofmt_ctx){
        av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto ERROR;
    }

    //stream_mapping记录视频、音频、字幕流的索引
    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = static_cast<int*>(av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping)));
    if (!stream_mapping){
        ret = AVERROR(ENOMEM);
        goto ERROR;
    }
 
    ofmt = ofmt_ctx->oformat;
    for (i = 0; i < ifmt_ctx->nb_streams; i++){
        AVStream *out_stream;
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;
 
        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE){
            stream_mapping[i] = -1;
            continue;
        }
 
        stream_mapping[i] = stream_index++;    //key is pkg.index , value as video=0,audio=1,subtitle=2 
 
        out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream){
            av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto ERROR;
        }
        //because mp4 and flv has same stream and audio format,so just demutex is OK
        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0){
            av_log(NULL, AV_LOG_ERROR, "Failed to copy codec parameters\n");
            goto ERROR;
        }
        out_stream->codecpar->codec_tag = 0;
    }
    av_dump_format(ofmt_ctx, 0, out_filename.c_str(), 1);
 
    av_init_packet(&pkt);
    pkt.size = 0;
    pkt.data = NULL;
 
    if (!(ofmt->flags & AVFMT_NOFILE)){
        ret = avio_open(&ofmt_ctx->pb, out_filename.c_str(), AVIO_FLAG_WRITE);
        if (ret < 0){
            av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", out_filename.c_str());
            goto ERROR;
        }
    }
    ret = avformat_write_header(ofmt_ctx, NULL);
 
    while (1){
        AVStream *in_stream, *out_stream;
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
        in_stream = ifmt_ctx->streams[pkt.stream_index];
 
        //Only store video,audio,metadata
        if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0){
            av_packet_unref(&pkt);
            continue;
        }
 
        pkt.stream_index = stream_mapping[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
 
        //rescale pts dts duration
        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);
        pkt.pos = -1;
 
        //ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        ret = av_write_frame(ofmt_ctx, &pkt);
        av_packet_unref(&pkt);
 
        if (ret < 0){
            av_log(NULL, AV_LOG_ERROR, "Error muxing packet\n");
            break;
        }
    }
 
    av_write_trailer(ofmt_ctx);
    av_log(NULL, AV_LOG_ERROR, "remux success \n");
    av_dump_format(ofmt_ctx, 0, out_filename.c_str(), 1);
 
ERROR:
    if (ifmt_ctx){
        avformat_close_input(&ifmt_ctx);
        avformat_free_context(ifmt_ctx);
    }
 
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)){
        avio_closep(&ofmt_ctx->pb);    //close pb
    }
    if (ofmt_ctx){
        avformat_free_context(ofmt_ctx);
    }
    if (stream_mapping){
        av_freep(&stream_mapping);
    }
    return ret;
}

转封装,mp4与flv的音视频格式都保持不变,只是将MP4格式的封装重新封装成了新的FLV视频文件格式。
转换过程:
在这里插入图片描述

mp4原始视频:
在这里插入图片描述

flv转码后视频:
在这里插入图片描述


总结

本文含有隐藏内容,请 开通VIP 后查看