通用格式转换
该功能利用 FFmpeg 实现音频格式转换,通过调用本地 DLL 中的 NativeMedia.convertTo
方法,将输入文件转换为指定目标格式。
使用示例
@Test
public void convertToMp3() {
// 输入文件路径(支持 Windows 下的绝对路径)
String inputFile = "G:\\video\\input.flv";
// 调用转换方法,targetFormat 参数可以传入编码器名称或容器格式名称
String outputPath = NativeMedia.convertTo(inputFile, "libmp3lame");
System.out.println(outputPath);
}
在上面的示例中,"libmp3lame"
表示使用 libmp3lame 编码器进行转换,最终输出文件将使用 MP3 容器格式。
targetFormat 参数支持的取值
targetFormat
参数既可以是 FFmpeg 识别的容器格式名称,也可以是编码器名称。内部会先尝试根据传入的字符串判断是否为容器格式,如果不识别,则按照编码器名称进行映射,选择合适的输出容器。常用的取值包括:
1. 容器格式名称
mp3
使用 MP3 容器格式,通常会选择默认的 MP3 编码器。wav
输出 WAV 格式文件。ogg
输出 OGG 格式文件(通常结合 Vorbis 编码器)。adts
针对 AAC 编码,输出 ADTS 格式文件。flac
输出 FLAC 格式文件。ipod
针对 ALAC 编码,生成 iPod 支持的文件。opus
输出 OPUS 格式文件。
2. 编码器名称
libmp3lame
指定使用 libmp3lame 编码器,内部映射为 MP3 容器格式。aac 或 libfdk_aac
指定 AAC 编码器,内部映射为 ADTS 容器格式。libvorbis
指定使用 libvorbis 编码器,内部映射为 OGG 容器格式。flac
使用 FLAC 编码,生成 FLAC 格式文件。alac
使用 ALAC 编码,内部映射为 iPod 格式。libopus
使用 libopus 编码,生成 OPUS 格式文件。pcm_s16le
指定 PCM 编码(16 位),输出 WAV 文件。
C 代码实现
native_media_av_convert.c
#include "com_litongjava_media_NativeMedia.h"
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// FFmpeg 头文件
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <libavutil/audio_fifo.h>
#ifdef _WIN32
#include <stringapiset.h>
#endif
JNIEXPORT jstring JNICALL
Java_com_litongjava_media_NativeMedia_convertTo(JNIEnv *env, jclass clazz, jstring inputPath, jstring targetFormat) {
// 从 Java 获取输入文件路径(UTF-8 编码)与目标格式字符串
const char *input_file = (*env)->GetStringUTFChars(env, inputPath, NULL);
const char *target_fmt = (*env)->GetStringUTFChars(env, targetFormat, NULL);
if (!input_file || !target_fmt) {
if (input_file) (*env)->ReleaseStringUTFChars(env, inputPath, input_file);
if (target_fmt) (*env)->ReleaseStringUTFChars(env, targetFormat, target_fmt);
return (*env)->NewStringUTF(env, "Error: Failed to get input parameters");
}
// 判断 target_fmt 是容器格式还是编码器名称
const char *container_ext = NULL;
const AVOutputFormat *ofmt = av_guess_format(target_fmt, NULL, NULL);
const AVCodec *encoder = NULL;
char error_buffer[1024] = {0};
int ret = 0;
if (ofmt) {
// 传入的是容器格式,比如 "mp3", "wav", "aac" 等
container_ext = target_fmt;
if (ofmt->audio_codec == AV_CODEC_ID_NONE) {
snprintf(error_buffer, sizeof(error_buffer),
"Error: The container format '%s' does not specify a default audio codec", target_fmt);
goto cleanup;
}
encoder = avcodec_find_encoder(ofmt->audio_codec);
if (!encoder) {
snprintf(error_buffer, sizeof(error_buffer),
"Error: Could not find default encoder for container format '%s'", target_fmt);
goto cleanup;
}
} else {
// 否则认为 target_fmt 是编码器名称,根据常见编码器名称映射容器格式
if (strcmp(target_fmt, "libmp3lame") == 0) {
container_ext = "mp3";
} else if (strcmp(target_fmt, "aac") == 0 || strcmp(target_fmt, "libfdk_aac") == 0) {
container_ext = "adts";
} else if (strcmp(target_fmt, "libvorbis") == 0) {
container_ext = "ogg";
} else if (strcmp(target_fmt, "flac") == 0) {
container_ext = "flac";
} else if (strcmp(target_fmt, "alac") == 0) {
container_ext = "ipod";
} else if (strcmp(target_fmt, "libopus") == 0) {
container_ext = "opus";
} else if (strcmp(target_fmt, "pcm_s16le") == 0) {
container_ext = "wav";
} else {
// 未知编码器时,默认用用户传入的字符串作为容器扩展名
container_ext = target_fmt;
}
encoder = avcodec_find_encoder_by_name(target_fmt);
if (!encoder) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not find encoder '%s'", target_fmt);
goto cleanup;
}
}
// 构造输出文件名:若存在扩展名则替换为 .container_ext,否则追加
char *output_file = NULL;
size_t input_len = strlen(input_file);
const char *dot = strrchr(input_file, '.');
if (dot != NULL) {
size_t base_len = dot - input_file;
output_file = (char *) malloc(base_len + 1 + strlen(container_ext) + 1); // base + '.' + container_ext + '\0'
if (!output_file) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Memory allocation failed");
goto cleanup;
}
strncpy(output_file, input_file, base_len);
output_file[base_len] = '\0';
strcat(output_file, ".");
strcat(output_file, container_ext);
} else {
output_file = (char *) malloc(input_len + 1 + strlen(container_ext) + 1);
if (!output_file) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Memory allocation failed");
goto cleanup;
}
strcpy(output_file, input_file);
strcat(output_file, ".");
strcat(output_file, container_ext);
}
// 初始化各变量
AVFormatContext *input_format_context = NULL;
AVFormatContext *output_format_context = NULL;
SwrContext *swr_context = NULL;
AVCodecContext *decoder_context = NULL;
AVCodecContext *encoder_context = NULL;
AVStream *audio_stream = NULL;
AVPacket *input_packet = NULL;
AVPacket *output_packet = NULL;
AVFrame *input_frame = NULL;
AVFrame *output_frame = NULL;
AVAudioFifo *fifo = NULL;
int audio_stream_index = -1;
int64_t next_pts = 0; // 用于输出帧的 pts
jstring result = NULL;
// 打开输入文件(处理 Windows 下中文路径)
#ifdef _WIN32
{
int wlen = MultiByteToWideChar(CP_UTF8, 0, input_file, -1, NULL, 0);
wchar_t *winput_file = malloc(wlen * sizeof(wchar_t));
if (winput_file) {
MultiByteToWideChar(CP_UTF8, 0, input_file, -1, winput_file, wlen);
int len = WideCharToMultiByte(CP_UTF8, 0, winput_file, -1, NULL, 0, NULL, NULL);
char *local_input_file = malloc(len);
if (local_input_file) {
WideCharToMultiByte(CP_UTF8, 0, winput_file, -1, local_input_file, len, NULL, NULL);
ret = avformat_open_input(&input_format_context, local_input_file, NULL, NULL);
free(local_input_file);
} else {
ret = -1;
}
free(winput_file);
} else {
ret = -1;
}
}
#else
ret = avformat_open_input(&input_format_context, input_file, NULL, NULL);
#endif
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not open input file: %s", error_buffer);
goto cleanup;
}
// 获取输入文件流信息
if ((ret = avformat_find_stream_info(input_format_context, NULL)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not find stream info: %s", error_buffer);
goto cleanup;
}
// 查找第一个音频流
for (unsigned int i = 0; i < input_format_context->nb_streams; i++) {
if (input_format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
break;
}
}
if (audio_stream_index == -1) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not find audio stream in input file");
goto cleanup;
}
// 查找音频解码器
//AVCodec *decoder = avcodec_find_decoder(input_format_context->streams[audio_stream_index]->codecpar->codec_id);
const AVCodec *decoder = avcodec_find_decoder(input_format_context->streams[audio_stream_index]->codecpar->codec_id);
if (!decoder) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not find decoder");
goto cleanup;
}
// 分配并初始化解码器上下文
decoder_context = avcodec_alloc_context3(decoder);
if (!decoder_context) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate decoder context");
goto cleanup;
}
if ((ret = avcodec_parameters_to_context(decoder_context,
input_format_context->streams[audio_stream_index]->codecpar)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not copy decoder parameters: %s", error_buffer);
goto cleanup;
}
if ((ret = avcodec_open2(decoder_context, decoder, NULL)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not open decoder: %s", error_buffer);
goto cleanup;
}
// 根据 container_ext 创建输出格式上下文
if ((ret = avformat_alloc_output_context2(&output_format_context, NULL, container_ext, output_file)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate output context: %s", error_buffer);
goto cleanup;
}
// 分配编码器上下文并设置参数
encoder_context = avcodec_alloc_context3(encoder);
if (!encoder_context) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate encoder context");
goto cleanup;
}
encoder_context->sample_rate = decoder_context->sample_rate;
encoder_context->bit_rate = 128000; // 128kbps
encoder_context->sample_fmt = AV_SAMPLE_FMT_S16P; // 常见音频编码格式
#if LIBAVUTIL_VERSION_MAJOR < 57
encoder_context->channels = 2;
encoder_context->channel_layout = AV_CH_LAYOUT_STEREO;
#else
av_channel_layout_default(&encoder_context->ch_layout, 2);
#endif
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
encoder_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
if ((ret = avcodec_open2(encoder_context, encoder, NULL)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not open encoder: %s", error_buffer);
goto cleanup;
}
// 如果没有现成的音频流,则创建一个
audio_stream = avformat_new_stream(output_format_context, NULL);
if (!audio_stream) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not create new audio stream");
goto cleanup;
}
audio_stream->time_base = (AVRational){1, encoder_context->sample_rate};
if ((ret = avcodec_parameters_from_context(audio_stream->codecpar, encoder_context)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not copy encoder parameters: %s", error_buffer);
goto cleanup;
}
// 创建重采样上下文
swr_context = swr_alloc();
if (!swr_context) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate resampler context");
goto cleanup;
}
#if LIBAVUTIL_VERSION_MAJOR < 57
av_opt_set_int(swr_context, "in_channel_layout", decoder_context->channel_layout, 0);
av_opt_set_int(swr_context, "out_channel_layout", encoder_context->channel_layout, 0);
av_opt_set_int(swr_context, "in_channel_count", decoder_context->channels, 0);
av_opt_set_int(swr_context, "out_channel_count", encoder_context->channels, 0);
#else
av_opt_set_chlayout(swr_context, "in_chlayout", &decoder_context->ch_layout, 0);
av_opt_set_chlayout(swr_context, "out_chlayout", &encoder_context->ch_layout, 0);
#endif
av_opt_set_int(swr_context, "in_sample_rate", decoder_context->sample_rate, 0);
av_opt_set_int(swr_context, "out_sample_rate", encoder_context->sample_rate, 0);
av_opt_set_sample_fmt(swr_context, "in_sample_fmt", decoder_context->sample_fmt, 0);
av_opt_set_sample_fmt(swr_context, "out_sample_fmt", encoder_context->sample_fmt, 0);
if ((ret = swr_init(swr_context)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not initialize resampler: %s", error_buffer);
goto cleanup;
}
// 打开输出文件(处理中文路径)
if (!(output_format_context->oformat->flags & AVFMT_NOFILE)) {
#ifdef _WIN32
{
int wlen = MultiByteToWideChar(CP_UTF8, 0, output_file, -1, NULL, 0);
wchar_t *woutput_file = malloc(wlen * sizeof(wchar_t));
if (woutput_file) {
MultiByteToWideChar(CP_UTF8, 0, output_file, -1, woutput_file, wlen);
int len = WideCharToMultiByte(CP_UTF8, 0, woutput_file, -1, NULL, 0, NULL, NULL);
char *local_output_file = malloc(len);
if (local_output_file) {
WideCharToMultiByte(CP_UTF8, 0, woutput_file, -1, local_output_file, len, NULL, NULL);
ret = avio_open(&output_format_context->pb, local_output_file, AVIO_FLAG_WRITE);
free(local_output_file);
} else {
ret = -1;
}
free(woutput_file);
} else {
ret = -1;
}
}
#else
ret = avio_open(&output_format_context->pb, output_file, AVIO_FLAG_WRITE);
#endif
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not open output file: %s", error_buffer);
goto cleanup;
}
}
// 写输出文件头
if ((ret = avformat_write_header(output_format_context, NULL)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not write output header: %s", error_buffer);
goto cleanup;
}
// 申请 FIFO 缓冲区,用于存放转换后的音频样本
#if LIBAVUTIL_VERSION_MAJOR < 57
fifo = av_audio_fifo_alloc(encoder_context->sample_fmt, encoder_context->channels, 1);
#else
fifo = av_audio_fifo_alloc(encoder_context->sample_fmt, encoder_context->ch_layout.nb_channels, 1);
#endif
if (!fifo) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate FIFO");
goto cleanup;
}
// 申请数据包和帧
input_packet = av_packet_alloc();
output_packet = av_packet_alloc();
if (!input_packet || !output_packet) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate packet");
goto cleanup;
}
input_frame = av_frame_alloc();
output_frame = av_frame_alloc();
if (!input_frame || !output_frame) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate frame");
goto cleanup;
}
// 准备输出重采样缓冲帧
output_frame->format = encoder_context->sample_fmt;
#if LIBAVUTIL_VERSION_MAJOR < 57
output_frame->channel_layout = encoder_context->channel_layout;
output_frame->channels = encoder_context->channels;
#else
av_channel_layout_copy(&output_frame->ch_layout, &encoder_context->ch_layout);
#endif
// 默认采样数设为 encoder_context->frame_size 或 1152
output_frame->nb_samples = encoder_context->frame_size > 0 ? encoder_context->frame_size : 1152;
if ((ret = av_frame_get_buffer(output_frame, 0)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate output frame buffer: %s", error_buffer);
goto cleanup;
}
// 主循环:读取输入数据包,解码,重采样,并写入 FIFO
while (av_read_frame(input_format_context, input_packet) >= 0) {
if (input_packet->stream_index == audio_stream_index) {
ret = avcodec_send_packet(decoder_context, input_packet);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error sending packet to decoder: %s", error_buffer);
goto cleanup;
}
while (1) {
ret = avcodec_receive_frame(decoder_context, input_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error receiving frame from decoder: %s", error_buffer);
goto cleanup;
}
if ((ret = av_frame_make_writable(output_frame)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error making frame writable: %s", error_buffer);
goto cleanup;
}
int nb_samples_converted = swr_convert(swr_context,
output_frame->data, output_frame->nb_samples,
(const uint8_t **) input_frame->data, input_frame->nb_samples);
if (nb_samples_converted < 0) {
av_strerror(nb_samples_converted, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error converting audio: %s", error_buffer);
goto cleanup;
}
output_frame->nb_samples = nb_samples_converted;
if (av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + nb_samples_converted) < 0) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not reallocate FIFO");
goto cleanup;
}
if (av_audio_fifo_write(fifo, (void **) output_frame->data, nb_samples_converted) < nb_samples_converted) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not write data to FIFO");
goto cleanup;
}
av_frame_unref(input_frame);
while (av_audio_fifo_size(fifo) >= encoder_context->frame_size) {
AVFrame *enc_frame = av_frame_alloc();
if (!enc_frame) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate encoding frame");
goto cleanup;
}
enc_frame->nb_samples = encoder_context->frame_size;
enc_frame->format = encoder_context->sample_fmt;
#if LIBAVUTIL_VERSION_MAJOR < 57
enc_frame->channel_layout = encoder_context->channel_layout;
enc_frame->channels = encoder_context->channels;
#else
av_channel_layout_copy(&enc_frame->ch_layout, &encoder_context->ch_layout);
#endif
if ((ret = av_frame_get_buffer(enc_frame, 0)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate buffer for encoding frame: %s",
error_buffer);
av_frame_free(&enc_frame);
goto cleanup;
}
if (av_audio_fifo_read(fifo, (void **) enc_frame->data, encoder_context->frame_size) <
encoder_context->frame_size) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not read data from FIFO");
av_frame_free(&enc_frame);
goto cleanup;
}
enc_frame->pts = next_pts;
next_pts += enc_frame->nb_samples;
ret = avcodec_send_frame(encoder_context, enc_frame);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error sending frame to encoder: %s", error_buffer);
av_frame_free(&enc_frame);
goto cleanup;
}
av_frame_free(&enc_frame);
while (1) {
ret = avcodec_receive_packet(encoder_context, output_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error receiving packet from encoder: %s", error_buffer);
goto cleanup;
}
output_packet->stream_index = 0;
av_packet_rescale_ts(output_packet,
encoder_context->time_base,
audio_stream->time_base);
ret = av_interleaved_write_frame(output_format_context, output_packet);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error writing packet: %s", error_buffer);
goto cleanup;
}
av_packet_unref(output_packet);
}
}
}
}
av_packet_unref(input_packet);
}
// 处理 FIFO 中剩余不足一帧的数据,补静音后发送
if (av_audio_fifo_size(fifo) > 0) {
int remaining = encoder_context->frame_size - av_audio_fifo_size(fifo);
AVFrame *enc_frame = av_frame_alloc();
if (!enc_frame) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate final encoding frame");
goto cleanup;
}
enc_frame->nb_samples = encoder_context->frame_size;
enc_frame->format = encoder_context->sample_fmt;
#if LIBAVUTIL_VERSION_MAJOR < 57
enc_frame->channel_layout = encoder_context->channel_layout;
enc_frame->channels = encoder_context->channels;
#else
av_channel_layout_copy(&enc_frame->ch_layout, &encoder_context->ch_layout);
#endif
if ((ret = av_frame_get_buffer(enc_frame, 0)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not allocate buffer for final frame: %s",
error_buffer);
av_frame_free(&enc_frame);
goto cleanup;
}
int fifo_samples = av_audio_fifo_size(fifo);
if (av_audio_fifo_read(fifo, (void **) enc_frame->data, fifo_samples) < fifo_samples) {
snprintf(error_buffer, sizeof(error_buffer), "Error: Could not read remaining data from FIFO");
av_frame_free(&enc_frame);
goto cleanup;
}
#if LIBAVUTIL_VERSION_MAJOR < 57
for (int ch = 0; ch < encoder_context->channels; ch++) {
memset(enc_frame->data[ch] + fifo_samples * av_get_bytes_per_sample(encoder_context->sample_fmt),
0, remaining * av_get_bytes_per_sample(encoder_context->sample_fmt));
}
#else
for (int ch = 0; ch < encoder_context->ch_layout.nb_channels; ch++) {
memset(enc_frame->data[ch] + fifo_samples * av_get_bytes_per_sample(encoder_context->sample_fmt),
0, remaining * av_get_bytes_per_sample(encoder_context->sample_fmt));
}
#endif
enc_frame->pts = next_pts;
ret = avcodec_send_frame(encoder_context, enc_frame);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error sending final frame to encoder: %s", error_buffer);
av_frame_free(&enc_frame);
goto cleanup;
}
av_frame_free(&enc_frame);
while (1) {
ret = avcodec_receive_packet(encoder_context, output_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error receiving final packet from encoder: %s", error_buffer);
goto cleanup;
}
output_packet->stream_index = 0;
av_packet_rescale_ts(output_packet,
encoder_context->time_base,
audio_stream->time_base);
ret = av_interleaved_write_frame(output_format_context, output_packet);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error writing final packet: %s", error_buffer);
goto cleanup;
}
av_packet_unref(output_packet);
}
}
// 冲刷编码器,发送 NULL 帧
ret = avcodec_send_frame(encoder_context, NULL);
while (ret >= 0) {
ret = avcodec_receive_packet(encoder_context, output_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error flushing encoder: %s", error_buffer);
goto cleanup;
}
output_packet->stream_index = 0;
av_packet_rescale_ts(output_packet,
encoder_context->time_base,
audio_stream->time_base);
ret = av_interleaved_write_frame(output_format_context, output_packet);
if (ret < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error writing flushed packet: %s", error_buffer);
goto cleanup;
}
av_packet_unref(output_packet);
}
// 写文件尾
if ((ret = av_write_trailer(output_format_context)) < 0) {
av_strerror(ret, error_buffer, sizeof(error_buffer));
snprintf(error_buffer, sizeof(error_buffer), "Error writing trailer: %s", error_buffer);
goto cleanup;
}
// 成功时将输出文件路径作为返回结果
strncpy(error_buffer, output_file, sizeof(error_buffer) - 1);
error_buffer[sizeof(error_buffer) - 1] = '\0';
cleanup:
if (input_frame) av_frame_free(&input_frame);
if (output_frame) av_frame_free(&output_frame);
if (input_packet) av_packet_free(&input_packet);
if (output_packet) av_packet_free(&output_packet);
if (decoder_context) avcodec_free_context(&decoder_context);
if (encoder_context) avcodec_free_context(&encoder_context);
if (swr_context) swr_free(&swr_context);
if (fifo) av_audio_fifo_free(fifo);
if (input_format_context) avformat_close_input(&input_format_context);
if (output_format_context) {
if (!(output_format_context->oformat->flags & AVFMT_NOFILE) && output_format_context->pb)
avio_closep(&output_format_context->pb);
avformat_free_context(output_format_context);
}
(*env)->ReleaseStringUTFChars(env, inputPath, input_file);
(*env)->ReleaseStringUTFChars(env, targetFormat, target_fmt);
if (output_file) free(output_file);
result = (*env)->NewStringUTF(env, error_buffer);
return result;
}