jeudi 22 juin 2023

FFmpeg avcodec C implementation producing distorted frames while converting YUV frame to jpg

I have a YUV frame that I need to convert to jpg. For the frames with resolution 1920x1080 or 2560x1080 or 2560x880 or 640x360, I'm easily able to encode and create a jpg using the following code. The frames with a resolution of 1512x982 with 12 bit per pixel I am getting a distorted image using the same code. Using the following ffmpeg command, I am able to get perfect looking jpg image from the same 1512x982 resolution YUV file:

fmpeg -y -s:v 1512x982 -pix_fmt yuv420p -i 0.yuv 0.jpg

Please help with the code part, as in what I may be missing here.

Distorted jpg

#include "ffmpeg-helper.h"

AVFrame * FFmpegHelper::allocPicture(enum AVPixelFormat pix_fmt, int width, int height)
{
    // Allocate a frame
    AVFrame * frame = av_frame_alloc();

    if (frame == NULL) {
        return NULL;
    }

    frame -> width = width;
    frame -> height = height;
    frame -> format = pix_fmt;
    int ret = av_frame_get_buffer(frame, 0);

    if (ret < 0) {
        return NULL;
    }
    ret = av_frame_make_writable(frame);
    if (ret < 0) {
        return NULL;
    }
    return frame;
}

void FFmpegHelper::frameConversion(char * y, char * u, char * v,
    int srcWidth, int srcHeight, string filePath)
{
    avcodec_register_all();
    AVFrame * src = allocPicture(AVPixelFormat::AV_PIX_FMT_YUV420P, srcWidth, srcHeight);
    src -> data[0] = (uint8_t * ) y;
    src -> data[1] = (uint8_t * ) u;
    src -> data[2] = (uint8_t * ) v;
    src -> pts = 1;

    auto encoderContext = getEncoderContext(AV_CODEC_ID_MJPEG, srcWidth, srcHeight);
    if (encoderContext == NULL) return;

    FILE * frameFile = fopen(filePath.c_str(), "ab+");

    auto pkt = av_packet_alloc();
    encode(encoderContext, src, pkt, frameFile);
    encode(encoderContext, NULL, pkt, frameFile);

    // memory cleanup
    avcodec_free_context( & encoderContext);
    av_packet_unref(pkt);
    av_frame_free( & src);
    fclose(frameFile);
}

void FFmpegHelper::encode(AVCodecContext * enc_ctx, AVFrame * frame, AVPacket * pkt, FILE * outfile)
{
    /* send the frame to the encoder */
    if (frame == NULL) {
        return;
    }
    int ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0) {
        return;
    }
    ret = avcodec_receive_packet(enc_ctx, pkt);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return;
    if (ret < 0) return;
    fwrite(pkt -> data, pkt -> size, 1, outfile);

}

AVCodecContext * FFmpegHelper::getEncoderContext(AVCodecID extension, int width, int height)
{
    auto codec = avcodec_find_encoder(extension);
    if (!codec) {
        return NULL;
    }
    auto encoderContext = avcodec_alloc_context3(codec);
    encoderContext -> width = width;
    encoderContext -> height = height;
    encoderContext -> pix_fmt = AVPixelFormat::AV_PIX_FMT_YUVJ420P;
    AVRational frameRate = {
        1,
        1
    };
    encoderContext -> time_base = frameRate;
    if (avcodec_open2(encoderContext, codec, NULL) < 0) {
        return;
    }

    return encoderContext;
}

The code starts from the function 'frameConversion' where I am providing data for Y, U, and V along with frame dimensions and file path to write.

Aucun commentaire:

Enregistrer un commentaire