Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APP2.HT.REV and APP2.HT.IRV constraint sets validation #368

Closed
wants to merge 16 commits into from

Conversation

palemieux
Copy link
Collaborator

@palemieux palemieux commented Jun 21, 2024

Closes #370

Validate the CPL essence descriptors against the APP2.HT.REV and APP2.HT.IRV constraint sets specified in ST 2067-21, Annex I.

The PR imposes constraints on the B parameter as suggested at SMPTE/st2067-21#7

To be finalized following the June 26, 2024 IMF plugfest.

@danielhdz13-netflix danielhdz13-netflix marked this pull request as ready for review June 25, 2024 20:54
@danielhdz13-netflix danielhdz13-netflix marked this pull request as draft June 25, 2024 20:56
isValid = false;
}
if (p.csiz[i].xrsiz != 1) {
if (i == 2 && p.csiz[i].xrsiz != 2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be a possible off-by-one bug?

If I am correctly interpreting the 2067-21 spec, I believe when the HT-J2K constraints (Annex I) refer to a specific component, these component numbers are one-based, rather than zero-based index values used by Java.

So in the case of the sub-sampling constraint, (Xrsizi=2 for i = {2,3} and XRsizi=1 for other i), I would interpret that as describing the "second and third component". In the java code, that means you would be looking at csiz index where i is 1 or 2, instead of when i is 2 or 3

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-06-25 at 4 09 13 PM

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these component numbers are one-based, rather than zero-based index values used by Java.

Will fix.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed at c057b3e

String.format("APP2.HT: invalid vertical sub-sampling for component %d", i));
isValid = false;
}
if (p.csiz[i].xrsiz != 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few combinations of sub-sampling that I don't think the current validation will catch. Some of these may not be realistic examples.

  • A non-one value for xrsiz for any component other than component2 or component3.
    Example: (4, 1, 1, 4)
  • Case where component3 is 1, but not equal to component2. I believe the spec requires that component2 and component3 must both be 2, if either of them are.
    Example: (1, 2, 1).
  • If component2 and component3 are both the same invalid value, such as 4, then an error will only be reported for component2, but not component3. As a practical matter, this isn't much of a problem, because it will still set isValid = false. But the lack of error reported for component3 might be slightly misleading.
    Example: (1, 4, 4)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed at c057b3e

}
/* CAP constraints */

if (p.cap == null || p.cap.pcap != 131072 || p.cap.ccap == null || p.cap.ccap.length != 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify where the pcap value of 131072 comes from?
Is that defined in SMPTE ST 2067-21? Or somewhere else? Thanks for clarifying

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ST 2067-21 specifies that Pcapi is 1 for i = 15, and 0 otherwise, which means that pcap = 2^(32-15) per T.800:

image

}

/* progression order */
if (p.cod.progressionOrder != 0b00000010)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is RPCL a requirement? ST 2067-21, Annex I lists Progression order: LRCP, RLCP, RPCL, PCRL, CPRL.
Are those other options allowable also?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide more context as to why a WARNING is needed here?

Copy link
Collaborator Author

@palemieux palemieux Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RPCL is not a requirement, but a strong recommendation, as indicated in ST 2067-21, Annex I, Note 3.

P.S.: I see no reason to do not anything but RPCL for IMF App 2E.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense. That also explains why it is a WARNING.
If any other HT-J2K users disagree with flagging this, we can always revisit this later. But I agree, based on Note 3, this seems like a reasonable recommendation to include by default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to introduce an Enum for these binary-encoded fields, just for better readability.

maxB += 1;
}

int codestreamB = (p.cap.ccap[0] & 0b11111) + 8;
Copy link
Contributor

@davidt-netflix davidt-netflix Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably a good idea to add a comment here, that the relationship of "MAGBp" to "Parameter B" is not always +8.
At least not in the HT-J2K spec. According to ITU-T T.814 , the table in 8.7.3 shows that the +8 rule only works up until MAGB19. After that, the values are less consistent. Referencing the table is required.

However, this is not currently an issue that Photon needs to be concerned with. In ST 2067-21, the largest Parameter B value used, is 21. So for any IMF use-cases, the MAGBp + 8 calculation will work still. But this assumption may eventually fail, if a future IMF update adds support for larger Parameter B values.

The code doesn't necessarily need to change. But it is probably best to document that the codestreamB calculation is actually based on a table in the HT-J2K spec.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

/* magbp */


int maxB = p.csiz[0].ssiz + 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest adding a comment, explaining that the maxB value, is based on values in Table I.2 and Table I.3.

The maxB calculations are a little hard to follow, since ST 2067-21 doesn't present these as a "formula", but rather just a table of hardcoded values.
What you are doing makes sense after reading through the code several times. It looks like you've got all the values correct. But adding a comment explaining the calculations might make this code easier for others to understand.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

