FFmpeg:视频转封装(FLV转成MP4,不需要转码)(参考remuxing.c)

如果不是特别熟悉C/C++,又要使用FFmpeg.API处理一些简单的音视频业务,那么可以使用org.bytedeco:ffmpeg-platform,下面记录一下使用ffmpeg-platform视频转封装的方法。

1. 基本流程

  1. 打开输入流
  2. 创建输出AVFormatContext
  3. 读流、写流

2. 完整代码

流程比较简单,这里直接给出完成代码

public class Remuxing {

    public static void main(String[] args) throws IOException {
        remuxing("test1.flv", "t1.mp4");
    }

    public static void remuxing(String input, String output) throws IOException {
        AVFormatContext ifmt_ctx = null;
        AVFormatContext ofmt_ctx = null;
        AVOutputFormat ofmt = null;
        AVPacket pkt = null;

        try {
            ifmt_ctx = avformat.avformat_alloc_context();
            int ret = avformat.avformat_open_input(ifmt_ctx, input, null, null);
            if (ret < 0) {
                throw new IOException(ret + ":avformat_open_input error");
            }

            ret = avformat.avformat_find_stream_info(ifmt_ctx, (AVDictionary) null);
            if (ret < 0) {
                throw new IOException(ret + ":avformat_find_stream_info error");
            }

            ofmt_ctx = new AVFormatContext(null);
            ret = avformat.avformat_alloc_output_context2(ofmt_ctx, null, null, output);
            if (ret < 0) {
                throw new IOException(ret + ":avformat_alloc_output_context2 error");
            }

            int stream_mapping_size = ifmt_ctx.nb_streams();
            int[] stream_mapping = new int[stream_mapping_size];
            int stream_index = 0;

            for (int i = 0; i < ifmt_ctx.nb_streams(); i++) {

                AVStream in_stream = ifmt_ctx.streams(i);
                AVCodecParameters in_codecpar = in_stream.codecpar();
                if (in_codecpar.codec_type() != avutil.AVMEDIA_TYPE_AUDIO
                        && in_codecpar.codec_type() != avutil.AVMEDIA_TYPE_VIDEO
                        && in_codecpar.codec_type() != avutil.AVMEDIA_TYPE_SUBTITLE) {
                    stream_mapping[i] = -1;
                    continue;
                }
                stream_mapping[i] = stream_index++;

                AVStream out_stream = avformat.avformat_new_stream(ofmt_ctx, null);
                if (out_stream == null) {
                    throw new IOException("avformat_new_stream error");
                }

                ret = avcodec.avcodec_parameters_copy(out_stream.codecpar(), in_codecpar);
                if (ret < 0) {
                    throw new IOException(ret + ":avcodec_parameters_copy error");
                }

                out_stream.codecpar().codec_tag(0);
            }

            ofmt = ofmt_ctx.oformat();
            if ((ofmt.flags() & avformat.AVFMT_NOFILE) == 0) {
                AVIOContext pb = new AVIOContext(null);
                ret = avformat.avio_open(pb, output, avformat.AVIO_FLAG_WRITE);
                if (ret < 0) {
                    throw new IOException(ret + ":avio_open error");
                }
                ofmt_ctx.pb(pb);
            }

            ret = avformat.avformat_write_header(ofmt_ctx, (AVDictionary) null);
            if (ret < 0) {
                throw new IOException(ret + ":avformat_write_header error");
            }

            AVStream in_stream, out_stream;
            pkt = avcodec.av_packet_alloc();
            while (true) {
                ret = avformat.av_read_frame(ifmt_ctx, pkt);
                if (ret < 0) {
                    break;
                }

                in_stream = ifmt_ctx.streams(pkt.stream_index());
                if (pkt.stream_index() >= stream_mapping_size || stream_mapping[pkt.stream_index()] < 0) {
                    avcodec.av_packet_unref(pkt);
                    continue;
                }

                pkt.stream_index(stream_mapping[pkt.stream_index()]);
                out_stream = ofmt_ctx.streams(pkt.stream_index());

                pkt.pts(avutil.av_rescale_q_rnd(pkt.pts(), in_stream.time_base(), out_stream.time_base(),
                        avutil.AV_ROUND_NEAR_INF | avutil.AV_ROUND_PASS_MINMAX));
                pkt.dts(avutil.av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), out_stream.time_base(),
                        avutil.AV_ROUND_NEAR_INF | avutil.AV_ROUND_PASS_MINMAX));
                pkt.duration(avutil.av_rescale_q(pkt.duration(), in_stream.time_base(), out_stream.time_base()));
                pkt.pos(-1);

                ret = avformat.av_interleaved_write_frame(ofmt_ctx, pkt);
                if (ret < 0) {
                    throw new IOException(ret + ":av_interleaved_write_frame error");
                }
            }

            ret = avformat.av_write_trailer(ofmt_ctx);
            if (ret < 0) {
                throw new IOException(ret + ":av_write_trailer error");
            }

        } finally {
            if (Objects.nonNull(pkt)) {
                avcodec.av_packet_free(pkt);
            }
            if (Objects.nonNull(ifmt_ctx)) {
                avformat.avformat_close_input(ifmt_ctx);
            }
            if (Objects.nonNull(ofmt_ctx)) {
                if (Objects.nonNull(ofmt)) {
                    if ((ofmt.flags() & avformat.AVFMT_NOFILE) == 0) {
                        avformat.avio_closep(ofmt_ctx.pb());
                    }
                }
                avformat.avformat_free_context(ofmt_ctx);
            }
        }
    }
}

注意:视频DTS有问题的话,av_interleaved_write_frame可能会有问题。

posted @   HiIT青年  阅读(47)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示