Android JNI C++内存泄漏求助:FFmpeg音频波形生成内存过高
Android JNI FFmpeg音频波形生成内存泄漏排查
开发Android平台JNI C++代码时遭遇内存泄漏,该代码基于FFmpeg读取音频样本并生成波形,内存占用随时间持续攀升至1GB,尝试ReleaseStringUTFChars、PopLocalFrame等操作后问题仍未解决。
原代码
extern "C" JNIEXPORT jint JNICALL Java_modules_Waveform_decode_1to_1pcm(JNIEnv *env, jobject thiz, jstring input, jstring output) { const char * filename = (*env).GetStringUTFChars(input, 0); const char * outfilename = (*env).GetStringUTFChars(output, 0); if((*env).PushLocalFrame(1) != JNI_OK) { __android_log_print(ANDROID_LOG_ERROR, "exception: ", "%s", "Failed to open capacity"); return -1; } /* Open File */ AVFormatContext * format_ctx{nullptr}; int result_open = avformat_open_input(&format_ctx, filename, nullptr, nullptr); result_open = avformat_find_stream_info(format_ctx, nullptr); int index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); /* Finding decoder */ AVStream *streams = format_ctx->streams[index]; const AVCodec *decoder = avcodec_find_decoder(streams->codecpar->codec_id); AVCodecContext *codec_ctx{avcodec_alloc_context3(decoder)}; avcodec_parameters_to_context(codec_ctx, streams->codecpar->codec_id); /* Opening decoder */ result_open = avcodec_open2(codec_ctx, decoder, nullptr); /* Decoding the audio */ AVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); SwrContext *resampler{swr_alloc_set_opts( nullptr, streams->codecpar->channel_layout, AV_SAMPLE_FMT_FLT, streams->codecpar->sample_rate, streams->codecpar->channel_layout, (AVSampleFormat) streams->codecpar->format, streams->codecpar->format, streams->codecpar->sample_rate, 0 )}; std::ofstream out(outfilename, std::ios::binary); while (av_read_frame(format_ctx, packet) == 0) { if(packet->stream_index != streams->index) { continue; } result_open = avcodec_send_packet(codec_ctx, packet); if(result_open < 0) { // AVERROR(EAGAIN) --> Send the packet again getting frames out! if(result_open != AVERROR(EAGAIN)) { __android_log_print(ANDROID_LOG_ERROR, "exception: ", "%s", "Error decoding..."); } } while (avcodec_receive_frame(codec_ctx, frame) == 0) { /* Resample the frame */ AVFrame *resampler_frame = av_frame_alloc(); resampler_frame->sample_rate = 100; resampler_frame->channel_layout = frame->channel_layout; resampler_frame->channels = frame->channels; resampler_frame->format = AV_SAMPLE_FMT_S16; result_open = swr_convert_frame(resampler, resampler_frame, frame); if(result_open >= 0) { int16_t *samples = (int16_t *) frame->data[0]; for(int c = 0; c < resampler_frame->channels; c ++) { float sum = 0; for(int i = 0; i < resampler_frame->nb_samples; i ++) { if(samples[i * resampler_frame->channels + c] < 0) { sum += (float) samples[i * resampler_frame->channels + c] * (-1); } else { sum += (float) samples[i * resampler_frame->channels + c]; } int average_point = (int) ((sum * 2) / (float) resampler_frame->nb_samples); if(average_point > 0) { out << average_point << "\n"; } } } } av_frame_unref(frame); av_frame_free(&resampler_frame); } } av_frame_free(&frame); av_packet_unref(packet); av_packet_free(&packet); out.close(); (*env).PopLocalFrame(nullptr); (*env).ReleaseStringUTFChars(input, filename); (*env).ReleaseStringUTFChars(output, outfilename); return 1; }
内存泄漏点定位
- AVFormatContext未释放:
avformat_open_input初始化的format_ctx,未调用avformat_close_input(&format_ctx)关闭输入,也未调用avformat_free_context(format_ctx)释放上下文内存,这是内存占用攀升的核心原因之一。 - AVCodecContext未释放:
avcodec_alloc_context3分配的codec_ctx,未调用avcodec_free_context(&codec_ctx)释放解码器上下文,解码器资源占用较大。 - SwrContext未释放:
swr_alloc_set_opts创建的重采样上下文resampler,未调用swr_free(&resampler)回收,重采样相关内存持续占用。 - 跳过的AVPacket未释放:当
packet->stream_index != streams->index时直接continue,未调用av_packet_unref(packet)释放该包的内存,导致非音频流的数据包内存泄漏。 - 解码失败的AVPacket未释放:
avcodec_send_packet返回非AVERROR(EAGAIN)错误时,未调用av_packet_unref(packet)释放数据包,造成泄漏。 - 错误路径资源未清理:代码中未对
avformat_open_input、avformat_find_stream_info、avcodec_open2等关键步骤的失败情况做资源回滚,一旦某步失败,已分配的资源会全部泄漏。
修复建议
- 在函数末尾或错误路径中,依次释放所有FFmpeg资源:
// 释放重采样上下文 if (resampler) swr_free(&resampler); // 释放解码器上下文 if (codec_ctx) avcodec_free_context(&codec_ctx); // 关闭并释放格式上下文 if (format_ctx) { avformat_close_input(&format_ctx); avformat_free_context(format_ctx); } - 处理跳过的数据包时添加
av_packet_unref(packet):if(packet->stream_index != streams->index) { av_packet_unref(packet); continue; } avcodec_send_packet失败时,非EAGAIN场景下释放数据包:result_open = avcodec_send_packet(codec_ctx, packet); if(result_open < 0) { if(result_open != AVERROR(EAGAIN)) { __android_log_print(ANDROID_LOG_ERROR, "exception: ", "%s", "Error decoding..."); } av_packet_unref(packet); // 释放数据包 }- 为每个关键步骤添加错误处理,失败时立即清理已分配资源并返回。
内容的提问来源于stack exchange,提问作者Edson Magombe
相关产品推荐
相关产品推荐

