使用FFmpeg解码视频帧时获得的撕裂图像

时间:2016-03-21 23:24:04

标签: c++ ffmpeg

我正在尝试使用dranger.com上的教程解码图像。以下是我正在使用的代码。除了 pgm_save()函数并替换已弃用的函数之外,代码几乎不受影响。

该程序已成功编译,但当我尝试处理视频时,我的撕裂效果如下:image1和此image2

(附带问题:我已尝试替换 av_image_copy_to_buffer()弃用的 avpicture_fill(),但我收到了访问冲突错误,所以我把它保留原样。我想知道是否有一种正确的方法让我将帧数据分配给缓冲区。)

我使用的图书馆是 ffmpeg-20160219-git-98a0053-win32-dev 。如果有人可以帮助我,我会非常感激。

// Decode video and save frames

char filename[] = "test%0.3d.ppm";
static void ppm_save(unsigned char *buf, int wrap, int xsize, int ysize,
                     int framenum )
{

    char filenamestr[sizeof(filename)];
    FILE *f;
    int i;

    sprintf_s(filenamestr, sizeof(filenamestr), filename, framenum);
    fopen_s(&f,filenamestr,"w");
    fprintf(f,"P6\n%d %d\n%d\n",xsize,ysize,255);
    for(i=0;i<ysize;i++)
        fwrite(buf + i * wrap,1,xsize*3,f);
    fclose(f);
}



int main(int argc, char** argv)
{

    AVFormatContext *pFormatCtx = NULL;
    AVCodecContext *codecCtx= NULL;
    AVCodec *codec;

    int videoStream;
    int frameFinished;
    AVFrame *inframe;
    AVFrame *outframe;

    struct SwsContext *sws_ctx = NULL;

    AVPacket avpkt;
    unsigned int i;



    printf("Video decoding application\n");

    // Register all formats and codecs
    av_register_all();

    // Open video file
    if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
        return -1; // Couldn't open file

    // Retrieve stream information
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
        return -1; // Couldn't find stream information

    // Dump information about file onto standard error (Not necessary)
    av_dump_format(pFormatCtx, 0, argv[1], 0);

    // Find the first video stream
    videoStream = -1;
    for (i = 0; i < pFormatCtx->nb_streams; i++)
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    if (videoStream == -1)
        return -1; // Didn't find a video stream

    /* find the video decoder */
    codec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codec->codec_id);
    if (!codec) {
        fprintf(stderr, "codec not found\n");
        exit(1);
    }

    codecCtx= avcodec_alloc_context3(codec);
    if(avcodec_copy_context(codecCtx, pFormatCtx->streams[i]->codec) != 0) {
        fprintf(stderr, "Couldn't copy codec context");
        return -1; // Error copying codec context
    }   

    /* open it */
    if (avcodec_open2(codecCtx, codec, NULL) < 0) {
        fprintf(stderr, "could not open codec\n");
        exit(1);
    }

    // Allocate video frame
    inframe= av_frame_alloc();
    if(inframe==NULL)
        return -1;

    // Allocate output frame
    outframe=av_frame_alloc();
    if(outframe==NULL)
        return -1;

    // Determine required buffer size and allocate buffer
    int numBytes=av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecCtx->width,
                    codecCtx->height,1);
    uint8_t* buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    // Assign appropriate parts of buffer to image planes in outframe
    // Note that outframe is an AVFrame, but AVFrame is a superset
    // of AVPicture


    avpicture_fill((AVPicture *)outframe, buffer, AV_PIX_FMT_RGB24,
         codecCtx->width, codecCtx->height );
    //av_image_copy_to_buffer(buffer, numBytes,
 //                           outframe->data, outframe->linesize,
 //                           AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height,1);

    // initialize SWS context for software scaling
    sws_ctx = sws_getContext(codecCtx->width,
               codecCtx->height,
               codecCtx->pix_fmt,
               codecCtx->width,
               codecCtx->height,
               AV_PIX_FMT_RGB24,
               SWS_BILINEAR,
               NULL,
               NULL,
               NULL
               );   


    // av_init_packet(&avpkt);


    i = 0;
    while(av_read_frame(pFormatCtx, &avpkt)>=0) {
        // Is this a packet from the video stream?
        if(avpkt.stream_index==videoStream) {
          // Decode video frame
          avcodec_decode_video2(codecCtx, inframe, &frameFinished, &avpkt);

          // Did we get a video frame?
          if(frameFinished) {
        // Convert the image from its native format to RGB
        sws_scale(sws_ctx, (uint8_t const * const *)inframe->data,
              inframe->linesize, 0, codecCtx->height,
              outframe->data, outframe->linesize);

        // Save the frame to disk
        if(++i%15 == 0)
            ppm_save(outframe->data[0], outframe->linesize[0], 
                        codecCtx->width, codecCtx->height, i);

          }
        }

    // Free the packet that was allocated by av_read_frame
    av_packet_unref(&avpkt);
    }


    // Free the RGB image
    av_free(buffer);
    av_frame_free(&outframe);

    // Free the original frame
    av_frame_free(&inframe);

    // Close the codecs
    avcodec_close(codecCtx);
    av_free(codecCtx);

    // Close the video file
    avformat_close_input(&pFormatCtx);


    printf("\n");


    return 0;
}

1 个答案:

答案 0 :(得分:1)

解决。问题出在 pgm_save()函数中。我在编写二进制数据时将文件作为文本文件打开。正确的模式应该是“wb”而不是“w”

正确的代码应该是:

// fopen_s(&f,filenamestr,"w"); // text mode
fopen_s(&f,filenamestr,"wb"); // binary mode

现在感觉很傻,我终于意识到问题与ffmpeg无关。从好的方面来说,至少现在我知道代码是有效的。 ; - )