Pogle wrote: ↑Thu Jul 31, 2025 4:44 pm
Has anyone tested v1.4.2.10?
The upcoming v1.4.2 won't save you from this because it's not technically a bug, the source metadata is wrong, we're just trying to do "the right thing" under the hood.
Pogle wrote: ↑Thu Jul 31, 2025 4:44 pm
We have a lot of MXF files (1920x1080, 10 bit, 25p, 50i, OP1a, AVC Intra 100) with incorrect metadata (Mediainfo - color range: limited, color range original: full).
Yep, that basically happens when the metadata in the mxf container was set to limited tv range, but the raw_video.h264 stream was encoded as full pc range and therefore you end up with a mismatch.
Pogle wrote: ↑Thu Jul 31, 2025 4:44 pm
In my FFAStrans workflows the misinterpreted level range only appears if I use the watermark filter to insert a logo.
Why is only the watermark filter leading to this problem?
Well, the very short answer would be: because we're going through an RGB roundtrip.
Let me explain.
Back in the days there were two main functions to paint an image over a video in Avisynth as a frameserver: Overlay and Layer. The first was working in YUV, while the latter was working in RGB. We're talking about a time when there were only RGB24, RGB32, YV12, YUY2 available. Anyway, in FFAStrans we picked the latter, so Layer, which is why we're going to RGB.
(As a tiny side note, plenty of other sampling, colorspaces and bit depth have been added to this awesome frameserver and the layer function now supports most of them).
Anyway, what happens under the hood in FFAStrans is:
1) We index the source with FFVideoSource()
2) We convert it to RGB24 with ConverttoRGB24()
3) We index the image with ImageSource() and set it of as many frames as the source video
4) We "overlay" the two with the Layer() function
5) We go back to YUV
The issue occurs at point 2.
In Avisynth that would correspond to something like this:
Code: Select all
#Indexing the source
FFVideoSource("test.mxf")
#Bringing everything to RGB
ConverttoRGB24() # <- here the issue occurs
video=last
#Indexing the picture
ImageSource("test.png", start=0, end=video.FrameCount, fps=25, pixel_type="RGB24")
picture=last
#Overlay them one of top of the other
Layer(video, picture, op="add", opacity=0.5, x=0, y=0)
#Going back to YUV
ConverttoYV24()
Obviously in the actual watermark node the opacity, x and y are actually variables as they're dependent on the input that the user is inserting, but I've just set them like this here as an example.
So, since this is the world wide web, let's take two of the most common things: colorbars (to mock the video source) and a picture of a cat I took in May while walking towards the office (to simulate the image).
As you can see the Colorbars() are limited tv range going from 0.0 to 0.7V (i.e 16-235 in 8bit) aside from the "superblack" section that goes below:
When I convert this to RGB with ConverttoRGB24() this becomes Full PC Range with the white touching 255 and the black touching 0.
When we overlay the picture of the cat in RGB Full PC Range nothing bad happens as we're still in 0-255:
And when we go back to YUV we're back in Limited TV Range, so 16-235
The problem occurs when the source is limited tv range but is actually flagged as full pc range. In Avisynth when an indexer like FFVideoSource() is invoked, it will "index" the source and output an uncompressed A/V stream living in RAM that you can work with in the frameserver. This means that in the case of your AVC Intra Class files with the container set to limited and the stream set to full, it will say "full" as it will honor whatever the raw_video.h264 was encoded as. It's possible to see this by checking the _ColorRange() value of the frame properties where 0 means "Full PC Range" and 1 means "Limited TV Range". Anyway, to simulate this, we can use PropSet("_ColorRange", 0).
What happens when we do this:
Code: Select all
#Indexing the source
ColorBars(848, 480, pixel_type="YV24")
ChangeFPS(25)
PropSet("_ColorRange", 0)
#Bringing everything to RGB Full PC Range
ConverttoRGB24()
is that the ConverttoRGB24() function will say "oh, cool, I have a full pc range source, no need to change levels from limited to full", so it will leave everything as it is, then we're gonna go through the rest of the flow and have the picture overlaid in RGB and guess what happens when we go back to YUV at the very end with ConverttoYV24()? Well, it *thinks* that it's dealing with a Full PC Range source in RGB so it will convert the levels back to Limited TV Range to go to YUV, but actually we were already in limited tv range, so we get the "limited of limited", so everything is shifted twice, hence the issue:
The only way to overcome this would be to change the frame properties right after indexing and to do that you can add a Custom AVS Script and set it like this:
Code: Select all
#Set to Limited TV Range
m_clip=propSet(m_clip, "_ColorRange", 1)
return m_clip