[FFmpeg-trac] #10514(undetermined:new): swscale alignment requirements (and issues) in YUV420P to RGB conversions

FFmpeg trac at avcodec.org
Thu Aug 10 17:41:48 EEST 2023


#10514: swscale alignment requirements (and issues) in YUV420P to RGB conversions
--------------------------------------+----------------------------------
             Reporter:  vkonovets     |                     Type:  defect
               Status:  new           |                 Priority:  normal
            Component:  undetermined  |                  Version:  4.3.6
             Keywords:                |               Blocked By:
             Blocking:                |  Reproduced by developer:  0
Analyzed by developer:  0             |
--------------------------------------+----------------------------------
 I'm working on an API which wraps around `swscale` to do conversions on
 memory not strictly allocated for `AVFrame` (i.e my own allocations).  For
 some image dimensions I noticed visual artifacts and memory corruption.
 Although there are no alignment requirements for rows stated in the
 documentation, reading from Stack Overflow and various tickets here I
 understood that there is a minimum alignment requirement for allocations
 and padding padding at the end of the row (although this does not solve
 the issue).

 https://trac.ffmpeg.org/ticket/9331 points out what I believe is the
 issue, however that ticket was dismissed because the memory allocation was
 not aligned (I ensure it is in my sample).  To quote:

 //the assembly code accelerates the process of convert yuv to rgb by using
 sse2 register... when the width of video frame is not a multiply of 16,
 extra wrong yuv pixel is read from the memory and writing extra rgb value
 to the memory allocated for the output image and cause the invalid
 writing//.

 From that ticket and `get_video_buffer()` source code
 (https://ffmpeg.org/doxygen/4.3/frame_8c_source.html#l00109), which
 allocates memory for `AVFrame`, it follows that not just the row that
 needs to be padded, but also the row //dimension// needs to be padded.

 I attach to this ticket sample code which takes width as an argument which
 you can use to observe the effects:
 {{{#!cpp
 #include <stdlib.h>
 extern "C"
 {
 #include <libswscale/swscale.h>
 }

 bool absDiffTol(unsigned char a, unsigned char b, int tol)
 {
     return abs((int)a - (int)b) <= tol;
 }

 int main(int argc, char** argv)
 {
     int width = atoi(argv[1]);
     int height = 8;
     int alignment = 64;

     // YUV420P image
     int srcLinesize[8] = {width - width % alignment + alignment,
                           width / 2 - (width / 2) % alignment + alignment,
                           width / 2 - (width / 2) % alignment + alignment,
                           0, 0, 0, 0, 0};

     // RGB image
     int dstLinesize[8] = {width * 3 - (width * 3) % alignment + alignment,
                           0, 0, 0, 0, 0, 0, 0};


     unsigned char* srcData[8];
     srcData[0] = (unsigned char*)aligned_alloc(alignment, srcLinesize[0] *
 height);
     srcData[1] = (unsigned char*)aligned_alloc(alignment, srcLinesize[1] *
 height);
     srcData[2] = (unsigned char*)aligned_alloc(alignment, srcLinesize[2] *
 height);

     unsigned char* dstData[8];
     dstData[0] = (unsigned char*)aligned_alloc(alignment, dstLinesize[0] *
 height);

     // Fill YUV420 with green
     for (int y(0); y < height; y++)
     {
         for(int x(0); x < width; x++)
         {
             srcData[0][y * srcLinesize[0] + x] = 145;
             srcData[1][(y / 2) * srcLinesize[1] + x / 2] = 54;
             srcData[2][(y / 2) * srcLinesize[2] + x / 2] = 34;
         }
     }

     SwsContext* ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
                                      width, height, AV_PIX_FMT_RGB24,
                                      0, nullptr, nullptr, nullptr);
     if (!ctx)
     {
         printf("Could not allocate context\n");
         return -1;
     }

     if(sws_scale(ctx, srcData, srcLinesize, 0, height, dstData,
 dstLinesize) != height)
     {
         printf("sws_scale returned with error.\n");
         return -1;
     }

     // Check RGB is in fact green
     for (int y(0); y < height; y++)
     {
         for (int x(0); x < width; x++)
         {
             unsigned char r = dstData[0][y * dstLinesize[0] + x*3];
             unsigned char g = dstData[0][y * dstLinesize[0] + x*3 + 1];
             unsigned char b = dstData[0][y * dstLinesize[0] + x*3 + 2];
             if (!absDiffTol(r, 0, 1) || !absDiffTol(g, 255, 1) ||
 !absDiffTol(b, 0, 1))
             {
                 printf("Pixel at (%d, %d) has unexpected value [%d, %d,
 %d]\n", x, y, r, g, b);
             }
         }
     }

     return 0;
 }

 }}}

 The sample program was linked against ffmpeg version:
 {{{
 ffmpeg version 4.3.6-0+deb11u1 Copyright (c) 2000-2023 the FFmpeg
 developers
   built with gcc 10 (Debian 10.2.1-6)
 }}}

 You can run
 {{{
 for ((i=16;i<=512;i+=2)); do valgrind --tool=memcheck ./sample $i |& tee
 out$i.log; done
 }}}
 and `grep "Invalid " ./*` in the generated logfiles to observe which
 widths produce invalid reads and writes.

 I would also appreciate the following clarifications:
 - `get_video_buffer()` pads the height during allocation.  Why is this
 necessary?  Is this to do with `swscale` or another component of `libav`?
 - Are there padding requirements for grayscale, grayscale+alpha, RGBA
 formats?
 - Is there some source I could refer to for alignment requirements on
 memory (forum posts, tickets, documentation)?
-- 
Ticket URL: <https://trac.ffmpeg.org/ticket/10514>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker


More information about the FFmpeg-trac mailing list