com.netflix.imfutility.cpl._2016.Cpl2016ContextBuilderStrategy.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.imfutility.cpl._2016.Cpl2016ContextBuilderStrategy.java

Source

/*
 * Copyright (C) 2016 Netflix, Inc.
 *
 *     This file is part of IMF Conversion Utility.
 *
 *     IMF Conversion Utility is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     IMF Conversion Utility is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with IMF Conversion Utility.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.netflix.imfutility.cpl._2016;

import com.netflix.imfutility.ConversionException;
import com.netflix.imfutility.asset.AssetMap;
import com.netflix.imfutility.conversion.templateParameter.context.ResourceKey;
import com.netflix.imfutility.conversion.templateParameter.context.TemplateParameterContextProvider;
import com.netflix.imfutility.conversion.templateParameter.context.parameters.ResourceContextParameters;
import com.netflix.imfutility.cpl.AbstractCplContextBuilderStrategy;
import com.netflix.imfutility.cpl.SequenceTypeCpl;
import com.netflix.imfutility.cpl.uuid.ResourceUUID;
import com.netflix.imfutility.cpl.uuid.SegmentUUID;
import com.netflix.imfutility.cpl.uuid.SequenceUUID;
import com.netflix.imfutility.cpl.uuid.UUID;
import com.netflix.imfutility.essencedescriptors.EssenceDescriptorsConstants;
import com.netflix.imfutility.generated.imf._2016.BaseResourceType;
import com.netflix.imfutility.generated.imf._2016.CompositionPlaylistType;
import com.netflix.imfutility.generated.imf._2016.CompositionPlaylistType.LocaleList;
import com.netflix.imfutility.generated.imf._2016.EssenceDescriptorBaseType;
import com.netflix.imfutility.generated.imf._2016.LocaleType;
import com.netflix.imfutility.generated.imf._2016.LocaleType.LanguageList;
import com.netflix.imfutility.generated.imf._2016.SegmentType;
import com.netflix.imfutility.generated.imf._2016.SequenceType;
import com.netflix.imfutility.generated.imf._2016.TrackFileResourceType;
import com.netflix.imfutility.util.ConversionHelper;
import com.netflix.imfutility.xml.XmlParser;
import com.netflix.imfutility.xml.XmlParsingException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math3.fraction.BigFraction;

import javax.xml.bind.JAXBElement;
import java.io.File;
import java.io.FileNotFoundException;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.netflix.imfutility.CoreConstants.CORE_CONSTRAINTS_2016_XSD;
import static com.netflix.imfutility.CoreConstants.CPL_2016_PACKAGE;
import static com.netflix.imfutility.CoreConstants.CPL_2016_XSD;
import static com.netflix.imfutility.CoreConstants.DCML_TYPES_XSD;
import static com.netflix.imfutility.CoreConstants.XMLDSIG_CORE_SCHEMA_XSD;

/**
 * A CPL parser for 2016 namespace.
 * <ul>
 * <li>Parses the given CPL</li>
 * <li>Inits segment, sequence and resource contexts.</li>
 * <li>Fills edit-unit-based resource parameters.</li>
 * </ul>
 */
public class Cpl2016ContextBuilderStrategy extends AbstractCplContextBuilderStrategy {

    private CompositionPlaylistType cpl2016;

    private BigFraction compositionEditRate;
    private SegmentUUID currentSegmentUuid;
    private SequenceType currentSequence;
    private SequenceUUID currentSequenceUuid;
    private com.netflix.imfutility.generated.conversion.SequenceType currentSequenceType;

    public Cpl2016ContextBuilderStrategy(TemplateParameterContextProvider contextProvider, AssetMap assetMap) {
        super(contextProvider, assetMap);
    }

