Impl. of the metadata getters.
In Metadata, implemented the method that unmarshall the data from a metadata parcel an return java entities. Add 2 new types of metadata (Boolean and Date) and fixed an issue in TimedText: the w3c standard specify a duration as well as the time to be played => added a duration field. In MetadataParserTest.java: Moved the util private methods at the bottom of the file. Added new tests for the getters (GETTERS section)
This commit is contained in:
@@ -20,10 +20,12 @@ import android.graphics.Bitmap;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
@@ -102,15 +104,20 @@ public class Metadata
|
||||
public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET;
|
||||
public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY);
|
||||
|
||||
public static final int STRING_VAL = 1;
|
||||
public static final int INTEGER_VAL = 2;
|
||||
public static final int LONG_VAL = 3;
|
||||
public static final int DOUBLE_VAL = 4;
|
||||
public static final int TIMED_TEXT_VAL = 5;
|
||||
private static final int LAST_TYPE = 5;
|
||||
public static final int STRING_VAL = 1;
|
||||
public static final int INTEGER_VAL = 2;
|
||||
public static final int BOOLEAN_VAL = 3;
|
||||
public static final int LONG_VAL = 4;
|
||||
public static final int DOUBLE_VAL = 5;
|
||||
public static final int TIMED_TEXT_VAL = 6;
|
||||
public static final int DATE_VAL = 7;
|
||||
public static final int BYTE_ARRAY_VAL = 8;
|
||||
// FIXME: misses a type for shared heap is missing (MemoryFile).
|
||||
// FIXME: misses a type for bitmaps.
|
||||
private static final int LAST_TYPE = 8;
|
||||
|
||||
private static final String TAG = "media.Metadata";
|
||||
private static final int kMetaHeaderSize = 8; // 8 bytes for the size + the marker
|
||||
private static final int kMetaHeaderSize = 8; // size + marker
|
||||
private static final int kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
|
||||
private static final int kRecordHeaderSize = 12; // size + id + type
|
||||
|
||||
@@ -122,21 +129,28 @@ public class Metadata
|
||||
// Used to look up if a key was present too.
|
||||
// Key: Metadata ID
|
||||
// Value: Offset of the metadata type field in the record.
|
||||
private final HashMap<Integer, Integer> mKeyToPosMap = new HashMap<Integer, Integer>();
|
||||
private final HashMap<Integer, Integer> mKeyToPosMap =
|
||||
new HashMap<Integer, Integer>();
|
||||
|
||||
/**
|
||||
* Helper class to hold a pair (time, text). Can be used to implement caption.
|
||||
* Helper class to hold a triple (time, duration, text). Can be used to
|
||||
* implement caption.
|
||||
*/
|
||||
public class TimedText {
|
||||
private Date mTime;
|
||||
private int mDuration; // millisec
|
||||
private String mText;
|
||||
public TimedText(final Date time, final String text) {
|
||||
|
||||
public TimedText(Date time, int duration, String text) {
|
||||
mTime = time;
|
||||
mDuration = duration;
|
||||
mText = text;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder res = new StringBuilder(80);
|
||||
res.append(mTime).append(":").append(mText);
|
||||
res.append(mTime).append("-").append(mDuration)
|
||||
.append(":").append(mText);
|
||||
return res.toString();
|
||||
}
|
||||
}
|
||||
@@ -300,44 +314,64 @@ public class Metadata
|
||||
return mKeyToPosMap.containsKey(metadataId);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
// Accessors.
|
||||
// Caller must make sure the key is present using the {@code has}
|
||||
// method otherwise a RuntimeException will occur.
|
||||
|
||||
public String getString(final int key) {
|
||||
// FIXME: Implement.
|
||||
return new String();
|
||||
checkType(key, STRING_VAL);
|
||||
return mParcel.readString();
|
||||
}
|
||||
|
||||
public int getInt(final int key) {
|
||||
// FIXME: Implement.
|
||||
return 0;
|
||||
checkType(key, INTEGER_VAL);
|
||||
return mParcel.readInt();
|
||||
}
|
||||
|
||||
public boolean getBoolean(final int key) {
|
||||
checkType(key, BOOLEAN_VAL);
|
||||
return mParcel.readInt() == 1;
|
||||
}
|
||||
|
||||
public long getLong(final int key) {
|
||||
// FIXME: Implement.
|
||||
return 0;
|
||||
checkType(key, LONG_VAL);
|
||||
return mParcel.readLong();
|
||||
}
|
||||
|
||||
public double getDouble(final int key) {
|
||||
// FIXME: Implement.
|
||||
return 0.0;
|
||||
checkType(key, DOUBLE_VAL);
|
||||
return mParcel.readDouble();
|
||||
}
|
||||
|
||||
public byte[] getByteArray(final int key) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public Bitmap getBitmap(final int key) {
|
||||
// FIXME: Implement.
|
||||
return null;
|
||||
checkType(key, BYTE_ARRAY_VAL);
|
||||
return mParcel.createByteArray();
|
||||
}
|
||||
|
||||
public Date getDate(final int key) {
|
||||
// FIXME: Implement.
|
||||
return new Date();
|
||||
checkType(key, DATE_VAL);
|
||||
final long timeSinceEpoch = mParcel.readLong();
|
||||
final String timeZone = mParcel.readString();
|
||||
|
||||
if (timeZone.length() == 0) {
|
||||
return new Date(timeSinceEpoch);
|
||||
} else {
|
||||
TimeZone tz = TimeZone.getTimeZone(timeZone);
|
||||
Calendar cal = Calendar.getInstance(tz);
|
||||
|
||||
cal.setTimeInMillis(timeSinceEpoch);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public TimedText getTimedText(final int key) {
|
||||
// FIXME: Implement.
|
||||
return new TimedText(new Date(0), "<missing>");
|
||||
checkType(key, TIMED_TEXT_VAL);
|
||||
final Date startTime = new Date(mParcel.readLong()); // epoch
|
||||
final int duration = mParcel.readInt(); // millisec
|
||||
|
||||
return new TimedText(startTime,
|
||||
duration,
|
||||
mParcel.readString());
|
||||
}
|
||||
|
||||
// @return the last available system metadata id. Ids are
|
||||
@@ -360,4 +394,16 @@ public class Metadata
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the type of the data match what is expected.
|
||||
private void checkType(final int key, final int expectedType) {
|
||||
final int pos = mKeyToPosMap.get(key);
|
||||
|
||||
mParcel.setDataPosition(pos);
|
||||
|
||||
final int type = mParcel.readInt();
|
||||
if (type != expectedType) {
|
||||
throw new IllegalStateException("Wrong type " + expectedType + " but got " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* Check the Java layer that parses serialized metadata in Parcel
|
||||
* works as expected.
|
||||
@@ -89,16 +92,6 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
|
||||
mParcel.writeInt(kMarker);
|
||||
}
|
||||
|
||||
// Insert a string record at the current position.
|
||||
private void writeStringRecord(int metadataId, String val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.STRING_VAL);
|
||||
mParcel.writeString(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// START OF THE TESTS
|
||||
|
||||
@@ -133,7 +126,9 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
|
||||
assertParse();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// RECORDS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// A record header should be at least 12 bytes long
|
||||
@SmallTest
|
||||
@@ -223,4 +218,221 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
|
||||
assertFalse(mMetadata.has(Metadata.GENRE));
|
||||
assertFalse(mMetadata.has(Metadata.firstCustomId()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// GETTERS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// getString
|
||||
@SmallTest
|
||||
public void testGetString() throws Exception {
|
||||
writeStringRecord(Metadata.TITLE, "a title");
|
||||
writeStringRecord(Metadata.GENRE, "comedy");
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals("a title", mMetadata.getString(Metadata.TITLE));
|
||||
assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
|
||||
}
|
||||
|
||||
// get an empty string.
|
||||
@SmallTest
|
||||
public void testGetEmptyString() throws Exception {
|
||||
writeStringRecord(Metadata.TITLE, "");
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals("", mMetadata.getString(Metadata.TITLE));
|
||||
}
|
||||
|
||||
// get a string when a NULL value was in the parcel
|
||||
@SmallTest
|
||||
public void testGetNullString() throws Exception {
|
||||
writeStringRecord(Metadata.TITLE, null);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(null, mMetadata.getString(Metadata.TITLE));
|
||||
}
|
||||
|
||||
// get a string when an integer is actually present
|
||||
@SmallTest
|
||||
public void testWrongType() throws Exception {
|
||||
writeIntRecord(Metadata.DURATION, 5);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
try {
|
||||
mMetadata.getString(Metadata.DURATION);
|
||||
} catch (IllegalStateException ise) {
|
||||
return;
|
||||
}
|
||||
fail("Exception was not thrown");
|
||||
}
|
||||
|
||||
// getInt
|
||||
@SmallTest
|
||||
public void testGetInt() throws Exception {
|
||||
writeIntRecord(Metadata.CD_TRACK_NUM, 1);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
|
||||
}
|
||||
|
||||
// getBoolean
|
||||
@SmallTest
|
||||
public void testGetBoolean() throws Exception {
|
||||
writeBooleanRecord(Metadata.DRM_CRIPPLED, true);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED));
|
||||
}
|
||||
|
||||
// getLong
|
||||
@SmallTest
|
||||
public void testGetLong() throws Exception {
|
||||
writeLongRecord(Metadata.DURATION, 1L);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
|
||||
}
|
||||
|
||||
// getDouble
|
||||
@SmallTest
|
||||
public void testGetDouble() throws Exception {
|
||||
writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
|
||||
}
|
||||
|
||||
// getByteArray
|
||||
@SmallTest
|
||||
public void testGetByteArray() throws Exception {
|
||||
byte data[] = new byte[]{1,2,3,4,5};
|
||||
|
||||
writeByteArrayRecord(Metadata.ALBUM_ART, data);
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
assertEquals(data[i], res[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// getDate
|
||||
@SmallTest
|
||||
public void testGetDate() throws Exception {
|
||||
writeDateRecord(Metadata.DATE, 0, "PST");
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
|
||||
}
|
||||
|
||||
// getTimedText
|
||||
@SmallTest
|
||||
public void testGetTimedText() throws Exception {
|
||||
Date now = Calendar.getInstance().getTime();
|
||||
writeTimedTextRecord(Metadata.CAPTION, now.getTime(),
|
||||
10, "Some caption");
|
||||
adjustSize();
|
||||
assertParse();
|
||||
|
||||
Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION);
|
||||
assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// HELPERS TO APPEND RECORDS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// Insert a string record at the current position.
|
||||
private void writeStringRecord(int metadataId, String val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.STRING_VAL);
|
||||
mParcel.writeString(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert an int record at the current position.
|
||||
private void writeIntRecord(int metadataId, int val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.INTEGER_VAL);
|
||||
mParcel.writeInt(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a boolean record at the current position.
|
||||
private void writeBooleanRecord(int metadataId, boolean val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.BOOLEAN_VAL);
|
||||
mParcel.writeInt(val ? 1 : 0);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a Long record at the current position.
|
||||
private void writeLongRecord(int metadataId, long val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.LONG_VAL);
|
||||
mParcel.writeLong(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a Double record at the current position.
|
||||
private void writeDoubleRecord(int metadataId, double val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.DOUBLE_VAL);
|
||||
mParcel.writeDouble(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a ByteArray record at the current position.
|
||||
private void writeByteArrayRecord(int metadataId, byte[] val) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
|
||||
mParcel.writeByteArray(val);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a Date record at the current position.
|
||||
private void writeDateRecord(int metadataId, long time, String tz) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.DATE_VAL);
|
||||
mParcel.writeLong(time);
|
||||
mParcel.writeString(tz);
|
||||
adjustSize(start);
|
||||
}
|
||||
|
||||
// Insert a TimedText record at the current position.
|
||||
private void writeTimedTextRecord(int metadataId, long begin,
|
||||
int duration, String text) {
|
||||
final int start = mParcel.dataPosition();
|
||||
mParcel.writeInt(-1); // Placeholder for the length
|
||||
mParcel.writeInt(metadataId);
|
||||
mParcel.writeInt(Metadata.TIMED_TEXT_VAL);
|
||||
mParcel.writeLong(begin);
|
||||
mParcel.writeInt(duration);
|
||||
mParcel.writeString(text);
|
||||
adjustSize(start);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user