如果不是特别熟悉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