int mxf_read_rip_and_size(MXFFile *mxfFile, MXFRIP *rip, uint32_t* size) { uint32_t rip_size; mxfKey key; uint8_t llen; uint64_t len; uint32_t numEntries; MXFRIPEntry *newEntry = NULL; MXFRIPEntry *entry; uint32_t i; mxf_initialise_list(&rip->entries, free); /* read RIP size (min is 16 + 1 + (4 + 8) * 1 + 4) at end of file */ if (!mxf_file_seek(mxfFile, -4, SEEK_END) || !mxf_read_uint32(mxfFile, &rip_size) || rip_size < 33) { return 0; } /* seek, read and check RIP key */ if (!mxf_file_seek(mxfFile, (int64_t)0 - rip_size, SEEK_CUR) || !mxf_read_k(mxfFile, &key) || !mxf_equals_key(&key, &g_RandomIndexPack_key) || !mxf_read_l(mxfFile, &llen, &len)) { return 0; } /* read RIP */ numEntries = ((uint32_t)len - 4) / 12; CHK_ORET(((uint32_t)len - 4) % 12 == 0); for (i = 0; i < numEntries; i++) { CHK_MALLOC_OFAIL(newEntry, MXFRIPEntry); CHK_OFAIL(mxf_append_list_element(&rip->entries, newEntry)); entry = newEntry; newEntry = NULL; /* entry assigned to list so set to NULL so not free'ed in fail */ CHK_OFAIL(mxf_read_uint32(mxfFile, &entry->bodySID)); CHK_OFAIL(mxf_read_uint64(mxfFile, &entry->thisPartition)); } *size = rip_size; return 1; fail: if (newEntry != NULL) { free(newEntry); } mxf_clear_list(&rip->entries); return 0; }
int mxf_append_partition_esscont_label(MXFPartition *partition, const mxfUL *label) { mxfUL *newLabel; CHK_MALLOC_ORET(newLabel, mxfUL); *newLabel = *label; CHK_OFAIL(mxf_append_list_element(&partition->essenceContainers, newLabel)); return 1; fail: SAFE_FREE(newLabel); return 0; }
static int add_item(MXFMetadataSet* set, MXFMetadataItem* item) { MXFMetadataItem* removedItem; /* if item already attached to set, then removed it first */ if (item->set != NULL) { CHK_ORET(mxf_remove_item(item->set, &item->key, &removedItem)); } CHK_ORET(mxf_append_list_element(&set->items, (void*)item)); item->set = set; return 1; }
static int add_metadef_to_list(MXFList* list, const mxfUL* identification, const mxfUUID* instanceUID) { MetaDefData* data = NULL; CHK_MALLOC_ORET(data, MetaDefData); data->identification = *identification; data->instanceUID = *instanceUID; CHK_OFAIL(mxf_append_list_element(list, (void*)data)); data = NULL; return 1; fail: SAFE_FREE(&data); return 0; }
int mxf_add_set(MXFHeaderMetadata* headerMetadata, MXFMetadataSet* set) { /* sets must have an instanceUID Note however that it is not neccessary to have an instanceUID __item__ when calling this function. See mxf_create_set() for example */ CHK_ORET(!mxf_equals_uuid(&set->instanceUID, &g_Null_UUID)); /* if set already attached to header metadata, then removed it first */ if (set->headerMetadata != NULL) { CHK_ORET(mxf_remove_set(set->headerMetadata, set)); } CHK_ORET(mxf_append_list_element(&headerMetadata->sets, (void*)set)); set->headerMetadata = headerMetadata; return 1; }
static int add_weakref_to_list(MXFList* list, MXFMetadataItem* item, int arrayIndex, const mxfUL* targetIdentification) { WeakRefData* data = NULL; CHK_MALLOC_ORET(data, WeakRefData); data->item = item; data->arrayIndex = arrayIndex; data->targetIdentification = *targetIdentification; CHK_OFAIL(mxf_append_list_element(list, (void*)data)); data = NULL; return 1; fail: SAFE_FREE(&data); return 0; }
int mxf_find_set_by_key(MXFHeaderMetadata* headerMetadata, const mxfKey* key, MXFList** setList) { MXFListIterator iter; MXFList* newList = NULL; CHK_ORET(mxf_create_list(&newList, NULL)); /* free func == NULL because newList doesn't own the data */ mxf_initialise_list_iter(&iter, &headerMetadata->sets); while (mxf_next_list_iter_element(&iter)) { MXFMetadataSet* set = (MXFMetadataSet*)mxf_get_iter_element(&iter); if (mxf_equals_key(key, &set->key)) { CHK_OFAIL(mxf_append_list_element(newList, (void*)set)); } } *setList = newList; return 1; fail: mxf_free_list(&newList); return 0; }
int mxf_append_partition(MXFFilePartitions *partitions, MXFPartition *partition) { CHK_ORET(mxf_append_list_element(partitions, partition)); return 1; }
int add_timecode_to_index(TimecodeIndex* index, ArchiveTimecode* timecode) { TimecodeIndexArray* newArray = NULL; TimecodeIndexArray* lastArray; int64_t timecodePos = timecode_to_position(timecode); if (mxf_get_list_length(&index->indexArrays) == 0) { CHK_MALLOC_OFAIL(newArray, TimecodeIndexArray); CHK_MALLOC_ARRAY_OFAIL(newArray->elements, TimecodeIndexElement, index->arraySize); newArray->numElements = 0; CHK_OFAIL(mxf_append_list_element(&index->indexArrays, newArray)); newArray = NULL; /* list has ownership */ } lastArray = (TimecodeIndexArray*)mxf_get_last_list_element(&index->indexArrays); if (lastArray->numElements != 0) { if (lastArray->elements[lastArray->numElements - 1].timecodePos + lastArray->elements[lastArray->numElements - 1].duration == timecodePos) { /* timecode is previous + 1 */ lastArray->elements[lastArray->numElements - 1].duration++; } else if (lastArray->elements[lastArray->numElements - 1].timecodePos == timecodePos && (lastArray->elements[lastArray->numElements - 1].frozen || lastArray->elements[lastArray->numElements - 1].duration == 1)) { /* timecode is frozen with the previous timecode value */ lastArray->elements[lastArray->numElements - 1].frozen = 1; lastArray->elements[lastArray->numElements - 1].duration++; } else { /* timecode is not frozen or previous + 1 */ if (lastArray->numElements == index->arraySize) { CHK_MALLOC_OFAIL(newArray, TimecodeIndexArray); CHK_MALLOC_ARRAY_OFAIL(newArray->elements, TimecodeIndexElement, index->arraySize); newArray->numElements = 0; CHK_OFAIL(mxf_append_list_element(&index->indexArrays, newArray)); newArray = NULL; /* list has ownership */ lastArray = (TimecodeIndexArray*)mxf_get_last_list_element(&index->indexArrays); } lastArray->numElements++; lastArray->elements[lastArray->numElements - 1].frozen = 0; lastArray->elements[lastArray->numElements - 1].timecodePos = timecodePos; lastArray->elements[lastArray->numElements - 1].duration = 1; } } else { lastArray->numElements++; lastArray->elements[lastArray->numElements - 1].frozen = 0; lastArray->elements[lastArray->numElements - 1].timecodePos = timecodePos; lastArray->elements[lastArray->numElements - 1].duration = 1; } return 1; fail: free_index_array(&newArray); return 0; }
static int get_file_partitions(MXFFile *mxfFile, MXFPartition *headerPartition, MXFList *partitions) { mxfKey key; uint8_t llen; uint64_t len; MXFPartition *partition = NULL; MXFPartition *partitionRef; uint64_t thisPartition; MXFRIP rip; MXFRIPEntry *ripEntry; MXFListIterator iter; mxf_initialise_list(partitions, free_partition_in_list); memset(&rip, 0, sizeof(MXFRIP)); /* use the RIP if there is one */ if (mxf_read_rip(mxfFile, &rip)) { mxf_initialise_list_iter(&iter, &rip.entries); while (mxf_next_list_iter_element(&iter)) { ripEntry = (MXFRIPEntry*)mxf_get_iter_element(&iter); /* seek to partition and read and add to list */ CHK_OFAIL(mxf_file_seek(mxfFile, mxf_get_runin_len(mxfFile) + ripEntry->thisPartition, SEEK_SET)); CHK_OFAIL(mxf_read_kl(mxfFile, &key, &llen, &len)); CHK_OFAIL(mxf_is_partition_pack(&key)); CHK_OFAIL(mxf_read_partition(mxfFile, &key, &partition)); CHK_OFAIL(mxf_append_list_element(partitions, partition)); partition = NULL; /* owned by list */ } } /* start from footer partition and index back to the header partition */ else { if (headerPartition->footerPartition == 0) { /* no footer partition or at unknown position, so we only index the header partition */ goto fail; } thisPartition = headerPartition->footerPartition; do { /* seek to partition and read and add to list */ CHK_OFAIL(mxf_file_seek(mxfFile, mxf_get_runin_len(mxfFile) + thisPartition, SEEK_SET)); CHK_OFAIL(mxf_read_kl(mxfFile, &key, &llen, &len)); CHK_OFAIL(mxf_is_partition_pack(&key)); CHK_OFAIL(mxf_read_partition(mxfFile, &key, &partition)); CHK_OFAIL(mxf_prepend_list_element(partitions, partition)); partitionRef = partition; partition = NULL; /* owned by list */ thisPartition = partitionRef->previousPartition; } while (partitionRef->thisPartition != partitionRef->previousPartition); } mxf_clear_rip(&rip); return 1; fail: /* if something failed then just add the header partition Note: some Omneon files had references to a footer partition which was not actually present in the file */ mxf_clear_list(partitions); mxf_free_partition(&partition); mxf_clear_rip(&rip); /* create copy of header partition pack */ CHK_ORET(mxf_create_from_partition(headerPartition, &partition)); partition->key = headerPartition->key; partition->majorVersion = headerPartition->majorVersion; partition->minorVersion = headerPartition->minorVersion; partition->kagSize = headerPartition->kagSize; partition->thisPartition = headerPartition->thisPartition; partition->previousPartition = headerPartition->previousPartition; partition->footerPartition = headerPartition->footerPartition; partition->headerByteCount = headerPartition->headerByteCount; partition->indexByteCount = headerPartition->indexByteCount; partition->indexSID = headerPartition->indexSID; partition->bodyOffset = headerPartition->bodyOffset; partition->bodySID = headerPartition->bodySID; /* add partition to list */ if (!mxf_append_list_element(partitions, partition)) { mxf_free_partition(&partition); mxf_log_error("Failed to append header partition to list" LOG_LOC_FORMAT, LOG_LOC_PARAMS); return 0; } return 1; }
static int process_metadata(MXFReader *reader, MXFPartition *partition) { MXFFile *mxfFile = reader->mxfFile; EssenceReader *essenceReader = reader->essenceReader; EssenceReaderData *data = essenceReader->data; mxfKey key; uint8_t llen; uint64_t len; MXFMetadataSet *essContainerDataSet; MXFMetadataSet *sourcePackageSet; MXFMetadataSet *sourcePackageTrackSet; MXFMetadataSet *materialPackageSet; MXFMetadataSet *materialPackageTrackSet; MXFMetadataSet *descriptorSet; MXFArrayItemIterator arrayIter; mxfUL dataDefUL; MXFTrack *track; EssenceTrack *essenceTrack; MXFList wrappedTracks; MXFList sortedWrappedTracks; WrappedTrack *newWrappedTrack = NULL; WrappedTrack *wrappedTrack; WrappedTrack *sortedWrappedTrack; WrappedTrack *prevSortedWrappedTrack; WrappedTrack *firstSortedWrappedTrack; MXFListIterator listIter; MXFListIterator sortedListIter; int wasInserted; int haveZeroTrackNumber; uint32_t trackID; mxf_initialise_list(&wrappedTracks, free); mxf_initialise_list(&sortedWrappedTracks, NULL); /* create and read the header metadata */ CHK_OFAIL(mxf_read_next_nonfiller_kl(mxfFile, &key, &llen, &len)); CHK_OFAIL(mxf_is_header_metadata(&key)); CHK_OFAIL(mxf_create_header_metadata(&data->headerMetadata, reader->dataModel)); CHK_OFAIL(mxf_read_header_metadata(mxfFile, data->headerMetadata, partition->headerByteCount, &key, llen, len)); /* check for metadata only files */ if (!mxf_find_singular_set_by_key(data->headerMetadata, &MXF_SET_K(EssenceContainerData), &essContainerDataSet)) { reader->isMetadataOnly = 1; return 1; } /* get the body and index SID from the (single essence container; external essence not supported) */ CHK_OFAIL(mxf_get_uint32_item(essContainerDataSet, &MXF_ITEM_K(EssenceContainerData, BodySID), &data->bodySID)); if (mxf_have_item(essContainerDataSet, &MXF_ITEM_K(EssenceContainerData, IndexSID))) { CHK_OFAIL(mxf_get_uint32_item(essContainerDataSet, &MXF_ITEM_K(EssenceContainerData, IndexSID), &data->indexSID)); } else { data->indexSID = 0; } /* get the clip duration */ CHK_OFAIL(get_clip_duration(data->headerMetadata, &reader->clip, 0)); /* get the tracks from the (single) material package */ haveZeroTrackNumber = 0; CHK_OFAIL(mxf_find_singular_set_by_key(data->headerMetadata, &MXF_SET_K(MaterialPackage), &materialPackageSet)); CHK_OFAIL(mxf_uu_get_package_tracks(materialPackageSet, &arrayIter)); while (mxf_uu_next_track(data->headerMetadata, &arrayIter, &materialPackageTrackSet)) { /* CHK_OFAIL(mxf_uu_get_track_datadef(materialPackageTrackSet, &dataDefUL)); */ /* NOTE: not failing because files from Omneon were found to have a missing DataDefinition item in the Sequence and DMSourceClip referenced by a static DM Track */ if (!mxf_uu_get_track_datadef(materialPackageTrackSet, &dataDefUL)) { continue; } if (mxf_is_picture(&dataDefUL) || mxf_is_sound(&dataDefUL)) { CHK_MALLOC_OFAIL(newWrappedTrack, WrappedTrack); memset(newWrappedTrack, 0, sizeof(WrappedTrack)); CHK_OFAIL(mxf_append_list_element(&wrappedTracks, newWrappedTrack)); wrappedTrack = newWrappedTrack; newWrappedTrack = NULL; /* assigned to list so set to NULL so not free'ed in fail */ CHK_OFAIL(add_track(reader, &track)); wrappedTrack->track = track; if (mxf_have_item(materialPackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackNumber))) { CHK_OFAIL(mxf_get_uint32_item(materialPackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackNumber), &wrappedTrack->trackNumber)); } else { wrappedTrack->trackNumber = 0; } CHK_OFAIL(mxf_get_uint32_item(materialPackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackID), &wrappedTrack->trackID)); CHK_OFAIL(mxf_get_rational_item(materialPackageTrackSet, &MXF_ITEM_K(Track, EditRate), &wrappedTrack->editRate)); CHK_OFAIL(mxf_uu_get_track_duration(materialPackageTrackSet, &wrappedTrack->duration)); CHK_OFAIL(mxf_uu_get_track_reference(materialPackageTrackSet, &wrappedTrack->sourcePackageUID, &wrappedTrack->sourceTrackID)); wrappedTrack->isVideo = mxf_is_picture(&dataDefUL); track->isVideo = wrappedTrack->isVideo; track->materialTrackID = wrappedTrack->trackID; track->materialTrackNumber = wrappedTrack->trackNumber; if (wrappedTrack->isVideo) { track->video.frameRate = wrappedTrack->editRate; } if (wrappedTrack->trackNumber == 0) { haveZeroTrackNumber = 1; } } } /* sort the tracks; use trackNumber if != 0 else use the trackID; video track is always first */ mxf_initialise_list_iter(&listIter, &wrappedTracks); while (mxf_next_list_iter_element(&listIter)) { wrappedTrack = (WrappedTrack*)mxf_get_iter_element(&listIter); wasInserted = 0; mxf_initialise_list_iter(&sortedListIter, &sortedWrappedTracks); while (mxf_next_list_iter_element(&sortedListIter)) { sortedWrappedTrack = (WrappedTrack*)mxf_get_iter_element(&sortedListIter); if ((wrappedTrack->track->isVideo && !sortedWrappedTrack->track->isVideo) || (wrappedTrack->track->isVideo == sortedWrappedTrack->track->isVideo && ((!haveZeroTrackNumber && wrappedTrack->trackNumber < sortedWrappedTrack->trackNumber) || (haveZeroTrackNumber && wrappedTrack->trackID < sortedWrappedTrack->trackID)))) { CHK_OFAIL(mxf_insert_list_element(&sortedWrappedTracks, mxf_get_list_iter_index(&sortedListIter), 1, wrappedTrack)); wasInserted = 1; break; } } if (!wasInserted) { CHK_OFAIL(mxf_append_list_element(&sortedWrappedTracks, wrappedTrack)); } } /* set the MXFTracks to the same order */ prevSortedWrappedTrack = NULL; firstSortedWrappedTrack = NULL; mxf_initialise_list_iter(&sortedListIter, &sortedWrappedTracks); while (mxf_next_list_iter_element(&sortedListIter)) { sortedWrappedTrack = (WrappedTrack*)mxf_get_iter_element(&sortedListIter); if (firstSortedWrappedTrack == NULL) { firstSortedWrappedTrack = sortedWrappedTrack; } if (prevSortedWrappedTrack != NULL) { prevSortedWrappedTrack->track->next = sortedWrappedTrack->track; } prevSortedWrappedTrack = sortedWrappedTrack; } if (prevSortedWrappedTrack != NULL) { prevSortedWrappedTrack->track->next = NULL; } if (firstSortedWrappedTrack != NULL) { reader->clip.tracks = firstSortedWrappedTrack->track; } /* process source package tracks and linked descriptors */ mxf_initialise_list_iter(&sortedListIter, &sortedWrappedTracks); while (mxf_next_list_iter_element(&sortedListIter)) { sortedWrappedTrack = (WrappedTrack*)mxf_get_iter_element(&sortedListIter); CHK_OFAIL(mxf_uu_get_referenced_track(data->headerMetadata, &sortedWrappedTrack->sourcePackageUID, sortedWrappedTrack->sourceTrackID, &sourcePackageTrackSet)); CHK_OFAIL(add_essence_track(essenceReader, &essenceTrack)); essenceTrack->isVideo = sortedWrappedTrack->isVideo; if (mxf_have_item(sourcePackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackNumber))) { CHK_OFAIL(mxf_get_uint32_item(sourcePackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackNumber), &essenceTrack->trackNumber)); } else { essenceTrack->trackNumber = 0; } CHK_OFAIL(mxf_get_uint32_item(sourcePackageTrackSet, &MXF_ITEM_K(GenericTrack, TrackID), &trackID)); essenceTrack->frameRate = reader->clip.frameRate; essenceTrack->playoutDuration = reader->clip.duration; essenceTrack->indexSID = data->indexSID; essenceTrack->bodySID = data->bodySID; /* process the descriptor */ CHK_OFAIL(mxf_uu_get_referenced_package(data->headerMetadata, &sortedWrappedTrack->sourcePackageUID, &sourcePackageSet)); CHK_OFAIL(mxf_uu_get_track_descriptor(sourcePackageSet, trackID, &descriptorSet)); if (mxf_is_subclass_of(data->headerMetadata->dataModel, &descriptorSet->key, &MXF_SET_K(CDCIEssenceDescriptor))) { CHK_OFAIL(process_cdci_descriptor(descriptorSet, sortedWrappedTrack->track, essenceTrack)); } else if (mxf_is_subclass_of(data->headerMetadata->dataModel, &descriptorSet->key, &MXF_SET_K(WaveAudioDescriptor))) { CHK_OFAIL(process_wav_descriptor(descriptorSet, sortedWrappedTrack->track, essenceTrack)); } else if (mxf_is_subclass_of(data->headerMetadata->dataModel, &descriptorSet->key, &MXF_SET_K(GenericSoundEssenceDescriptor))) { CHK_OFAIL(process_sound_descriptor(descriptorSet, track, essenceTrack)); } else { mxf_log_error("Unsupported file descriptor" LOG_LOC_FORMAT, LOG_LOC_PARAMS); return 0; } } /* initialise the playout timecode */ if (!initialise_playout_timecode(reader, materialPackageSet)) { CHK_ORET(initialise_default_playout_timecode(reader)); } /* initialise the source timecodes */ initialise_source_timecodes(reader, sourcePackageSet); mxf_clear_list(&wrappedTracks); mxf_clear_list(&sortedWrappedTracks); return 1; fail: SAFE_FREE(newWrappedTrack); mxf_clear_list(&wrappedTracks); mxf_clear_list(&sortedWrappedTracks); return 0; }