2016-11-28 122 views
0

我的目標是將我解碼的幀寫入文件。我知道我很好地捕捉了它,因爲它顯示在我的SDL播放中,並且之後我沒有任何問題對它進行編碼。但似乎我無法正確地將幀寫入文件。這裏是代碼:無法將YUV幀從ffmpeg寫入.yuv文件

#define PIXFMT AV_PIX_FMT_YUV420P 
#define WIDTH 1280 
#define HEIGHT 720 
// initialize SWS context for software scaling 

sws_ctx = sws_getContext(pCodecCtx->width, 
    pCodecCtx->height, 
    pCodecCtx->pix_fmt, 
    WIDTH, 
    HEIGHT, 
    PIXFMT, 
    SWS_LANCZOS, 
    NULL, 
    NULL, 
    NULL 
); 

FfmpegEncoder enc("rtsp://127.0.0.1:1935/live/myStream", pParser); 
//SetPixelArray(); 

i = 0; 
enc.FillYuvImage(pFrameRGB, 0, this->pCodecCtx->width, this->pCodecCtx->height); 
FILE *pFile; 
while (av_read_frame(pFormatCtx, &packet) >= 0 && !exit) { 
    if (packet.stream_index == videoindex) { 
     // Decode video frame 
     avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 

     if (frameFinished) { 

      i++; 
      sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); 
      if (i < 500) 
      { 
       pFile = fopen(std::string("./screenshots/screen.yuv").c_str(), "a+"); 
       for (int y = 0; y < pCodecCtx->height; y++) 
       { 
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, pCodecCtx->width * pCodecCtx->height, pFile); 
        fwrite(pFrame->data[1] + y * pFrame->linesize[1], 1, pCodecCtx->width * pCodecCtx->height/4, pFile); 
        fwrite(pFrame->data[2] + y * pFrame->linesize[2], 1, pCodecCtx->width * pCodecCtx->height/4, pFile); 
       } 
       fclose(pFile); 
      } 
      enc.encodeFrame(pFrameRGB); 
     } 
    } 
    // Free the packet that was allocated by av_read_frame 
    av_free_packet(&packet); 
} 

當它試圖寫入幀時,程序崩潰。

+0

檢查fopen是否成功。 – MayurK

回答

0

首先,感謝您的所有意見和答案! 這裏是固定的代碼:

while (av_read_frame(pFormatCtx, &packet) >= 0 && !exit && i < 1000) { 
    if (packet.stream_index == videoindex) { 
     // Decode video frame 
     avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 

     if (frameFinished) { 

      i++; 
      sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); 
      if ((pFile = fopen(std::string("./screenshots/screen.yuv").c_str(), "ab")) == NULL) 
       continue; 
      fwrite(pFrameRGB->data[0], 1, pCodecCtx->width * pCodecCtx->height, pFile); 
      fwrite(pFrameRGB->data[1], 1, pCodecCtx->width * pCodecCtx->height/4, pFile); 
      fwrite(pFrameRGB->data[2], 1, pCodecCtx->width * pCodecCtx->height/4, pFile); 
      fclose(pFile); 
      enc.encodeFrame(pFrameRGB); 
     } 
    } 
    // Free the packet that was allocated by av_read_frame 
    av_free_packet(&packet); 
} 

我之前使用錯誤的幀,作爲藏漢錯誤的數據和大小。

+0

這可能會產生錯誤的結果;不能保證平面中的各條線是連續的,因此您不能將它們編寫爲單個數據塊。 IIRC libavutil將「填充」分配的圖片,以便每行對齊到32個字節。它將適用於1280x720,但可能會因不同的分辨率而失敗(嘗試使用「奇怪」尺寸的視頻,例如258x142)。 –

0

正如我在評論中提到的,您需要檢查fopen是否成功。

但我認爲問題是你寫的比緩衝區中存在的東西多。我知道關於yuv的知識有限。我認爲你應該做以下事情。這是基於我從單幀YUV420圖片this page的理解。

abcfor (int y = 0; y < pCodecCtx->height; y++) //plane 0: same width and height 
{ 
    fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, pCodecCtx->width, pFile); 
} 


for (int y = 0; y < pCodecCtx->height/4; y++) //plane1: Same width and quarter height. 
{ 
    fwrite(pFrame->data[1] + y * pFrame->linesize[1], 1, pCodecCtx->width, pFile); 
} 

for (int y = 0; y < pCodecCtx->height/4; y++) //plane2: Same width and quarter height. 
{ 
    fwrite(pFrame->data[2] + y * pFrame->linesize[2], 1, pCodecCtx->width, pFile); 
} 

請注意,寫入順序取決於顏色格式。我希望您關注每次迭代中循環迭代次數和fwrite()中的字節數。

+1

對於飛機1和2,YUV420P具有'高度'線,每個'寬度'寬平面0和'高度'/ 2線每個'寬度'/ 2寬。但'.yuv'擴展通常意味着YUV444P像素格式...並且即使在YUV420P中,它也可能是平面的而不是行交錯的色度。 –

+0

你的意思是,對於plane1和plane2,for循環是從0到height/2並且做fwrite寬度/ 2字節? – MayurK

+0

是的。平面1和2是平面0在每個方向上尺寸的一半。 –