I'm making an app that reads a ProRes file, processes each frame through metal to resize and scale it, then outputs a new ProRes file. In the future the app will support other codecs but for now just ProRes. I'm reading the ProRes 422 buffers in the kCVPixelFormatType_422YpCbCr16 pixel format. This is what's recommended by Apple in this video https://developer.apple.com/wwdc20/10090?time=599.
When the MTLTexture is run through a metal performance shader, the colorspace seems to force RGB or is just not allowing yCbCr textures as the output is all green/purple. If you look at the render code, you will see there's a commented out block of code to just blit copy the outputTexture, if you perform the copy instead of the scaling through MPS, the output colorspace is fine. So it appears the issue is from Metal Performance Shaders. Side note - I noticed that when using this format, it brings in the YpCbCr texture as a single plane. I thought it's preferred to handle this as two separate planes? That said, if I have two separate planes, that makes my app more complicated as I would need to scale both planes or merge it to RGB. But I'm going for the most performance possible.
A sample project can be found here: https://www.dropbox.com/scl/fo/jsfwh9euc2ns2o3bbmyhn/AIomDYRhxCPVaWw9XH-qaN0?rlkey=sp8g0sb86af1u44p3xy9qa3b9&dl=0
Inside the supporting files, there is a test movie. For ease, I would move this to somewhere easily accessible (i.e Desktop).
- Load and run the example project.
- Click 'Select Video'
- Select that video you placed on your desktop
- It will now output a new video next to the selected one, named "Output.mov"
- The new video should just be scaled at 50%, but the colorspace is all wrong.
Below is a photo of before and after the metal performance shader.
Hello @Joebayld,
kCVPixelFormatType_422YpCbCr16 is a packed YUV format with chroma subsampling where 2 pixels are stored in 64 bits. (Take a look at https://developer.apple.com/library/archive/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG8-V216__4_2_2_COMPRESSION_TYPE for a detailed description of the pixel format).
Your code is taking each pixel from this buffer and mapping it into a 32-bit RGBA texture (using CVMetalTextureCache), and then filtering that texture with MPSImageBilinearScale. So one of the problems is that the filter has no way of knowing that the data represents packed YUV data with chroma subsampling, so I'm not surprised that produced the result in your screenshot. You are welcome to file an enhancement request using Feedback Assistant, but in the meantime, you will need to find a different solution.
My recommendation to you is to have AVFoundation convert the sample to an RGB format for you, kCVPixelFormatType_64RGBAHalf would maintain the precision of the source.
Best regards,
Greg