diff options
Diffstat (limited to 'media/java')
| -rw-r--r-- | media/java/android/media/AudioManager.java | 21 | ||||
| -rw-r--r-- | media/java/android/media/AudioService.java | 136 | ||||
| -rw-r--r-- | media/java/android/media/DataSource.java | 43 | ||||
| -rw-r--r-- | media/java/android/media/MediaExtractor.java | 9 | ||||
| -rw-r--r-- | media/java/android/media/MediaFile.java | 2 | ||||
| -rw-r--r-- | media/java/android/media/MediaMetadataRetriever.java | 5 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer.java | 26 | ||||
| -rw-r--r-- | media/java/android/media/MediaRecorder.java | 8 | ||||
| -rw-r--r-- | media/java/android/media/MediaScanner.java | 64 |
9 files changed, 249 insertions, 65 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b6e4659..b5613f3 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -49,6 +49,7 @@ public class AudioManager { private final Context mContext; private long mVolumeKeyUpTime; private final boolean mUseMasterVolume; + private final boolean mUseVolumeKeySounds; private static String TAG = "AudioManager"; /** @@ -412,6 +413,8 @@ public class AudioManager { mContext = context; mUseMasterVolume = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useMasterVolume); + mUseVolumeKeySounds = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_useVolumeKeySounds); } private static IAudioService getService() @@ -463,6 +466,7 @@ public class AudioManager { * responsive to the user. */ int flags = FLAG_SHOW_UI | FLAG_VIBRATE; + if (mUseMasterVolume) { adjustMasterVolume( keyCode == KeyEvent.KEYCODE_VOLUME_UP @@ -502,18 +506,17 @@ public class AudioManager { * Play a sound. This is done on key up since we don't want the * sound to play when a user holds down volume down to mute. */ - if (mUseMasterVolume) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + if (mUseVolumeKeySounds) { + if (mUseMasterVolume) { adjustMasterVolume(ADJUST_SAME, FLAG_PLAY_SOUND); + } else { + int flags = FLAG_PLAY_SOUND; + adjustSuggestedStreamVolume( + ADJUST_SAME, + stream, + flags); } - } else { - int flags = FLAG_PLAY_SOUND; - adjustSuggestedStreamVolume( - ADJUST_SAME, - stream, - flags); } - mVolumeKeyUpTime = SystemClock.uptimeMillis(); break; } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2e153dd..aea8a88 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -57,6 +57,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; @@ -143,11 +144,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_REEVALUATE_REMOTE = 17; private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18; private static final int MSG_RCC_NEW_VOLUME_OBS = 19; + private static final int MSG_SET_FORCE_BT_A2DP_USE = 20; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) - private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 20; - private static final int MSG_SET_A2DP_CONNECTION_STATE = 21; + private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21; + private static final int MSG_SET_A2DP_CONNECTION_STATE = 22; // end of messages handled under wakelock // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be @@ -381,7 +383,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // message looper for SoundPool listener private Looper mSoundPoolLooper = null; // volume applied to sound played with playSoundEffect() - private static int SOUND_EFFECT_VOLUME_DB; + private static int sSoundEffectVolumeDb; // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification // stopped private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000; @@ -438,7 +440,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { "ro.config.vc_call_vol_steps", MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); - SOUND_EFFECT_VOLUME_DB = context.getResources().getInteger( + sSoundEffectVolumeDb = context.getResources().getInteger( com.android.internal.R.integer.config_soundEffectVolumeDb); mVolumePanel = new VolumePanel(context, this); @@ -929,6 +931,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return delta; } + private void sendBroadcastToAll(Intent intent) { + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void sendStickyBroadcastToAll(Intent intent) { + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + // UI update and Broadcast Intent private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) { if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) { @@ -943,7 +963,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); - mContext.sendBroadcast(intent); + sendBroadcastToAll(intent); } // UI update and Broadcast Intent @@ -953,7 +973,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION); intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume); intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume); - mContext.sendBroadcast(intent); + sendBroadcastToAll(intent); } // UI update and Broadcast Intent @@ -967,9 +987,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_REPLACE_PENDING); - long origCallerIdentityToken = Binder.clearCallingIdentity(); - mContext.sendStickyBroadcast(intent); - Binder.restoreCallingIdentity(origCallerIdentityToken); + sendStickyBroadcastToAll(intent); } /** @@ -1701,7 +1719,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { /** @see AudioManager#setBluetoothA2dpOn() */ public void setBluetoothA2dpOn(boolean on) { - setBluetoothA2dpOnInt(on); + synchronized (mBluetoothA2dpEnabledLock) { + mBluetoothA2dpEnabled = on; + sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE, + AudioSystem.FOR_MEDIA, + mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP, + null, 0); + } } /** @see AudioManager#isBluetoothA2dpOn() */ @@ -1985,7 +2009,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, mScoConnectionState); - mContext.sendStickyBroadcast(newIntent); + sendStickyBroadcastToAll(newIntent); mScoConnectionState = state; } } @@ -2276,9 +2300,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode); broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_REPLACE_PENDING); - long origCallerIdentityToken = Binder.clearCallingIdentity(); - mContext.sendStickyBroadcast(broadcast); - Binder.restoreCallingIdentity(origCallerIdentityToken); + sendStickyBroadcastToAll(broadcast); } private void broadcastVibrateSetting(int vibrateType) { @@ -2287,7 +2309,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); - mContext.sendBroadcast(broadcast); + sendBroadcastToAll(broadcast); } } @@ -2875,7 +2897,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { float volFloat; // use default if volume is not specified by caller if (volume < 0) { - volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20); + volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20); } else { volFloat = (float) volume / 1000.0f; } @@ -3049,6 +3071,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { break; case MSG_SET_FORCE_USE: + case MSG_SET_FORCE_BT_A2DP_USE: setForceUse(msg.arg1, msg.arg2); break; @@ -3167,7 +3190,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } private void sendBecomingNoisyIntent() { - mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); + sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); } // must be called synchronized on mConnectedDevices @@ -3286,7 +3309,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // sent if none of these devices is connected. int mBecomingNoisyIntentDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE | - AudioSystem.DEVICE_OUT_ALL_A2DP; + AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL | + AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET | + AudioSystem.DEVICE_OUT_ALL_USB; // must be called before removing the device from mConnectedDevices private int checkSendBecomingNoisyIntent(int device, int state) { @@ -3356,7 +3381,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } - ActivityManagerNative.broadcastStickyIntent(intent, null); + final long ident = Binder.clearCallingIdentity(); + try { + ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } } private void onSetWiredDeviceConnectionState(int device, int state, String name) @@ -3508,7 +3538,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); - mContext.sendStickyBroadcast(newIntent); + sendStickyBroadcastToAll(newIntent); } } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { mBootCompleted = true; @@ -3525,7 +3555,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - mContext.sendStickyBroadcast(newIntent); + sendStickyBroadcastToAll(newIntent); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { @@ -3652,9 +3682,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { FocusStackEntry fse = stackIterator.next(); - pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId + pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId + " -- duration: " + fse.mFocusChangeType - + " -- uid: " + fse.mCallingUid); + + " -- uid: " + fse.mCallingUid + + " -- stream: " + fse.mStreamType); } } } @@ -3915,8 +3946,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mMediaEventWakeLock.acquire(); keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); } - mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone, - mAudioHandler, Activity.RESULT_OK, null, null); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, + null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null); + } finally { + Binder.restoreCallingIdentity(ident); + } } /** @@ -3949,8 +3985,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if (needWakeLock) { keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); } - mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone, - mAudioHandler, Activity.RESULT_OK, null, null); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, + null, mKeyEventDone, + mAudioHandler, Activity.RESULT_OK, null, null); + } finally { + Binder.restoreCallingIdentity(ident); + } } } } @@ -4014,7 +4056,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { startVoiceBasedInteractions(needWakeLock); break; case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS: - if (DEBUG_RC) Log.v(TAG, " send simulated key event"); + if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock); sendSimulatedMediaButtonEvent(keyEvent, needWakeLock); break; } @@ -4646,17 +4688,40 @@ public class AudioService extends IAudioService.Stub implements OnFinished { clearRemoteControlDisplay_syncAfRcs(); return; } - // if the top of the two stacks belong to different packages, there is a mismatch, clear + + // determine which entry in the AudioFocus stack to consider, and compare against the + // top of the stack for the media button event receivers : simply using the top of the + // stack would make the entry disappear from the RemoteControlDisplay in conditions such as + // notifications playing during music playback. + // crawl the AudioFocus stack until an entry is found with the following characteristics: + // - focus gain on STREAM_MUSIC stream + // - non-transient focus gain on a stream other than music + FocusStackEntry af = null; + Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); + while(stackIterator.hasNext()) { + FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); + if ((fse.mStreamType == AudioManager.STREAM_MUSIC) + || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) { + af = fse; + break; + } + } + if (af == null) { + clearRemoteControlDisplay_syncAfRcs(); + return; + } + + // if the audio focus and RC owners belong to different packages, there is a mismatch, clear if ((mRCStack.peek().mCallingPackageName != null) - && (mFocusStack.peek().mPackageName != null) + && (af.mPackageName != null) && !(mRCStack.peek().mCallingPackageName.compareTo( - mFocusStack.peek().mPackageName) == 0)) { + af.mPackageName) == 0)) { clearRemoteControlDisplay_syncAfRcs(); return; } // if the audio focus didn't originate from the same Uid as the one in which the remote // control information will be retrieved, clear - if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) { + if (mRCStack.peek().mCallingUid != af.mCallingUid) { clearRemoteControlDisplay_syncAfRcs(); return; } @@ -5290,10 +5355,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public void setBluetoothA2dpOnInt(boolean on) { synchronized (mBluetoothA2dpEnabledLock) { mBluetoothA2dpEnabled = on; - sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, - AudioSystem.FOR_MEDIA, - mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP, - null, 0); + mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE); + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, + mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP); } } diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java new file mode 100644 index 0000000..347bd5f --- /dev/null +++ b/media/java/android/media/DataSource.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.media; + +import java.io.Closeable; + +/** + * An abstraction for a media data source, e.g. a file or an http stream + * {@hide} + */ +public interface DataSource extends Closeable { + /** + * Reads data from the data source at the requested position + * + * @param offset where in the source to read + * @param buffer the buffer to read the data into + * @param size how many bytes to read + * @return the number of bytes read, or -1 if there was an error + */ + public int readAt(long offset, byte[] buffer, int size); + + /** + * Gets the size of the data source. + * + * @return size of data source, or -1 if the length is unknown + */ + public long getSize(); +} diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index 687d3a5..749ef12 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -22,6 +22,7 @@ import android.content.res.AssetFileDescriptor; import android.media.MediaCodec; import android.media.MediaFormat; import android.net.Uri; + import java.io.FileDescriptor; import java.io.IOException; import java.nio.ByteBuffer; @@ -44,7 +45,7 @@ import java.util.Map; * } * ByteBuffer inputBuffer = ByteBuffer.allocate(...) * while (extractor.readSampleData(inputBuffer, ...) >= 0) { - * int trackIndex = extractor.getTrackIndex(); + * int trackIndex = extractor.getSampleTrackIndex(); * long presentationTimeUs = extractor.getSampleTime(); * ... * extractor.advance(); @@ -60,6 +61,12 @@ final public class MediaExtractor { } /** + * Sets the DataSource object to be used as the data source for this extractor + * {@hide} + */ + public native final void setDataSource(DataSource source); + + /** * Sets the data source as a content Uri. * * @param context the Context to use when resolving the Uri diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index d21ada4..06d43a2 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -325,7 +325,7 @@ public class MediaFile { } int lastDot = fileName.lastIndexOf('.'); if (lastDot > 0) { - String extension = fileName.substring(lastDot + 1); + String extension = fileName.substring(lastDot + 1).toUpperCase(); Integer value = sFileTypeToFormatMap.get(extension); if (value != null) { return value.intValue(); diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index aef631f..cc59d02 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -483,5 +483,10 @@ public class MediaMetadataRetriever * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance. */ public static final int METADATA_KEY_LOCATION = 23; + /** + * This key retrieves the video rotation angle in degrees, if available. + * The video rotation angle may be 0, 90, 180, or 270 degrees. + */ + public static final int METADATA_KEY_VIDEO_ROTATION = 24; // Add more here... } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index cd25865..ef0da3a 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2265,6 +2265,16 @@ public class MediaPlayer */ public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; + /** File or network related operation errors. */ + public static final int MEDIA_ERROR_IO = -1004; + /** Bitstream is not conforming to the related coding standard or file spec. */ + public static final int MEDIA_ERROR_MALFORMED = -1007; + /** Bitstream is conforming to the related coding standard or file spec, but + * the media framework does not support the feature. */ + public static final int MEDIA_ERROR_UNSUPPORTED = -1010; + /** Some operation takes too long to complete, usually more than 3-5 seconds. */ + public static final int MEDIA_ERROR_TIMED_OUT = -110; + /** * Interface definition of a callback to be invoked when there * has been an error during an asynchronous operation (other errors @@ -2282,7 +2292,13 @@ public class MediaPlayer * <li>{@link #MEDIA_ERROR_SERVER_DIED} * </ul> * @param extra an extra code, specific to the error. Typically - * implementation dependant. + * implementation dependent. + * <ul> + * <li>{@link #MEDIA_ERROR_IO} + * <li>{@link #MEDIA_ERROR_MALFORMED} + * <li>{@link #MEDIA_ERROR_UNSUPPORTED} + * <li>{@link #MEDIA_ERROR_TIMED_OUT} + * </ul> * @return True if the method handled the error, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the OnCompletionListener to be called. @@ -2319,6 +2335,11 @@ public class MediaPlayer */ public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; + /** The player just pushed the very first video frame for rendering. + * @see android.media.MediaPlayer.OnInfoListener + */ + public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; + /** The video is too complex for the decoder: it can't decode frames fast * enough. Possibly only the audio plays fine at this stage. * @see android.media.MediaPlayer.OnInfoListener @@ -2374,6 +2395,7 @@ public class MediaPlayer * <ul> * <li>{@link #MEDIA_INFO_UNKNOWN} * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING} + * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START} * <li>{@link #MEDIA_INFO_BUFFERING_START} * <li>{@link #MEDIA_INFO_BUFFERING_END} * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING} @@ -2381,7 +2403,7 @@ public class MediaPlayer * <li>{@link #MEDIA_INFO_METADATA_UPDATE} * </ul> * @param extra an extra code, specific to the info. Typically - * implementation dependant. + * implementation dependent. * @return True if the method handled the info, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the info to be discarded. diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 613354f..2d1be02 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -720,12 +720,17 @@ public class MediaRecorder public native int getMaxAmplitude() throws IllegalStateException; /* Do not change this value without updating its counterpart - * in include/media/mediarecorder.h! + * in include/media/mediarecorder.h or mediaplayer.h! */ /** Unspecified media recorder error. * @see android.media.MediaRecorder.OnErrorListener */ public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; + /** Media server died. In this case, the application must release the + * MediaRecorder object and instantiate a new one. + * @see android.media.MediaRecorder.OnErrorListener + */ + public static final int MEDIA_ERROR_SERVER_DIED = 100; /** * Interface definition for a callback to be invoked when an error @@ -740,6 +745,7 @@ public class MediaRecorder * @param what the type of error that has occurred: * <ul> * <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN} + * <li>{@link #MEDIA_ERROR_SERVER_DIED} * </ul> * @param extra an extra code, specific to the error type */ diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index fd37bcf..88cf4ac 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -314,6 +314,7 @@ public class MediaScanner private int mMtpObjectHandle; private final String mExternalStoragePath; + private final boolean mExternalIsEmulated; /** whether to use bulk inserts or individual inserts for each item */ private static final boolean ENABLE_BULK_INSERTS = true; @@ -392,6 +393,7 @@ public class MediaScanner setDefaultRingtoneFileNames(); mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath(); + mExternalIsEmulated = Environment.isExternalStorageEmulated(); //mClient.testGenreNameConverter(); } @@ -543,13 +545,28 @@ public class MediaScanner boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) || (!ringtones && !notifications && !alarms && !podcasts); + boolean isaudio = MediaFile.isAudioFileType(mFileType); + boolean isvideo = MediaFile.isVideoFileType(mFileType); + boolean isimage = MediaFile.isImageFileType(mFileType); + + if (isaudio || isvideo || isimage) { + if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) { + // try to rewrite the path to bypass the sd card fuse layer + String directPath = Environment.getMediaStorageDirectory() + + path.substring(mExternalStoragePath.length()); + File f = new File(directPath); + if (f.exists()) { + path = directPath; + } + } + } + // we only extract metadata for audio and video files - if (MediaFile.isAudioFileType(mFileType) - || MediaFile.isVideoFileType(mFileType)) { + if (isaudio || isvideo) { processFile(path, mimeType, this); } - if (MediaFile.isImageFileType(mFileType)) { + if (isimage) { processImageFile(path); } @@ -972,7 +989,6 @@ public class MediaScanner } values.put(FileColumns.MEDIA_TYPE, mediaType); } - mMediaProvider.update(result, values, null, null); } @@ -1448,24 +1464,42 @@ public class MediaScanner } FileEntry makeEntryFor(String path) { - String key = path; String where; String[] selectionArgs; - if (mCaseInsensitivePaths) { - // the 'like' makes it use the index, the 'lower()' makes it correct - // when the path contains sqlite wildcard characters - where = "_data LIKE ?1 AND lower(_data)=lower(?1)"; - selectionArgs = new String[] { path }; - } else { - where = Files.FileColumns.DATA + "=?"; - selectionArgs = new String[] { path }; - } Cursor c = null; try { + boolean hasWildCards = path.contains("_") || path.contains("%"); + + if (hasWildCards || !mCaseInsensitivePaths) { + // if there are wildcard characters in the path, the "like" match + // will be slow, and it's worth trying an "=" comparison + // first, since in most cases the case will match. + // Also, we shouldn't do a "like" match on case-sensitive filesystems + where = Files.FileColumns.DATA + "=?"; + selectionArgs = new String[] { path }; + } else { + // if there are no wildcard characters in the path, then the "like" + // match will be just as fast as the "=" case, because of the index + where = "_data LIKE ?1 AND lower(_data)=lower(?1)"; + selectionArgs = new String[] { path }; + } c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, null, null); - if (c.moveToNext()) { + if (!c.moveToFirst() && hasWildCards && mCaseInsensitivePaths) { + // Try again with case-insensitive match. This will be slower, especially + // if the path contains wildcard characters. + // The 'like' makes it use the index, the 'lower()' makes it correct + // when the path contains sqlite wildcard characters, + where = "_data LIKE ?1 AND lower(_data)=lower(?1)"; + selectionArgs = new String[] { path }; + c.close(); + c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, + where, selectionArgs, null, null); + // TODO update the path in the db with the correct case so the fast + // path works next time? + } + if (c.moveToFirst()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); |
