1

I want to use FFmpeg4.2.2 to convert the input NV12 format to output NV12 format with the same height and width. I used sws_scale conversion, but the output frame's colors are all green.

P.S. It seems no need to use swscale to get the same width,same height and same format frame,but it is neccessary in my project for dealing with other frames.

I have successfully converted the input NV12 format to output NV12 format with the different height and width, the output frame's colors were right.But I FAILED to convert NV12 to NV12 with the same height and width. It was so weird, I couldn't know why:(

I want to know what the reason is and what I should do. The following is my code.swsCtx4 was used for converting NV12 format to output NV12 format. Others were used for other formats converted test. Thank you for you help~

//the main code is    
    AVFrame* frame_nv12 = av_frame_alloc();
    frame_nv12->width = in_width;
    frame_nv12->height = in_height;
    frame_nv12->format = AV_PIX_FMT_NV12;
    uint8_t* frame_buffer_nv12 = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_NV12, in_width, in_height , 1));
    av_image_fill_arrays(frame_nv12->data, frame_nv12->linesize, frame_buffer_nv12, AV_PIX_FMT_NV12, in_width, in_height, 1);


    AVFrame* frame2_nv12 = av_frame_alloc();
    frame2_nv12->width = in_width1;
    frame2_nv12->height = in_height1;
    frame2_nv12->format = AV_PIX_FMT_NV12;

    uint8_t* frame2_buffer_nv12 = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_NV12, in_width1, in_height1, 1));
    av_image_fill_arrays(frame2_nv12->data, frame2_nv12->linesize, frame2_buffer_nv12, AV_PIX_FMT_NV12, in_width1, in_height1, 1);
 
    SwsContext* swsCtx4 = nullptr;
    swsCtx4 = sws_getContext(in_width, in_height, AV_PIX_FMT_NV12, in_width1, in_height1, AV_PIX_FMT_NV12,
        SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    printf("swsCtx4\n");
 
    ret = sws_scale(swsCtx4, frame_nv12->data, frame_nv12->linesize, 0, frame_nv12->height, frame2_nv12->data, frame2_nv12->linesize);
        if (ret < 0) {
            printf("sws_4scale failed\n");
        }
 
//the complete code
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#include <seeker/loggerApi.h>
#include "seeker/common.h"
#include <iostream>

//解决原因:pts设置为0,dts设置为0
#define FILE_SRC "testPicFilter.yuv" //源文件
#define FILE_DES "test11.yuv" //源文件

int count = 0;


int main(int argc, char* argv[])
{
    av_register_all();

    int ret = 0;
    
    //std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    int count1 = 1;
    int piccount;
    int align = 1;


    /*打开输入yuv文件*/
    FILE* fp_in = fopen(FILE_SRC, "rb+");
    if (fp_in == NULL)
    {
        printf("文件打开失败\n");
        return 0;
    }
    int in_width = 640;
    int in_height = 360;
    int in_width1 = 640;
    int in_height1 = 360;
    


    /*处理后的文件*/
    FILE* fp_out = fopen(FILE_DES, "wb+");
    if (fp_out == NULL)
    {
        printf("文件创建失败\n");
        return 0;
    }
    char buff[50];

    AVFrame* frame_in = av_frame_alloc();
    unsigned char* frame_buffer_in;
    frame_buffer_in = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    /*根据图像设置图像指针和内存对齐方式*/
    av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in, AV_PIX_FMT_YUV420P, in_width, in_height, 1);

    frame_in->width = in_width;
    frame_in->height = in_height;
    frame_in->format = AV_PIX_FMT_YUV420P;


    //输入yuv转成frame_nv12
    AVFrame* frame_nv12 = av_frame_alloc();
    frame_nv12->width = in_width;
    frame_nv12->height = in_height;
    frame_nv12->format = AV_PIX_FMT_NV12;
    uint8_t* frame_buffer_nv12 = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_NV12, in_width, in_height , 1));
    av_image_fill_arrays(frame_nv12->data, frame_nv12->linesize, frame_buffer_nv12, AV_PIX_FMT_NV12, in_width, in_height, 1);


    AVFrame* frame2_nv12 = av_frame_alloc();
    frame2_nv12->width = in_width1;
    frame2_nv12->height = in_height1;
    frame2_nv12->format = AV_PIX_FMT_NV12;

    uint8_t* frame2_buffer_nv12 = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_NV12, in_width1, in_height1, 1));
    av_image_fill_arrays(frame2_nv12->data, frame2_nv12->linesize, frame2_buffer_nv12, AV_PIX_FMT_NV12, in_width1, in_height1, 1);


    
    //输入rgb转成yuv
    AVFrame* frame_yuv = av_frame_alloc();
    frame_yuv->width = in_width;
    frame_yuv->height = in_height;
    frame_yuv->format = AV_PIX_FMT_YUV420P;
    uint8_t* frame_buffer_yuv = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, frame_buffer_yuv,
        AV_PIX_FMT_YUV420P, in_width, in_height, 1);



    SwsContext* swsCtx = nullptr;
    swsCtx = sws_getContext(in_width, in_height, AV_PIX_FMT_YUV420P, in_width, in_height, AV_PIX_FMT_NV12,
        SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    printf("swsCtx\n");

    SwsContext* swsCtx4 = nullptr;
    swsCtx4 = sws_getContext(in_width, in_height, AV_PIX_FMT_NV12, in_width1, in_height1, AV_PIX_FMT_NV12,
        SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    printf("swsCtx4\n");

    
    SwsContext* swsCtx2 = nullptr;
    swsCtx2 = sws_getContext(in_width1, in_height1, AV_PIX_FMT_NV12, in_width, in_height, AV_PIX_FMT_YUV420P,
        SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    printf("swsCtx2\n");





    while (1)
    {


        count++;

        if (fread(frame_buffer_in, 1, in_width * in_height * 3 / 2, fp_in) != in_width * in_height * 3 / 2)
        {
            break;
        }

        frame_in->data[0] = frame_buffer_in;
        frame_in->data[1] = frame_buffer_in + in_width * in_height;
        frame_in->data[2] = frame_buffer_in + in_width * in_height * 5 / 4;


            //转NV12格式
        int ret = sws_scale(swsCtx, frame_in->data, frame_in->linesize, 0, frame_in->height, frame_nv12->data, frame_nv12->linesize);
        if (ret < 0) {
            printf("sws_scale swsCtx failed\n");
        }


        ret = sws_scale(swsCtx4, frame_nv12->data, frame_nv12->linesize, 0, frame_nv12->height, frame2_nv12->data, frame2_nv12->linesize);
        if (ret < 0) {
            printf("sws_scale  swsCtx4 failed\n");
        }
        

        if (ret > 0) {
        
            int ret2 = sws_scale(swsCtx2, frame2_nv12->data, frame2_nv12->linesize, 0, frame2_nv12->height, frame_yuv->data, frame_yuv->linesize);
            if (ret2 < 0) {
                printf("sws_scale swsCtx2 failed\n");
            }
            I_LOG("frame_yuv:{},{}", frame_yuv->width, frame_yuv->height);

        
            //I_LOG("frame_yuv:{}", frame_yuv->format);

            if (frame_yuv->format == AV_PIX_FMT_YUV420P)
            {

                for (int i = 0; i < frame_yuv->height; i++)
                {
                    fwrite(frame_yuv->data[0] + frame_yuv->linesize[0] * i, 1, frame_yuv->width, fp_out);
                }
                for (int i = 0; i < frame_yuv->height / 2; i++)
                {
                    fwrite(frame_yuv->data[1] + frame_yuv->linesize[1] * i, 1, frame_yuv->width / 2, fp_out);
                }
                for (int i = 0; i < frame_yuv->height / 2; i++)
                {
                    fwrite(frame_yuv->data[2] + frame_yuv->linesize[2] * i, 1, frame_yuv->width / 2, fp_out);
                }
                printf("yuv to file\n");
            }
        }

    }


    fclose(fp_in);
    fclose(fp_out);
    av_frame_free(&frame_in);
    av_frame_free(&frame_nv12);
    av_frame_free(&frame_yuv);
    sws_freeContext(swsCtx);
    sws_freeContext(swsCtx2);
    sws_freeContext(swsCtx4);

    //std::this_thread::sleep_for(std::chrono::milliseconds(8000));

    return 0;

}



Chun Wang
  • 19
  • 2
  • Can you please post a reproducible code sample that we can also build and execute? What version of FFmpeg are you using? Please add a `C` tag. – Rotem Aug 31 '22 at 16:23
  • Thank you for your suggestion, so I modified my post based on it. Looking forward to your reply : ) – Chun Wang Sep 02 '22 at 03:50
  • You should narrow down the problem on your own. Do a unit test with only one SwsContext, one input AVFrame and one output AVFrame. – relent95 Sep 02 '22 at 05:38
  • I already did the unit tests like yuv420p->nv12->yuv420p , yuv420p->yuv420p with the same height and width, nv12->nv12 with different height or width, they all goes right . Only when converting nv12->nv12 with the same height and width, the color wil become green. And I couldn't figure out why, and that is the reason why I posted this problem and wish you nice guys to give me some suggestions about this weird problem: ) – Chun Wang Sep 02 '22 at 07:31

1 Answers1

0

You found a bug in the ffmpeg library. Report it!(See this.)

As a remedy, I suggest you call av_frame_copy() if the format, width and height of a source frame are the same as ones of a destination frame.

As a side note, you can see the problematic code at line #1817.

relent95
  • 3,703
  • 1
  • 14
  • 17