5

I'm processing raw yuv420 biplane frames data which I receive from the network and need to create CVPixelBuffer in order to process it into Core Image as well as writing into disk using AVAssetWriter.

When I try to create a CVPixelBuffer with code below with width such as 120, 240 or 480 it allocates memory and creates a proper CVPixelBuffer with correct bytePerRow values for both planes (for example width 120 produces a value of 120 bytePerRow).

However, when I input a frame with width such as 90, 180 or 360 it produces a erroneous bytePerRow such as 192 bytePerRow for a frame width of 180. This causes issues of drawing later in CoreImage or AVAssetWriter.

Please see code below to create the CVPixelBuffer.

CGSize frameSize = CGSizeMake(180,240);
CVPixelBufferRef pixelBuffer = NULL;
NSDictionary *pixelAttributes = @{(id)kCVPixelBufferIOSurfaceOpenGLESFBOCompatibilityKey : (id)kCFBooleanTrue,
                                  (id)kCVPixelBufferIOSurfaceCoreAnimationCompatibilityKey : (id)kCFBooleanTrue,     
                                  (id)kCVPixelBufferIOSurfaceOpenGLESTextureCompatibilityKey : (id)kCFBooleanTrue,     
                                  (id)kCVPixelBufferOpenGLESCompatibilityKey: (id)kCFBooleanTrue};

CVReturn result = CVPixelBufferCreate(NULL, frameSize.width, frameSize.height, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, (__bridge CFDictionaryRef _Nullable)(pixelAttributes), &pixelBuffer);

Please note that I cannot use CVPixelBufferCreateWithPlanarBytes which forces me to allocate memory myself and causes a memory leak later on when used with Core Image which is not the subject of this issue.

David Attias
  • 490
  • 5
  • 15

1 Answers1

7

I've found out the reason for this bug and simultaneously received an answer from Apple DTS which matches my intuitions. Here is the answers:

According to Core Video engineering, the reason that the bytes per row is rounded up from 180 to 196 is because of a required 16 byte alignment. 180 / 16 = 11.25; 192 / 16 = 12.0.

There are ways to force an exact bytes per row, but that sounds like a bad idea here. The reason for the required alignment is graphics cards have a hardware restriction. It sounds like you want to use CoreImage. Using CVPixelBuffers that are not aligned will either not work, or force an extra copy somewhere.

We recommend filling your buffers line by line. Something like this:

int srcRowbytes = 180; // Or whatever it is from wherever
int dstRowbytes = CVPixelBufferGetBytesPerRowOfPlane( dstBuffer, plane );
void * dstBytes = CVPixelBufferGetBaseAddressOfPlane( dstBuffer, plane );
for( int line = 0; line < height; line++ ) {
    memcpy( dstBytes, srcBytes, srcRowbytes );
    srcBytes += srcRowbytes;
    dstBytes += dstRowbytes;
}
David Attias
  • 490
  • 5
  • 15
  • I'm seeing something similar to this, where a CVPixelBuffer from the front camera has a width of 1440, but a bytes per row of 1472. The latter is divisible by 64; I wonder if the alignment requirement has increased or whether this is something else. – icodestuff Mar 20 '21 at 07:18