Validate Fragmented Files
Validation of fragmented (fmp4, DASH, HLS) files utilizes the ClaimValidator
, but the workflow differs from that described in Validation. Rather than a single call to validate_media()
, there are several supporting objects and methods involved in a validation of multiple fragment files.
NOTE: This process applies to a fragmented MP4 file where the media fragments are spread across multiple files. For fmp4 files where all the fragments are in the same file ("flat" fragmented files) the standard Validation procedure should be used.
Call Flow Overview
Here is a sample call flow for validating a fragmented file:
#include "C2PA/C2PAFactory.hpp"
auto cv = c2pa::C2PAFactory::new_claim_validator();
// Validate the initialization segment
auto init_result = cv->start_fragmented_file_validation(
c2pa::C2PAFactory::new_read_only_file_asset(
"video/initialization_segment.mp4"));
// Check the result
if (!init_result.is_initialization_segment_valid) {
// Handle error
}
// Preliminary manifest store report JSON
std::cout << init_result.validation_report << std::endl;
// Validate the fragments
auto fv = init_result.fragment_validator_factory->make_new_fragment_validator();
// The result structure is updated when each fragment is validated
FragmentValidationResult fragmets_result;
for (auto i = 0; i < num_fragments; i++) {
auto fragment_asset = c2pa::C2PAFactory::new_read_only_file_asset(
"video/segment" + std::to_string(i) + ".m4s");
fragments_result = fv->validate_fragment(fragment_asset);
if (!fragments_result.is_fragment_valid) {
// Handle error
}
if (fragments_result.validated_fragment_numbers.back() != i) {
// Handle out-of-order fragments
}
}
// Final manifest store report JSON
std::cout << fragments_result.validation_report << std::endl;
Order of operations
In order to validate a fragmented file, the following steps must be performed, in order:
- Create a
ClaimValidator
instance. - Call
start_fragmented_file_validation()
passing in the initialization segment. - The result of the above call will contain a
FragmentValidatorFactory
object in it. Use that factory to create one or more instances ofFragmentValidator
object(s). - For each fragment, call the
validate_fragment()
method of theFragmentValidator
. - Each call will update the validation report (JSON string). After your final call to
validate_fragment()
, the returned validation report will represent the final validation status.
Continuity of Fragments
According to the C2PA specification, "the validator must validate each portion of the content before it is rendered ... [including] validating that the location values for a given Merkle tree start at zero and increments by one for each following chunk."
Checks performed by libc2pa
The FragmentValidator
will perform some continuity checking. If the same instance of FragmentValidator
is called twice with discontinuous fragments, then it will fail the validation of the second fragment. However, this continuity validation is not performed across separate FragmentValidator
instances, and the FragmentValidator
also doesn't fail validation if the first fragment presented to it has a non-zero location
value.
Fragment validation scenarios
Here are a few use cases where continuity validation will be treated differently:
- Validating a fragmented file sequentially from start to finish.
- Validating random portions of a fragmented file (presumably) because the user is scrubbing through the media.
- Validating multiple fragments of a file in parallel.
In the first case, it makes sense to create a single FragmentValidator
instance and feed each fragment, sequentially, to that single instance.
In the second example, the client should create a new FragmentValidator
instance whenever a discontinuity is expected. Each new instance will begin monitoring continuity of fragments starting with the first fragment that it receives.
In the third example, it's a good idea to create multiple FragmentValidator
instances, one for each fragment being processed. The library will perform no continuity validation because each instance is only provided a single fragment. It's up to the client code to ensure that all fragments are present and in the correct order.
Information provided by libc2pa
In the cases where continuity will be enforced by the client code and not by libc2pa, the client can make use of the validated_fragment_numbers
property returned by the FragmentValidator
. This is a vector indicating all the fragments that have been validated so far by that specific instance of FragmentValidator
.
Starting Fragmented File Validation
Fragmented file validation must always start with the initialization segment. The main C2PA manifest (with all the hash validation info in it) is contained in the initialization segment. It is impossible to validate fragments without first validating the initialization segment.
This can be done with the start_fragmented_file_validation()
method of the ClaimValidator
object. Similar to how other methods work on the ClaimValidator
, there are three forms of this method: buffer, stream, and file.
start_fragmented_file_validation()
start_fragmented_file_validation()
This returns a FragmentValidationInitialResult
structure containing information about the validation. This structure also contains a FragmentValidatorFactory
that can be used to create FragmentValidator
instances to validate the file fragments.
c2pa::ReadOnlyAssetPtr media
- AnAsset
object containing the raw data of the initialization segment for the file to validate.
auto initial_result = cv->start_fragmented_file_validation(init_segment_asset);
FragmentValidationInitialResult
structure
FragmentValidationInitialResult
structureThe start_fragmented_file_validation()
method returns a FragmentValidationInitialResult
structure. This structure has the following form:
struct FragmentValidationInitialResult {
bool is_initialization_segment_valid;
std::string validation_report;
std::vector<std::unique_ptr<C2PAThumbnailInfo>> thumbnails;
std::unique_ptr<FragmentValidatorFactory> fragment_validator_factory;
};
is_initialization_segment_valid
- This is a single value indicating the overall validation status. If there were any parts of the Manifest Store marked asINVALID
, or if there were any other serious errors encountered during the validation process, then this value will befalse
.validation_report
- This will be populated with a serialized JSON structure with detailed information about the C2PA Manifest Store and the status of its validation.thumbnails
- This will be populated with an array of thumbnail info structures; one for each thumbnail assertion found in the C2PA Manifest Store.fragment_validator_factory
- This is a factory object that can be used to create one or more instances of theFragmentValidator
class. TheseFragmentValidator
instances can then be used to validate the file fragments.NOTE: The
fragment_validator_factory
will benullptr
ifis_initialization_segment_valid
isfalse
. The library will not allow fragment validation if the initialization segment is invalid.
Validating File Fragments
File fragments can only be validated once the initialization segment has been validated with the start_fragmented_file_validation()
method. The result of that call will contain a FragmentValidatorFactory
object that can be used to create FragmentValidator
instances.
make_new_fragment_validator()
make_new_fragment_validator()
This is a method of the FragmentValidatorFactory
class. It is used to create instances of the FragmentValidator
class. Each FragmentValidator
instance contains all the required information from the C2PA manifest in the initialization segment in order to validate file fragments.
auto fv = initial_result.fragment_validator_factory->make_new_fragment_validator();
validate_fragment()
validate_fragment()
This is a method of the FileValidator
class. It is used to perform validation of a file fragment and it returns a FragmentValidationResult
structure containing information about the validation.
c2pa::ReadOnlyAssetPtr fragment
- AnAsset
object containing the raw data of the file fragment to validate.
FragmentValidationResult
structure
FragmentValidationResult
structureThe validate_fragment()
method returns a FragmentValidationResult
structure. This structure has the following form:
struct FragmentValidationResult {
bool is_fragment_valid;
std::string validation_report;
std::vector<uint32_t> validated_fragment_numbers;
};
is_fragment_valid
- This is a single value indicating the overall validation status. If there were any parts of the Manifest Store marked asINVALID
, or if there were any other serious errors encountered during the validation process, then this value will befalse
.validation_report
- This will be populated with a serialized JSON structure with detailed information about the C2PA Manifest Store and the status of its validation.validated_fragment_numbers
- This is an array of numbers indicating thelocation
field of the C2PA manifest in each of the fragments processed by this particular instance ofFragmentValidator
. This can be used by the client to verify continuity of the fragments in the file.
Partial Fragment Validation
Similar to how partial validation can be performed on "flat" files, it can also be performed on fragmented files. The initialization segment and the fragments can each be passed to the library in small chunks. This can be handy when working with streamed content.
The buffer version of start_fragmented_file_validation()
can be called with just the beginning of the initialization segment, and if the ClaimValidator
is able to find the C2PA Manifest Store in the provided data, then it will go ahead and perform validation on it. The hash assertion, which relies on a hash of the entire file, is almost guaranteed to fail, but the rest of the validation_report
might provide some valuable information about the media.
If no Manifest Store is found, or if only a part of the Manifest Store is found, then the start_fragmented_file_validation()
call will throw a common::runtime_error_with_context
exception, and the structure returned by the manifest_parser_info()
method of the exception may provide enough information to determine the size of the Manifest Store. The client can then use the update_initialization_segment_data()
method of the ClaimValidator
to add more media data and attempt to revalidate. See libc2pa Exceptions
If update_initialization_segment_data()
is being called in a loop in one part of a program, to validate a media file in piecemeal chunks, then the set_progress_update_method()
method can be used to notify another part of the program of the progress of the validation.
In a similar fashion, the buffer version of the validate_fragment()
method of the FragmentValidator
class can be called with just the beginning of a fragment. Then update_fragment_data()
can be called repeatedly to add more data and attempt to revalidate the fragment.
Validating Files in a Set
When multiple fragmented files are signed together in a set , it is possible to determine later if those signed files all belong to the same set.
initialization_segments_are_in_set()
initialization_segments_are_in_set()
This function takes in an array of initialization segments and returns an array of bool
s, one boolean for each input asset. The returned boolean values will only be true
if the corresponding asset validates correctly and if it belongs to a set with the first asset in the array.
std::vector<c2pa::ReadOnlyAssetPtr> initialization_segments
- The list of assets to validate.
auto result_vector = cv->initialization_segments_are_in_set({
init_segment_1,
init_segment_2,
});
Updated 4 months ago