文章目录
一 native层内存泄漏
在上一篇文章中,实现了音视频播放与同步
NDK27_FFmpeg音视频同步
当退出播放后,native的内存没有降低,内存产生了泄漏,C++中需要对内存进行主动回收
二 内存回收
1 设置超时时间
连接是耗时操作,设置连接超时时间AVDictionary,并释放AVDictionary
AVDictionary *options = 0;
//设置超时时间 微妙 超时时间5秒
av_dict_set(&options, "timeout", "5000000", 0);
int ret = avformat_open_input(&formatContext, dataSource, 0, &options);
av_dict_free(&options);
2 DNFFmpeg内存释放
- 释放JavaCallHelper,不去调用java的方法去播放
- 定义stop方法,start中申请的内存在stop中释放
start是多线程,不能直接结束,用pthread_join等待的话;由于stop在主线程,会卡顿,所以要开线程去释放
void DNFFmpeg::_prepare() {
...
AVDictionary *options = 0;
//设置超时时间 微妙 超时时间5秒
av_dict_set(&options, "timeout", "5000000", 0);
int ret = avformat_open_input(&formatContext, dataSource, 0, &options);
av_dict_free(&options);
...
}
void DNFFmpeg::_start() {
//1、读取媒体数据包(音视频数据包)
int ret;
while (isPlaying) {
//读取文件的时候没有网络请求,一下子读完了,可能导致oom
//特别是读本地文件的时候 一下子就读完了
if (audioChannel && audioChannel->packets.size() > 100) {
//10ms
av_usleep(1000 * 10);
continue;
}
if (videoChannel && videoChannel->packets.size() > 100) {
av_usleep(1000 * 10);
continue;
}
AVPacket *packet = av_packet_alloc();
ret = av_read_frame(formatContext, packet);
//=0成功 其他:失败
if (ret == 0) {
//stream_index 这一个流的一个序号
if (audioChannel && packet->stream_index == audioChannel->id) {
audioChannel->packets.push(packet);
} else if (videoChannel && packet->stream_index == videoChannel->id) {
videoChannel->packets.push(packet);
}
} else if (ret == AVERROR_EOF) {
//读取完成 但是可能还没播放完
if (audioChannel->packets.empty() && audioChannel->frames.empty()
&& videoChannel->packets.empty() && videoChannel->frames.empty()) {
break;
}
//为什么这里要让它继续循环 而不是sleep
//如果是做直播 ,可以sleep
//如果要支持点播(播放本地文件) seek 后退
} else {
//
break;
}
}
isPlaying = 0;
audioChannel->stop();
videoChannel->stop();
};
void *async_stop(void *args) {
DNFFmpeg *ffmpeg = static_cast<DNFFmpeg *>(args);
// 等待prepare结束
pthread_join(ffmpeg->pid, 0);
ffmpeg->isPlaying = 0;
// 保证 start线程结束
pthread_join(ffmpeg->pid_play, 0);
DELETE(ffmpeg->videoChannel);
DELETE(ffmpeg->audioChannel);
// 这时候释放就不会出现问题了
if (ffmpeg->formatContext) {
//先关闭读取 (关闭fileintputstream)
avformat_close_input(&ffmpeg->formatContext);
avformat_free_context(ffmpeg->formatContext);
ffmpeg->formatContext = 0;
}
DELETE(ffmpeg);
return 0;
}
void DNFFmpeg::stop() {
isPlaying = 0;
callHelper = 0;
// formatContext
pthread_create(&pid_stop, 0, aync_stop, this);
};
回收AVCodecContext 并定义stop方法用于AudioChannel和VideoChannel回收
virtual ~BaseChannel() {
frames.clear();
packets.clear();
if (avCodecContext) {
avcodec_close(avCodecContext);
avcodec_free_context(&avCodecContext);
avCodecContext = 0;
}
}
virtual void stop() = 0;
3 VideoChannel.stop
释放swsContext,队列停止工作,保证线程安全
void VideoChannel::render() {
...
isPlaying = 0;
sws_freeContext(swsContext);
swsContext = 0;
}
void VideoChannel::stop() {
isPlaying = 0;
frames.setWork(0);
packets.setWork(0);
pthread_join(pid_decode, 0);
pthread_join(pid_render, 0);
}
4 AudioChannel->stop
AVFrame使用完后释放,播放器、混音器、引擎释放,队列停止工作
int AudioChannel::getPcm() {
...
releaseAvFrame(&frame);
return data_size;
}
void AudioChannel::stop() {
isPlaying = 0;
packets.setWork(0);
frames.setWork(0);
pthread_join(pid_audio_decode,0);
pthread_join(pid_audio_play,0);
if(swrContext){
swr_free(&swrContext);
swrContext = 0;
}
//释放播放器
if(bqPlayerObject){
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = 0;
bqPlayerInterface = 0;
bqPlayerBufferQueueInterface = 0;
}
//释放混音器
if(outputMixObject){
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = 0;
}
//释放引擎
if(engineObject){
(*engineObject)->Destroy(engineObject);
engineObject = 0;
engineInterface = 0;
}
}
5 队列
当内容没有被添加到队列的时候,需要进行释放
if (work) {
q.push(new_value);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}else{
releaseCallback(&new_value);
}
6 释放Window
public void setSurfaceView(SurfaceView surfaceView) {
if(null != holder){
holder.removeCallback(this);
}
holder = surfaceView.getHolder();
holder.addCallback(this);
}
public void release(){
holder.removeCallback(this);
native_release();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_cn_ray_player_DNPlayer_native_1stop(JNIEnv *env, jobject instance) {
if (ffmpeg) {
ffmpeg->stop();
}
DELETE(helper);
}
JNIEXPORT void JNICALL
Java_com_cn_ray_player_DNPlayer_native_1release(JNIEnv *env, jobject instance) {
pthread_mutex_lock(&mutex);
if (window) {
//把老的释放
ANativeWindow_release(window);
window = 0;
}
pthread_mutex_unlock(&mutex);
}
7 读取限制
注意:AudioChannel和VideoChannel的frames也要限制大小,否则会OOM
void DNFFmpeg::_start() {
//读取文件的时候没有网络请求,一下子读完了,可能导致oom
//特别是读本地文件的时候 一下子就读完了
if (audioChannel && audioChannel->packets.size() > 100) {
//10ms
av_usleep(1000 * 10);
continue;
}
}
8 AudioChannel释放AVFrame
int AudioChannel::getPcm() {
...
if (!isPlaying) {
if (ret) {
releaseAvFrame(&frame);
}
return data_size;
}
...
releaseAvFrame(&frame);
return data_size;
}