amazon-kinesis-video-streams-parser-library icon indicating copy to clipboard operation
amazon-kinesis-video-streams-parser-library copied to clipboard

[BUG] java.lang.IllegalStateException: Duplicate key 28 when parksing MKV Tags

Open dmitry-vlt opened this issue 1 year ago • 1 comments

Logging

java.lang.IllegalStateException: Duplicate key 28 (attempted merging values 91343852333195104960095234440354545640198248828 and 91343852333195104960095234440354545640198248828)
        at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
        at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
        at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at com.amazonaws.kinesisvideo.parser.utilities.FragmentMetadataVisitor.getTagNameToValueMap(FragmentMetadataVisitor.java:295)
        at com.amazonaws.kinesisvideo.parser.utilities.FragmentMetadataVisitor.setMillisBehindLatestAndContinuationToken(FragmentMetadataVisitor.java:205)
        at com.amazonaws.kinesisvideo.parser.utilities.FragmentMetadataVisitor.access$400(FragmentMetadataVisitor.java:48)
        at com.amazonaws.kinesisvideo.parser.utilities.FragmentMetadataVisitor$StateMachineVisitor.visit(FragmentMetadataVisitor.java:167)
        at com.amazonaws.kinesisvideo.parser.mkv.MkvEndMasterElement.accept(MkvEndMasterElement.java:36)
        at com.amazonaws.kinesisvideo.parser.mkv.visitors.CompositeMkvElementVisitor.visitAll(CompositeMkvElementVisitor.java:66)
        at com.amazonaws.kinesisvideo.parser.mkv.visitors.CompositeMkvElementVisitor.visit(CompositeMkvElementVisitor.java:48)
        at com.amazonaws.kinesisvideo.parser.mkv.MkvEndMasterElement.accept(MkvEndMasterElement.java:36)
        at com.amazonaws.kinesisvideo.parser.mkv.visitors.CompositeMkvElementVisitor.visitAll(CompositeMkvElementVisitor.java:66)
        at com.amazonaws.kinesisvideo.parser.mkv.visitors.CompositeMkvElementVisitor.visit(CompositeMkvElementVisitor.java:48)
        at com.amazonaws.kinesisvideo.parser.mkv.MkvEndMasterElement.accept(MkvEndMasterElement.java:36)
        at com.amazonaws.kinesisvideo.parser.mkv.StreamingMkvReader.apply(StreamingMkvReader.java:131)

Describe the bug When connecting to KVS Stream and parsing the data, occasionally the process will fail with duplicate keys in KVS Tag parsing logic.

SDK version number

"com.amazonaws" % "amazon-kinesis-video-streams-parser-library" % "1.2.5"

Additional context This is the code in FragmentMetadataVisitor.java that is problematic.

    private Map<String, String> getTagNameToValueMap() {
        List<MkvElement> tagElements = tagCollector.copyOfCollection();
        Map<String, Long> tagNameToParentElementNumber = tagElements.stream().filter(e -> MkvTypeInfos.TAGNAME.equals(e.getElementMetaData().getTypeInfo())).filter(e -> MkvTypeInfos.SIMPLETAG.equals(getParentElement(e).getTypeInfo())).filter(e -> isTagFromKinesisVideo((MkvDataElement) e)).collect(Collectors.toMap(this::getMkvElementStringVal, e -> getParentElement(e).getElementNumber(), (a, b) -> b));
        Map<Long, String> parentElementNumberToTagValue = tagElements.stream().filter(e -> MkvTypeInfos.TAGSTRING.equals(e.getElementMetaData().getTypeInfo())).filter(e -> MkvTypeInfos.SIMPLETAG.equals(getParentElement(e).getTypeInfo())).collect(Collectors.toMap(e -> getParentElement(e).getElementNumber(), this::getMkvElementStringVal));
        return tagNameToParentElementNumber.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> parentElementNumberToTagValue.getOrDefault(e.getValue(), "")));
    }

Proposed solution: https://github.com/aws/amazon-kinesis-video-streams-parser-library/pull/186

private Map<String, String> getTagNameToValueMap() {
    List<MkvElement> tagElements = tagCollector.copyOfCollection();

    Map<String, Long> tagNameToParentElementNumber = tagElements.stream()
        .filter(e -> MkvTypeInfos.TAGNAME.equals(e.getElementMetaData().getTypeInfo()))
        .filter(e -> MkvTypeInfos.SIMPLETAG.equals(getParentElement(e).getTypeInfo()))
        .filter(e -> isTagFromKinesisVideo((MkvDataElement) e))
        .collect(Collectors.toMap(
            this::getMkvElementStringVal, 
            e -> getParentElement(e).getElementNumber(), 
            (existing, replacement) -> replacement
        ));

    Map<Long, String> parentElementNumberToTagValue = tagElements.stream()
        .filter(e -> MkvTypeInfos.TAGSTRING.equals(e.getElementMetaData().getTypeInfo()))
        .filter(e -> MkvTypeInfos.SIMPLETAG.equals(getParentElement(e).getTypeInfo()))
        .collect(Collectors.toMap(
            e -> getParentElement(e).getElementNumber(), 
            this::getMkvElementStringVal, 
            (existing, replacement) -> existing // In case of a conflict, keep the existing value
        ));

    return tagNameToParentElementNumber.entrySet().stream()
        .collect(Collectors.toMap(
            Map.Entry::getKey, 
            e -> parentElementNumberToTagValue.getOrDefault(e.getValue(), "")
        ));
}

dmitry-vlt avatar Nov 01 '23 20:11 dmitry-vlt

Hi @dmitry-vlt, thanks for the contribution!

Could you please add a unit test and some sample media to confirm that the tag is parsed properly. Thanks!

sirknightj avatar Nov 02 '23 17:11 sirknightj