ffmpeg - sws_scale 生成格式错误的视频

标签 ffmpeg cairo yuv libav swscale

我必须使用 sws_scale 对从 CAIRO_FORMAT_ARGB32 到 AV_PIX_FMT_YUV420P 的一系列帧进行编码。从 ffmpeg 文档我了解到源格式的 AV 等效项是 AV_PIX_FMT_ARGB 所以这是我的代码:

 // Set up conversion context
    img->sws_ctx = sws_getCachedContext(
        img->sws_ctx,
        img->video_size[0],
        img->video_size[1],
        AV_PIX_FMT_ARGB,
        img->video_size[0],
        img->video_size[1],
        AV_PIX_FMT_YUV420P,
        SWS_BILINEAR,
        NULL,
        NULL,
        NULL);

    width  = cairo_image_surface_get_width( surface );
    height = cairo_image_surface_get_height( surface );
    stride = cairo_image_surface_get_stride( surface );
    pix    = cairo_image_surface_get_data( surface );
    const int in_linesize[1] = { stride };
    
    sws_scale(  img->sws_ctx, (const uint8_t * const *) &pix, in_linesize, 0,
                img->video_size[1], img->video_frame->data, img->video_frame->linesize);
    img->video_frame->pts++;
遗憾的是,视频无法播放,VLC 显示了一堆这些无用的消息:
[h264 @ 0x7f6ce0cbc1c0] mmco: unref short failure
[h264 @ 0x7f6ce0c39a80] co located POCs unavailable
[h264 @ 0x7f6ce0c82800] co located POCs unavailable
[h264 @ 0x7f6ce0c9f400] mmco: unref short failure
编码过程运行良好。我也试过 const int in_linesize[1] = { 3 * width };我哪里错了?

最佳答案

以下答案显示了如何使用 sws_scale 用于将 ARGB 转换为 YUV420p。
您必须进行一些调整以将转换集成到您的代码中。
代码示例是“独立”的 sws_scale 示例,它不使用 CAIRO。

使用 FFmpeg(命令行工具)创建 BGRA 输入样本:

ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -vcodec rawvideo -pix_fmt argb -frames 1 -f rawvideo argb_image.bin

