diff options
Diffstat (limited to 'isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java')
-rw-r--r-- | isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java new file mode 100644 index 0000000..74246d0 --- /dev/null +++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java @@ -0,0 +1,253 @@ +package com.googlecode.mp4parser.boxes.piff; + +import com.coremedia.iso.IsoFile; +import com.coremedia.iso.IsoTypeReader; +import com.coremedia.iso.IsoTypeWriter; +import com.googlecode.mp4parser.util.Path; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.7. ASF GUIDs + * <p/> + * <p/> + * ASF_Protection_System_Identifier_Object + * 9A04F079-9840-4286-AB92E65BE0885F95 + * <p/> + * ASF_Content_Protection_System_Microsoft_PlayReady + * F4637010-03C3-42CD-B932B48ADF3A6A54 + * <p/> + * ASF_StreamType_PlayReady_Encrypted_Command_Media + * 8683973A-6639-463A-ABD764F1CE3EEAE0 + * <p/> + * <p/> + * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.5. Data Objects > 2.5.1. Payload Extension for AES in Counter Mode + * <p/> + * The sample Id is used as the IV in CTR mode. Block offset, starting at 0 and incremented by 1 after every 16 bytes, from the beginning of the sample is used as the Counter. + * <p/> + * The sample ID for each sample (media object) is stored as an ASF payload extension system with the ID of ASF_Payload_Extension_Encryption_SampleID = {6698B84E-0AFA-4330-AEB2-1C0A98D7A44D}. The payload extension can be stored as a fixed size extension of 8 bytes. + * <p/> + * The sample ID is always stored in big-endian byte order. + */ +public class PlayReadyHeader extends ProtectionSpecificHeader { + private long length; + private List<PlayReadyRecord> records; + + public PlayReadyHeader() { + + } + + @Override + public void parse(ByteBuffer byteBuffer) { + /* + Length DWORD 32 + + PlayReady Record Count WORD 16 + + PlayReady Records See Text Varies + + */ + + length = IsoTypeReader.readUInt32BE(byteBuffer); + int recordCount = IsoTypeReader.readUInt16BE(byteBuffer); + + records = PlayReadyRecord.createFor(byteBuffer, recordCount); + } + + @Override + public ByteBuffer getData() { + + int size = 4 + 2; + for (PlayReadyRecord record : records) { + size += 2 + 2; + size += record.getValue().rewind().limit(); + } + ByteBuffer byteBuffer = ByteBuffer.allocate(size); + + IsoTypeWriter.writeUInt32BE(byteBuffer, size); + IsoTypeWriter.writeUInt16BE(byteBuffer, records.size()); + for (PlayReadyRecord record : records) { + IsoTypeWriter.writeUInt16BE(byteBuffer, record.type); + IsoTypeWriter.writeUInt16BE(byteBuffer, record.getValue().limit()); + ByteBuffer tmp4debug = record.getValue(); + byteBuffer.put(tmp4debug); + } + + return byteBuffer; + } + + public void setRecords(List<PlayReadyRecord> records) { + this.records = records; + } + + public List<PlayReadyRecord> getRecords() { + return Collections.unmodifiableList(records); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("PlayReadyHeader"); + sb.append("{length=").append(length); + sb.append(", recordCount=").append(records.size()); + sb.append(", records=").append(records); + sb.append('}'); + return sb.toString(); + } + + public static abstract class PlayReadyRecord { + int type; + + + public PlayReadyRecord(int type) { + this.type = type; + } + + public static List<PlayReadyRecord> createFor(ByteBuffer byteBuffer, int recordCount) { + List<PlayReadyRecord> records = new ArrayList<PlayReadyRecord>(recordCount); + + for (int i = 0; i < recordCount; i++) { + PlayReadyRecord record; + int type = IsoTypeReader.readUInt16BE(byteBuffer); + int length = IsoTypeReader.readUInt16BE(byteBuffer); + switch (type) { + case 0x1: + record = new RMHeader(); + break; + case 0x2: + record = new DefaulPlayReadyRecord(0x02); + break; + case 0x3: + record = new EmeddedLicenseStore(); + break; + default: + record = new DefaulPlayReadyRecord(type); + } + record.parse((ByteBuffer) byteBuffer.slice().limit(length)); + byteBuffer.position(byteBuffer.position() + length); + records.add(record); + } + + return records; + } + + public abstract void parse(ByteBuffer bytes); + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("PlayReadyRecord"); + sb.append("{type=").append(type); + sb.append(", length=").append(getValue().limit()); +// sb.append(", value=").append(Hex.encodeHex(getValue())).append('\''); + sb.append('}'); + return sb.toString(); + } + + public abstract ByteBuffer getValue(); + + public static class RMHeader extends PlayReadyRecord { + String header; + + public RMHeader() { + super(0x01); + } + + @Override + public void parse(ByteBuffer bytes) { + try { + byte[] str = new byte[bytes.slice().limit()]; + bytes.get(str); + header = new String(str, "UTF-16LE"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + @Override + public ByteBuffer getValue() { + byte[] headerBytes; + try { + headerBytes = header.getBytes("UTF-16LE"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return ByteBuffer.wrap(headerBytes); + } + + public void setHeader(String header) { + this.header = header; + } + + public String getHeader() { + return header; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("RMHeader"); + sb.append("{length=").append(getValue().limit()); + sb.append(", header='").append(header).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + public static class EmeddedLicenseStore extends PlayReadyRecord { + ByteBuffer value; + + public EmeddedLicenseStore() { + super(0x03); + } + + @Override + public void parse(ByteBuffer bytes) { + this.value = bytes.duplicate(); + } + + @Override + public ByteBuffer getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("EmeddedLicenseStore"); + sb.append("{length=").append(getValue().limit()); + //sb.append(", value='").append(Hex.encodeHex(getValue())).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + public static class DefaulPlayReadyRecord extends PlayReadyRecord { + ByteBuffer value; + + public DefaulPlayReadyRecord(int type) { + super(type); + } + + @Override + public void parse(ByteBuffer bytes) { + this.value = bytes.duplicate(); + } + + @Override + public ByteBuffer getValue() { + return value; + } + + } + + } + +} |