首页 > 其他分享 >FFmpeg:视频解码(FFmpeg 5.x 新API)(参考decode_video.c)

FFmpeg:视频解码(FFmpeg 5.x 新API)(参考decode_video.c)

时间:2023-01-14 19:55:06浏览次数:49  
标签:FFmpeg frame ctx ret decode API IOException new ic

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

1. 代码实现

下面是一个将MP4中的视频数据解码出来并转化成RGB24格式的例子:

public class DecodeVideo {

    public static void main(String[] args) throws IOException {
        decode_video("t.mp4", "t.rgb24");
    }

    public static void decode_video(String input, String output) throws IOException {
        AVFormatContext ifmt_ctx = new AVFormatContext(null);
        AVCodecContext ic = null;
        SwsContext sws_ctx = null;
        AVFrame frame = null;
        AVPacket pkt = null;
        PointerPointer<BytePointer> dst_data = new PointerPointer<>(1);
        IntPointer dst_linesize = new IntPointer(1);

        try (OutputStream os = new FileOutputStream(output)) {
            int ret = avformat_open_input(ifmt_ctx, input, null, null);
            if (ret < 0) {
                throw new IOException(ret + ":avformat_open_input error");
            }

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

            int nb_streams = ifmt_ctx.nb_streams();
            int video_index = -1;
            for (int i = 0; i < nb_streams; i++) {
                if (ifmt_ctx.streams(i).codecpar().codec_type() == AVMEDIA_TYPE_VIDEO) {
                    video_index = i;
                    break;
                }
            }
            if (video_index == -1) {
                throw new IOException("video index = -1");
            }

            AVCodec codec = avcodec_find_decoder(ifmt_ctx.streams(video_index).codecpar().codec_id());
            if (Objects.isNull(codec)) {
                throw new IOException("avcodec_find_decoder error");
            }

            ic = avcodec_alloc_context3(codec);
            if (Objects.isNull(ic)) {
                throw new IOException("avcodec_alloc_context3 error");
            }

            /* Copy codec parameters from input stream to output codec context */
            ret = avcodec_parameters_to_context(ic, ifmt_ctx.streams(video_index).codecpar());
            if (ret < 0) {
                throw new IOException(ret + ":avcodec_parameters_to_context error");
            }

            ret = avcodec_open2(ic, codec, (AVDictionary) null);
            if (ret < 0) {
                throw new IOException(ret + "avcodec_open2 error");
            }

            frame = av_frame_alloc();
            if (Objects.isNull(frame)) {
                throw new IOException("av_frame_alloc");
            }

            pkt = av_packet_alloc();
            if (Objects.isNull(pkt)) {
                throw new IOException("av_packet_alloc error");
            }

            sws_ctx = sws_getContext(ic.width(), ic.height(), ic.pix_fmt(), ic.width(), ic.height(), AV_PIX_FMT_RGB24,
                    SWS_BICUBIC, null, null, (DoublePointer) null);
            if (Objects.isNull(sws_ctx)) {
                throw new IOException("sws_getContext error");
            }

            ret = av_image_alloc(dst_data, dst_linesize, ic.width(), ic.height(), AV_PIX_FMT_RGB24, 1);
            if (ret < 0) {
                throw new IOException(ret + ":av_image_alloc error");
            }
            byte[] buffer = new byte[ret];

            while (true) {
                ret = av_read_frame(ifmt_ctx, pkt);
                if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    throw new IOException(ret + ":av_read_frame error");
                }
                if (pkt.stream_index() != video_index) {
                    continue;
                }

                ret = avcodec_send_packet(ic, pkt);
                if (ret < 0) {
                    throw new IOException(ret + ":avcodec_send_packet error");
                }

                while (true) {
                    ret = avcodec_receive_frame(ic, frame);
                    if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF) {
                        break;
                    } else if (ret < 0) {
                        throw new IOException(ret + ":avcodec_receive_frame error");
                    }
                    sws_scale(sws_ctx, frame.data(), frame.linesize(), 0, ic.height(), dst_data, dst_linesize);
                    dst_data.get(BytePointer.class, 0).get(buffer);
                    os.write(buffer);
                }
            }

            System.out.printf(
                    "Scaling succeeded. Play the output file with the command:\n"
                            + "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
                    av_get_pix_fmt_name(AV_PIX_FMT_RGB24).getString(), ic.width(), ic.height(), output);
        } finally {
            dst_data.close();
            dst_linesize.close();
            if (Objects.nonNull(pkt)) {
                av_packet_free(pkt);
            }
            if (Objects.nonNull(frame)) {
                av_frame_free(frame);
            }
            if (Objects.nonNull(ic)) {
                avcodec_free_context(ic);
            }
            if (Objects.nonNull(sws_ctx)) {
                sws_freeContext(sws_ctx);
            }
            avformat_close_input(ifmt_ctx);
        }
    }
}

2. 结果展示

转化的RGB24视频数据,有使用ffplay播放,效果如下:

播放命令:

ffplay -f rawvideo -pix_fmt rgb24 -video_size 352x288 t.rgb24

完整代码:扫描左侧头像小程序获取,或私信联系。

标签:FFmpeg,frame,ctx,ret,decode,API,IOException,new,ic
From: https://www.cnblogs.com/michong2022/p/17052435.html

相关文章