cffmpeg

Green image when encoding a video frame to PNG


I'm wrote the following code to convert a decoded video frame to a PNG image. The code doesn't crash but the image data stored in 'avpkt' results in an all green image. What am I doing wrong? Any help would be appreciated.

// pFrame - the decoded frame
// avpkt - the packet to fill with the converted image
void convert_image(AVCodecContext *pCodecCtx, AVFrame *pFrame, AVPacket *avpkt, int *got_packet_ptr) {
    AVCodecContext *codecCtx;
    AVCodec *codec;

    *got_packet_ptr = 0;

    codec = avcodec_find_encoder(TARGET_IMAGE_CODEC);
    if (!codec) {
        printf("avcodec_find_decoder() failed to find decoder\n");
        goto fail;
    }

    codecCtx = avcodec_alloc_context3(codec);
    if (!codecCtx) {
        printf("avcodec_alloc_context3 failed\n");
        goto fail;
    }

    codecCtx->bit_rate = pCodecCtx->bit_rate;
    codecCtx->width = pCodecCtx->width;
    codecCtx->height = pCodecCtx->height;
    codecCtx->pix_fmt = TARGET_IMAGE_FORMAT;
    codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    codecCtx->time_base.num = pCodecCtx->time_base.num;
    codecCtx->time_base.den = pCodecCtx->time_base.den;

    if (!codec || avcodec_open2(codecCtx, codec, NULL) < 0) {
        printf("avcodec_open2() failed\n");
        goto fail;
    }

    int src_width = pCodecCtx->width;
    int src_height = pCodecCtx->height;
    enum PixelFormat src_pixfmt = pCodecCtx->pix_fmt;
    int dst_width = pCodecCtx->width;
    int dst_height = pCodecCtx->height;

    struct SwsContext *scalerCtx;

    scalerCtx = sws_getContext(src_width,
            src_height,
            src_pixfmt,
            dst_width,
            dst_height,
            TARGET_IMAGE_FORMAT,
            SWS_BILINEAR, //SWS_BICUBIC
            NULL, NULL, NULL);

    if (!scalerCtx) {
        printf("sws_getContext() failed\n");
        goto fail;
    }

    AVFrame *pSrcFrame = avcodec_alloc_frame();

    if (!pSrcFrame) {
        goto fail;
    }

    AVFrame *pFrameRGB = avcodec_alloc_frame();

    if (!pFrameRGB) {
        goto fail;
    }

    if (avpicture_fill((AVPicture *) pSrcFrame,
            pFrame->data,
            src_pixfmt,
            src_width,
            src_height) < 0) {

        printf("avpicture_fill() failed\n");
        goto fail;
    }

    int numBytes = avpicture_get_size(TARGET_IMAGE_FORMAT, src_width, src_height);
    uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    if (avpicture_fill((AVPicture *) pFrameRGB,
            buffer,
            TARGET_IMAGE_FORMAT,
            src_width,
            src_height) < 0) {
        printf("avpicture_fill() failed\n");
        goto fail;
    }

    sws_scale(scalerCtx,
              (const uint8_t * const *) pSrcFrame->data, 
              pSrcFrame->linesize,
              0,
              src_height, 
              pFrameRGB->data, 
              pFrameRGB->linesize);

    int ret = avcodec_encode_video2(codecCtx, avpkt, pFrameRGB, got_packet_ptr);

    if (ret < 0) {
        *got_packet_ptr = 0;
    }

    fail:
    if (codecCtx) {
        avcodec_close(codecCtx);
    }

    if (scalerCtx) {
        sws_freeContext(scalerCtx);
    }

    if (ret < 0 || !*got_packet_ptr) {
        av_free_packet(avpkt);
    }
}

Solution

  • The following code reads the first frame and saves it to PNG.

        #define CHECK_ERR(ERR) {if ((ERR)<0) return -1; }
    
        int convert_first_frame_to_png(std::string const & inputVideoFileName, std::string const & outputPngName)
        {
            av_register_all();
            avcodec_register_all();
    
            ::AVFormatContext * ctx = NULL;
            int err = avformat_open_input(&ctx, inputVideoFileName.c_str(), NULL, NULL);
            CHECK_ERR(err);
            err = av_find_stream_info(ctx);
            CHECK_ERR(err);
    
            AVCodec * codec = NULL;
            int strm = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
    
            AVCodecContext * codecCtx = ctx->streams[strm]->codec;
            err = avcodec_open2(codecCtx, codec, NULL);
            CHECK_ERR(err);
    
            SwsContext * swCtx = sws_getContext(codecCtx->width, 
                codecCtx->height, 
                codecCtx->pix_fmt, 
                codecCtx->width, 
                codecCtx->height, 
                PIX_FMT_RGB24, 
                SWS_FAST_BILINEAR, 0, 0, 0);
    
            for (;;)
            {
                AVPacket pkt;
                err = av_read_frame(ctx, &pkt);
                CHECK_ERR(err);
    
                if (pkt.stream_index == strm)
                {
                    int got = 0;
                    AVFrame * frame = avcodec_alloc_frame();
                    err = avcodec_decode_video2(codecCtx, frame, &got, &pkt);
                    CHECK_ERR(err);
    
                    if (got)
                    {
                        AVFrame * rgbFrame = avcodec_alloc_frame();
                        avpicture_alloc((AVPicture *)rgbFrame, PIX_FMT_RGB24, codecCtx->width, codecCtx->height);
                        sws_scale(swCtx, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize);
    
                        AVCodec *outCodec = avcodec_find_encoder(CODEC_ID_PNG);
                        AVCodecContext *outCodecCtx = avcodec_alloc_context3(codec);
                        if (!codecCtx) 
                            return -1;                  
    
                        outCodecCtx->width = codecCtx->width;
                        outCodecCtx->height = codecCtx->height;
                        outCodecCtx->pix_fmt = PIX_FMT_RGB24;
                        outCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
                        outCodecCtx->time_base.num = codecCtx->time_base.num;
                        outCodecCtx->time_base.den = codecCtx->time_base.den;
    
                        if (!outCodec || avcodec_open2(outCodecCtx, outCodec, NULL) < 0) {
                            return -1;
                        }
    
                        AVPacket outPacket;
                        av_init_packet(&outPacket);
                        outPacket.size = 0;
                        outPacket.data = NULL;
                        int gotFrame = 0;
                        int ret = avcodec_encode_video2(outCodecCtx, &outPacket, rgbFrame, &gotFrame);
                        if (ret >= 0 && gotFrame)
                        {
                            FILE * outPng = fopen(outputPngName.c_str(), "wb");
                            fwrite(outPacket.data, outPacket.size, 1, outPng);
                            fclose(outPng);
                        }
    
                        avcodec_close(outCodecCtx);
                        av_free(outCodecCtx);
    
                        break;
                    }
                    avcodec_free_frame(&frame);
                }
            }
        }