    @Override
    public void parse(File cplFile) throws XmlParsingException, FileNotFoundException {
        cpl2016 = XmlParser.parse(cplFile,
                new String[] { XMLDSIG_CORE_SCHEMA_XSD, DCML_TYPES_XSD, CPL_2016_XSD, CORE_CONSTRAINTS_2016_XSD },
                CPL_2016_PACKAGE + ":" + EssenceDescriptorsConstants.ESSENCE_DESCRIPTORS_PACKAGES,
                CompositionPlaylistType.class);
    }

    @Override
    public Map<String, List<Object>> getEssenceDescriptors() {
        if (cpl2016.getEssenceDescriptorList() == null) {
            return Collections.emptyMap();
        }
        Map<String, List<Object>> result = new HashMap<>();
        for (EssenceDescriptorBaseType essenceDescriptorBase : cpl2016.getEssenceDescriptorList()
                .getEssenceDescriptor()) {
            result.put(essenceDescriptorBase.getId(), essenceDescriptorBase.getAny());
        }
        return result;
    }

    @Override
    public String getCompositionTimecodeStart() {
        if (cpl2016.getCompositionTimecode() == null) {
            return null;
        }
        String timecode = cpl2016.getCompositionTimecode().getTimecodeStartAddress();
        if (StringUtils.isEmpty(timecode)) {
            return null;
        }
        return timecode;
    }

    @Override
    public BigFraction getCompositionTimecodeRate() {
        if (cpl2016.getCompositionTimecode() == null) {
            return null;
        }
        BigInteger rate = cpl2016.getCompositionTimecode().getTimecodeRate();
        boolean isDropFrame = cpl2016.getCompositionTimecode().isTimecodeDropFrame();
        if (rate == null || BigInteger.ZERO.equals(rate)) {
            return null;
        }

        // return as-is if non-drop
        if (!isDropFrame) {
            return new BigFraction(rate);
        }

        // return as 30000/1001 for 30 if drop frame
        return new BigFraction(rate.multiply(BigInteger.valueOf(1000)), BigInteger.valueOf(1001));
    }

    @Override
    protected void buildFromCpl() {
        // 1. get a composition edit rate (it's used if no specific edit rate is specified for a segment).
        this.compositionEditRate = ConversionHelper.parseEditRate(cpl2016.getEditRate());

        // 2. go through all segments and all sequences and build segment, sequence and resource contexts.
        for (SegmentType segment : cpl2016.getSegmentList().getSegment()) {
            this.currentSegmentUuid = SegmentUUID.create(segment.getId());

            contextProvider.getSegmentContext().initSegment(currentSegmentUuid);

            for (Object anySeqJaxb : segment.getSequenceList().getAny()) {
                if (!(anySeqJaxb instanceof JAXBElement)) {
                    throw new ConversionException(
                            String.format("Could not understand a sequence '%s'", anySeqJaxb.toString()));
                }

                JAXBElement jaxbElement = (JAXBElement) (anySeqJaxb);
                Object anySeq = jaxbElement.getValue();

                SequenceTypeCpl currentSequenceTypeCpl = SequenceTypeCpl
                        .fromName(jaxbElement.getName().getLocalPart());
                if ((currentSequenceTypeCpl != null) && (anySeq instanceof SequenceType)) {
                    this.currentSequence = (SequenceType) anySeq;
                    this.currentSequenceType = currentSequenceTypeCpl.toSequenceType();
                    this.currentSequenceUuid = SequenceUUID.create(currentSequence.getTrackId());
                    processSequence();
                }
            }
        }
    }

    private void processSequence() {
        // 1. check that the sequence type is known
        if (currentSequenceType == null) {
            throw new ConversionException(
                    String.format("Sequence '%s': Unknown sequence type", currentSequence.getId()));
        }

        // 2. init the sequence
        contextProvider.getSequenceContext().initSequence(currentSequenceType, currentSequenceUuid);

        // 3. process all resources within the sequence and segment and fill the Resource context
        currentSequence.getResourceList().getResource().forEach(this::processResource);
    }