以下代码示例应用以下阶段:
  • 从二进制文件中读取 ARGB(样本)输入。
  • 从存储 YUV420p 输出中分配内存缓冲区。
  • 获取 SWS 上下文。
  • 应用颜色转换。
  • 将 YUV420p 输出图像写入二进制文件(用于测试)。
  • 释放分配的内存。

  • C++ 代码示例:
    #include <stdio.h>
    #include <string.h>
    #include <stdint.h>
    
    extern "C"
    {
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
    }
    
    
    int main()
    {
        //Use FFmpeg for building raw ARGB image (used as input).
        //ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -vcodec rawvideo -pix_fmt argb -frames 1 -f rawvideo argb_image.bin
        
        const int width = 192;
        const int height = 108;
        unsigned char* argb_in = new uint8_t[width * height * 4];   //Allocate 4 bytes per pixel (applies ARGB)
    
        const enum AVPixelFormat out_pix_fmt = AV_PIX_FMT_YUV420P;
    
        //Read input image for binary file (for testing)
        ////////////////////////////////////////////////////////////////////////////
        FILE* f = fopen("argb_image.bin", "rb"); //For using fopen in Visual Studio, define: _CRT_SECURE_NO_WARNINGS (or use fopen_s).
        fread(argb_in, 1, width * height * 4, f);
        fclose(f);
        ////////////////////////////////////////////////////////////////////////////    
    
        //Allocate output buffers:
        ////////////////////////////////////////////////////////////////////////////
        // YUV420p data is separated in three planes
        // 1. Y - intensity plane, resolution: width x height
        // 2. U - Color plane, resolution: width/2 x height/2
        // 3. V - Color plane, resolution: width/2 x height/2
    
        int out_linesize[4] = {0, 0, 0, 0};
        uint8_t* out_planes[4] = { nullptr, nullptr, nullptr, nullptr };   
    
        int sts = av_image_alloc(out_planes,    //uint8_t * pointers[4], 
                                 out_linesize,  //int linesizes[4], 
                                 width,         //int w, 
                                 height,        //int h, 
                                 out_pix_fmt,   //enum AVPixelFormat pix_fmt, 
                                 32);           //int align);   //Align to 32 bytes address may result faster execution time compared to 1 byte aligenment.
    
        if (sts < 0)
        {
            printf("Error: av_image_alloc response = %d\n", sts);
            return -1;
        }
        ////////////////////////////////////////////////////////////////////////////
    
        
        
        //Get SWS context
        ////////////////////////////////////////////////////////////////////////////
        struct SwsContext* sws_context = nullptr;
    
        sws_context = sws_getCachedContext(sws_context,         //struct SwsContext *context,
                                           width,               //int srcW,
                                           height,              //int srcH,
                                           AV_PIX_FMT_ARGB,     //enum AVPixelFormat srcFormat,
                                           width,               //int dstW,
                                           height,              //int dstH,
                                           out_pix_fmt,         //enum AVPixelFormat dstFormat,
                                           SWS_BILINEAR,        //int flags,
                                           nullptr,             //SwsFilter *srcFilter,
                                           nullptr,             //SwsFilter *dstFilter,
                                           nullptr);            //const double *param);
    
        if (sws_context == nullptr)
        {
            printf("Error: sws_getCachedContext returned nullptr\n");
            return -1;
        }
        ////////////////////////////////////////////////////////////////////////////
    
    
        //Apply color conversion
        ////////////////////////////////////////////////////////////////////////////
        const int in_linesize[1] = { 4 * width }; // ARGB stride (4 bytes per pixel - assume data is continuous).
        const uint8_t* in_planes[1] = { argb_in };
    
        int response = sws_scale(sws_context,   //struct SwsContext *c, 
                                 in_planes,     //const uint8_t *const srcSlice[],
                                 in_linesize,   //const int srcStride[], 
                                 0,             //int srcSliceY, 
                                 height,        //int srcSliceH,
                                 out_planes,    //uint8_t *const dst[], 
                                 out_linesize); //const int dstStride[]);
    
    
        if (response < 0)
        {
            printf("Error: sws_scale response = %d\n", response);
            return -1;
        }
        ////////////////////////////////////////////////////////////////////////////
    
    
        //Write YUV420p output image to binary file (for testing)
        //You may execute FFmpeg after conversion for testing the output:
        //ffmpeg -y -f rawvideo -s 192x108 -pixel_format yuv420p -i yuv420p_image.bin rgb.png
        ////////////////////////////////////////////////////////////////////////////
        f = fopen("yuv420p_image.bin", "wb");
        fwrite(out_planes[0], 1, width * height, f);
        fwrite(out_planes[1], 1, width * height / 4, f);
        fwrite(out_planes[2], 1, width * height / 4, f);
        fclose(f);
        ////////////////////////////////////////////////////////////////////////////
    
    
        //Free allocated memory
        ////////////////////////////////////////////////////////////////////////////
        av_freep(out_planes);
        sws_freeContext(sws_context);
        delete[] argb_in;
        ////////////////////////////////////////////////////////////////////////////
    
        return 0;
    }
    

    为了测试输出,转换 yuv420p_image.bin使用 FFmpeg 转为 PNG 图像:
    ffmpeg -y -f rawvideo -s 192x108 -pixel_format yuv420p -i yuv420p_image.bin rgb.png
    
    rgb.png (FFmpeg转换的结果):
    enter image description here

    关于ffmpeg - sws_scale 生成格式错误的视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70291740/

    相关文章:

    video - ffmpeg - 合并 mp3 和 mp4(持续时间差异)

    c - 如何使用 cairo 确定 Gtk+ 3 中的非矩形绘图区域?

    python - 如何使用 easy_install 为 Python 2.7 的 Apple 版本安装 pycairo

    android - YUV (NV21) 到移动设备上的 BGR 转换( native 代码)

    c++ - 如何旋转yuv420数据?

    python - 使用python仅在特定时间间隔静音音频文件

    c++ - 从 16 位 PCM 中去除 C++ 中的音频噪声(嘶嘶声)

    shell脚本ffmpeg在2个作业后停止

    pdf-generation - gnuplot 中的 pdfcairo 'unknown or ambiguous terminal type'

    ffmpeg - 如何将 y4m 文件转换为 yuv?