私は、フレームを読み込んでキューに入れるためのスレッドを生成するクラスを実装しており、メインスレッドはこれらのフレームをOpenGL経由で表示します。 OpenGLテクスチャに画像データをバインドした後に割り当てられたメモリを解放しようとしましたが、メモリが解放されていないようです。メモリ使用量は、システムがメモリ不足になるまで増加を続け、最終的にメモリ割り当ての失敗により新しいフレームを取得することはできません。誰かが私が逃したかもしれないことについて私を助けてくれますか?ありがとうございました。ffmpegを使用するとメモリリークが発生する
これは、フレームリーダースレッドのコードである:
void AVIReader::frameReaderThreadFunc()
{
AVPacket packet;
while (readFrames) {
// Allocate necessary memory
AVFrame* pFrame = av_frame_alloc();
if (pFrame == nullptr)
{
continue;
}
AVFrame* pFrameRGB = av_frame_alloc();
if (pFrameRGB == nullptr)
{
av_frame_free(&pFrame);
continue;
}
// Determine required buffer size and allocate buffer
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
uint8_t* buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
if (buffer == nullptr)
{
av_frame_free(&pFrame);
av_frame_free(&pFrameRGB);
continue;
}
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
if (av_read_frame(pFormatCtx, &packet) >= 0) {
// Is this a packet from the video stream?
if (packet.stream_index == videoStream) {
// Decode video frame
int frameFinished;
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (frameFinished) {
// Convert the image from its native format to RGB
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
VideoFrame vf;
vf.frame = pFrameRGB;
vf.pts = av_frame_get_best_effort_timestamp(pFrame) * time_base;
frameQueue.enqueue(vf);
av_frame_unref(pFrame);
av_frame_free(&pFrame);
}
}
//av_packet_unref(&packet);
av_free_packet(&packet);
}
}
}
これはキューに入れられたフレームグラブおよびOpenGLテクスチャにバインドコードです。私は明示的に前のフレームを保存して次のフレームに切り替えるまで保存します。それ以外の場合は、segfaultが発生するようです。
class VideoFrame {
public:
AVFrame* frame;
double pts;
};
更新:
void AVIReader::GrabAVIFrame() { if (curFrame.pts >= clock_pts) { return; } if (frameQueue.empty()) return; // Get a packet from the queue VideoFrame videoFrame = frameQueue.top(); while (!frameQueue.empty() && frameQueue.top().pts < clock_pts) { videoFrame = frameQueue.dequeue(); } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, videoFrame.frame->data[0]); // release previous frame if (curFrame.frame) { av_free(curFrame.frame->data[0]); } av_frame_unref(curFrame.frame); // set current frame to new frame curFrame = videoFrame; }
frameQueue
は、次のように定義VideoFrameがを保持しているスレッドセーフなプライオリティキューです新しいフレームに現在のフレームを設定した順序で愚かなエラーが発生しました。私はいくつかの事を試した後にそれを元に戻すのを忘れていました。 @ ivan_onysの提案も取り入れましたが、それは問題を解決していないようです。アップデート2:私は無条件PFRAMEとパケットを解放するために@Alバンディの提案を採択したが、問題は解決しません。
バッファはglTexSubImage2D()で使用する必要のある実際のイメージデータを含んでいるので、画面に表示するまでは解放できません(それ以外の場合はsegfaultを取得します)。 avpicture_fill()はframe-> data [0] = bufferを代入するので、av_free(curFrame.frame-> data [0]);を呼び出すと思います。テクスチャマッピング後の前のフレームで、新しいフレームは割り当てられたバッファを解放すべきである。解決
void AVIReader::frameReaderThreadFunc()
{
AVPacket packet;
while (readFrames) {
// Allocate necessary memory
AVFrame* pFrame = av_frame_alloc();
if (pFrame == nullptr)
{
continue;
}
AVFrame* pFrameRGB = av_frame_alloc();
if (pFrameRGB == nullptr)
{
av_frame_free(&pFrame);
continue;
}
// Determine required buffer size and allocate buffer
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
uint8_t* buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
if (buffer == nullptr)
{
av_frame_free(&pFrame);
av_frame_free(&pFrameRGB);
continue;
}
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
if (av_read_frame(pFormatCtx, &packet) >= 0) {
// Is this a packet from the video stream?
if (packet.stream_index == videoStream) {
// Decode video frame
int frameFinished;
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (frameFinished) {
// Convert the image from its native format to RGB
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
VideoFrame vf;
vf.frame = pFrameRGB;
vf.pts = av_frame_get_best_effort_timestamp(pFrame) * time_base;
frameQueue.enqueue(vf);
}
}
}
av_frame_unref(pFrame);
av_frame_free(&pFrame);
av_packet_unref(&packet);
av_free_packet(&packet);
}
}
:パケットが非ビデオストリーム(例えばオーディオ)からであった場合には、漏れが起こったが判明ここ
更新フレームリーダスレッドコードです。また、GrabAVIFrame()のwhileループでスキップされたフレームのリソースを解放する必要がありました。
avpicture_fill()はバッファをframe-> data [0]にバインドしないので、curFrame.frame-> data [0]でav_free()を呼び出すと動作するはずですか? – se210
私は明示的にバッファポインタを保存してav_free()を呼び出そうとしましたが、それでも同じ振る舞いを示しています。 また、新しいフレームの設定の順序を更新しました。いくつかの異なるソリューションを試してみると、それを元に戻すのを忘れてしまったようです。 – se210
更新された回答ありがとうございます。無条件にメモリを解放することは理にかなっています。私はあなたの提案を採用しましたが、私はまだ同じ問題があります。前に述べたように、avpicture_fill()はframe-> data [0] = bufferを割り当てます。バッファはglTexSubImage2D()で使用する必要がある実際のイメージデータを含んでいるので、画面に表示するまでは解放できません(そうでなければsegfaultを取得します)。だから私はav_free(curFrame.frame-> data [0])を呼び出します。新しいフレームをテクスチャマッピングした後の前のフレームに表示する。 – se210