2017-01-21 134 views
1

私は、フレームを読み込んでキューに入れるためのスレッドを生成するクラスを実装しており、メインスレッドはこれらのフレームを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ループでスキップされたフレームのリソースを解放する必要がありました。

答えて

1

無料ではありませんbuffer。あなたの流路を確認してください。今のところ、それは本当に意味をなさないものです。また、ivan_onysが何を答えたかを考えてみてください。


編集:私は書いたよう

、流れを確認してください。次の3つの割り当てがあります

  • PFRAMEを
  • pFrameRGB
  • バッファ

しかし、このコマンドはtrueされていない場合にのみ、あなたはそれらを解放

if (av_read_frame(pFormatCtx, &packet) >= 0)if (frameFinished)bufferんリリースされました。

ここに問題があるようです。

 if (av_read_frame(pFormatCtx, &packet) >= 0) { 
     ... 
     if (frameFinished) { 
//   av_frame_unref(pFrame); --> remove this line 
//   av_frame_free(&pFrame); --> remove this line 
     } 
     } 
     // here is the body from while 
     // release it here - unconditional 
     av_packet_unref(&packet); 
     av_free_packet(&packet); 
     av_free(buffer); 
    } // while 
}  // frameReaderThreadFunc() 
+0

avpicture_fill()はバッファをframe-> data [0]にバインドしないので、curFrame.frame-> data [0]でav_free()を呼び出すと動作するはずですか? – se210

+0

私は明示的にバッファポインタを保存してav_free()を呼び出そうとしましたが、それでも同じ振る舞いを示しています。 また、新しいフレームの設定の順序を更新しました。いくつかの異なるソリューションを試してみると、それを元に戻すのを忘れてしまったようです。 – se210

+0

更新された回答ありがとうございます。無条件にメモリを解放することは理にかなっています。私はあなたの提案を採用しましたが、私はまだ同じ問題があります。前に述べたように、avpicture_fill()はframe-> data [0] = bufferを割り当てます。バッファはglTexSubImage2D()で使用する必要がある実際のイメージデータを含んでいるので、画面に表示するまでは解放できません(そうでなければsegfaultを取得します)。だから私はav_free(curFrame.frame-> data [0])を呼び出します。新しいフレームをテクスチャマッピングした後の前のフレームに表示する。 – se210

1

は、私はここで複数の問題を参照してください。それが割り当てられていないとき

  1. 試みは、割り当てられたメモリを解放します。あなたが成功した上記のループの本体に割り当てられたすべてのメモリを解放する必要があり続ける前に

    AVFrame* pFrameRGB = av_frame_alloc(); 
    if (pFrameRGB == nullptr) 
    { 
        av_frame_free(&pFrameRGB); 
        continue; 
    } 
    

:割り当てられたメモリを(1からPFRAMEが解放されません)解放されない

AVFrame* pFrame = av_frame_alloc(); 
if (pFrame == nullptr) 
{ 
    av_frame_free(&pFrame); 
    continue; 
} 
  • +0

    は、私はあなたの提案を試みたが、問題を解決していないようだ:whileが終了する前に、私はすべてのポインタを解放します。メモリは非常に速くいっぱいに見えるので、イメージバッファの割り当てに関係していると思います。 – se210

    関連する問題