简体   繁体   English

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

[英]Torn images acquired when decoding video frames with FFmpeg

I am trying to decode the images using the tutorial at dranger.com.我正在尝试使用 dranger.com 上的教程解码图像。 Below is the code I'm working with.下面是我正在使用的代码。 The code is pretty much untouched aside from pgm_save() function and replacing the deprecated functions.除了pgm_save()函数和替换已弃用的函数之外,代码几乎没有改动

The program compiled successfully, but when I tried to process a video, I'm getting tearing effect like this: image1 and this image2 .程序编译成功,但是当我尝试处理视频时,我得到了这样的撕裂效果: image1和 this image2

(Side question: I've tried to replace avpicture_fill() which is deprecated with av_image_copy_to_buffer() but I'm getting an access violation error, so I left it as is. I wonder if there is a proper way for me to assign the frame data to a buffer.) 附带问题:我尝试用av_image_copy_to_buffer()替换已弃用的avpicture_fill(),但出现访问冲突错误,所以我将其保留原样。我想知道是否有合适的方法来分配帧数据到缓冲区。)

The library that I'm using is ffmpeg-20160219-git-98a0053-win32-dev .我使用的库是ffmpeg-20160219-git-98a0053-win32-dev Would really appreciate it if someone could help me with this.如果有人能帮我解决这个问题,我将不胜感激。

// 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;
}

Solved.解决了。 The problem lies in the pgm_save() function.问题在于pgm_save()函数。 I was opening the file as a text file when I was writing binary data.我在编写二进制数据时将文件作为文本文件打开。 The correct mode should have been "wb" not "w" .正确的模式应该是"wb"而不是"w"

The correct code should have been:正确的代码应该是:

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

It feels silly now that I realised that the problem is unrelated to ffmpeg after all.现在我意识到这个问题毕竟与 ffmpeg 无关,这感觉很傻。 On the bright side, at least now I know the code works.从好的方面来说,至少现在我知道代码有效。 ;-) ;-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM