Compound-media integration¶
A host creates one representation for a sequence, span, or package. It should not import every member as an unrelated asset. The representation owns a content structure; its resources carry content evidence and one or more locators.
Add an image sequence¶
An image-sequence representation is described compactly: directory, filename prefix and suffix, frame-number padding, first and last frame, frame step, an exact rational frame rate, and any frames already known to be missing. The example adds a derived render sequence to an existing asset and reads back the stored descriptor:
static pp_error_code_t add_render_sequence(pp_production_t *production,
const pp_uuid_t *asset_id,
const char *directory,
pp_uuid_t *out_sequence_id,
pp_error_t **error) {
const int64_t missing_frames[] = {1003};
pp_transaction_t *transaction = NULL;
pp_representation_set_t *representations = NULL;
pp_error_code_t status =
pp_production_begin_transaction(production, &transaction, error);
if (status == PP_OK) {
status = pp_transaction_add_image_sequence_representation(
transaction, asset_id, PP_REPRESENTATION_DERIVED, directory,
"shot010.", ".exr", 4, 1001, 1004, 1, 24000, 1001, missing_frames, 1,
out_sequence_id, error);
}
if (status == PP_OK) {
status = pp_transaction_commit(transaction, error);
}
if (status == PP_OK) {
status = pp_production_representations(production, asset_id,
&representations, error);
}
for (uint64_t index = 0;
status == PP_OK && index < pp_representation_set_count(representations);
++index) {
pp_uuid_t id;
pp_uuid_t owner;
pp_representation_kind_t kind;
pp_content_structure_kind_t structure;
uint64_t members = 0;
uint64_t resources = 0;
uint64_t fingerprints = 0;
status = pp_representation_set_get(representations, index, &id, &owner,
&kind, &structure, &members, &resources,
&fingerprints, error);
if (status == PP_OK && structure == PP_CONTENT_IMAGE_SEQUENCE) {
const char *prefix = NULL;
const char *suffix = NULL;
uint8_t padding = 0;
int64_t start = 0;
int64_t end = 0;
uint32_t step = 0;
uint32_t rate_numerator = 0;
uint32_t rate_denominator = 0;
uint64_t missing_count = 0;
status = pp_representation_set_get_sequence(
representations, index, &prefix, &suffix, &padding, &start, &end,
&step, &rate_numerator, &rate_denominator, &missing_count, error);
if (status == PP_OK) {
printf("%s#%s frames %lld-%lld, %llu known missing\n", prefix, suffix,
(long long)start, (long long)end,
(unsigned long long)missing_count);
}
}
}
pp_representation_set_release(representations);
pp_transaction_release(transaction);
return status;
}
postproject::Uuid add_render_sequence(postproject::Production &production,
const postproject::Uuid &asset_id,
const std::string &directory) {
postproject::ImageSequenceInput sequence{};
sequence.directory = directory;
sequence.prefix = "shot010.";
sequence.suffix = ".exr";
sequence.padding = 4;
sequence.start = 1001;
sequence.end = 1004;
sequence.step = 1;
sequence.rate_numerator = 24000;
sequence.rate_denominator = 1001;
sequence.missing_frames = {1003};
auto transaction = production.beginTransaction();
const auto sequence_id = transaction.addImageSequenceRepresentation(
asset_id, postproject::RepresentationKind::derived, sequence);
transaction.commit();
for (const auto &representation : production.representations(asset_id)) {
if (representation.id == sequence_id && representation.image_sequence) {
const auto &stored = *representation.image_sequence;
std::cout << stored.prefix << '#' << stored.suffix << " frames "
<< stored.start << '-' << stored.end << ", "
<< stored.missing_frames.size() << " known missing\n";
}
}
return sequence_id;
}
def add_render_sequence(
production: Production, asset_id: AssetId, directory: Path
) -> RepresentationId:
with production.transaction() as transaction:
sequence_id = transaction.add_image_sequence_representation(
asset_id,
RepresentationKind.DERIVED,
ImageSequenceInput(
directory=str(directory),
prefix="shot010.",
suffix=".exr",
padding=4,
start=1001,
end=1004,
step=1,
rate_numerator=24000,
rate_denominator=1001,
missing_frames=(1003,),
),
)
stored = next(
item for item in production.representations[asset_id] if item.id == sequence_id
)
sequence = stored.image_sequence
assert sequence is not None
print(
f"{sequence.prefix}#{sequence.suffix} frames {sequence.start}-{sequence.end}, "
f"{len(sequence.missing_frames)} known missing"
)
return sequence_id
fn add_render_sequence(
production: &mut SqliteProduction,
asset_id: AssetId,
directory: &Path,
) -> Result<RepresentationId> {
let source = ImageSequenceSource::new(
directory,
ImageSequencePattern::new("shot010.", ".exr", 4)?,
FrameRange::new(1001, 1004, 1)?,
RationalRate::new(24000, 1001)?,
vec![1003],
);
let import =
prepare_image_sequence_representation(asset_id, RepresentationKind::Derived, &source)?;
let sequence_id = import.representation().id();
{
let mut transaction = production.begin_transaction()?;
transaction.add_representation(&import)?;
transaction.commit()?;
}
let stored = production
.representations(asset_id)?
.into_iter()
.find(|representation| representation.id() == sequence_id)
.expect("committed representation");
println!("structure: {:?}", stored.content_structure().kind());
Ok(sequence_id)
}
cat > shot010.json <<'EOF'
{
"structure": "image_sequence",
"directory": "renders/shot010",
"prefix": "shot010.",
"suffix": ".exr",
"padding": 4,
"start": 1001,
"end": 1004,
"step": 1,
"rate_numerator": 24000,
"rate_denominator": 1001,
"missing_frames": [1003]
}
EOF
SEQUENCE_ID=$(postproject --json representation add production.pproj \
"$ASSET_ID" derived shot010.json | jq -r .representation_id)
postproject media show production.pproj "$ASSET_ID"
Ordered spans and packages are added the same way from a list of file members. Each member has an open-world role and is required unless marked optional; the order of an ordered-parts list is significant.
Read the stored structure¶
After commit, enumerate representations rather than retaining a private host index. Each representation exposes:
the content-structure kind;
ordered members, open-world roles, and requiredness;
the compact sequence descriptor;
resources and their fingerprints;
resource locators; and
representation fingerprints separately from resource fingerprints.
Availability¶
Resolution returns one aggregate availability value plus resource results and issues. Missing sequence frames are sorted individual frame numbers. Optional package members may produce issues but do not reduce availability. Never choose one ambiguous candidate in integration code; present the candidates to the user and persist only an explicit confirmation.
Recognition¶
The Rust media adapter additionally recognizes compound media from filenames
and layouts: numbered image groups, numbered camera spans, same-stem sidecars,
and the checked AVCHD card layout. The CLI reaches the same adapter when
media add receives a directory, and with --recognize-companions for
sidecars. Recognition is not yet exposed through the C ABI, C++ wrapper, or
Python binding; those surfaces can still create every recognized structure
explicitly with the operations above.
AVCHD recognition assigns open-world PostProject roles for essence, clip-information, playlist, and navigation members. These labels describe the adapter’s preservation model; they do not claim conformance validation or interpret vendor metadata.