The code needs additional comments, which I plan to add once we are somewhat happy with the results and the overall flow/structure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the errors reported under validateHT use the ErrorLevel NON_FATAL instead of FATAL?
I noticed that the existing validateImageCharacteristics method uses NON_FATAL, in case we wanted to consider matching that, for consistency.

But I don't know enough about HT-J2K myself, to make informed decisions about the severity of each constraint violation. So whatever ErrorLevel you feel is best, go ahead and use that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about this. Couple of questions/data points:

  • what is the difference between NON_FATAL and FATAL? Does it refer to the ability of the code to continue or to the validity of the codestream? A codestream that deviates from the APP2.HT.REV and APP2.HT.IRV constraints is fatally invalid, but does not stop validation.
  • is there the notion of WARNING in Photon, i.e., a deviation from a SHOULD/recommendation in the spec?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will see if I can track down any sort of "official" explanation of the different error levels. They were defined that way long before I started working with Photon, so I don't have firsthand knowledge of the original decision.

As a practical matter, I generally try to match whatever value is already being used in similar code. 😁 But that isn't really a good answer.

Within Photon itself, the biggest difference I can see between NON_FATAL and FATAL, is the error logger hasFatalErrors method. And hasFatalErrors is used in a several places to bail out of validation early, by throwing an exception.


Based on my own observations, it seems that generally a FATAL error is reported when something is not able to be read, or parsing fails due to a structural issue. These are typically cases where the validation is not going to be able to proceed. Examples: Failing to deserialize the PKL file, or Invalid MXF Header Partition.

NON_FATAL errors seem to generally be reported for "Hey, the spec says you can't use that value." problems, which do not necessarily prevent you from running the rest of your validations still. Example: An unsupported width/height reported.

WARNING seems to be considerably less common. It looks like they are mostly used to report values that are not recognized, but are not necessarily prohibited either. I think your use of WARNING for the RCPL recommendation is an excellent example. We should encourage more of these throughout the code.


Like I said, I'll try to track down a more definitive answer. But once we settle on something (and I welcome your feedback, if you have specific preferences), I'll plan to go ahead and document this somewhere "officially", within the Photon code base. That way we can try to keep our usage consistent.

@@ -388,6 +400,217 @@ public TransferCharacteristic getTransferCharacteristic() {
return paletteLayout;
}

static public class J2KHeaderParameters {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a major problem, but how practical would it be to reuse and enhance the existing JPEG2000PictureSubDescriptor class, rather than defining a new J2KHeaderParameters? My understanding, is that both classes essentially represent the same information, right?

We have similar cases all throughout Photon. Usually, one class will be defined once for a piece of data extracted from the MXF file, and then a second class will be defined elsewhere, to represent the same piece of data, when it is extracted from the CPL. Ideally, both cases should be sharing the same Java class.

This is an area of tech debt that we would like to clean up in a future revision of Photon.
Obviously, all of those other duplicate classes are unrelated to your pull request. And you don't need to change anything right now, especially if that is not a practical change to make.
But it might be worth considering. Changing it now is much easier than reorganizing things in a future refactoring, once people begin to reference J2KHeaderParameters.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a major problem, but how practical would it be to reuse and enhance the existing JPEG2000PictureSubDescriptor class, rather than defining a new J2KHeaderParameters?

Ok. Will give a shot.

@davidt-netflix
Copy link
Contributor

Looks good to me. 👍
I left several comments about minor changes, or code style. But I don't see any major issues. You did a really good job of adding validations for pretty much every constraint in Annex I.

I'll let you consider some of the comments, and give everyone else a chance to review the PR as well. But otherwise I'll plan to mark it approved, and merge in the next few days, whenever you are ready.

if (codestreamB > maxB) {
logger.addError(
IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking this issue into consideration, should we lower the fatality to a WARNING here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

A B value over, say, 24 should probably be an error since it should never happen and could break software decoders.


return isValid;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @deprecated Instead use {@link #isValidJ2KProfile(CompositionImageEssenceDescriptorModel imageDescriptor, IMFErrorLogger logger)}
*/
@Deprecated
public static boolean isValidJ2KProfile(CompositionImageEssenceDescriptorModel imageDescriptor) {
return isValidJ2KProfile(imageDescriptor, new com.netflix.imflibrary.IMFErrorLoggerImpl());
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid breaking changes to public interface for now.

if (codestreamB > maxB) {
logger.addError(
IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
codestreamB > 24 ? IMFErrorLogger.IMFErrors.ErrorLevels.FATAL : IMFErrorLogger.IMFErrors.ErrorLevels.WARNING,

@palemieux palemieux marked this pull request as ready for review August 14, 2024 15:48
@fschleich
Copy link
Contributor

PR replaced by PR #375 (merged)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve APP2.HT.REV and APP2.HT.IRV validation
4 participants