Skip to content

Commit

Permalink
Support emsg metadata decoding
Browse files Browse the repository at this point in the history
Note: End to end emsg support is still non-functional.
There's some additional plumbing that still needs to
be done.

Issue: #2176

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=143775147
  • Loading branch information
ojw28 committed Jan 6, 2017
1 parent 9d5c750 commit f2634d1
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.metadata.emsg;

import android.test.MoreAsserts;
import com.google.android.exoplayer2.metadata.Metadata;
import junit.framework.TestCase;

/**
* Test for {@link EventMessageDecoder}.
*/
public final class EventMessageDecoderTest extends TestCase {

public void testDecodeEventMessage() {
byte[] rawEmsgBody = new byte[] {
117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
49, 50, 51, 0, // value = "123"
0, 0, -69, -128, // timescale = 48000
0, 0, 0, 0, // presentation_time_delta (ignored) = 0
0, 2, 50, -128, // event_duration = 144000
0, 15, 67, -45, // id = 1000403
0, 1, 2, 3, 4}; // message_data = {0, 1, 2, 3, 4}
EventMessageDecoder decoder = new EventMessageDecoder();
Metadata metadata = decoder.decode(rawEmsgBody, rawEmsgBody.length);
assertEquals(1, metadata.length());
EventMessage eventMessage = (EventMessage) metadata.get(0);
assertEquals("urn:test", eventMessage.schemeIdUri);
assertEquals("123", eventMessage.value);
assertEquals(3000, eventMessage.durationMs);
assertEquals(1000403, eventMessage.id);
MoreAsserts.assertEquals(new byte[] {0, 1, 2, 3, 4}, eventMessage.messageData);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.metadata.emsg;

import android.os.Parcel;
import junit.framework.TestCase;

/**
* Test for {@link EventMessage}.
*/
public final class EventMessageTest extends TestCase {

public void testEventMessageParcelable() {
EventMessage eventMessage = new EventMessage("urn:test", "123", 3000, 1000403,
new byte[] {0, 1, 2, 3, 4});
// Write to parcel.
Parcel parcel = Parcel.obtain();
eventMessage.writeToParcel(parcel, 0);
// Create from parcel.
parcel.setDataPosition(0);
EventMessage fromParcelEventMessage = EventMessage.CREATOR.createFromParcel(parcel);
// Assert equals.
assertEquals(eventMessage, fromParcelEventMessage);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
import junit.framework.TestCase;

/**
* Test for {@link Id3Decoder}
* Test for {@link Id3Decoder}.
*/
public class Id3DecoderTest extends TestCase {
public final class Id3DecoderTest extends TestCase {

public void testDecodeTxxxFrame() throws MetadataDecoderException {
byte[] rawId3 = new byte[] {73, 68, 51, 4, 0, 0, 0, 0, 0, 41, 84, 88, 88, 88, 0, 0, 0, 31, 0, 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.metadata.emsg;

import android.os.Parcel;
import android.os.Parcelable;

import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;

/**
* An Event Message (emsg) as defined in ISO 23009-1.
*/
public final class EventMessage implements Metadata.Entry {

/**
* The message scheme.
*/
public final String schemeIdUri;

/**
* The value for the event.
*/
public final String value;

/**
* The duration of the event in milliseconds.
*/
public final long durationMs;

/**
* The instance identifier.
*/
public final long id;

/**
* The body of the message.
*/
public final byte[] messageData;

// Lazily initialized hashcode.
private int hashCode;

/**
*
* @param schemeIdUri The message scheme.
* @param value The value for the event.
* @param durationMs The duration of the event in milliseconds.
* @param id The instance identifier.
* @param messageData The body of the message.
*/
public EventMessage(String schemeIdUri, String value, long durationMs, long id,
byte[] messageData) {
this.schemeIdUri = schemeIdUri;
this.value = value;
this.durationMs = durationMs;
this.id = id;
this.messageData = messageData;
}

/* package */ EventMessage(Parcel in) {
schemeIdUri = in.readString();
value = in.readString();
durationMs = in.readLong();
id = in.readLong();
messageData = in.createByteArray();
}

@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + (schemeIdUri != null ? schemeIdUri.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (int) (durationMs ^ (durationMs >>> 32));
result = 31 * result + (int) (id ^ (id >>> 32));
result = 31 * result + Arrays.hashCode(messageData);
hashCode = result;
}
return hashCode;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
EventMessage other = (EventMessage) obj;
return durationMs == other.durationMs && id == other.id
&& Util.areEqual(schemeIdUri, other.schemeIdUri) && Util.areEqual(value, other.value)
&& Arrays.equals(messageData, other.messageData);
}

// Parcelable implementation.

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(schemeIdUri);
dest.writeString(value);
dest.writeLong(durationMs);
dest.writeLong(id);
dest.writeByteArray(messageData);
}

public static final Parcelable.Creator<EventMessage> CREATOR =
new Parcelable.Creator<EventMessage>() {

@Override
public EventMessage createFromParcel(Parcel in) {
return new EventMessage(in);
}

@Override
public EventMessage[] newArray(int size) {
return new EventMessage[size];
}

};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.metadata.emsg;

import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataDecoder;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Arrays;

/**
* Decodes Event Message (emsg) atoms, as defined in ISO 23009-1.
* <p>
* Atom data should be provided to the decoder without the full atom header (i.e. starting from the
* first byte of the scheme_id_uri field).
*/
public final class EventMessageDecoder implements MetadataDecoder {

@Override
public boolean canDecode(String mimeType) {
return MimeTypes.APPLICATION_EMSG.equals(mimeType);
}

@Override
public Metadata decode(byte[] data, int size) {
ParsableByteArray emsgData = new ParsableByteArray(data, size);
String schemeIdUri = emsgData.readNullTerminatedString();
String value = emsgData.readNullTerminatedString();
long timescale = emsgData.readUnsignedInt();
emsgData.skipBytes(4); // presentation_time_delta
long durationMs = (emsgData.readUnsignedInt() * 1000) / timescale;
long id = emsgData.readUnsignedInt();
byte[] messageData = Arrays.copyOfRange(data, emsgData.getPosition(), size);
return new Metadata(new EventMessage(schemeIdUri, value, durationMs, id, messageData));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public final class MimeTypes {
public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";
public static final String APPLICATION_SCTE35 = BASE_TYPE_APPLICATION + "/x-scte35";
public static final String APPLICATION_CAMERA_MOTION = BASE_TYPE_APPLICATION + "/x-camera-motion";
public static final String APPLICATION_EMSG = BASE_TYPE_APPLICATION + "/x-emsg";

private MimeTypes() {}

Expand Down

0 comments on commit f2634d1

Please sign in to comment.