FileDocCategorySizeDatePackage
Metadata.javaAPI DocAndroid 5.1 API17133Thu Mar 12 22:22:30 GMT 2015android.media

Metadata

public class Metadata extends Object
Class to hold the media's metadata. Metadata are used for human consumption and can be embedded in the media (e.g shoutcast) or available from an external source. The source can be local (e.g thumbnail stored in the DB) or remote. Metadata is like a Bundle. It is sparse and each key can occur at most once. The key is an integer and the value is the actual metadata. The caller is expected to know the type of the metadata and call the right get* method to fetch its value.
hide
deprecated
Use {@link MediaMetadata}.

Fields Summary
public static final int
ANY
{@hide}
public static final int
PAUSE_AVAILABLE
Indicate whether the media can be paused
public static final int
SEEK_BACKWARD_AVAILABLE
Indicate whether the media can be backward seeked
public static final int
SEEK_FORWARD_AVAILABLE
Indicate whether the media can be forward seeked
public static final int
SEEK_AVAILABLE
Indicate whether the media can be seeked
public static final int
TITLE
{@hide}
public static final int
COMMENT
{@hide}
public static final int
COPYRIGHT
{@hide}
public static final int
ALBUM
{@hide}
public static final int
ARTIST
{@hide}
public static final int
AUTHOR
{@hide}
public static final int
COMPOSER
{@hide}
public static final int
GENRE
{@hide}
public static final int
DATE
{@hide}
public static final int
DURATION
{@hide}
public static final int
CD_TRACK_NUM
{@hide}
public static final int
CD_TRACK_MAX
{@hide}
public static final int
RATING
{@hide}
public static final int
ALBUM_ART
{@hide}
public static final int
VIDEO_FRAME
{@hide}
public static final int
BIT_RATE
{@hide}
public static final int
AUDIO_BIT_RATE
{@hide}
public static final int
VIDEO_BIT_RATE
{@hide}
public static final int
AUDIO_SAMPLE_RATE
{@hide}
public static final int
VIDEO_FRAME_RATE
{@hide}
public static final int
MIME_TYPE
{@hide}
public static final int
AUDIO_CODEC
{@hide}
public static final int
VIDEO_CODEC
{@hide}
public static final int
VIDEO_HEIGHT
{@hide}
public static final int
VIDEO_WIDTH
{@hide}
public static final int
NUM_TRACKS
{@hide}
public static final int
DRM_CRIPPLED
{@hide}
private static final int
LAST_SYSTEM
private static final int
FIRST_CUSTOM
public static final Set
MATCH_NONE
{@hide}
public static final Set
MATCH_ALL
{@hide}
public static final int
STRING_VAL
{@hide}
public static final int
INTEGER_VAL
{@hide}
public static final int
BOOLEAN_VAL
{@hide}
public static final int
LONG_VAL
{@hide}
public static final int
DOUBLE_VAL
{@hide}
public static final int
DATE_VAL
{@hide}
public static final int
BYTE_ARRAY_VAL
{@hide}
private static final int
LAST_TYPE
private static final String
TAG
private static final int
kInt32Size
private static final int
kMetaHeaderSize
private static final int
kRecordHeaderSize
private static final int
kMetaMarker
private android.os.Parcel
mParcel
private final HashMap
mKeyToPosMap
Constructors Summary
public Metadata()
{@hide}


          
       
Methods Summary
private booleancheckMetadataId(int val)
Check val is either a system id or a custom one.

param
val Metadata key to test.
return
true if it is in a valid range.

        if (val <= ANY || (LAST_SYSTEM < val && val < FIRST_CUSTOM)) {
            Log.e(TAG, "Invalid metadata ID " + val);
            return false;
        }
        return true;
    
private voidcheckType(int key, int expectedType)
Check the type of the data match what is expected.

        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);
        }
    
public static intfirstCustomId()

return
the first available cutom metadata id. {@hide}

 return FIRST_CUSTOM; 
public booleangetBoolean(int key)
Get the boolean value indicated by key

        checkType(key, BOOLEAN_VAL);
        return mParcel.readInt() == 1;
    
public byte[]getByteArray(int key)
{@hide}

        checkType(key, BYTE_ARRAY_VAL);
        return mParcel.createByteArray();
    
public java.util.DategetDate(int key)
{@hide}

        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 doublegetDouble(int key)
{@hide}

        checkType(key, DOUBLE_VAL);
        return mParcel.readDouble();
    
public intgetInt(int key)
{@hide}

        checkType(key, INTEGER_VAL);
        return mParcel.readInt();
    
