Loading media/java/android/media/Metadata.java +77 −31 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -104,13 +106,18 @@ public class Metadata 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 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 Loading @@ -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(); } } Loading Loading @@ -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 Loading @@ -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); } } } media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java +222 −10 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -133,7 +126,9 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { assertParse(); } // ---------------------------------------------------------------------- // RECORDS // ---------------------------------------------------------------------- // A record header should be at least 12 bytes long @SmallTest Loading Loading @@ -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); } } Loading
media/java/android/media/Metadata.java +77 −31 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -104,13 +106,18 @@ public class Metadata 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 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 Loading @@ -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(); } } Loading Loading @@ -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 Loading @@ -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); } } }
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java +222 −10 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -133,7 +126,9 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { assertParse(); } // ---------------------------------------------------------------------- // RECORDS // ---------------------------------------------------------------------- // A record header should be at least 12 bytes long @SmallTest Loading Loading @@ -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); } }