    private void processResource(BaseResourceType resource) {
        if (!(resource instanceof TrackFileResourceType)) {
            return;
        }
        TrackFileResourceType trackFileResource = (TrackFileResourceType) resource;

        BigInteger repeatCount = trackFileResource.getRepeatCount() != null ? trackFileResource.getRepeatCount()
                : BigInteger.ONE;

        for (long i = 0; i < repeatCount.longValue(); i++) {
            processResourceRepeat(trackFileResource, i);
        }
    }

    private void processResourceRepeat(TrackFileResourceType trackFileResource, long repeat) {
        // 1. init resource context
        ResourceUUID resourceId = ResourceUUID.create(trackFileResource.getId(), repeat);
        ResourceKey resourceKey = ResourceKey.create(currentSegmentUuid, currentSequenceUuid, currentSequenceType);
        contextProvider.getResourceContext().initResource(resourceKey, resourceId);

        // 2. Init essence parameter in Edit Units (as defined in CPL)
        // Check that we have a corresponding track file in assetmap
        // asset map already contains full absolute paths
        UUID trackId = UUID.create(trackFileResource.getTrackFileId());
        String assetPath = assetMap.getAsset(trackId);
        if (assetPath == null) {
            throw new ConversionException(
                    String.format("Resource track file '%s' isn't present in assetmap.xml", trackId));
        }
        assetMap.markAssetReferenced(trackId);
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.ESSENCE, assetPath);

        // 3. init edit rate parameter
        BigFraction editRate = ((trackFileResource.getEditRate() != null)
                && !trackFileResource.getEditRate().isEmpty())
                        ? ConversionHelper.parseEditRate(trackFileResource.getEditRate())
                        : compositionEditRate;
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.EDIT_RATE, ConversionHelper.toEditRate(editRate));

        // 4. Init startTime parameter
        BigInteger startTimeEditUnit = trackFileResource.getEntryPoint() != null ? trackFileResource.getEntryPoint()
                : BigInteger.valueOf(0);
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.START_TIME_EDIT_UNIT, startTimeEditUnit.toString());

        // 5. init duration parameter
        BigInteger durationEditUnit;
        if (trackFileResource.getSourceDuration() != null) {
            durationEditUnit = trackFileResource.getSourceDuration();
        } else {
            durationEditUnit = trackFileResource.getIntrinsicDuration().subtract(startTimeEditUnit);
        }
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.DURATION_EDIT_UNIT, durationEditUnit.toString());

        // 6. init endTime parameter
        BigInteger endTimeEditUnit = startTimeEditUnit.add(durationEditUnit);
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.END_TIME_EDIT_UNIT, endTimeEditUnit.toString());

        // 7. init total repeat count parameter
        BigInteger repeatCount = trackFileResource.getRepeatCount() != null ? trackFileResource.getRepeatCount()
                : BigInteger.ONE;
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.REPEAT_COUNT, repeatCount.toString());

        // 8. init trackFile ID
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.TRACK_FILE_ID, trackId.getUuid());

        // 9. init essence descriptor ID
        String essenceDescId = trackFileResource.getSourceEncoding();
        contextProvider.getResourceContext().addResourceParameter(resourceKey, resourceId,
                ResourceContextParameters.ESSENCE_DESC_ID, essenceDescId);
    }

    @Override
    protected String getDefaultCplLanguage() {
        // assume default language to be first language of first locale
        LocaleList localeList = cpl2016.getLocaleList();
        //  no locales defined
        if (localeList == null) {
            return null;
        }

        Optional<LocaleType> locale = localeList.getLocale().stream().findFirst();
        if (!locale.isPresent()) {
            return null;
        }

        // no languages defined
        LanguageList languageList = locale.get().getLanguageList();
        if (languageList == null) {
            return null;
        }

        Optional<String> language = languageList.getLanguage().stream().findFirst();
        return language.orElse(null);
    }

}