public longgetLong(int key)
{@hide}

        checkType(key, LONG_VAL);    /**
     * {@hide}
     */
        return mParcel.readLong();
    
public java.lang.StringgetString(int key)
{@hide}

        checkType(key, STRING_VAL);
        return mParcel.readString();
    
public booleanhas(int metadataId)

return
true if a value is present for the given key.

        if (!checkMetadataId(metadataId)) {
            throw new IllegalArgumentException("Invalid key: " + metadataId);
        }
        return mKeyToPosMap.containsKey(metadataId);
    
public java.util.SetkeySet()

return
The set of metadata ID found.

        return mKeyToPosMap.keySet();
    
public static intlastSytemId()

return
the last available system metadata id. Ids are 1-indexed. {@hide}

 return LAST_SYSTEM; 
public static intlastType()

return
the last value of known type. Types are 1-indexed. {@hide}

 return LAST_TYPE; 
public booleanparse(android.os.Parcel parcel)
Check a parcel containing metadata is well formed. The header is checked as well as the individual records format. However, the data inside the record is not checked because we do lazy access (we check/unmarshall only data the user asks for.) Format of a metadata parcel:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| metadata total size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'M' | 'E' | 'T' | 'A' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| .... metadata records .... |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

param
parcel With the serialized data. Metadata keeps a reference on it to access it later on. The caller should not modify the parcel after this call (and not call recycle on it.)
return
false if an error occurred. {@hide}

        if (parcel.dataAvail() < kMetaHeaderSize) {
            Log.e(TAG, "Not enough data " + parcel.dataAvail());
            return false;
        }

        final int pin = parcel.dataPosition();  // to roll back in case of errors.
        final int size = parcel.readInt();

        // The extra kInt32Size below is to account for the int32 'size' just read.
        if (parcel.dataAvail() + kInt32Size < size || size < kMetaHeaderSize) {
            Log.e(TAG, "Bad size " + size + " avail " + parcel.dataAvail() + " position " + pin);
            parcel.setDataPosition(pin);
            return false;
        }

        // Checks if the 'M' 'E' 'T' 'A' marker is present.
        final int kShouldBeMetaMarker = parcel.readInt();
        if (kShouldBeMetaMarker != kMetaMarker ) {
            Log.e(TAG, "Marker missing " + Integer.toHexString(kShouldBeMetaMarker));
            parcel.setDataPosition(pin);
            return false;
        }

        // Scan the records to collect metadata ids and offsets.
        if (!scanAllRecords(parcel, size - kMetaHeaderSize)) {
            parcel.setDataPosition(pin);
            return false;
        }
        mParcel = parcel;
        return true;
    
private booleanscanAllRecords(android.os.Parcel parcel, int bytesLeft)
Go over all the records, collecting metadata keys and records' type field offset in the Parcel. These are stored in mKeyToPosMap for latter retrieval. Format of a metadata record:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| record size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| metadata key | // TITLE
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| metadata type | // STRING_VAL
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| .... metadata payload .... |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

param
parcel With the serialized records.
param
bytesLeft How many bytes in the parcel should be processed.
return
false if an error occurred during parsing.

        int recCount = 0;
        boolean error = false;

        mKeyToPosMap.clear();
        while (bytesLeft > kRecordHeaderSize) {
            final int start = parcel.dataPosition();
            // Check the size.
            final int size = parcel.readInt();

            if (size <= kRecordHeaderSize) {  // at least 1 byte should be present.
                Log.e(TAG, "Record is too short");
                error = true;
                break;
            }

            // Check the metadata key.
            final int metadataId = parcel.readInt();
            if (!checkMetadataId(metadataId)) {
                error = true;
                break;
            }

            // Store the record offset which points to the type
            // field so we can later on read/unmarshall the record
            // payload.
            if (mKeyToPosMap.containsKey(metadataId)) {
                Log.e(TAG, "Duplicate metadata ID found");
                error = true;
                break;
            }

            mKeyToPosMap.put(metadataId, parcel.dataPosition());

            // Check the metadata type.
            final int metadataType = parcel.readInt();
            if (metadataType <= 0 || metadataType > LAST_TYPE) {
                Log.e(TAG, "Invalid metadata type " + metadataType);
                error = true;
                break;
            }

            // Skip to the next one.
            parcel.setDataPosition(start + size);
            bytesLeft -= size;
            ++recCount;
        }

        if (0 != bytesLeft || error) {
            Log.e(TAG, "Ran out of data or error on record " + recCount);
            mKeyToPosMap.clear();
            return false;
        } else {
            return true;
        }