diff options
Diffstat (limited to 'media/java')
| -rw-r--r-- | media/java/android/media/AudioTrack.java | 48 | ||||
| -rw-r--r-- | media/java/android/media/MediaFile.java | 7 | ||||
| -rw-r--r-- | media/java/android/media/MediaScanner.java | 81 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpPropertyGroup.java | 1 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpServer.java | 30 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpStorage.java | 11 |
6 files changed, 115 insertions, 63 deletions
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index b97c3c4..b20a6e9 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -32,24 +32,25 @@ import android.util.Log; * It allows to stream PCM audio buffers to the audio hardware for playback. This is * achieved by "pushing" the data to the AudioTrack object using one of the * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods. - * + * * <p>An AudioTrack instance can operate under two modes: static or streaming.<br> * In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using - * one of the write() methods. These are blocking and return when the data has been transferred - * from the Java layer to the native layer and queued for playback. The streaming mode - * is most useful when playing blocks of audio data that for instance are: + * one of the {@code write()} methods. These are blocking and return when the data has been + * transferred from the Java layer to the native layer and queued for playback. The streaming + * mode is most useful when playing blocks of audio data that for instance are: + * * <ul> * <li>too big to fit in memory because of the duration of the sound to play,</li> * <li>too big to fit in memory because of the characteristics of the audio data * (high sampling rate, bits per sample ...)</li> * <li>received or generated while previously queued audio is playing.</li> * </ul> + * * The static mode is to be chosen when dealing with short sounds that fit in memory and - * that need to be played with the smallest latency possible. AudioTrack instances in static mode - * can play the sound without the need to transfer the audio data from Java to native layer - * each time the sound is to be played. The static mode will therefore be preferred for UI and - * game sounds that are played often, and with the smallest overhead possible. - * + * that need to be played with the smallest latency possible. The static mode will + * therefore be preferred for UI and game sounds that are played often, and with the + * smallest overhead possible. + * * <p>Upon creation, an AudioTrack object initializes its associated audio buffer. * The size of this buffer, specified during the construction, determines how long an AudioTrack * can play before running out of data.<br> @@ -816,6 +817,7 @@ public class AudioTrack //-------------------- /** * Starts playing an AudioTrack. + * * @throws IllegalStateException */ public void play() @@ -832,6 +834,7 @@ public class AudioTrack /** * Stops playing the audio data. + * * @throws IllegalStateException */ public void stop() @@ -848,7 +851,10 @@ public class AudioTrack } /** - * Pauses the playback of the audio data. + * Pauses the playback of the audio data. Data that has not been played + * back will not be discarded. Subsequent calls to {@link #play} will play + * this data back. + * * @throws IllegalStateException */ public void pause() @@ -871,9 +877,9 @@ public class AudioTrack //-------------------- /** - * Flushes the audio data currently queued for playback. + * Flushes the audio data currently queued for playback. Any data that has + * not been played back will be discarded. */ - public void flush() { if (mState == STATE_INITIALIZED) { // flush the data in native layer @@ -883,9 +889,14 @@ public class AudioTrack } /** - * Writes the audio data to the audio hardware for playback. + * Writes the audio data to the audio hardware for playback. Will block until + * all data has been written to the audio mixer. + * Note that the actual playback of this data might occur after this function + * returns. This function is thread safe with respect to {@link #stop} calls, + * in which case all of the specified data might not be written to the mixer. + * * @param audioData the array that holds the data to play. - * @param offsetInBytes the offset expressed in bytes in audioData where the data to play + * @param offsetInBytes the offset expressed in bytes in audioData where the data to play * starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION} @@ -914,7 +925,12 @@ public class AudioTrack /** - * Writes the audio data to the audio hardware for playback. + * Writes the audio data to the audio hardware for playback. Will block until + * all data has been written to the audio mixer. + * Note that the actual playback of this data might occur after this function + * returns. This function is thread safe with respect to {@link #stop} calls, + * in which case all of the specified data might not be written to the mixer. + * * @param audioData the array that holds the data to play. * @param offsetInShorts the offset expressed in shorts in audioData where the data to play * starts. @@ -988,7 +1004,7 @@ public class AudioTrack /** * Sets the send level of the audio track to the attached auxiliary effect - * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0. + * {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0. * <p>By default the send level is 0, so even if an effect is attached to the player * this method must be called for the effect to be applied. * <p>Note that the passed level value is a raw scalar. UI controls should be scaled diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 6df2f73..816d215 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -104,10 +104,9 @@ public class MediaFile { public static final int FILE_TYPE_MS_POWERPOINT = 106; public static final int FILE_TYPE_ZIP = 107; - static class MediaFileType { - - int fileType; - String mimeType; + public static class MediaFileType { + public final int fileType; + public final String mimeType; MediaFileType(int fileType, String mimeType) { this.fileType = fileType; diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index c55338a..e89be08 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -101,6 +101,8 @@ import java.util.Iterator; * Java MyMediaScanner handleStringTag. * Once MediaScanner processFile returns, an entry is inserted in to the database. * + * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner. + * * {@hide} */ public class MediaScanner @@ -368,6 +370,34 @@ public class MediaScanner } } + private class FileInserter { + + ContentValues[] mValues = new ContentValues[1000]; + int mIndex = 0; + + public Uri insert(ContentValues values) { + if (mIndex == mValues.length) { + flush(); + } + mValues[mIndex++] = values; + // URI not needed when doing bulk inserts + return null; + } + + public void flush() { + while (mIndex < mValues.length) { + mValues[mIndex++] = null; + } + try { + mMediaProvider.bulkInsert(mFilesUri, mValues); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in FileInserter.flush()", e); + } + mIndex = 0; + } + } + private FileInserter mFileInserter; + // hashes file path to FileCacheEntry. // path should be lower case if mCaseInsensitivePaths is true private HashMap<String, FileCacheEntry> mFileCache; @@ -805,35 +835,42 @@ public class MediaScanner } } + // For inserts we always use the file URI so we can insert in bulk. + // For updates we compute the URI based on the media type. Uri tableUri = mFilesUri; - if (!mNoMedia) { - if (MediaFile.isVideoFileType(mFileType)) { - tableUri = mVideoUri; - } else if (MediaFile.isImageFileType(mFileType)) { - tableUri = mImagesUri; - } else if (MediaFile.isAudioFileType(mFileType)) { - tableUri = mAudioUri; - } - } Uri result = null; if (rowId == 0) { if (mMtpObjectHandle != 0) { values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle); } - if (tableUri == mFilesUri) { - int format = entry.mFormat; - if (format == 0) { - format = MediaFile.getFormatCode(entry.mPath, mMimeType); - } - values.put(Files.FileColumns.FORMAT, format); + int format = entry.mFormat; + if (format == 0) { + format = MediaFile.getFormatCode(entry.mPath, mMimeType); } + values.put(Files.FileColumns.FORMAT, format); + // new file, insert it - result = mMediaProvider.insert(tableUri, values); + if (mFileInserter != null) { + result = mFileInserter.insert(values); + } else { + result = mMediaProvider.insert(tableUri, values); + } + if (result != null) { rowId = ContentUris.parseId(result); entry.mRowId = rowId; } } else { + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + tableUri = mVideoUri; + } else if (MediaFile.isImageFileType(mFileType)) { + tableUri = mImagesUri; + } else if (MediaFile.isAudioFileType(mFileType)) { + tableUri = mAudioUri; + } + } + // updated file result = ContentUris.withAppendedId(tableUri, rowId); // path should never change, and we want to avoid replacing mixed cased paths @@ -854,7 +891,7 @@ public class MediaScanner new String[] { genre }, null); if (cursor == null || cursor.getCount() == 0) { // genre does not exist, so create the genre in the genre table - values.clear(); + values = new ContentValues(); values.put(MediaStore.Audio.Genres.NAME, genre); uri = mMediaProvider.insert(mGenresUri, values); } else { @@ -876,7 +913,7 @@ public class MediaScanner if (uri != null) { // add entry to audio_genre_map - values.clear(); + values = new ContentValues(); values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId)); mMediaProvider.insert(uri, values); } @@ -1152,10 +1189,6 @@ public class MediaScanner mPlaylistsUri = Playlists.getContentUri(volumeName); mCaseInsensitivePaths = true; - if (!Process.supportsProcesses()) { - // Simulator uses host file system, so it should be case sensitive. - mCaseInsensitivePaths = false; - } } } @@ -1165,10 +1198,14 @@ public class MediaScanner initialize(volumeName); prescan(null, true); long prescan = System.currentTimeMillis(); + mFileInserter = new FileInserter(); for (int i = 0; i < directories.length; i++) { processDirectory(directories[i], mClient); } + mFileInserter.flush(); + mFileInserter = null; + long scan = System.currentTimeMillis(); postscan(directories); long end = System.currentTimeMillis(); diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index b75b11a..76c8569 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -330,7 +330,6 @@ class MtpPropertyGroup { } int count = (c == null ? 1 : c.getCount()); - Log.d(TAG, "count: " + count); MtpPropertyList result = new MtpPropertyList(count * mProperties.length, MtpConstants.RESPONSE_OK); diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index 0133cf6..f561cc0 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -16,18 +16,13 @@ package android.mtp; -import android.util.Log; - /** * Java wrapper for MTP/PTP support as USB responder. * {@hide} */ -public class MtpServer { - - private final Object mLock = new Object(); - private boolean mStarted; +public class MtpServer implements Runnable { - private static final String TAG = "MtpServer"; + private int mNativeContext; // accessed by native methods static { System.loadLibrary("media_jni"); @@ -38,19 +33,14 @@ public class MtpServer { } public void start() { - synchronized (mLock) { - native_start(); - mStarted = true; - } + Thread thread = new Thread(this, "MtpServer"); + thread.start(); } - public void stop() { - synchronized (mLock) { - if (mStarted) { - native_stop(); - mStarted = false; - } - } + @Override + public void run() { + native_run(); + native_cleanup(); } public void sendObjectAdded(int handle) { @@ -70,8 +60,8 @@ public class MtpServer { } private native final void native_setup(MtpDatabase database, boolean usePtp); - private native final void native_start(); - private native final void native_stop(); + private native final void native_run(); + private native final void native_cleanup(); private native final void native_send_object_added(int handle); private native final void native_send_object_removed(int handle); private native final void native_add_storage(MtpStorage storage); diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 7932d34..da190a6 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -32,6 +32,7 @@ public class MtpStorage { private final String mDescription; private final long mReserveSpace; private final boolean mRemovable; + private final long mMaxFileSize; public MtpStorage(StorageVolume volume) { mStorageId = volume.getStorageId(); @@ -39,6 +40,7 @@ public class MtpStorage { mDescription = volume.getDescription(); mReserveSpace = volume.getMtpReserveSpace(); mRemovable = volume.isRemovable(); + mMaxFileSize = volume.getMaxFileSize(); } /** @@ -98,4 +100,13 @@ public class MtpStorage { public final boolean isRemovable() { return mRemovable; } + + /** + * Returns maximum file size for the storage, or zero if it is unbounded. + * + * @return maximum file size + */ + public long getMaxFileSize() { + return mMaxFileSize; + } } |
