如果不是特别熟悉C/C++,又要使用FFmpeg.API处理一些简单的音视频业务,那么可以使用org.bytedeco:ffmpeg-platform,下面记录一下使用ffmpeg-platform视频转封装的方法。
1. 基本流程
- 打开输入流
- 创建输出AVFormatContext
- 读流、写流
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可能会有问题。
标签:FFmpeg,stream,转码,ctx,ret,FLV,avformat,ofmt,pkt From: https://www.cnblogs.com/michong2022/p/17052419.html