package org.mp4parser.muxer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mp4parser.Container;
import org.mp4parser.boxes.samplegrouping.CencSampleEncryptionInformationGroupEntry;
import org.mp4parser.muxer.builder.DefaultMp4Builder;
import org.mp4parser.muxer.builder.FragmentedMp4Builder;
import org.mp4parser.muxer.builder.Mp4Builder;
import org.mp4parser.muxer.container.mp4.MovieCreator;
import org.mp4parser.muxer.tracks.CencDecryptingTrackImpl;
import org.mp4parser.muxer.tracks.CencEncryptedTrack;
import org.mp4parser.muxer.tracks.CencEncryptingTrackImpl;
import org.mp4parser.tools.ByteBufferByteChannel;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.util.*;
public class CencFileRoundtripTest {
String baseDir = CencFileRoundtripTest.class.getProtectionDomain().getCodeSource().getLocation().getFile();
Map<UUID, SecretKey> keys;
HashMap<CencSampleEncryptionInformationGroupEntry, long[]> keyRotation1;
HashMap<CencSampleEncryptionInformationGroupEntry, long[]> keyRotation2;
HashMap<CencSampleEncryptionInformationGroupEntry, long[]> keyRotation3;
UUID uuidDefault;
@Before
public void setUp() throws Exception {
uuidDefault = UUID.randomUUID();
UUID uuidAlt = UUID.randomUUID();
SecretKey cekDefault = new SecretKeySpec(new byte[]{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, "AES");
SecretKey cekAlt = new SecretKeySpec(new byte[]{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, "AES");
keys = new HashMap<UUID, SecretKey>();
keys.put(uuidDefault, cekDefault);
keys.put(uuidAlt, cekAlt);
CencSampleEncryptionInformationGroupEntry cencNone = new CencSampleEncryptionInformationGroupEntry();
cencNone.setEncrypted(false);
CencSampleEncryptionInformationGroupEntry cencAlt = new CencSampleEncryptionInformationGroupEntry();
cencAlt.setKid(uuidAlt);
cencAlt.setIvSize(8);
cencAlt.setEncrypted(true);
CencSampleEncryptionInformationGroupEntry cencDefault = new CencSampleEncryptionInformationGroupEntry();
cencAlt.setKid(uuidDefault);
cencAlt.setIvSize(8);
cencAlt.setEncrypted(true);
keyRotation1 = new HashMap<CencSampleEncryptionInformationGroupEntry, long[]>();
keyRotation1.put(cencNone, new long[]{0, 1, 2, 3, 4});
keyRotation1.put(cencAlt, new long[]{10, 11, 12, 13});
keyRotation2 = new HashMap<CencSampleEncryptionInformationGroupEntry, long[]>();
keyRotation2.put(cencNone, new long[]{0, 2, 4, 6, 8});
keyRotation3 = new HashMap<CencSampleEncryptionInformationGroupEntry, long[]>();
keyRotation3.put(cencDefault, new long[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15});
}
@Test
public void testDefaultPlainFragMp4_cbc1() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation3, "cbc1", null, false);
}
@Test
public void testDefaultPlainFragMp4_cenc() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation3, "cenc", null, false);
}
@Test
public void testDefaultPlainStdMp4_cbc1() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation3, "cbc1", null, false);
}
@Test
public void testDefaultPlainStdMp4_cenc() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation3, "cenc", null, false);
}
@Test
public void testSingleKeyMp4_cbc1() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, null, "cbc1", uuidDefault, false);
}
@Test
public void testSingleKeyMp4_cenc() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, null, "cenc", uuidDefault, false);
}
@Test
public void testSingleKeyFragMp4_cenc() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, null, "cenc", uuidDefault, false);
}
@Test
public void testMultipleKeysStdMp4_cbc1() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation1, "cbc1", uuidDefault, false);
}
@Test
public void testMultipleKeysFragMp4_cbc1() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation1, "cbc1", uuidDefault, false);
}
@Test
public void testMultipleKeysStdMp4_2_cbc1() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation2, "cbc1", uuidDefault, false);
}
@Test
public void testMultipleKeysFragMp4_2_cbc1() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation2, "cbc1", uuidDefault, false);
}
@Test
public void testMultipleKeysStdMp4_cenc() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation1, "cenc", uuidDefault, false);
}
@Test
public void testMultipleKeysFragMp4_cenc() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation1, "cenc", uuidDefault, false);
}
@Test
public void testMultipleKeysStdMp4_2_cenc() throws IOException {
testMultipleKeys(new DefaultMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation2, "cenc", uuidDefault, false);
}
@Test
public void testMultipleKeysFragMp4_2_cenc() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation2, "cenc", uuidDefault, false);
}
@Test
public void testMultipleKeysFragMp4_2_cenc_pseudo_encrypted() throws IOException {
testMultipleKeys(new FragmentedMp4Builder(), baseDir + "/BBB_qpfile_10sec/BBB_fixedres_B_180x320_80.mp4", keys, keyRotation2, "cenc", uuidDefault, true);
}
public void testMultipleKeys(Mp4Builder builder, String testFile, Map<UUID, SecretKey> keys,
HashMap<CencSampleEncryptionInformationGroupEntry, long[]> keyRotation,
String encAlgo, UUID uuidDefault, boolean encryptButClear) throws IOException {
Movie m1 = MovieCreator.build(testFile);
Movie m2 = new Movie();
for (Track track : m1.getTracks()) {
CencEncryptingTrackImpl cencEncryptingTrack = new CencEncryptingTrackImpl(track, uuidDefault, keys, keyRotation, encAlgo, false, encryptButClear);
m2.addTrack(cencEncryptingTrack);
}
Container c = builder.build(m2);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeContainer(Channels.newChannel(baos));
// new FileOutputStream("c:\\dev\\mp4parser\\m2.mp4").write(baos.toByteArray());
Movie m3 = MovieCreator.build(new ByteBufferByteChannel(baos.toByteArray()), new InMemRandomAccessSourceImpl(baos.toByteArray()), "inmem");
Movie m4 = new Movie();
for (Track track : m3.getTracks()) {
CencDecryptingTrackImpl cencDecryptingTrack =
new CencDecryptingTrackImpl((CencEncryptedTrack) track, keys);
m4.addTrack(cencDecryptingTrack);
}
Container c2 = builder.build(m4);
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
c2.writeContainer(Channels.newChannel(baos2));
Movie m5 = MovieCreator.build(new ByteBufferByteChannel(baos2.toByteArray()), new InMemRandomAccessSourceImpl(baos2.toByteArray()), "inmem");
Iterator<Track> tracksPlainIter = m1.getTracks().iterator();
Iterator<Track> roundTrippedTracksIter = m5.getTracks().iterator();
while (tracksPlainIter.hasNext() && roundTrippedTracksIter.hasNext()) {
verifySampleEquality(
tracksPlainIter.next().getSamples(),
roundTrippedTracksIter.next().getSamples());
}
}
public void verifySampleEquality(List<Sample> orig, List<Sample> roundtripped) throws IOException {
int i = 0;
Iterator<Sample> origIter = orig.iterator();
Iterator<Sample> roundTrippedIter = roundtripped.iterator();
while (origIter.hasNext() && roundTrippedIter.hasNext()) {
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
origIter.next().writeTo(Channels.newChannel(baos1));
roundTrippedIter.next().writeTo(Channels.newChannel(baos2));
Assert.assertArrayEquals("Sample " + i + " differs", baos1.toByteArray(), baos2.toByteArray());
i++;
}
}
}