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:
Nicolas Catania
2009-07-16 10:00:12 -07:00
parent 52e4ad89ad
commit cb2e00eedc
2 changed files with 299 additions and 41 deletions

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}