summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioService.java471
-rw-r--r--media/java/android/media/CamcorderProfile.java2
-rw-r--r--media/java/android/media/MediaFile.java11
-rw-r--r--media/java/android/media/MediaInserter.java74
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java7
-rw-r--r--media/java/android/media/MediaPlayer.java22
-rw-r--r--media/java/android/media/MediaRecorder.java17
-rw-r--r--media/java/android/media/MediaScanner.java62
-rw-r--r--media/java/android/media/videoeditor/MediaArtistNativeHelper.java4
-rwxr-xr-xmedia/java/android/media/videoeditor/MediaImageItem.java27
-rwxr-xr-xmedia/java/android/media/videoeditor/VideoEditor.java11
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java188
-rw-r--r--media/jni/android_media_MediaScanner.cpp64
-rwxr-xr-xmedia/jni/mediaeditor/VideoEditorMain.cpp4
-rw-r--r--media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp71
-rw-r--r--media/libeffects/visualizer/EffectVisualizer.cpp28
-rw-r--r--media/libmedia/IMediaPlayer.cpp17
-rw-r--r--media/libmedia/IStreamSource.cpp2
-rw-r--r--media/libmedia/MediaProfiles.cpp10
-rw-r--r--media/libmedia/mediaplayer.cpp8
-rw-r--r--media/libmediaplayerservice/Android.mk2
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp12
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h1
-rw-r--r--media/libmediaplayerservice/MidiFile.h1
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.cpp6
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.h1
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/Android.mk2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp115
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp23
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp11
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h1
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp354
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.h109
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp17
-rw-r--r--media/libstagefright/ACodec.cpp36
-rw-r--r--media/libstagefright/AVIExtractor.cpp2
-rw-r--r--media/libstagefright/Android.mk2
-rw-r--r--media/libstagefright/AwesomePlayer.cpp163
-rwxr-xr-xmedia/libstagefright/CameraSource.cpp9
-rw-r--r--media/libstagefright/CameraSourceTimeLapse.cpp22
-rw-r--r--media/libstagefright/DataSource.cpp2
-rw-r--r--media/libstagefright/MP3Extractor.cpp19
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp152
-rw-r--r--media/libstagefright/MediaDefs.cpp3
-rw-r--r--media/libstagefright/MediaExtractor.cpp3
-rwxr-xr-xmedia/libstagefright/OMXCodec.cpp155
-rw-r--r--media/libstagefright/SampleTable.cpp17
-rw-r--r--media/libstagefright/StagefrightMediaScanner.cpp2
-rw-r--r--media/libstagefright/StagefrightMetadataRetriever.cpp1
-rw-r--r--media/libstagefright/SurfaceMediaSource.cpp2
-rw-r--r--media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp6
-rw-r--r--media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp6
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp7
-rw-r--r--media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp17
-rw-r--r--media/libstagefright/codecs/on2/h264dec/SoftAVC.h2
-rw-r--r--media/libstagefright/include/ARTSPController.h97
-rw-r--r--media/libstagefright/include/AwesomePlayer.h14
-rw-r--r--media/libstagefright/include/MPEG2PSExtractor.h80
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp85
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.h26
-rw-r--r--media/libstagefright/mpeg2ts/Android.mk1
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp25
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.h3
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.cpp21
-rw-r--r--media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp715
-rw-r--r--media/libstagefright/rtsp/APacketSource.cpp163
-rw-r--r--media/libstagefright/rtsp/APacketSource.h42
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.cpp62
-rw-r--r--media/libstagefright/rtsp/ARTSPConnection.cpp134
-rw-r--r--media/libstagefright/rtsp/ARTSPConnection.h2
-rw-r--r--media/libstagefright/rtsp/ARTSPController.cpp214
-rw-r--r--media/libstagefright/rtsp/Android.mk1
-rw-r--r--media/libstagefright/rtsp/MyHandler.h278
-rw-r--r--media/mtp/MtpDataPacket.cpp6
-rw-r--r--media/mtp/MtpServer.cpp11
-rw-r--r--media/tests/MediaFrameworkTest/Android.mk2
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java14
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java85
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java79
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java89
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/videoeditor/VideoEditorPreviewTest.java38
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java147
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java246
-rw-r--r--media/tests/players/invoke_mock_media_player.cpp1
89 files changed, 3394 insertions, 1695 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2f32bd8..228786e 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -21,7 +21,6 @@ import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import android.app.ActivityManagerNative;
-import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.bluetooth.BluetoothA2dp;
@@ -320,8 +319,6 @@ public class AudioService extends IAudioService.Stub {
private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
// previous volume adjustment direction received by checkForRingerModeChange()
private int mPrevVolDirection = AudioManager.ADJUST_SAME;
- // Keyguard manager proxy
- private KeyguardManager mKeyguardManager;
///////////////////////////////////////////////////////////////////////////
// Construction
@@ -506,10 +503,9 @@ public class AudioService extends IAudioService.Stub {
streamType = getActiveStreamType(suggestedStreamType);
}
- // Play sounds on STREAM_RING only and if lock screen is not on.
+ // Play sounds on STREAM_RING only.
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
- ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING) ||
- (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
+ ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING))) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
@@ -521,6 +517,9 @@ public class AudioService extends IAudioService.Stub {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
+ // use stream type alias here so that streams with same alias have the same behavior,
+ // including with regard to silent mode control (e.g the use of STREAM_RING below and in
+ // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
@@ -529,9 +528,8 @@ public class AudioService extends IAudioService.Stub {
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
- (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
- streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
- (mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_RING)) {
+ streamTypeAlias == AudioSystem.STREAM_RING ||
+ (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
// do not vibrate if already in vibrate mode
if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
flags &= ~AudioManager.FLAG_VIBRATE;
@@ -545,10 +543,19 @@ public class AudioService extends IAudioService.Stub {
int index;
if (streamState.muteCount() != 0) {
if (adjustVolume) {
- streamState.adjustLastAudibleIndex(direction);
- // Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
- SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ // adjust volume on all stream types sharing the same alias otherwise a query
+ // on last audible index for an alias would not give the correct value
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int i = numStreamTypes - 1; i >= 0; i--) {
+ if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) {
+ VolumeStreamState s = mStreamStates[i];
+
+ s.adjustLastAudibleIndex(direction);
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, i,
+ SENDMSG_REPLACE, 0, 1, s, PERSIST_DELAY);
+ }
+ }
}
index = streamState.mLastAudibleIndex;
} else {
@@ -1565,49 +1572,83 @@ public class AudioService extends IAudioService.Stub {
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- synchronized (mScoClients) {
- // Discard timeout message
- mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
- mBluetoothHeadset = (BluetoothHeadset) proxy;
- List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+ BluetoothDevice btDevice;
+ List<BluetoothDevice> deviceList;
+ switch(profile) {
+ case BluetoothProfile.A2DP:
+ BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
+ deviceList = a2dp.getConnectedDevices();
if (deviceList.size() > 0) {
- mBluetoothHeadsetDevice = deviceList.get(0);
- } else {
- mBluetoothHeadsetDevice = null;
- }
- // Refresh SCO audio state
- checkScoAudioState();
- // Continue pending action if any
- if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
- mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
- mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
- boolean status = false;
- if (mBluetoothHeadsetDevice != null) {
- switch (mScoAudioState) {
- case SCO_STATE_ACTIVATE_REQ:
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- break;
- case SCO_STATE_DEACTIVATE_REQ:
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- break;
- case SCO_STATE_DEACTIVATE_EXT_REQ:
- status = mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
+ btDevice = deviceList.get(0);
+ handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
+ }
+ break;
+
+ case BluetoothProfile.HEADSET:
+ synchronized (mScoClients) {
+ // Discard timeout message
+ mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ deviceList = mBluetoothHeadset.getConnectedDevices();
+ if (deviceList.size() > 0) {
+ mBluetoothHeadsetDevice = deviceList.get(0);
+ } else {
+ mBluetoothHeadsetDevice = null;
}
- if (!status) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
- SENDMSG_REPLACE, 0, 0, null, 0);
+ // Refresh SCO audio state
+ checkScoAudioState();
+ // Continue pending action if any
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ boolean status = false;
+ if (mBluetoothHeadsetDevice != null) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_EXT_REQ:
+ status = mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice);
+ }
+ }
+ if (!status) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
}
}
+ break;
+
+ default:
+ break;
}
}
public void onServiceDisconnected(int profile) {
- synchronized (mScoClients) {
- mBluetoothHeadset = null;
+ switch(profile) {
+ case BluetoothProfile.A2DP:
+ synchronized (mConnectedDevices) {
+ if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
+ makeA2dpDeviceUnavailableNow(
+ mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ }
+ }
+ break;
+
+ case BluetoothProfile.HEADSET:
+ synchronized (mScoClients) {
+ mBluetoothHeadset = null;
+ }
+ break;
+
+ default:
+ break;
}
}
};
@@ -2186,15 +2227,17 @@ public class AudioService extends IAudioService.Stub {
AudioSystem.setParameters("restarting=true");
// Restore device connection states
- Set set = mConnectedDevices.entrySet();
- Iterator i = set.iterator();
- while(i.hasNext()){
- Map.Entry device = (Map.Entry)i.next();
- AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
- AudioSystem.DEVICE_STATE_AVAILABLE,
- (String)device.getValue());
+ synchronized (mConnectedDevices) {
+ Set set = mConnectedDevices.entrySet();
+ Iterator i = set.iterator();
+ while(i.hasNext()){
+ Map.Entry device = (Map.Entry)i.next();
+ AudioSystem.setDeviceConnectionState(
+ ((Integer)device.getKey()).intValue(),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ (String)device.getValue());
+ }
}
-
// Restore call state
AudioSystem.setPhoneState(mMode);
@@ -2233,7 +2276,9 @@ public class AudioService extends IAudioService.Stub {
case MSG_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- makeA2dpDeviceUnavailableNow( (String) msg.obj );
+ synchronized (mConnectedDevices) {
+ makeA2dpDeviceUnavailableNow( (String) msg.obj );
+ }
break;
case MSG_SET_FORCE_USE:
@@ -2293,6 +2338,7 @@ public class AudioService extends IAudioService.Stub {
}
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceAvailable(String address) {
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -2303,6 +2349,7 @@ public class AudioService extends IAudioService.Stub {
address);
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableNow(String address) {
Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(noisyIntent);
@@ -2312,6 +2359,7 @@ public class AudioService extends IAudioService.Stub {
mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableLater(String address) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
@@ -2324,14 +2372,60 @@ public class AudioService extends IAudioService.Stub {
}
+ // must be called synchronized on mConnectedDevices
private void cancelA2dpDeviceTimeout() {
mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
}
+ // must be called synchronized on mConnectedDevices
private boolean hasScheduledA2dpDockTimeout() {
return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
}
+ private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
+ {
+ if (btDevice == null) {
+ return;
+ }
+ String address = btDevice.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ address = "";
+ }
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
+ mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
+
+ if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
+ if (btDevice.isBluetoothDock()) {
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ // introduction of a delay for transient disconnections of docks when
+ // power is rapidly turned off/on, this message will be canceled if
+ // we reconnect the dock under a preset delay
+ makeA2dpDeviceUnavailableLater(address);
+ // the next time isConnected is evaluated, it will be false for the dock
+ }
+ } else {
+ makeA2dpDeviceUnavailableNow(address);
+ }
+ } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+ if (btDevice.isBluetoothDock()) {
+ // this could be a reconnection after a transient disconnection
+ cancelA2dpDeviceTimeout();
+ mDockAddress = address;
+ } else {
+ // this could be a connection of another A2DP device before the timeout of
+ // a dock: cancel the dock timeout, and make the dock unavailable now
+ if(hasScheduledA2dpDockTimeout()) {
+ cancelA2dpDeviceTimeout();
+ makeA2dpDeviceUnavailableNow(mDockAddress);
+ }
+ }
+ makeA2dpDeviceAvailable(address);
+ }
+ }
+ }
+
/* cache of the address of the last dock the device was connected to */
private String mDockAddress;
@@ -2369,44 +2463,8 @@ public class AudioService extends IAudioService.Stub {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (btDevice == null) {
- return;
- }
- String address = btDevice.getAddress();
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
- }
- boolean isConnected =
- (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
- mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
-
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- if (btDevice.isBluetoothDock()) {
- if (state == BluetoothProfile.STATE_DISCONNECTED) {
- // introduction of a delay for transient disconnections of docks when
- // power is rapidly turned off/on, this message will be canceled if
- // we reconnect the dock under a preset delay
- makeA2dpDeviceUnavailableLater(address);
- // the next time isConnected is evaluated, it will be false for the dock
- }
- } else {
- makeA2dpDeviceUnavailableNow(address);
- }
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- if (btDevice.isBluetoothDock()) {
- // this could be a reconnection after a transient disconnection
- cancelA2dpDeviceTimeout();
- mDockAddress = address;
- } else {
- // this could be a connection of another A2DP device before the timeout of
- // a dock: cancel the dock timeout, and make the dock unavailable now
- if(hasScheduledA2dpDockTimeout()) {
- cancelA2dpDeviceTimeout();
- makeA2dpDeviceUnavailableNow(mDockAddress);
- }
- }
- makeA2dpDeviceAvailable(address);
- }
+
+ handleA2dpConnectionStateChange(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
@@ -2435,103 +2493,126 @@ public class AudioService extends IAudioService.Stub {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
- boolean isConnected = (mConnectedDevices.containsKey(device) &&
- mConnectedDevices.get(device).equals(address));
- synchronized (mScoClients) {
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- AudioSystem.setDeviceConnectionState(device,
+ synchronized (mConnectedDevices) {
+ boolean isConnected = (mConnectedDevices.containsKey(device) &&
+ mConnectedDevices.get(device).equals(address));
+
+ synchronized (mScoClients) {
+ if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
address);
- mConnectedDevices.remove(device);
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- AudioSystem.setDeviceConnectionState(device,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- address);
- mConnectedDevices.put(new Integer(device), address);
- mBluetoothHeadsetDevice = btDevice;
+ mConnectedDevices.remove(device);
+ mBluetoothHeadsetDevice = null;
+ resetBluetoothSco();
+ } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ address);
+ mConnectedDevices.put(new Integer(device), address);
+ mBluetoothHeadsetDevice = btDevice;
+ }
}
}
} else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", 0);
int microphone = intent.getIntExtra("microphone", 0);
- if (microphone != 0) {
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE,
- "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+ synchronized (mConnectedDevices) {
+ if (microphone != 0) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+ }
+ } else {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+ }
}
- } else {
+ }
+ } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
+ int state = intent.getIntExtra("state", 0);
+ Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
+ synchronized (mConnectedDevices) {
boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
- AudioSystem.DEVICE_STATE_UNAVAILABLE,
- "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
} else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- "");
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+ new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
}
}
- } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
- int state = intent.getIntExtra("state", 0);
- Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
- }
} else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
int state = intent.getIntExtra("state", 0);
Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
+ }
}
} else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", 0);
Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+ }
}
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
@@ -2584,8 +2665,6 @@ public class AudioService extends IAudioService.Stub {
sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
0, 0, null, 0);
- mKeyguardManager =
- (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
getBluetoothHeadset();
@@ -2595,6 +2674,12 @@ public class AudioService extends IAudioService.Stub {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
mContext.sendStickyBroadcast(newIntent);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.A2DP);
+ }
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
@@ -2683,10 +2768,21 @@ public class AudioService extends IAudioService.Stub {
}
public void unlinkToDeath() {
- if (mSourceRef != null && mHandler != null) {
- mSourceRef.unlinkToDeath(mHandler, 0);
+ try {
+ if (mSourceRef != null && mHandler != null) {
+ mSourceRef.unlinkToDeath(mHandler, 0);
+ mHandler = null;
+ }
+ } catch (java.util.NoSuchElementException e) {
+ Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
}
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ unlinkToDeath(); // unlink exception handled inside method
+ super.finalize();
+ }
}
private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
@@ -2717,7 +2813,7 @@ public class AudioService extends IAudioService.Stub {
* focus, notify the next item in the stack it gained focus.
*/
private void removeFocusStackEntry(String clientToRemove, boolean signal) {
- // is the current top of the focus stack abandoning focus? (because of death or request)
+ // is the current top of the focus stack abandoning focus? (because of request, not death)
if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
{
//Log.i(TAG, " removeFocusStackEntry() removing top of stack");
@@ -2750,7 +2846,7 @@ public class AudioService extends IAudioService.Stub {
/**
* Helper function:
* Called synchronized on mAudioFocusLock
- * Remove focus listeners from the focus stack for a particular client.
+ * Remove focus listeners from the focus stack for a particular client when it has died.
*/
private void removeFocusStackEntryForClient(IBinder cb) {
// is the owner of the audio focus part of the client to remove?
@@ -2763,6 +2859,7 @@ public class AudioService extends IAudioService.Stub {
Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
+ fse.mClientId);
stackIterator.remove();
+ // the client just died, no need to unlink to its death
}
}
if (isTopOfStackForClientToRemove) {
@@ -2847,11 +2944,15 @@ public class AudioService extends IAudioService.Stub {
// if focus is already owned by this client and the reason for acquiring the focus
// hasn't changed, don't do anything
if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+ // unlink death handler so it can be gc'ed.
+ // linkToDeath() creates a JNI global reference preventing collection.
+ cb.unlinkToDeath(afdh, 0);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
// the reason for the audio focus request has changed: remove the current top of
// stack and respond as if we had a new focus owner
- mFocusStack.pop();
+ FocusStackEntry fse = mFocusStack.pop();
+ fse.unlinkToDeath();
}
// notify current top of stack it is losing focus
@@ -3039,6 +3140,7 @@ public class AudioService extends IAudioService.Stub {
if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
try {
mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
+ mRcClientDeathHandler = null;
} catch (java.util.NoSuchElementException e) {
// not much we can do here
Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
@@ -3046,6 +3148,12 @@ public class AudioService extends IAudioService.Stub {
}
}
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ unlinkToRcClientDeath();// unlink exception handled inside method
+ super.finalize();
+ }
}
/**
@@ -3092,6 +3200,7 @@ public class AudioService extends IAudioService.Stub {
if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
// a stack entry is from the package being removed, remove it from the stack
stackIterator.remove();
+ rcse.unlinkToRcClientDeath();
}
}
if (mRCStack.empty()) {
@@ -3172,6 +3281,7 @@ public class AudioService extends IAudioService.Stub {
RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
if(rcse.mMediaIntent.equals(pi)) {
stackIterator.remove();
+ rcse.unlinkToRcClientDeath();
break;
}
}
@@ -3371,7 +3481,7 @@ public class AudioService extends IAudioService.Stub {
updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
}
- /**
+ /**
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
* precondition: mediaIntent != null, target != null
*/
@@ -3387,7 +3497,7 @@ public class AudioService extends IAudioService.Stub {
}
}
- /**
+ /**
* see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
* precondition: mediaIntent != null, eventReceiver != null
*/
@@ -3433,7 +3543,7 @@ public class AudioService extends IAudioService.Stub {
rcse.mCallingPackageName = callingPackageName;
rcse.mCallingUid = Binder.getCallingUid();
if (rcClient == null) {
- rcse.mRcClientDeathHandler = null;
+ // here rcse.mRcClientDeathHandler is null;
break;
}
@@ -3489,7 +3599,6 @@ public class AudioService extends IAudioService.Stub {
rcse.unlinkToRcClientDeath();
// reset the client-related fields
rcse.mRcClient = null;
- rcse.mRcClientDeathHandler = null;
rcse.mCallingPackageName = null;
}
}
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 51a45cd..7d60c55 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -82,7 +82,6 @@ public class CamcorderProfile
/**
* Quality level corresponding to the QVGA (320x240) resolution.
- * {@hide}
*/
public static final int QUALITY_QVGA = 7;
@@ -127,7 +126,6 @@ public class CamcorderProfile
/**
* Time lapse quality level corresponding to the QVGA (320 x 240) resolution.
- * {@hide}
*/
public static final int QUALITY_TIME_LAPSE_QVGA = 1007;
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 8793841..e275aa6 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -71,6 +71,11 @@ public class MediaFile {
private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WEBM;
+ // More video file types
+ public static final int FILE_TYPE_MP2PS = 200;
+ private static final int FIRST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
+ private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
+
// Image file types
public static final int FILE_TYPE_JPEG = 31;
public static final int FILE_TYPE_GIF = 32;
@@ -235,6 +240,8 @@ public class MediaFile {
addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/mspowerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION);
addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac", MtpConstants.FORMAT_FLAC);
addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
+ addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p");
+ addFileType("MPEG", FILE_TYPE_MP2PS, "video/mp2p");
}
public static boolean isAudioFileType(int fileType) {
@@ -246,7 +253,9 @@ public class MediaFile {
public static boolean isVideoFileType(int fileType) {
return (fileType >= FIRST_VIDEO_FILE_TYPE &&
- fileType <= LAST_VIDEO_FILE_TYPE);
+ fileType <= LAST_VIDEO_FILE_TYPE)
+ || (fileType >= FIRST_VIDEO_FILE_TYPE2 &&
+ fileType <= LAST_VIDEO_FILE_TYPE2);
}
public static boolean isImageFileType(int fileType) {
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
new file mode 100644
index 0000000..a998407
--- /dev/null
+++ b/media/java/android/media/MediaInserter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 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 android.content.ContentValues;
+import android.content.IContentProvider;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A MediaScanner helper class which enables us to do lazy insertion on the
+ * given provider. This class manages buffers internally and flushes when they
+ * are full. Note that you should call flushAll() after using this class.
+ * {@hide}
+ */
+public class MediaInserter {
+ private HashMap<Uri, List<ContentValues>> mRowMap =
+ new HashMap<Uri, List<ContentValues>>();
+
+ private IContentProvider mProvider;
+ private int mBufferSizePerUri;
+
+ public MediaInserter(IContentProvider provider, int bufferSizePerUri) {
+ mProvider = provider;
+ mBufferSizePerUri = bufferSizePerUri;
+ }
+
+ public void insert(Uri tableUri, ContentValues values) throws RemoteException {
+ List<ContentValues> list = mRowMap.get(tableUri);
+ if (list == null) {
+ list = new ArrayList<ContentValues>();
+ mRowMap.put(tableUri, list);
+ }
+ list.add(new ContentValues(values));
+ if (list.size() >= mBufferSizePerUri) {
+ flush(tableUri);
+ }
+ }
+
+ public void flushAll() throws RemoteException {
+ for (Uri tableUri : mRowMap.keySet()){
+ flush(tableUri);
+ }
+ mRowMap.clear();
+ }
+
+ private void flush(Uri tableUri) throws RemoteException {
+ List<ContentValues> list = mRowMap.get(tableUri);
+ if (!list.isEmpty()) {
+ ContentValues[] valuesArray = new ContentValues[list.size()];
+ valuesArray = list.toArray(valuesArray);
+ mProvider.bulkInsert(tableUri, valuesArray);
+ list.clear();
+ }
+ }
+}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 10694c3..11ecd1f 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -458,5 +458,12 @@ public class MediaMetadataRetriever
* @hide
*/
public static final int METADATA_KEY_IS_DRM = 22;
+ /**
+ * This key retrieves the location information, if available.
+ * The location should be specified according to ISO-6709 standard, under
+ * a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude
+ * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
+ */
+ public static final int METADATA_KEY_LOCATION = 23;
// Add more here...
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index eb32563..8d71dcf 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1149,14 +1149,20 @@ public class MediaPlayer
/**
* Releases resources associated with this MediaPlayer object.
* It is considered good practice to call this method when you're
- * done using the MediaPlayer. For instance, whenever the Activity
- * of an application is paused, this method should be invoked to
- * release the MediaPlayer object. In addition to unnecessary resources
- * (such as memory and instances of codecs) being hold, failure to
- * call this method immediately if a MediaPlayer object is no longer
- * needed may also lead to continuous battery consumption for mobile
- * devices, and playback failure if no multiple instances of the
- * same codec is supported on a device.
+ * done using the MediaPlayer. In particular, whenever an Activity
+ * of an application is paused (its onPause() method is called),
+ * or stopped (its onStop() method is called), this method should be
+ * invoked to release the MediaPlayer object, unless the application
+ * has a special need to keep the object around. In addition to
+ * unnecessary resources (such as memory and instances of codecs)
+ * being held, failure to call this method immediately if a
+ * MediaPlayer object is no longer needed may also lead to
+ * continuous battery consumption for mobile devices, and playback
+ * failure for other applications if no multiple instances of the
+ * same codec are supported on a device. Even if multiple instances
+ * of the same codec are supported, some performance degradation
+ * may be expected when unnecessary multiple instances are used
+ * at the same time.
*/
public void release() {
stayAwake(false);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 7b42ac3..08e6032 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -305,7 +305,7 @@ public class MediaRecorder
setVideoEncodingBitRate(profile.videoBitRate);
setVideoEncoder(profile.videoCodec);
if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
- profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_1080P) {
+ profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
// Enable time lapse. Also don't set audio for time lapse.
setParameter(String.format("time-lapse-enable=1"));
} else {
@@ -926,7 +926,20 @@ public class MediaRecorder
/**
* Releases resources associated with this MediaRecorder object.
* It is good practice to call this method when you're done
- * using the MediaRecorder.
+ * using the MediaRecorder. In particular, whenever an Activity
+ * of an application is paused (its onPause() method is called),
+ * or stopped (its onStop() method is called), this method should be
+ * invoked to release the MediaRecorder object, unless the application
+ * has a special need to keep the object around. In addition to
+ * unnecessary resources (such as memory and instances of codecs)
+ * being held, failure to call this method immediately if a
+ * MediaRecorder object is no longer needed may also lead to
+ * continuous battery consumption for mobile devices, and recording
+ * failure for other applications if no multiple instances of the
+ * same codec are supported on a device. Even if multiple instances
+ * of the same codec are supported, some performance degradation
+ * may be expected when unnecessary multiple instances are used
+ * at the same time.
*/
public native void release();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 2d927ad..386986e 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -377,43 +377,7 @@ public class MediaScanner
}
}
- private class FileInserter {
-
- private final Uri mUri;
- private final ContentValues[] mValues;
- private int mIndex;
-
- public FileInserter(Uri uri, int count) {
- mUri = uri;
- mValues = new ContentValues[count];
- }
-
- 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(mUri, mValues);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in FileInserter.flush()", e);
- }
- mIndex = 0;
- }
- }
-
- private FileInserter mAudioInserter;
- private FileInserter mVideoInserter;
- private FileInserter mImageInserter;
- private FileInserter mFileInserter;
+ private MediaInserter mMediaInserter;
// hashes file path to FileCacheEntry.
// path should be lower case if mCaseInsensitivePaths is true
@@ -880,17 +844,14 @@ public class MediaScanner
}
Uri tableUri = mFilesUri;
- FileInserter inserter = mFileInserter;
+ MediaInserter inserter = mMediaInserter;
if (!mNoMedia) {
if (MediaFile.isVideoFileType(mFileType)) {
tableUri = mVideoUri;
- inserter = mVideoInserter;
} else if (MediaFile.isImageFileType(mFileType)) {
tableUri = mImagesUri;
- inserter = mImageInserter;
} else if (MediaFile.isAudioFileType(mFileType)) {
tableUri = mAudioUri;
- inserter = mAudioInserter;
}
}
Uri result = null;
@@ -913,7 +874,7 @@ public class MediaScanner
if (inserter == null || entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
result = mMediaProvider.insert(tableUri, values);
} else {
- result = inserter.insert(values);
+ inserter.insert(tableUri, values);
}
if (result != null) {
@@ -1212,11 +1173,8 @@ public class MediaScanner
long prescan = System.currentTimeMillis();
if (ENABLE_BULK_INSERTS) {
- // create FileInserters for bulk inserts
- mAudioInserter = new FileInserter(mAudioUri, 500);
- mVideoInserter = new FileInserter(mVideoUri, 500);
- mImageInserter = new FileInserter(mImagesUri, 500);
- mFileInserter = new FileInserter(mFilesUri, 500);
+ // create MediaInserter for bulk inserts
+ mMediaInserter = new MediaInserter(mMediaProvider, 500);
}
for (int i = 0; i < directories.length; i++) {
@@ -1225,14 +1183,8 @@ public class MediaScanner
if (ENABLE_BULK_INSERTS) {
// flush remaining inserts
- mAudioInserter.flush();
- mVideoInserter.flush();
- mImageInserter.flush();
- mFileInserter.flush();
- mAudioInserter = null;
- mVideoInserter = null;
- mImageInserter = null;
- mFileInserter = null;
+ mMediaInserter.flushAll();
+ mMediaInserter = null;
}
long scan = System.currentTimeMillis();
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 03ae62a..4756078 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -1845,7 +1845,7 @@ class MediaArtistNativeHelper {
@SuppressWarnings("unused")
private void onPreviewProgressUpdate(int progress, boolean isFinished,
- boolean updateOverlay, String filename, int renderingMode) {
+ boolean updateOverlay, String filename, int renderingMode, int error) {
if (mPreviewProgressListener != null) {
if (mIsFirstProgress) {
mPreviewProgressListener.onStart(mVideoEditor);
@@ -1870,6 +1870,8 @@ class MediaArtistNativeHelper {
if (isFinished) {
mPreviewProgressListener.onStop(mVideoEditor);
+ } else if (error != 0) {
+ mPreviewProgressListener.onError(mVideoEditor, error);
} else {
mPreviewProgressListener.onProgress(mVideoEditor, progress, overlayData);
}
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index a862d00..590b4ae 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -154,7 +154,7 @@ public class MediaImageItem extends MediaItem {
final Bitmap imageBitmap;
- if (mHeight > maxResolution.second) {
+ if (mWidth > maxResolution.first || mHeight > maxResolution.second) {
/**
* We need to scale the image
*/
@@ -971,14 +971,13 @@ public class MediaImageItem extends MediaItem {
/**
* Create the bitmap from file
*/
- if (nativeWidth / bitmapWidth > 1) {
-
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = nativeWidth / (int)bitmapWidth;
- srcBitmap = BitmapFactory.decodeFile(filename, options);
- } else {
- srcBitmap = BitmapFactory.decodeFile(filename);
- }
+ int sampleSize = (int) Math.ceil(Math.max(
+ (float) nativeWidth / bitmapWidth,
+ (float) nativeHeight / bitmapHeight));
+ sampleSize = nextPowerOf2(sampleSize);
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = sampleSize;
+ srcBitmap = BitmapFactory.decodeFile(filename, options);
} else {
bitmapWidth = width;
bitmapHeight = height;
@@ -1009,4 +1008,14 @@ public class MediaImageItem extends MediaItem {
srcBitmap.recycle();
return bitmap;
}
+
+ public static int nextPowerOf2(int n) {
+ n -= 1;
+ n |= n >>> 16;
+ n |= n >>> 8;
+ n |= n >>> 4;
+ n |= n >>> 2;
+ n |= n >>> 1;
+ return n + 1;
+ }
}
diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java
index 424e436..08d27d4 100755
--- a/media/java/android/media/videoeditor/VideoEditor.java
+++ b/media/java/android/media/videoeditor/VideoEditor.java
@@ -107,6 +107,17 @@ public interface VideoEditor {
* @param videoEditor The VideoEditor instance
*/
public void onStop(VideoEditor videoEditor);
+
+ /**
+ * This method notifies the listener when error has occurred during
+ * previewing a project.
+ *
+ * @param videoEditor The VideoEditor instance
+ * @param error The error that has occurred
+ * FIXME: We should pass well-defined error code to the application;
+ * but for now, we just pass whatever error code reported by the native layer.
+ */
+ public void onError(VideoEditor videoEditor, int error);
}
/**
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 98617d2..19db1c0 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -51,7 +51,15 @@ public class MtpDatabase {
private final IContentProvider mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
- private final String mMediaStoragePath; // path to primary storage
+ // path to primary storage
+ private final String mMediaStoragePath;
+ // if not null, restrict all queries to these subdirectories
+ private final String[] mSubDirectories;
+ // where clause for restricting queries to files in mSubDirectories
+ private String mSubDirectoriesWhere;
+ // where arguments for restricting queries to files in mSubDirectories
+ private String[] mSubDirectoriesWhereArgs;
+
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
// cached property groups for single properties
@@ -112,7 +120,8 @@ public class MtpDatabase {
System.loadLibrary("media_jni");
}
- public MtpDatabase(Context context, String volumeName, String storagePath) {
+ public MtpDatabase(Context context, String volumeName, String storagePath,
+ String[] subDirectories) {
native_setup();
mContext = context;
@@ -122,6 +131,31 @@ public class MtpDatabase {
mObjectsUri = Files.getMtpObjectsUri(volumeName);
mMediaScanner = new MediaScanner(context);
+ mSubDirectories = subDirectories;
+ if (subDirectories != null) {
+ // Compute "where" string for restricting queries to subdirectories
+ StringBuilder builder = new StringBuilder();
+ builder.append("(");
+ int count = subDirectories.length;
+ for (int i = 0; i < count; i++) {
+ builder.append(Files.FileColumns.DATA + "=? OR "
+ + Files.FileColumns.DATA + " LIKE ?");
+ if (i != count - 1) {
+ builder.append(" OR ");
+ }
+ }
+ builder.append(")");
+ mSubDirectoriesWhere = builder.toString();
+
+ // Compute "where" arguments for restricting queries to subdirectories
+ mSubDirectoriesWhereArgs = new String[count * 2];
+ for (int i = 0, j = 0; i < count; i++) {
+ String path = subDirectories[i];
+ mSubDirectoriesWhereArgs[j++] = path;
+ mSubDirectoriesWhereArgs[j++] = path + "/%";
+ }
+ }
+
// Set locale to MediaScanner.
Locale locale = context.getResources().getConfiguration().locale;
if (locale != null) {
@@ -190,9 +224,44 @@ public class MtpDatabase {
}
}
+ // check to see if the path is contained in one of our storage subdirectories
+ // returns true if we have no special subdirectories
+ private boolean inStorageSubDirectory(String path) {
+ if (mSubDirectories == null) return true;
+ if (path == null) return false;
+
+ boolean allowed = false;
+ int pathLength = path.length();
+ for (int i = 0; i < mSubDirectories.length && !allowed; i++) {
+ String subdir = mSubDirectories[i];
+ int subdirLength = subdir.length();
+ if (subdirLength < pathLength &&
+ path.charAt(subdirLength) == '/' &&
+ path.startsWith(subdir)) {
+ allowed = true;
+ }
+ }
+ return allowed;
+ }
+
+ // check to see if the path matches one of our storage subdirectories
+ // returns true if we have no special subdirectories
+ private boolean isStorageSubDirectory(String path) {
+ if (mSubDirectories == null) return false;
+ for (int i = 0; i < mSubDirectories.length; i++) {
+ if (path.equals(mSubDirectories[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private int beginSendObject(String path, int format, int parent,
int storageId, long size, long modified) {
- // first make sure the object does not exist
+ // if mSubDirectories is not null, do not allow copying files to any other locations
+ if (!inStorageSubDirectory(path)) return -1;
+
+ // make sure the object does not exist
if (path != null) {
Cursor c = null;
try {
@@ -269,33 +338,40 @@ public class MtpDatabase {
}
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
+ String where;
+ String[] whereArgs;
+
if (storageID == 0xFFFFFFFF) {
// query all stores
if (format == 0) {
// query all formats
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, null, null, null);
- }
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
+ where = null;
+ whereArgs = null;
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(parent) };
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, PARENT_WHERE,
- new String[] { Integer.toString(parent) }, null);
} else {
// query specific format
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_WHERE,
- new String[] { Integer.toString(format) }, null);
- }
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
+ where = FORMAT_WHERE;
+ whereArgs = new String[] { Integer.toString(format) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = FORMAT_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(format),
+ Integer.toString(parent) };
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_PARENT_WHERE,
- new String[] { Integer.toString(format), Integer.toString(parent) }, null);
}
} else {
// query specific store
@@ -303,35 +379,61 @@ public class MtpDatabase {
// query all formats
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_WHERE,
- new String[] { Integer.toString(storageID) }, null);
- }
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
+ where = STORAGE_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = STORAGE_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(parent) };
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_PARENT_WHERE,
- new String[] { Integer.toString(storageID), Integer.toString(parent) },
- null);
} else {
// query specific format
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_WHERE,
- new String[] { Integer.toString(storageID), Integer.toString(format) },
- null);
+ where = STORAGE_FORMAT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(format) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = STORAGE_FORMAT_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(format),
+ Integer.toString(parent) };
}
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
+ }
+ }
+
+ // if we are restricting queries to mSubDirectories, we need to add the restriction
+ // onto our "where" arguments
+ if (mSubDirectoriesWhere != null) {
+ if (where == null) {
+ where = mSubDirectoriesWhere;
+ whereArgs = mSubDirectoriesWhereArgs;
+ } else {
+ where = where + " AND " + mSubDirectoriesWhere;
+
+ // create new array to hold whereArgs and mSubDirectoriesWhereArgs
+ String[] newWhereArgs =
+ new String[whereArgs.length + mSubDirectoriesWhereArgs.length];
+ int i, j;
+ for (i = 0; i < whereArgs.length; i++) {
+ newWhereArgs[i] = whereArgs[i];
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_PARENT_WHERE,
- new String[] { Integer.toString(storageID),
- Integer.toString(format),
- Integer.toString(parent) },
- null);
+ for (j = 0; j < mSubDirectoriesWhereArgs.length; i++, j++) {
+ newWhereArgs[i] = mSubDirectoriesWhereArgs[j];
+ }
+ whereArgs = newWhereArgs;
}
}
+
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where, whereArgs, null);
}
private int[] getObjectList(int storageID, int format, int parent) {
@@ -613,6 +715,11 @@ public class MtpDatabase {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
+ // do not allow renaming any of the special subdirectories
+ if (isStorageSubDirectory(path)) {
+ return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ }
+
// now rename the file. make sure this succeeds before updating database
File oldFile = new File(path);
int lastSlash = path.lastIndexOf('/');
@@ -794,6 +901,11 @@ public class MtpDatabase {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
+ // do not allow deleting any of the special subdirectories
+ if (isStorageSubDirectory(path)) {
+ return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ }
+
if (format == MtpConstants.FORMAT_ASSOCIATION) {
// recursive case - delete all children first
Uri uri = Files.getMtpObjectsUri(mVolumeName);
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index b88296f..09152f5 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -56,6 +56,53 @@ static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* meth
return OK;
}
+// stolen from dalvik/vm/checkJni.cpp
+static bool isValidUtf8(const char* bytes) {
+ while (*bytes != '\0') {
+ unsigned char utf8 = *(bytes++);
+ // Switch on the high four bits.
+ switch (utf8 >> 4) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ // Bit pattern 0xxx. No need for any extra bytes.
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f:
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ return false;
+ case 0x0e:
+ // Bit pattern 1110, so there are two additional bytes.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ // Fall through to take care of the final byte.
+ case 0x0c:
+ case 0x0d:
+ // Bit pattern 110x, so there is one additional byte.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
class MyMediaScannerClient : public MediaScannerClient
{
public:
@@ -123,7 +170,22 @@ public:
mEnv->ExceptionClear();
return NO_MEMORY;
}
- if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
+ char *cleaned = NULL;
+ if (!isValidUtf8(value)) {
+ cleaned = strdup(value);
+ char *chp = cleaned;
+ char ch;
+ while ((ch = *chp)) {
+ if (ch & 0x80) {
+ *chp = '?';
+ }
+ chp++;
+ }
+ value = cleaned;
+ }
+ valueStr = mEnv->NewStringUTF(value);
+ free(cleaned);
+ if (valueStr == NULL) {
mEnv->DeleteLocalRef(nameStr);
mEnv->ExceptionClear();
return NO_MEMORY;
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index ed4e92e..3d6d857 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -477,7 +477,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType,
pEnv->CallVoidMethod(pContext->engine,
pContext->onPreviewProgressUpdateMethodId,
currentMs,isFinished, pContext->mIsUpdateOverlay,
- tmpFileName, pContext->mOverlayRenderingMode);
+ tmpFileName, pContext->mOverlayRenderingMode, error);
if (pContext->mIsUpdateOverlay) {
pContext->mIsUpdateOverlay = false;
@@ -1630,7 +1630,7 @@ videoEditor_populateSettings(
"not initialized");
pContext->onPreviewProgressUpdateMethodId = pEnv->GetMethodID(engineClass,
- "onPreviewProgressUpdate", "(IZZLjava/lang/String;I)V");
+ "onPreviewProgressUpdate", "(IZZLjava/lang/String;II)V");
// Check if the context is valid (required because the context is dereferenced).
if (needToBeLoaded) {
// Make sure that we are in a correct state.
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index efa1c45..5a1e93a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -49,6 +49,16 @@ extern "C" const struct effect_interface_s gLvmEffectInterface;
}\
}
+
+static inline int16_t clamp16(int32_t sample)
+{
+ // check overflow for both positive and negative values:
+ // all bits above short range must me equal to sign bit
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
// Namespaces
namespace android {
namespace {
@@ -707,13 +717,6 @@ int LvmBundle_init(EffectContext *pContext){
} /* end LvmBundle_init */
-static inline int16_t clamp16(int32_t sample)
-{
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
-
//----------------------------------------------------------------------------
// LvmBundle_process()
//----------------------------------------------------------------------------
@@ -2459,6 +2462,9 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled)
LOGV("\tEffect_setEnabled() type %d, enabled %d", pContext->EffectType, enabled);
if (enabled) {
+ // Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due
+ // to their nature.
+ bool tempDisabled = false;
switch (pContext->EffectType) {
case LVM_BASS_BOOST:
if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
@@ -2471,6 +2477,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled)
pContext->pBundledContext->SamplesToExitCountBb =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bBassEnabled = LVM_TRUE;
+ tempDisabled = pContext->pBundledContext->bBassTempDisabled;
break;
case LVM_EQUALIZER:
if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
@@ -2495,6 +2502,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled)
pContext->pBundledContext->SamplesToExitCountVirt =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
+ tempDisabled = pContext->pBundledContext->bVirtualizerTempDisabled;
break;
case LVM_VOLUME:
if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) {
@@ -2508,7 +2516,9 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled)
LOGV("\tEffect_setEnabled() invalid effect type");
return -EINVAL;
}
- LvmEffect_enable(pContext);
+ if (!tempDisabled) {
+ LvmEffect_enable(pContext);
+ }
} else {
switch (pContext->EffectType) {
case LVM_BASS_BOOST:
@@ -2683,12 +2693,19 @@ int Effect_process(effect_handle_t self,
LOGV("\tLVM_ERROR : LvmBundle_process returned error %d", lvmStatus);
return lvmStatus;
}
- }else{
+ } else {
//LOGV("\tEffect_process Not Calling process with %d effects enabled, %d called: Effect %d",
//pContext->pBundledContext->NumberEffectsEnabled,
//pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
// 2 is for stereo input
- memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount*sizeof(LVM_INT16)*2);
+ if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i=0; i < outBuffer->frameCount*2; i++){
+ outBuffer->s16[i] =
+ clamp16((LVM_INT32)outBuffer->s16[i] + (LVM_INT32)inBuffer->s16[i]);
+ }
+ } else {
+ memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount*sizeof(LVM_INT16)*2);
+ }
}
return status;
@@ -3047,9 +3064,10 @@ int Effect_command(effect_handle_t self,
LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_DEVICE start");
uint32_t device = *(uint32_t *)pCmdData;
- if(pContext->EffectType == LVM_BASS_BOOST){
- if((device == AUDIO_DEVICE_OUT_SPEAKER)||(device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
- (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
+ if (pContext->EffectType == LVM_BASS_BOOST) {
+ if((device == AUDIO_DEVICE_OUT_SPEAKER) ||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
LOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
LOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_BAS_BOOST");
@@ -3058,30 +3076,31 @@ int Effect_command(effect_handle_t self,
// the effect must still report its original state as this can only be changed
// by the ENABLE/DISABLE command
- if(pContext->pBundledContext->bBassEnabled == LVM_TRUE){
+ if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE disable LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
android::LvmEffect_disable(pContext);
- pContext->pBundledContext->bBassTempDisabled = LVM_TRUE;
}
- }else{
+ pContext->pBundledContext->bBassTempDisabled = LVM_TRUE;
+ } else {
LOGV("\tEFFECT_CMD_SET_DEVICE device is valid for LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
// If a device supports bassboost and the effect has been temporarily disabled
// previously then re-enable it
- if(pContext->pBundledContext->bBassTempDisabled == LVM_TRUE){
+ if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE re-enable LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
android::LvmEffect_enable(pContext);
- pContext->pBundledContext->bBassTempDisabled = LVM_FALSE;
}
+ pContext->pBundledContext->bBassTempDisabled = LVM_FALSE;
}
}
- if(pContext->EffectType == LVM_VIRTUALIZER){
- if((device == AUDIO_DEVICE_OUT_SPEAKER)||(device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
- (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
+ if (pContext->EffectType == LVM_VIRTUALIZER) {
+ if((device == AUDIO_DEVICE_OUT_SPEAKER)||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
LOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
LOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_VIRTUALIZER");
@@ -3090,25 +3109,25 @@ int Effect_command(effect_handle_t self,
// the effect must still report its original state as this can only be changed
// by the ENABLE/DISABLE command
- if(pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE){
+ if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE disable LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
android::LvmEffect_disable(pContext);
- pContext->pBundledContext->bVirtualizerTempDisabled = LVM_TRUE;
}
- }else{
+ pContext->pBundledContext->bVirtualizerTempDisabled = LVM_TRUE;
+ } else {
LOGV("\tEFFECT_CMD_SET_DEVICE device is valid for LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
// If a device supports virtualizer and the effect has been temporarily disabled
// previously then re-enable it
- if(pContext->pBundledContext->bVirtualizerTempDisabled == LVM_TRUE){
+ if(pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE){
LOGV("\tEFFECT_CMD_SET_DEVICE re-enable LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
android::LvmEffect_enable(pContext);
- pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
}
+ pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
}
}
LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_DEVICE end");
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 3c3af8f..1a06cc6 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -47,17 +47,22 @@ enum visualizer_state_e {
VISUALIZER_STATE_ACTIVE,
};
+// maximum number of reads from same buffer before resetting capture buffer. This means
+// that the framework has stopped playing audio and we must start returning silence
+#define MAX_STALL_COUNT 10
+
struct VisualizerContext {
const struct effect_interface_s *mItfe;
effect_config_t mConfig;
- uint32_t mState;
uint32_t mCaptureIdx;
uint32_t mCaptureSize;
- uint32_t mCurrentBuf;
+ uint8_t mState;
+ uint8_t mCurrentBuf;
+ uint8_t mLastBuf;
+ uint8_t mStallCount;
uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
};
-
//
//--- Local functions
//
@@ -66,6 +71,8 @@ void Visualizer_reset(VisualizerContext *pContext)
{
pContext->mCaptureIdx = 0;
pContext->mCurrentBuf = 0;
+ pContext->mLastBuf = 1;
+ pContext->mStallCount = 0;
memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
}
@@ -417,9 +424,24 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
memcpy(pReplyData,
pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
pContext->mCaptureSize);
+ // if audio framework has stopped playing audio although the effect is still
+ // active we must clear the capture buffer to return silence
+ if (pContext->mLastBuf == pContext->mCurrentBuf) {
+ if (pContext->mStallCount < MAX_STALL_COUNT) {
+ if (++pContext->mStallCount == MAX_STALL_COUNT) {
+ memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
+ 0x80,
+ pContext->mCaptureSize);
+ }
+ }
+ } else {
+ pContext->mStallCount = 0;
+ }
+ pContext->mLastBuf = pContext->mCurrentBuf;
} else {
memset(pReplyData, 0x80, pContext->mCaptureSize);
}
+
break;
default:
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 50a41ca..9c1e6b7 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -35,7 +35,6 @@ enum {
SET_DATA_SOURCE_URL,
SET_DATA_SOURCE_FD,
SET_DATA_SOURCE_STREAM,
- SET_VIDEO_SURFACE,
PREPARE_ASYNC,
START,
STOP,
@@ -112,16 +111,6 @@ public:
return reply.readInt32();
}
- // pass the buffered Surface to the media player service
- status_t setVideoSurface(const sp<Surface>& surface)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
- Surface::writeToParcel(surface, &data);
- remote()->transact(SET_VIDEO_SURFACE, data, &reply);
- return reply.readInt32();
- }
-
// pass the buffered ISurfaceTexture to the media player service
status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture)
{
@@ -345,12 +334,6 @@ status_t BnMediaPlayer::onTransact(
reply->writeInt32(setDataSource(source));
return NO_ERROR;
}
- case SET_VIDEO_SURFACE: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
- sp<Surface> surface = Surface::readFromParcel(data);
- reply->writeInt32(setVideoSurface(surface));
- return NO_ERROR;
- } break;
case SET_VIDEO_SURFACETEXTURE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
sp<ISurfaceTexture> surfaceTexture =
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index b311f35..078be94 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -30,7 +30,7 @@ namespace android {
const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS";
// static
-const char *const IStreamListener::kKeyFormatChange = "format-change";
+const char *const IStreamListener::kKeyDiscontinuityMask = "discontinuity-mask";
enum {
// IStreamSource
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index ad55ff8..6096b72 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -404,11 +404,12 @@ MediaProfiles::createVideoEditorCap(const char **atts, MediaProfiles *profiles)
CHECK(!strcmp("maxInputFrameWidth", atts[0]) &&
!strcmp("maxInputFrameHeight", atts[2]) &&
!strcmp("maxOutputFrameWidth", atts[4]) &&
- !strcmp("maxOutputFrameHeight", atts[6]));
+ !strcmp("maxOutputFrameHeight", atts[6]) &&
+ !strcmp("maxPrefetchYUVFrames", atts[8]));
MediaProfiles::VideoEditorCap *pVideoEditorCap =
new MediaProfiles::VideoEditorCap(atoi(atts[1]), atoi(atts[3]),
- atoi(atts[5]), atoi(atts[7]));
+ atoi(atts[5]), atoi(atts[7]), atoi(atts[9]));
logVideoEditorCap(*pVideoEditorCap);
profiles->mVideoEditorCap = pVideoEditorCap;
@@ -850,7 +851,8 @@ MediaProfiles::createDefaultVideoEditorCap(MediaProfiles *profiles)
VIDEOEDITOR_DEFAULT_MAX_INPUT_FRAME_WIDTH,
VIDEOEDITOR_DEFUALT_MAX_INPUT_FRAME_HEIGHT,
VIDEOEDITOR_DEFAULT_MAX_OUTPUT_FRAME_WIDTH,
- VIDEOEDITOR_DEFUALT_MAX_OUTPUT_FRAME_HEIGHT);
+ VIDEOEDITOR_DEFUALT_MAX_OUTPUT_FRAME_HEIGHT,
+ VIDEOEDITOR_DEFAULT_MAX_PREFETCH_YUV_FRAMES);
}
/*static*/ void
MediaProfiles::createDefaultExportVideoProfiles(MediaProfiles *profiles)
@@ -1019,6 +1021,8 @@ int MediaProfiles::getVideoEditorCapParamByName(const char *name) const
return mVideoEditorCap->mMaxOutputFrameWidth;
if (!strcmp("videoeditor.output.height.max", name))
return mVideoEditorCap->mMaxOutputFrameHeight;
+ if (!strcmp("maxPrefetchYUVFrames", name))
+ return mVideoEditorCap->mMaxPrefetchYUVFrames;
LOGE("The given video editor param name %s is not found", name);
return -1;
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index f72300b..c2e1ddf 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -219,14 +219,6 @@ status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *m
return mPlayer->getMetadata(update_only, apply_filter, metadata);
}
-status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
-{
- LOGV("setVideoSurface");
- Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return NO_INIT;
- return mPlayer->setVideoSurface(surface);
-}
-
status_t MediaPlayer::setVideoSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture)
{
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index ec7d8a0..a3e2517 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -32,8 +32,8 @@ LOCAL_SHARED_LIBRARIES := \
libdl
LOCAL_STATIC_LIBRARIES := \
- libstagefright_rtsp \
libstagefright_nuplayer \
+ libstagefright_rtsp \
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE) \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index ba9f54f..e8d0f0c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -589,6 +589,10 @@ player_type getPlayerType(const char* url)
}
}
+ if (!strncasecmp("rtsp://", url, 7)) {
+ return NU_PLAYER;
+ }
+
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -784,14 +788,6 @@ status_t MediaPlayerService::Client::setDataSource(
return mStatus;
}
-status_t MediaPlayerService::Client::setVideoSurface(const sp<Surface>& surface)
-{
- LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get());
- sp<MediaPlayerBase> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setVideoSurface(surface);
-}
-
void MediaPlayerService::Client::disconnectNativeWindow() {
if (mConnectedWindow != NULL) {
status_t err = native_window_api_disconnect(mConnectedWindow.get(),
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 62214ba..04d9e28 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -248,7 +248,6 @@ private:
// IMediaPlayer interface
virtual void disconnect();
- virtual status_t setVideoSurface(const sp<Surface>& surface);
virtual status_t setVideoSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture);
virtual status_t prepareAsync();
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index b35696f..3469389 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -35,7 +35,6 @@ public:
const char* path, const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
- virtual status_t setVideoSurface(const sp<Surface>& surface) { return UNKNOWN_ERROR; }
virtual status_t setVideoSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture)
{ return UNKNOWN_ERROR; }
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index cd4b1ef..598d573 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -69,12 +69,6 @@ status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) {
return mPlayer->setDataSource(source);
}
-status_t StagefrightPlayer::setVideoSurface(const sp<Surface> &surface) {
- LOGV("setVideoSurface");
-
- return mPlayer->setSurface(surface);
-}
-
status_t StagefrightPlayer::setVideoSurfaceTexture(
const sp<ISurfaceTexture> &surfaceTexture) {
LOGV("setVideoSurfaceTexture");
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index cbc6d49..e89e18a 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -40,7 +40,6 @@ public:
virtual status_t setDataSource(const sp<IStreamSource> &source);
- virtual status_t setVideoSurface(const sp<Surface> &surface);
virtual status_t setVideoSurfaceTexture(
const sp<ISurfaceTexture> &surfaceTexture);
virtual status_t prepare();
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index 802a11b..91ffa7d 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -75,9 +75,6 @@ class TestPlayerStub : public MediaPlayerInterface {
// All the methods below wrap the mPlayer instance.
- virtual status_t setVideoSurface(const android::sp<android::Surface>& s) {
- return mPlayer->setVideoSurface(s);
- }
virtual status_t setVideoSurfaceTexture(
const android::sp<android::ISurfaceTexture>& st) {
return mPlayer->setVideoSurfaceTexture(st);
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index e761509..33e2f93 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -8,6 +8,7 @@ LOCAL_SRC_FILES:= \
NuPlayerDriver.cpp \
NuPlayerRenderer.cpp \
NuPlayerStreamListener.cpp \
+ RTSPSource.cpp \
StreamingSource.cpp \
LOCAL_C_INCLUDES := \
@@ -15,6 +16,7 @@ LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/media/libstagefright/include \
$(TOP)/frameworks/base/media/libstagefright/mpeg2ts \
$(TOP)/frameworks/base/media/libstagefright/httplive \
+ $(TOP)/frameworks/base/media/libstagefright/rtsp \
LOCAL_MODULE:= libstagefright_nuplayer
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 6c54130..93ab704 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -25,6 +25,7 @@
#include "NuPlayerDriver.h"
#include "NuPlayerRenderer.h"
#include "NuPlayerSource.h"
+#include "RTSPSource.h"
#include "StreamingSource.h"
#include "ATSParser.h"
@@ -53,6 +54,7 @@ NuPlayer::NuPlayer()
mVideoEOS(false),
mScanSourcesPending(false),
mScanSourcesGeneration(0),
+ mTimeDiscontinuityPending(false),
mFlushingAudio(NONE),
mFlushingVideo(NONE),
mResetInProgress(false),
@@ -87,13 +89,14 @@ void NuPlayer::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
- msg->setObject("source", new HTTPLiveSource(url, headers, mUIDValid, mUID));
- msg->post();
-}
+ if (!strncasecmp(url, "rtsp://", 7)) {
+ msg->setObject(
+ "source", new RTSPSource(url, headers, mUIDValid, mUID));
+ } else {
+ msg->setObject(
+ "source", new HTTPLiveSource(url, headers, mUIDValid, mUID));
+ }
-void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
- sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
- msg->setObject("native-window", new NativeWindowWrapper(surface));
msg->post();
}
@@ -460,11 +463,24 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
{
LOGV("kWhatReset");
+ if (mRenderer != NULL) {
+ // There's an edge case where the renderer owns all output
+ // buffers and is paused, therefore the decoder will not read
+ // more input data and will never encounter the matching
+ // discontinuity. To avoid this, we resume the renderer.
+
+ if (mFlushingAudio == AWAITING_DISCONTINUITY
+ || mFlushingVideo == AWAITING_DISCONTINUITY) {
+ mRenderer->resume();
+ }
+ }
+
if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
// We're currently flushing, postpone the reset until that's
// completed.
- LOGV("postponing reset");
+ LOGV("postponing reset mFlushingAudio=%d, mFlushingVideo=%d",
+ mFlushingAudio, mFlushingVideo);
mResetPostponed = true;
break;
@@ -475,6 +491,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ mTimeDiscontinuityPending = true;
+
if (mAudioDecoder != NULL) {
flushDecoder(true /* audio */, true /* needShutdown */);
}
@@ -538,7 +556,10 @@ void NuPlayer::finishFlushIfPossible() {
LOGV("both audio and video are flushed now.");
- mRenderer->signalTimeDiscontinuity();
+ if (mTimeDiscontinuityPending) {
+ mRenderer->signalTimeDiscontinuity();
+ mTimeDiscontinuityPending = false;
+ }
if (mAudioDecoder != NULL) {
mAudioDecoder->signalResume();
@@ -568,8 +589,15 @@ void NuPlayer::finishReset() {
CHECK(mAudioDecoder == NULL);
CHECK(mVideoDecoder == NULL);
+ ++mScanSourcesGeneration;
+ mScanSourcesPending = false;
+
mRenderer.clear();
- mSource.clear();
+
+ if (mSource != NULL) {
+ mSource->stop();
+ mSource.clear();
+ }
if (mDriver != NULL) {
sp<NuPlayerDriver> driver = mDriver.promote();
@@ -654,10 +682,15 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
bool formatChange =
- type == ATSParser::DISCONTINUITY_FORMATCHANGE;
+ (audio &&
+ (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
+ || (!audio &&
+ (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));
- LOGV("%s discontinuity (formatChange=%d)",
- audio ? "audio" : "video", formatChange);
+ bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;
+
+ LOGI("%s discontinuity (formatChange=%d, time=%d)",
+ audio ? "audio" : "video", formatChange, timeChange);
if (audio) {
mSkipRenderingAudioUntilMediaTimeUs = -1;
@@ -665,26 +698,45 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
mSkipRenderingVideoUntilMediaTimeUs = -1;
}
- sp<AMessage> extra;
- if (accessUnit->meta()->findMessage("extra", &extra)
- && extra != NULL) {
- int64_t resumeAtMediaTimeUs;
- if (extra->findInt64(
- "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
- LOGI("suppressing rendering of %s until %lld us",
- audio ? "audio" : "video", resumeAtMediaTimeUs);
-
- if (audio) {
- mSkipRenderingAudioUntilMediaTimeUs =
- resumeAtMediaTimeUs;
- } else {
- mSkipRenderingVideoUntilMediaTimeUs =
- resumeAtMediaTimeUs;
+ if (timeChange) {
+ sp<AMessage> extra;
+ if (accessUnit->meta()->findMessage("extra", &extra)
+ && extra != NULL) {
+ int64_t resumeAtMediaTimeUs;
+ if (extra->findInt64(
+ "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
+ LOGI("suppressing rendering of %s until %lld us",
+ audio ? "audio" : "video", resumeAtMediaTimeUs);
+
+ if (audio) {
+ mSkipRenderingAudioUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ } else {
+ mSkipRenderingVideoUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ }
}
}
}
- flushDecoder(audio, formatChange);
+ mTimeDiscontinuityPending =
+ mTimeDiscontinuityPending || timeChange;
+
+ if (formatChange || timeChange) {
+ flushDecoder(audio, formatChange);
+ } else {
+ // This stream is unaffected by the discontinuity
+
+ if (audio) {
+ mFlushingAudio = FLUSHED;
+ } else {
+ mFlushingVideo = FLUSHED;
+ }
+
+ finishFlushIfPossible();
+
+ return -EWOULDBLOCK;
+ }
}
reply->setInt32("err", err);
@@ -781,10 +833,15 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
return;
}
- driver->sendEvent(msg, ext1, ext2);
+ driver->notifyListener(msg, ext1, ext2);
}
void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
+ if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) {
+ LOGI("flushDecoder %s without decoder present",
+ audio ? "audio" : "video");
+ }
+
// Make sure we don't continue to scan sources until we finish flushing.
++mScanSourcesGeneration;
mScanSourcesPending = false;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index a5382b4..ffc710e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -42,7 +42,6 @@ struct NuPlayer : public AHandler {
void setDataSource(
const char *url, const KeyedVector<String8, String8> *headers);
- void setVideoSurface(const sp<Surface> &surface);
void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
void start();
@@ -68,6 +67,7 @@ private:
struct Renderer;
struct Source;
struct StreamingSource;
+ struct RTSPSource;
enum {
kWhatSetDataSource = '=DaS',
@@ -112,6 +112,10 @@ private:
SHUT_DOWN,
};
+ // Once the current flush is complete this indicates whether the
+ // notion of time has changed.
+ bool mTimeDiscontinuityPending;
+
FlushStatus mFlushingAudio;
FlushStatus mFlushingVideo;
bool mResetInProgress;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index b1e917d..5aa99bf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -35,6 +35,7 @@ NuPlayerDriver::NuPlayerDriver()
mNumFramesDropped(0),
mLooper(new ALooper),
mState(UNINITIALIZED),
+ mAtEOS(false),
mStartupSeekTimeUs(-1) {
mLooper->setName("NuPlayerDriver Looper");
@@ -88,12 +89,6 @@ status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
return OK;
}
-status_t NuPlayerDriver::setVideoSurface(const sp<Surface> &surface) {
- mPlayer->setVideoSurface(surface);
-
- return OK;
-}
-
status_t NuPlayerDriver::setVideoSurfaceTexture(
const sp<ISurfaceTexture> &surfaceTexture) {
mPlayer->setVideoSurfaceTexture(surfaceTexture);
@@ -106,7 +101,7 @@ status_t NuPlayerDriver::prepare() {
}
status_t NuPlayerDriver::prepareAsync() {
- sendEvent(MEDIA_PREPARED);
+ notifyListener(MEDIA_PREPARED);
return OK;
}
@@ -117,6 +112,7 @@ status_t NuPlayerDriver::start() {
return INVALID_OPERATION;
case STOPPED:
{
+ mAtEOS = false;
mPlayer->start();
if (mStartupSeekTimeUs >= 0) {
@@ -173,7 +169,7 @@ status_t NuPlayerDriver::pause() {
}
bool NuPlayerDriver::isPlaying() {
- return mState == PLAYING;
+ return mState == PLAYING && !mAtEOS;
}
status_t NuPlayerDriver::seekTo(int msec) {
@@ -190,6 +186,7 @@ status_t NuPlayerDriver::seekTo(int msec) {
case PLAYING:
case PAUSED:
{
+ mAtEOS = false;
mPlayer->seekToAsync(seekTimeUs);
break;
}
@@ -291,7 +288,7 @@ void NuPlayerDriver::notifyPosition(int64_t positionUs) {
}
void NuPlayerDriver::notifySeekComplete() {
- sendEvent(MEDIA_SEEK_COMPLETE);
+ notifyListener(MEDIA_SEEK_COMPLETE);
}
void NuPlayerDriver::notifyFrameStats(
@@ -320,4 +317,12 @@ status_t NuPlayerDriver::dump(int fd, const Vector<String16> &args) const {
return OK;
}
+void NuPlayerDriver::notifyListener(int msg, int ext1, int ext2) {
+ if (msg == MEDIA_PLAYBACK_COMPLETE || msg == MEDIA_ERROR) {
+ mAtEOS = true;
+ }
+
+ sendEvent(msg, ext1, ext2);
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 181c37d..4a0026c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -37,7 +37,6 @@ struct NuPlayerDriver : public MediaPlayerInterface {
virtual status_t setDataSource(const sp<IStreamSource> &source);
- virtual status_t setVideoSurface(const sp<Surface> &surface);
virtual status_t setVideoSurfaceTexture(
const sp<ISurfaceTexture> &surfaceTexture);
virtual status_t prepare();
@@ -67,6 +66,7 @@ struct NuPlayerDriver : public MediaPlayerInterface {
void notifyPosition(int64_t positionUs);
void notifySeekComplete();
void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
+ void notifyListener(int msg, int ext1 = 0, int ext2 = 0);
protected:
virtual ~NuPlayerDriver();
@@ -95,6 +95,7 @@ private:
};
State mState;
+ bool mAtEOS;
int64_t mStartupSeekTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 61a7ba4..0cb7f45 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -219,7 +219,9 @@ void NuPlayer::Renderer::signalAudioSinkChanged() {
bool NuPlayer::Renderer::onDrainAudioQueue() {
uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+ if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
+ return false;
+ }
ssize_t numFramesAvailableToWrite =
mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
@@ -626,11 +628,16 @@ void NuPlayer::Renderer::onPause() {
mAudioSink->pause();
}
+ LOGV("now paused audio queue has %d entries, video has %d entries",
+ mAudioQueue.size(), mVideoQueue.size());
+
mPaused = true;
}
void NuPlayer::Renderer::onResume() {
- CHECK(mPaused);
+ if (!mPaused) {
+ return;
+ }
if (mHasAudio) {
mAudioSink->start();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 8a7eece..531b29f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -28,6 +28,7 @@ struct NuPlayer::Source : public RefBase {
Source() {}
virtual void start() = 0;
+ virtual void stop() {}
// Returns OK iff more data was available,
// an error or ERROR_END_OF_STREAM if not.
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
new file mode 100644
index 0000000..e72adc4
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "RTSPSource"
+#include <utils/Log.h>
+
+#include "RTSPSource.h"
+
+#include "AnotherPacketSource.h"
+#include "MyHandler.h"
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+NuPlayer::RTSPSource::RTSPSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers,
+ bool uidValid,
+ uid_t uid)
+ : mURL(url),
+ mUIDValid(uidValid),
+ mUID(uid),
+ mFlags(0),
+ mState(DISCONNECTED),
+ mFinalResult(OK),
+ mDisconnectReplyID(0) {
+ if (headers) {
+ mExtraHeaders = *headers;
+
+ ssize_t index =
+ mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log"));
+
+ if (index >= 0) {
+ mFlags |= kFlagIncognito;
+
+ mExtraHeaders.removeItemsAt(index);
+ }
+ }
+}
+
+NuPlayer::RTSPSource::~RTSPSource() {
+ if (mLooper != NULL) {
+ mLooper->stop();
+ }
+}
+
+void NuPlayer::RTSPSource::start() {
+ if (mLooper == NULL) {
+ mLooper = new ALooper;
+ mLooper->setName("rtsp");
+ mLooper->start();
+
+ mReflector = new AHandlerReflector<RTSPSource>(this);
+ mLooper->registerHandler(mReflector);
+ }
+
+ CHECK(mHandler == NULL);
+
+ sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id());
+
+ mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
+ mLooper->registerHandler(mHandler);
+
+ CHECK_EQ(mState, (int)DISCONNECTED);
+ mState = CONNECTING;
+
+ mHandler->connect();
+}
+
+void NuPlayer::RTSPSource::stop() {
+ sp<AMessage> msg = new AMessage(kWhatDisconnect, mReflector->id());
+
+ sp<AMessage> dummy;
+ msg->postAndAwaitResponse(&dummy);
+}
+
+status_t NuPlayer::RTSPSource::feedMoreTSData() {
+ return mFinalResult;
+}
+
+sp<MetaData> NuPlayer::RTSPSource::getFormat(bool audio) {
+ sp<AnotherPacketSource> source = getSource(audio);
+
+ if (source == NULL) {
+ return NULL;
+ }
+
+ return source->getFormat();
+}
+
+status_t NuPlayer::RTSPSource::dequeueAccessUnit(
+ bool audio, sp<ABuffer> *accessUnit) {
+ sp<AnotherPacketSource> source = getSource(audio);
+
+ if (source == NULL) {
+ return -EWOULDBLOCK;
+ }
+
+ status_t finalResult;
+ if (!source->hasBufferAvailable(&finalResult)) {
+ return finalResult == OK ? -EWOULDBLOCK : finalResult;
+ }
+
+ return source->dequeueAccessUnit(accessUnit);
+}
+
+sp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) {
+ return audio ? mAudioTrack : mVideoTrack;
+}
+
+status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) {
+ *durationUs = 0ll;
+
+ int64_t audioDurationUs;
+ if (mAudioTrack != NULL
+ && mAudioTrack->getFormat()->findInt64(
+ kKeyDuration, &audioDurationUs)
+ && audioDurationUs > *durationUs) {
+ *durationUs = audioDurationUs;
+ }
+
+ int64_t videoDurationUs;
+ if (mVideoTrack != NULL
+ && mVideoTrack->getFormat()->findInt64(
+ kKeyDuration, &videoDurationUs)
+ && videoDurationUs > *durationUs) {
+ *durationUs = videoDurationUs;
+ }
+
+ return OK;
+}
+
+status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
+ if (mState != CONNECTED) {
+ return UNKNOWN_ERROR;
+ }
+
+ mState = SEEKING;
+ mHandler->seek(seekTimeUs);
+
+ return OK;
+}
+
+bool NuPlayer::RTSPSource::isSeekable() {
+ return true;
+}
+
+void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
+ if (msg->what() == kWhatDisconnect) {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ mDisconnectReplyID = replyID;
+ finishDisconnectIfPossible();
+ return;
+ }
+
+ CHECK_EQ(msg->what(), (int)kWhatNotify);
+
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ switch (what) {
+ case MyHandler::kWhatConnected:
+ onConnected();
+ break;
+
+ case MyHandler::kWhatDisconnected:
+ onDisconnected(msg);
+ break;
+
+ case MyHandler::kWhatSeekDone:
+ {
+ mState = CONNECTED;
+ break;
+ }
+
+ case MyHandler::kWhatAccessUnit:
+ {
+ size_t trackIndex;
+ CHECK(msg->findSize("trackIndex", &trackIndex));
+ CHECK_LT(trackIndex, mTracks.size());
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("accessUnit", &obj));
+
+ sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+
+ int32_t damaged;
+ if (accessUnit->meta()->findInt32("damaged", &damaged)
+ && damaged) {
+ LOGI("dropping damaged access unit.");
+ break;
+ }
+
+ const TrackInfo &info = mTracks.editItemAt(trackIndex);
+ sp<AnotherPacketSource> source = info.mSource;
+ if (source != NULL) {
+#if 1
+ uint32_t rtpTime;
+ CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+ int64_t nptUs =
+ ((double)rtpTime - (double)info.mRTPTime)
+ / info.mTimeScale
+ * 1000000ll
+ + info.mNormalPlaytimeUs;
+
+ accessUnit->meta()->setInt64("timeUs", nptUs);
+#endif
+
+ source->queueAccessUnit(accessUnit);
+ }
+ break;
+ }
+
+ case MyHandler::kWhatEOS:
+ {
+ size_t trackIndex;
+ CHECK(msg->findSize("trackIndex", &trackIndex));
+ CHECK_LT(trackIndex, mTracks.size());
+
+ int32_t finalResult;
+ CHECK(msg->findInt32("finalResult", &finalResult));
+ CHECK_NE(finalResult, (status_t)OK);
+
+ TrackInfo *info = &mTracks.editItemAt(trackIndex);
+ sp<AnotherPacketSource> source = info->mSource;
+ if (source != NULL) {
+ source->signalEOS(finalResult);
+ }
+
+ break;
+ }
+
+ case MyHandler::kWhatSeekDiscontinuity:
+ {
+ size_t trackIndex;
+ CHECK(msg->findSize("trackIndex", &trackIndex));
+ CHECK_LT(trackIndex, mTracks.size());
+
+ TrackInfo *info = &mTracks.editItemAt(trackIndex);
+ sp<AnotherPacketSource> source = info->mSource;
+ if (source != NULL) {
+ source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL);
+ }
+
+ break;
+ }
+
+ case MyHandler::kWhatNormalPlayTimeMapping:
+ {
+ size_t trackIndex;
+ CHECK(msg->findSize("trackIndex", &trackIndex));
+ CHECK_LT(trackIndex, mTracks.size());
+
+ uint32_t rtpTime;
+ CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime));
+
+ int64_t nptUs;
+ CHECK(msg->findInt64("nptUs", &nptUs));
+
+ TrackInfo *info = &mTracks.editItemAt(trackIndex);
+ info->mRTPTime = rtpTime;
+ info->mNormalPlaytimeUs = nptUs;
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void NuPlayer::RTSPSource::onConnected() {
+ CHECK(mAudioTrack == NULL);
+ CHECK(mVideoTrack == NULL);
+
+ size_t numTracks = mHandler->countTracks();
+ for (size_t i = 0; i < numTracks; ++i) {
+ int32_t timeScale;
+ sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale);
+
+ const char *mime;
+ CHECK(format->findCString(kKeyMIMEType, &mime));
+
+ bool isAudio = !strncasecmp(mime, "audio/", 6);
+ bool isVideo = !strncasecmp(mime, "video/", 6);
+
+ TrackInfo info;
+ info.mTimeScale = timeScale;
+ info.mRTPTime = 0;
+ info.mNormalPlaytimeUs = 0ll;
+
+ if ((isAudio && mAudioTrack == NULL)
+ || (isVideo && mVideoTrack == NULL)) {
+ sp<AnotherPacketSource> source = new AnotherPacketSource(format);
+
+ if (isAudio) {
+ mAudioTrack = source;
+ } else {
+ mVideoTrack = source;
+ }
+
+ info.mSource = source;
+ }
+
+ mTracks.push(info);
+ }
+
+ mState = CONNECTED;
+}
+
+void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) {
+ status_t err;
+ CHECK(msg->findInt32("result", &err));
+ CHECK_NE(err, (status_t)OK);
+
+ mLooper->unregisterHandler(mHandler->id());
+ mHandler.clear();
+
+ mState = DISCONNECTED;
+ mFinalResult = err;
+
+ if (mDisconnectReplyID != 0) {
+ finishDisconnectIfPossible();
+ }
+}
+
+void NuPlayer::RTSPSource::finishDisconnectIfPossible() {
+ if (mState != DISCONNECTED) {
+ mHandler->disconnect();
+ return;
+ }
+
+ (new AMessage)->postReply(mDisconnectReplyID);
+ mDisconnectReplyID = 0;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
new file mode 100644
index 0000000..66eab72
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef RTSP_SOURCE_H_
+
+#define RTSP_SOURCE_H_
+
+#include "NuPlayerSource.h"
+
+#include <media/stagefright/foundation/AHandlerReflector.h>
+
+namespace android {
+
+struct ALooper;
+struct AnotherPacketSource;
+struct MyHandler;
+
+struct NuPlayer::RTSPSource : public NuPlayer::Source {
+ RTSPSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers,
+ bool uidValid = false,
+ uid_t uid = 0);
+
+ virtual void start();
+ virtual void stop();
+
+ virtual status_t feedMoreTSData();
+
+ virtual sp<MetaData> getFormat(bool audio);
+ virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+
+ virtual status_t getDuration(int64_t *durationUs);
+ virtual status_t seekTo(int64_t seekTimeUs);
+ virtual bool isSeekable();
+
+ void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+ virtual ~RTSPSource();
+
+private:
+ enum {
+ kWhatNotify = 'noti',
+ kWhatDisconnect = 'disc',
+ };
+
+ enum State {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED,
+ SEEKING,
+ };
+
+ enum Flags {
+ // Don't log any URLs.
+ kFlagIncognito = 1,
+ };
+
+ struct TrackInfo {
+ sp<AnotherPacketSource> mSource;
+
+ int32_t mTimeScale;
+ uint32_t mRTPTime;
+ int64_t mNormalPlaytimeUs;
+ };
+
+ AString mURL;
+ KeyedVector<String8, String8> mExtraHeaders;
+ bool mUIDValid;
+ uid_t mUID;
+ uint32_t mFlags;
+ State mState;
+ status_t mFinalResult;
+ uint32_t mDisconnectReplyID;
+
+ sp<ALooper> mLooper;
+ sp<AHandlerReflector<RTSPSource> > mReflector;
+ sp<MyHandler> mHandler;
+
+ Vector<TrackInfo> mTracks;
+ sp<AnotherPacketSource> mAudioTrack;
+ sp<AnotherPacketSource> mVideoTrack;
+
+ sp<AnotherPacketSource> getSource(bool audio);
+
+ void onConnected();
+ void onDisconnected(const sp<AMessage> &msg);
+ void finishDisconnectIfPossible();
+
+ DISALLOW_EVIL_CONSTRUCTORS(RTSPSource);
+};
+
+} // namespace android
+
+#endif // RTSP_SOURCE_H_
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index f795654..2e63b3b 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -63,17 +63,22 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() {
mFinalResult = ERROR_END_OF_STREAM;
break;
} else if (n == INFO_DISCONTINUITY) {
- ATSParser::DiscontinuityType type = ATSParser::DISCONTINUITY_SEEK;
+ int32_t type = ATSParser::DISCONTINUITY_SEEK;
- int32_t formatChange;
+ int32_t mask;
if (extra != NULL
&& extra->findInt32(
- IStreamListener::kKeyFormatChange, &formatChange)
- && formatChange != 0) {
- type = ATSParser::DISCONTINUITY_FORMATCHANGE;
+ IStreamListener::kKeyDiscontinuityMask, &mask)) {
+ if (mask == 0) {
+ LOGE("Client specified an illegal discontinuity type.");
+ return ERROR_UNSUPPORTED;
+ }
+
+ type = mask;
}
- mTSParser->signalDiscontinuity(type, extra);
+ mTSParser->signalDiscontinuity(
+ (ATSParser::DiscontinuityType)type, extra);
} else if (n < 0) {
CHECK_EQ(n, -EWOULDBLOCK);
break;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9cb18de..dbc9b7e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -342,6 +342,7 @@ void ACodec::initiateSetup(const sp<AMessage> &msg) {
}
void ACodec::signalFlush() {
+ LOGV("[%s] signalFlush", mComponentName.c_str());
(new AMessage(kWhatFlush, id()))->post();
}
@@ -681,6 +682,10 @@ void ACodec::setComponentRole(
static const MimeToRole kMimeToRole[] = {
{ MEDIA_MIMETYPE_AUDIO_MPEG,
"audio_decoder.mp3", "audio_encoder.mp3" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I,
+ "audio_decoder.mp1", "audio_encoder.mp1" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II,
+ "audio_decoder.mp2", "audio_encoder.mp2" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB,
"audio_decoder.amrnb", "audio_encoder.amrnb" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB,
@@ -1088,6 +1093,20 @@ status_t ACodec::initNativeWindow() {
return OK;
}
+size_t ACodec::countBuffersOwnedByComponent(OMX_U32 portIndex) const {
+ size_t n = 0;
+
+ for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+ const BufferInfo &info = mBuffers[portIndex].itemAt(i);
+
+ if (info.mStatus == BufferInfo::OWNED_BY_COMPONENT) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
bool ACodec::allYourBuffersAreBelongToUs(
OMX_U32 portIndex) {
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
@@ -2037,6 +2056,14 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
case kWhatFlush:
{
+ LOGV("[%s] ExecutingState flushing now "
+ "(codec owns %d/%d input, %d/%d output).",
+ mCodec->mComponentName.c_str(),
+ mCodec->countBuffersOwnedByComponent(kPortIndexInput),
+ mCodec->mBuffers[kPortIndexInput].size(),
+ mCodec->countBuffersOwnedByComponent(kPortIndexOutput),
+ mCodec->mBuffers[kPortIndexOutput].size());
+
mActive = false;
CHECK_EQ(mCodec->mOMX->sendCommand(
@@ -2176,6 +2203,12 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
err);
mCodec->signalError();
+
+ // This is technically not correct, since we were unable
+ // to allocate output buffers and therefore the output port
+ // remains disabled. It is necessary however to allow us
+ // to shutdown the codec properly.
+ mCodec->changeState(mCodec->mExecutingState);
}
return true;
@@ -2404,6 +2437,9 @@ bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
bool ACodec::FlushingState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ LOGV("[%s] FlushingState onOMXEvent(%d,%ld)",
+ mCodec->mComponentName.c_str(), event, data1);
+
switch (event) {
case OMX_EventCmdComplete:
{
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
index 0be2ca4..815f987 100644
--- a/media/libstagefright/AVIExtractor.cpp
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -1094,7 +1094,7 @@ status_t AVIExtractor::addH264CodecSpecificData(size_t trackIndex) {
CHECK(meta->findData(kKeyAVCC, &type, &csd, &csdSize));
track->mMeta->setInt32(kKeyWidth, width);
- track->mMeta->setInt32(kKeyHeight, width);
+ track->mMeta->setInt32(kKeyHeight, height);
track->mMeta->setData(kKeyAVCC, type, csd, csdSize);
return OK;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 0b1a2af..0aeb515 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -58,7 +58,6 @@ LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/external/flac/include \
$(TOP)/external/tremolo \
- $(TOP)/frameworks/base/media/libstagefright/rtsp \
$(TOP)/external/openssl/include \
LOCAL_SHARED_LIBRARIES := \
@@ -88,7 +87,6 @@ LOCAL_STATIC_LIBRARIES := \
libvpx \
libstagefright_mpeg2ts \
libstagefright_httplive \
- libstagefright_rtsp \
libstagefright_id3 \
libFLAC \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 1165af5..18dd8ef 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -22,7 +22,6 @@
#include <dlfcn.h>
-#include "include/ARTSPController.h"
#include "include/AwesomePlayer.h"
#include "include/DRMExtractor.h"
#include "include/SoftwareRenderer.h"
@@ -53,7 +52,6 @@
#include <gui/SurfaceTextureClient.h>
#include <surfaceflinger/ISurfaceComposer.h>
-#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <cutils/properties.h>
@@ -65,7 +63,6 @@ namespace android {
static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
static int64_t kHighWaterMarkUs = 5000000ll; // 5secs
-static int64_t kHighWaterMarkRTSPUs = 4000000ll; // 4secs
static const size_t kLowWaterMarkBytes = 40000;
static const size_t kHighWaterMarkBytes = 200000;
@@ -227,17 +224,18 @@ AwesomePlayer::~AwesomePlayer() {
mClient.disconnect();
}
-void AwesomePlayer::cancelPlayerEvents(bool keepBufferingGoing) {
+void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) {
mQueue.cancelEvent(mVideoEvent->eventID());
mVideoEventPending = false;
- mQueue.cancelEvent(mStreamDoneEvent->eventID());
- mStreamDoneEventPending = false;
- mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
- mAudioStatusEventPending = false;
mQueue.cancelEvent(mVideoLagEvent->eventID());
mVideoLagEventPending = false;
- if (!keepBufferingGoing) {
+ if (!keepNotifications) {
+ mQueue.cancelEvent(mStreamDoneEvent->eventID());
+ mStreamDoneEventPending = false;
+ mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
+ mAudioStatusEventPending = false;
+
mQueue.cancelEvent(mBufferingEvent->eventID());
mBufferingEventPending = false;
}
@@ -388,10 +386,12 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
+ const char *_mime;
+ CHECK(meta->findCString(kKeyMIMEType, &_mime));
+
+ String8 mime = String8(_mime);
- if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
+ if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
setVideoSource(extractor->getTrack(i));
haveVideo = true;
@@ -412,9 +412,9 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
- stat->mMIME = mime;
+ stat->mMIME = mime.string();
}
- } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
+ } else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
haveAudio = true;
@@ -424,10 +424,10 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
- stat->mMIME = mime;
+ stat->mMIME = mime.string();
}
- if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+ if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_AUDIO_VORBIS)) {
// Only do this for vorbis audio, none of the other audio
// formats even support this ringtone specific hack and
// retrieving the metadata on some extractors may turn out
@@ -439,7 +439,7 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
modifyFlags(AUTO_LOOPING, SET);
}
}
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+ } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
addTextSource(extractor->getTrack(i));
}
}
@@ -485,9 +485,6 @@ void AwesomePlayer::reset_l() {
if (mConnectingDataSource != NULL) {
LOGI("interrupting the connection process");
mConnectingDataSource->disconnect();
- } else if (mConnectingRTSPController != NULL) {
- LOGI("interrupting the connection process");
- mConnectingRTSPController->disconnect();
}
if (mFlags & PREPARING_CONNECTED) {
@@ -534,11 +531,6 @@ void AwesomePlayer::reset_l() {
mVideoRenderer.clear();
- if (mRTSPController != NULL) {
- mRTSPController->disconnect();
- mRTSPController.clear();
- }
-
if (mVideoSource != NULL) {
shutdownVideoDecoder_l();
}
@@ -612,10 +604,7 @@ bool AwesomePlayer::getBitrate(int64_t *bitrate) {
bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
int64_t bitrate;
- if (mRTSPController != NULL) {
- *durationUs = mRTSPController->getQueueDurationUs(eos);
- return true;
- } else if (mCachedSource != NULL && getBitrate(&bitrate)) {
+ if (mCachedSource != NULL && getBitrate(&bitrate)) {
status_t finalStatus;
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
*durationUs = cachedDataRemaining * 8000000ll / bitrate;
@@ -751,9 +740,6 @@ void AwesomePlayer::onBufferingUpdate() {
LOGV("cachedDurationUs = %.2f secs, eos=%d",
cachedDurationUs / 1E6, eos);
- int64_t highWaterMarkUs =
- (mRTSPController != NULL) ? kHighWaterMarkRTSPUs : kHighWaterMarkUs;
-
if ((mFlags & PLAYING) && !eos
&& (cachedDurationUs < kLowWaterMarkUs)) {
LOGI("cache is running low (%.2f secs) , pausing.",
@@ -763,7 +749,7 @@ void AwesomePlayer::onBufferingUpdate() {
ensureCacheIsFetching_l();
sendCacheStats();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
- } else if (eos || cachedDurationUs > highWaterMarkUs) {
+ } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
if (mFlags & CACHE_UNDERRUN) {
LOGI("cache has filled up (%.2f secs), resuming.",
cachedDurationUs / 1E6);
@@ -1081,7 +1067,8 @@ void AwesomePlayer::initRenderer_l() {
if (USE_SURFACE_ALLOC
&& !strncmp(component, "OMX.", 4)
- && strncmp(component, "OMX.google.", 11)) {
+ && strncmp(component, "OMX.google.", 11)
+ && strcmp(component, "OMX.Nvidia.mpeg2v.decode")) {
// Hardware decoders avoid the CPU color conversion by decoding
// directly to ANativeBuffers, so we must use a renderer that
// just pushes those buffers to the ANativeWindow.
@@ -1109,7 +1096,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) {
return OK;
}
- cancelPlayerEvents(true /* keepBufferingGoing */);
+ cancelPlayerEvents(true /* keepNotifications */);
if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) {
if (at_eos) {
@@ -1153,18 +1140,9 @@ bool AwesomePlayer::isPlaying() const {
return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
}
-status_t AwesomePlayer::setSurface(const sp<Surface> &surface) {
- Mutex::Autolock autoLock(mLock);
-
- mSurface = surface;
- return setNativeWindow_l(surface);
-}
-
status_t AwesomePlayer::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
Mutex::Autolock autoLock(mLock);
- mSurface.clear();
-
status_t err;
if (surfaceTexture != NULL) {
err = setNativeWindow_l(new SurfaceTextureClient(surfaceTexture));
@@ -1263,10 +1241,7 @@ status_t AwesomePlayer::getDuration(int64_t *durationUs) {
}
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
- if (mRTSPController != NULL) {
- *positionUs = mRTSPController->getNormalPlayTimeUs();
- }
- else if (mSeeking != NO_SEEK) {
+ if (mSeeking != NO_SEEK) {
*positionUs = mSeekTimeUs;
} else if (mVideoSource != NULL
&& (mAudioPlayer == NULL || !(mFlags & VIDEO_AT_EOS))) {
@@ -1316,25 +1291,7 @@ status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
}
}
-// static
-void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) {
- static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone();
-}
-
-void AwesomePlayer::onRTSPSeekDone() {
- if (!mSeekNotificationSent) {
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mSeekNotificationSent = true;
- }
-}
-
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
- if (mRTSPController != NULL) {
- mSeekNotificationSent = false;
- mRTSPController->seekAsync(timeUs, OnRTSPSeekDoneWrapper, this);
- return OK;
- }
-
if (mFlags & CACHE_UNDERRUN) {
modifyFlags(CACHE_UNDERRUN, CLEAR);
play_l();
@@ -1770,7 +1727,6 @@ void AwesomePlayer::onVideoEvent() {
int64_t latenessUs = nowUs - timeUs;
if (latenessUs > 500000ll
- && mRTSPController == NULL
&& mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(
&realTimeUs, &mediaTimeUs)) {
@@ -1993,6 +1949,8 @@ status_t AwesomePlayer::finishSetDataSource_l() {
mUri = newURI;
}
+ AString sniffedMIME;
+
if (!strncasecmp("http://", mUri.string(), 7)
|| !strncasecmp("https://", mUri.string(), 8)
|| isWidevineStreaming) {
@@ -2042,7 +2000,6 @@ status_t AwesomePlayer::finishSetDataSource_l() {
mConnectingDataSource.clear();
-
String8 contentType = dataSource->getMIMEType();
if (strncasecmp(contentType.string(), "audio/", 6)) {
@@ -2064,16 +2021,51 @@ status_t AwesomePlayer::finishSetDataSource_l() {
mLock.unlock();
+ // Initially make sure we have at least 128 bytes for the sniff
+ // to complete without blocking.
+ static const size_t kMinBytesForSniffing = 128;
+
+ off64_t metaDataSize = -1ll;
for (;;) {
status_t finalStatus;
size_t cachedDataRemaining =
mCachedSource->approxDataRemaining(&finalStatus);
- if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+ if (finalStatus != OK
+ || (metaDataSize >= 0
+ && cachedDataRemaining >= metaDataSize)
|| (mFlags & PREPARE_CANCELLED)) {
break;
}
+ LOGV("now cached %d bytes of data", cachedDataRemaining);
+
+ if (metaDataSize < 0
+ && cachedDataRemaining >= kMinBytesForSniffing) {
+ String8 tmp;
+ float confidence;
+ sp<AMessage> meta;
+ if (!dataSource->sniff(&tmp, &confidence, &meta)) {
+ mLock.lock();
+ return UNKNOWN_ERROR;
+ }
+
+ // We successfully identified the file's extractor to
+ // be, remember this mime type so we don't have to
+ // sniff it again when we call MediaExtractor::Create()
+ // below.
+ sniffedMIME = tmp.string();
+
+ if (meta == NULL
+ || !meta->findInt64(
+ "meta-data-size", &metaDataSize)) {
+ metaDataSize = kHighWaterMarkBytes;
+ }
+
+ CHECK_GE(metaDataSize, 0ll);
+ LOGV("metaDataSize = %lld bytes", metaDataSize);
+ }
+
usleep(200000);
}
@@ -2085,34 +2077,6 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return UNKNOWN_ERROR;
}
}
- } else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
- if (mLooper == NULL) {
- mLooper = new ALooper;
- mLooper->setName("rtsp");
- mLooper->start();
- }
- mRTSPController = new ARTSPController(mLooper);
- mConnectingRTSPController = mRTSPController;
-
- if (mUIDValid) {
- mConnectingRTSPController->setUID(mUID);
- }
-
- mLock.unlock();
- status_t err = mRTSPController->connect(mUri.string());
- mLock.lock();
-
- mConnectingRTSPController.clear();
-
- LOGI("ARTSPController::connect returned %d", err);
-
- if (err != OK) {
- mRTSPController.clear();
- return err;
- }
-
- sp<MediaExtractor> extractor = mRTSPController.get();
- return setDataSource_l(extractor);
} else {
dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
}
@@ -2139,7 +2103,8 @@ status_t AwesomePlayer::finishSetDataSource_l() {
mWVMExtractor->setAdaptiveStreamingMode(true);
extractor = mWVMExtractor;
} else {
- extractor = MediaExtractor::Create(dataSource);
+ extractor = MediaExtractor::Create(
+ dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
if (extractor == NULL) {
return UNKNOWN_ERROR;
@@ -2224,7 +2189,7 @@ void AwesomePlayer::onPrepareAsyncEvent() {
modifyFlags(PREPARING_CONNECTED, SET);
- if (isStreamingHTTP() || mRTSPController != NULL) {
+ if (isStreamingHTTP()) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 256f3ba..57989c5 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -33,6 +33,8 @@
namespace android {
+static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
+
struct CameraSourceListener : public CameraListener {
CameraSourceListener(const sp<CameraSource> &source);
@@ -156,6 +158,7 @@ CameraSource::CameraSource(
mLastFrameTimestampUs(0),
mStarted(false),
mNumFramesEncoded(0),
+ mTimeBetweenFrameCaptureUs(0),
mFirstFrameTimeUs(0),
mNumFramesDropped(0),
mNumGlitches(0),
@@ -644,7 +647,8 @@ status_t CameraSource::stop() {
releaseQueuedFrames();
while (!mFramesBeingEncoded.empty()) {
if (NO_ERROR !=
- mFrameCompleteCondition.waitRelative(mLock, 3000000000LL)) {
+ mFrameCompleteCondition.waitRelative(mLock,
+ mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
LOGW("Timed out waiting for outstanding frames being encoded: %d",
mFramesBeingEncoded.size());
}
@@ -736,7 +740,8 @@ status_t CameraSource::read(
Mutex::Autolock autoLock(mLock);
while (mStarted && mFramesReceived.empty()) {
if (NO_ERROR !=
- mFrameAvailableCondition.waitRelative(mLock, 1000000000LL)) {
+ mFrameAvailableCondition.waitRelative(mLock,
+ mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
if (mCameraRecordingProxy != 0 &&
!mCameraRecordingProxy->asBinder()->isBinderAlive()) {
LOGW("camera recording proxy is gone");
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 1ba79e5..eb456f4 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -39,12 +39,12 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(
Size videoSize,
int32_t videoFrameRate,
const sp<Surface>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs) {
+ int64_t timeBetweenFrameCaptureUs) {
CameraSourceTimeLapse *source = new
CameraSourceTimeLapse(camera, proxy, cameraId,
videoSize, videoFrameRate, surface,
- timeBetweenTimeLapseFrameCaptureUs);
+ timeBetweenFrameCaptureUs);
if (source != NULL) {
if (source->initCheck() != OK) {
@@ -62,15 +62,15 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(
Size videoSize,
int32_t videoFrameRate,
const sp<Surface>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs)
+ int64_t timeBetweenFrameCaptureUs)
: CameraSource(camera, proxy, cameraId, videoSize, videoFrameRate, surface, true),
- mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
mLastTimeLapseFrameRealTimestampUs(0),
mSkipCurrentFrame(false) {
+ mTimeBetweenFrameCaptureUs = timeBetweenFrameCaptureUs;
LOGD("starting time lapse mode: %lld us",
- mTimeBetweenTimeLapseFrameCaptureUs);
+ mTimeBetweenFrameCaptureUs);
mVideoWidth = videoSize.width;
mVideoHeight = videoSize.height;
@@ -257,6 +257,12 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
mForceRead = false;
*timestampUs =
mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
+
+ // Really make sure that this video recording frame will not be dropped.
+ if (*timestampUs < mStartTimeUs) {
+ LOGI("set timestampUs to start time stamp %lld us", mStartTimeUs);
+ *timestampUs = mStartTimeUs;
+ }
return false;
}
}
@@ -265,14 +271,14 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
// The first 2 output frames from the encoder are: decoder specific info and
// the compressed video frame data for the first input video frame.
if (mNumFramesEncoded >= 1 && *timestampUs <
- (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
+ (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenFrameCaptureUs)) {
// Skip all frames from last encoded frame until
- // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+ // sufficient time (mTimeBetweenFrameCaptureUs) has passed.
// Tell the camera to release its recording frame and return.
LOGV("dataCallbackTimestamp timelapse: skipping intermediate frame");
return true;
} else {
- // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+ // Desired frame has arrived after mTimeBetweenFrameCaptureUs time:
// - Reset mLastTimeLapseFrameRealTimestampUs to current time.
// - Artificially modify timestampUs to be one frame time (1/framerate) ahead
// of the last encoded frame's time stamp.
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index c16b3b5..70523c1 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -20,6 +20,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "include/MPEG2PSExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "include/NuCachedSource2.h"
#include "include/HTTPBase.h"
@@ -113,6 +114,7 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffAAC);
RegisterSniffer(SniffAVI);
+ RegisterSniffer(SniffMPEG2PS);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 92e84c2..34e9cd7 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -25,11 +25,11 @@
#include "include/VBRISeeker.h"
#include "include/XINGSeeker.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -289,9 +289,24 @@ MP3Extractor::MP3Extractor(
GetMPEGAudioFrameSize(
header, &frame_size, &sample_rate, &num_channels, &bitrate);
+ unsigned layer = 4 - ((header >> 17) & 3);
+
mMeta = new MetaData;
- mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ switch (layer) {
+ case 1:
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
+ break;
+ case 2:
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
+ break;
+ case 3:
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ break;
+ default:
+ TRESPASS();
+ }
+
mMeta->setInt32(kKeySampleRate, sample_rate);
mMeta->setInt32(kKeyBitRate, bitrate * 1000);
mMeta->setInt32(kKeyChannelCount, num_channels);
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 1e24599..f6b06c7 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -30,6 +30,7 @@
#include <string.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
@@ -1136,6 +1137,41 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
break;
}
+ // @xyz
+ case FOURCC('\xA9', 'x', 'y', 'z'):
+ {
+ // Best case the total data length inside "@xyz" box
+ // would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/",
+ // where "\x00\x04" is the text string length with value = 4,
+ // "\0x15\xc7" is the language code = en, and "0+0" is a
+ // location (string) value with longitude = 0 and latitude = 0.
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ // Worst case the location string length would be 18,
+ // for instance +90.0000-180.0000, without the trailing "/" and
+ // the string length + language code.
+ char buffer[18];
+
+ // Substracting 5 from the data size is because the text string length +
+ // language code takes 4 bytes, and the trailing slash "/" takes 1 byte.
+ off64_t location_length = chunk_data_size - 5;
+ if (location_length >= (off64_t) sizeof(buffer)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset + 4, buffer, location_length) < location_length) {
+ return ERROR_IO;
+ }
+
+ buffer[location_length] = '\0';
+ mFileMetaData->setCString(kKeyLocation, buffer);
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('e', 's', 'd', 's'):
{
if (chunk_data_size < 4) {
@@ -2266,51 +2302,121 @@ static bool isCompatibleBrand(uint32_t fourcc) {
// Attempt to actually parse the 'ftyp' atom and determine if a suitable
// compatible brand is present.
+// Also try to identify where this file's metadata ends
+// (end of the 'moov' atom) and report it to the caller as part of
+// the metadata.
static bool BetterSniffMPEG4(
- const sp<DataSource> &source, String8 *mimeType, float *confidence) {
- uint8_t header[12];
- if (source->readAt(0, header, 12) != 12
- || memcmp("ftyp", &header[4], 4)) {
- return false;
- }
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *meta) {
+ // We scan up to 128 bytes to identify this file as an MP4.
+ static const off64_t kMaxScanOffset = 128ll;
- size_t atomSize = U32_AT(&header[0]);
- if (atomSize < 16 || (atomSize % 4) != 0) {
- return false;
- }
+ off64_t offset = 0ll;
+ bool foundGoodFileType = false;
+ off64_t moovAtomEndOffset = -1ll;
+ bool done = false;
- bool success = false;
- if (isCompatibleBrand(U32_AT(&header[8]))) {
- success = true;
- } else {
- size_t numCompatibleBrands = (atomSize - 16) / 4;
- for (size_t i = 0; i < numCompatibleBrands; ++i) {
- uint8_t tmp[4];
- if (source->readAt(16 + i * 4, tmp, 4) != 4) {
+ while (!done && offset < kMaxScanOffset) {
+ uint32_t hdr[2];
+ if (source->readAt(offset, hdr, 8) < 8) {
+ return false;
+ }
+
+ uint64_t chunkSize = ntohl(hdr[0]);
+ uint32_t chunkType = ntohl(hdr[1]);
+ off64_t chunkDataOffset = offset + 8;
+
+ if (chunkSize == 1) {
+ if (source->readAt(offset + 8, &chunkSize, 8) < 8) {
+ return false;
+ }
+
+ chunkSize = ntoh64(chunkSize);
+ chunkDataOffset += 8;
+
+ if (chunkSize < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
return false;
}
+ } else if (chunkSize < 8) {
+ // The smallest valid chunk is 8 bytes long.
+ return false;
+ }
+
+ off64_t chunkDataSize = offset + chunkSize - chunkDataOffset;
+
+ switch (chunkType) {
+ case FOURCC('f', 't', 'y', 'p'):
+ {
+ if (chunkDataSize < 8) {
+ return false;
+ }
+
+ uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
+ for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
+ if (i == 1) {
+ // Skip this index, it refers to the minorVersion,
+ // not a brand.
+ continue;
+ }
+
+ uint32_t brand;
+ if (source->readAt(
+ chunkDataOffset + 4 * i, &brand, 4) < 4) {
+ return false;
+ }
+
+ brand = ntohl(brand);
+
+ if (isCompatibleBrand(brand)) {
+ foundGoodFileType = true;
+ break;
+ }
+ }
+
+ if (!foundGoodFileType) {
+ return false;
+ }
+
+ break;
+ }
+
+ case FOURCC('m', 'o', 'o', 'v'):
+ {
+ moovAtomEndOffset = offset + chunkSize;
- if (isCompatibleBrand(U32_AT(&tmp[0]))) {
- success = true;
+ done = true;
break;
}
+
+ default:
+ break;
}
+
+ offset += chunkSize;
}
- if (!success) {
+ if (!foundGoodFileType) {
return false;
}
*mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.4f;
+ if (moovAtomEndOffset >= 0) {
+ *meta = new AMessage;
+ (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
+
+ LOGV("found metadata size: %lld", moovAtomEndOffset);
+ }
+
return true;
}
bool SniffMPEG4(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
- if (BetterSniffMPEG4(source, mimeType, confidence)) {
+ sp<AMessage> *meta) {
+ if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
return true;
}
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 01f1fba..444e823 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -30,6 +30,8 @@ const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2";
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
@@ -45,6 +47,7 @@ const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p";
const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index a8023df..2221268 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -24,6 +24,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "include/MPEG2PSExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "include/DRMExtractor.h"
#include "include/WVMExtractor.h"
@@ -115,6 +116,8 @@ sp<MediaExtractor> MediaExtractor::Create(
ret = new WVMExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
+ ret = new MPEG2PSExtractor(source);
}
if (ret != NULL) {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index b20bfcb..86b3fe4 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -109,6 +109,7 @@ static const CodecInfo kDecoderInfo[] = {
{ MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, "OMX.Nvidia.mp2.decoder" },
// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" },
@@ -519,6 +520,85 @@ sp<MediaSource> OMXCodec::Create(
return NULL;
}
+status_t OMXCodec::parseAVCCodecSpecificData(
+ const void *data, size_t size,
+ unsigned *profile, unsigned *level) {
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ // verify minimum size and configurationVersion == 1.
+ if (size < 7 || ptr[0] != 1) {
+ return ERROR_MALFORMED;
+ }
+
+ *profile = ptr[1];
+ *level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ if (size < 1) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ return OK;
+}
+
status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
LOGV("configureCodec protected=%d",
(mFlags & kEnableGrallocUsageProtected) ? 1 : 0);
@@ -541,66 +621,17 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
- const uint8_t *ptr = (const uint8_t *)data;
-
- CHECK(size >= 7);
- CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
- uint8_t profile = ptr[1];
- uint8_t level = ptr[3];
-
- // There is decodable content out there that fails the following
- // assertion, let's be lenient for now...
- // CHECK((ptr[4] >> 2) == 0x3f); // reserved
-
- size_t lengthSize = 1 + (ptr[4] & 3);
-
- // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
- // violates it...
- // CHECK((ptr[5] >> 5) == 7); // reserved
-
- size_t numSeqParameterSets = ptr[5] & 31;
-
- ptr += 6;
- size -= 6;
-
- for (size_t i = 0; i < numSeqParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
- }
-
- CHECK(size >= 1);
- size_t numPictureParameterSets = *ptr;
- ++ptr;
- --size;
-
- for (size_t i = 0; i < numPictureParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
+ unsigned profile, level;
+ status_t err;
+ if ((err = parseAVCCodecSpecificData(
+ data, size, &profile, &level)) != OK) {
+ LOGE("Malformed AVC codec specific data.");
+ return err;
}
CODEC_LOGI(
- "AVC profile = %d (%s), level = %d",
- (int)profile, AVCProfileToString(profile), level);
+ "AVC profile = %u (%s), level = %u",
+ profile, AVCProfileToString(profile), level);
if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 30)) {
@@ -1471,7 +1502,9 @@ OMXCodec::OMXCodec(
mOutputPortSettingsChangedPending(false),
mLeftOverBuffer(NULL),
mPaused(false),
- mNativeWindow(!strncmp(componentName, "OMX.google.", 11)
+ mNativeWindow(
+ (!strncmp(componentName, "OMX.google.", 11)
+ || !strcmp(componentName, "OMX.Nvidia.mpeg2v.decode"))
? NULL : nativeWindow) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -1492,6 +1525,12 @@ void OMXCodec::setComponentRole(
static const MimeToRole kMimeToRole[] = {
{ MEDIA_MIMETYPE_AUDIO_MPEG,
"audio_decoder.mp3", "audio_encoder.mp3" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I,
+ "audio_decoder.mp1", "audio_encoder.mp1" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II,
+ "audio_decoder.mp2", "audio_encoder.mp2" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG,
+ "audio_decoder.mp3", "audio_encoder.mp3" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB,
"audio_decoder.amrnb", "audio_encoder.amrnb" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB,
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index ebad321..69d1785 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -631,14 +631,7 @@ status_t SampleTable::findSyncSampleNear(
--left;
}
- uint32_t x;
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
- return ERROR_IO;
- }
-
- x = ntohl(x);
- --x;
+ uint32_t x = mSyncSamples[left];
if (left + 1 < mNumSyncSamples) {
uint32_t y = mSyncSamples[left + 1];
@@ -679,13 +672,7 @@ status_t SampleTable::findSyncSampleNear(
if (x > start_sample_index) {
CHECK(left > 0);
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) {
- return ERROR_IO;
- }
-
- x = ntohl(x);
- --x;
+ x = mSyncSamples[left - 1];
CHECK(x <= start_sample_index);
}
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index f693c72..2505096 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -38,7 +38,7 @@ static bool FileHasAcceptableExtension(const char *extension) {
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
- ".avi",
+ ".avi", ".mpeg", ".mpg"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index c74cb5a..4491c97 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -418,6 +418,7 @@ void StagefrightMetadataRetriever::parseMetaData() {
{ kKeyYear, METADATA_KEY_YEAR },
{ kKeyWriter, METADATA_KEY_WRITER },
{ kKeyCompilation, METADATA_KEY_COMPILATION },
+ { kKeyLocation, METADATA_KEY_LOCATION },
};
static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 2b27ee2..86b33d1 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -336,7 +336,7 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
(uint32_t(buffer->height) != h) ||
(uint32_t(buffer->format) != format) ||
((uint32_t(buffer->usage) & usage) != usage)) {
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ usage |= GraphicBuffer::USAGE_HW_VIDEO_ENCODER;
status_t error;
sp<GraphicBuffer> graphicBuffer(
mGraphicBufferAlloc->createGraphicBuffer(
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index 94a79ab..d361ef4 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -82,7 +82,11 @@ status_t AMRNBEncoder::start(MetaData *params) {
&mEncState, &mSidState, false /* dtx_enable */),
0);
- mSource->start(params);
+ status_t err = mSource->start(params);
+ if (err != OK) {
+ LOGE("AudioSource is not available");
+ return err;
+ }
mAnchorTimeUs = 0;
mNumFramesOutput = 0;
diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
index 002f055..5eacc16 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -137,8 +137,12 @@ status_t AMRWBEncoder::start(MetaData *params) {
CHECK_EQ(OK, initCheck());
mNumFramesOutput = 0;
- mSource->start(params);
+ status_t err = mSource->start(params);
+ if (err != OK) {
+ LOGE("AudioSource is not available");
+ return err;
+ }
mStarted = true;
return OK;
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index ddced5f..aa07e57 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -421,8 +421,13 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
int32_t bufferSize = inHeader->nFilledLen;
+ // The PV decoder is lying to us, sometimes it'll claim to only have
+ // consumed a subset of the buffer when it clearly consumed all of it.
+ // ignore whatever it says...
+ int32_t tmp = bufferSize;
+
if (PVDecodeVideoFrame(
- mHandle, &bitstream, &timestamp, &bufferSize,
+ mHandle, &bitstream, &timestamp, &tmp,
&useExtTimestamp,
outHeader->pBuffer) != PV_TRUE) {
LOGE("failed to decode video frame.");
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
index 740c957..dede3ac 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
@@ -76,7 +76,8 @@ SoftAVC::SoftAVC(
mPicId(0),
mHeadersDecoded(false),
mEOSStatus(INPUT_DATA_AVAILABLE),
- mOutputPortSettingsChange(NONE) {
+ mOutputPortSettingsChange(NONE),
+ mSignalledError(false) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
@@ -287,7 +288,7 @@ OMX_ERRORTYPE SoftAVC::getConfig(
}
void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
- if (mOutputPortSettingsChange != NONE) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
@@ -298,7 +299,6 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
H264SwDecRet ret = H264SWDEC_PIC_RDY;
- status_t err = OK;
bool portSettingsChanged = false;
while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
&& outQueue.size() == kNumOutputBuffers) {
@@ -372,7 +372,12 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
inPicture.dataLen = 0;
if (ret < 0) {
LOGE("Decoder failed: %d", ret);
- err = ERROR_MALFORMED;
+
+ notify(OMX_EventError, OMX_ErrorUndefined,
+ ERROR_MALFORMED, NULL);
+
+ mSignalledError = true;
+ return;
}
}
}
@@ -400,10 +405,6 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture;
drainOneOutputBuffer(picId, data);
}
-
- if (err != OK) {
- notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
- }
}
}
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
index 1cc85e8..879b014 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
@@ -88,6 +88,8 @@ private:
};
OutputPortSettingChange mOutputPortSettingsChange;
+ bool mSignalledError;
+
void initPorts();
status_t initDecoder();
void updatePortDefinitions();
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
deleted file mode 100644
index 2bd5be6..0000000
--- a/media/libstagefright/include/ARTSPController.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef A_RTSP_CONTROLLER_H_
-
-#define A_RTSP_CONTROLLER_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AHandlerReflector.h>
-#include <media/stagefright/MediaExtractor.h>
-
-namespace android {
-
-struct ALooper;
-struct MyHandler;
-
-struct ARTSPController : public MediaExtractor {
- ARTSPController(const sp<ALooper> &looper);
-
- void setUID(uid_t uid);
-
- status_t connect(const char *url);
- void disconnect();
-
- void seekAsync(int64_t timeUs, void (*seekDoneCb)(void *), void *cookie);
-
- virtual size_t countTracks();
- virtual sp<MediaSource> getTrack(size_t index);
-
- virtual sp<MetaData> getTrackMetaData(
- size_t index, uint32_t flags);
-
- int64_t getNormalPlayTimeUs();
- int64_t getQueueDurationUs(bool *eos);
-
- void onMessageReceived(const sp<AMessage> &msg);
-
- virtual uint32_t flags() const {
- // Seeking 10secs forward or backward is a very expensive operation
- // for rtsp, so let's not enable that.
- // The user can always use the seek bar.
-
- return CAN_PAUSE | CAN_SEEK;
- }
-
-protected:
- virtual ~ARTSPController();
-
-private:
- enum {
- kWhatConnectDone = 'cdon',
- kWhatDisconnectDone = 'ddon',
- kWhatSeekDone = 'sdon',
- };
-
- enum State {
- DISCONNECTED,
- CONNECTED,
- CONNECTING,
- };
-
- Mutex mLock;
- Condition mCondition;
-
- State mState;
- status_t mConnectionResult;
-
- sp<ALooper> mLooper;
- sp<MyHandler> mHandler;
- sp<AHandlerReflector<ARTSPController> > mReflector;
-
- bool mUIDValid;
- uid_t mUID;
-
- void (*mSeekDoneCb)(void *);
- void *mSeekDoneCookie;
- int64_t mLastSeekCompletedTimeUs;
-
- DISALLOW_EVIL_CONSTRUCTORS(ARTSPController);
-};
-
-} // namespace android
-
-#endif // A_RTSP_CONTROLLER_H_
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 8e73121..0985f47 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -38,9 +38,6 @@ struct MediaSource;
struct NuCachedSource2;
struct ISurfaceTexture;
-struct ALooper;
-struct ARTSPController;
-
class DrmManagerClinet;
class DecryptHandle;
@@ -84,7 +81,6 @@ struct AwesomePlayer {
bool isPlaying() const;
- status_t setSurface(const sp<Surface> &surface);
status_t setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
status_t setLooping(bool shouldLoop);
@@ -157,7 +153,6 @@ private:
bool mUIDValid;
uid_t mUID;
- sp<Surface> mSurface;
sp<ANativeWindow> mNativeWindow;
sp<MediaPlayerBase::AudioSink> mAudioSink;
@@ -233,10 +228,6 @@ private:
sp<HTTPBase> mConnectingDataSource;
sp<NuCachedSource2> mCachedSource;
- sp<ALooper> mLooper;
- sp<ARTSPController> mRTSPController;
- sp<ARTSPController> mConnectingRTSPController;
-
DrmManagerClient *mDrmManagerClient;
sp<DecryptHandle> mDecryptHandle;
@@ -259,7 +250,7 @@ private:
void notifyVideoSize_l();
void seekAudioIfNecessary_l();
- void cancelPlayerEvents(bool keepBufferingGoing = false);
+ void cancelPlayerEvents(bool keepNotifications = false);
void setAudioSource(sp<MediaSource> source);
status_t initAudioDecoder();
@@ -287,9 +278,6 @@ private:
static bool ContinuePreparation(void *cookie);
- static void OnRTSPSeekDoneWrapper(void *cookie);
- void onRTSPSeekDone();
-
bool getBitrate(int64_t *bitrate);
void finishSeekIfNecessary(int64_t videoTimeUs);
diff --git a/media/libstagefright/include/MPEG2PSExtractor.h b/media/libstagefright/include/MPEG2PSExtractor.h
new file mode 100644
index 0000000..fb76564
--- /dev/null
+++ b/media/libstagefright/include/MPEG2PSExtractor.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef MPEG2_PS_EXTRACTOR_H_
+
+#define MPEG2_PS_EXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+struct Track;
+struct String8;
+
+struct MPEG2PSExtractor : public MediaExtractor {
+ MPEG2PSExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+ virtual uint32_t flags() const;
+
+protected:
+ virtual ~MPEG2PSExtractor();
+
+private:
+ struct Track;
+ struct WrappedTrack;
+
+ mutable Mutex mLock;
+ sp<DataSource> mDataSource;
+
+ off64_t mOffset;
+ status_t mFinalResult;
+ sp<ABuffer> mBuffer;
+ KeyedVector<unsigned, sp<Track> > mTracks;
+ bool mScanning;
+
+ bool mProgramStreamMapValid;
+ KeyedVector<unsigned, unsigned> mStreamTypeByESID;
+
+ status_t feedMore();
+
+ status_t dequeueChunk();
+ ssize_t dequeuePack();
+ ssize_t dequeueSystemHeader();
+ ssize_t dequeuePES();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MPEG2PSExtractor);
+};
+
+bool SniffMPEG2PS(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // MPEG2_PS_EXTRACTOR_H_
+
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 72f1282..6cec63a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -123,6 +123,9 @@ private:
void extractAACFrames(const sp<ABuffer> &buffer);
+ bool isAudio() const;
+ bool isVideo() const;
+
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
@@ -401,7 +404,7 @@ ATSParser::Stream::Stream(
case STREAMTYPE_H264:
mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264);
break;
- case STREAMTYPE_MPEG2_AUDIO_ATDS:
+ case STREAMTYPE_MPEG2_AUDIO_ADTS:
mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
break;
case STREAMTYPE_MPEG1_AUDIO:
@@ -486,6 +489,31 @@ status_t ATSParser::Stream::parse(
return OK;
}
+bool ATSParser::Stream::isVideo() const {
+ switch (mStreamType) {
+ case STREAMTYPE_H264:
+ case STREAMTYPE_MPEG1_VIDEO:
+ case STREAMTYPE_MPEG2_VIDEO:
+ case STREAMTYPE_MPEG4_VIDEO:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool ATSParser::Stream::isAudio() const {
+ switch (mStreamType) {
+ case STREAMTYPE_MPEG1_AUDIO:
+ case STREAMTYPE_MPEG2_AUDIO:
+ case STREAMTYPE_MPEG2_AUDIO_ADTS:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
void ATSParser::Stream::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
if (mQueue == NULL) {
@@ -495,34 +523,34 @@ void ATSParser::Stream::signalDiscontinuity(
mPayloadStarted = false;
mBuffer->setRange(0, 0);
- switch (type) {
- case DISCONTINUITY_SEEK:
- case DISCONTINUITY_FORMATCHANGE:
- {
- bool isASeek = (type == DISCONTINUITY_SEEK);
-
- mQueue->clear(!isASeek);
+ bool clearFormat = false;
+ if (isAudio()) {
+ if (type & DISCONTINUITY_AUDIO_FORMAT) {
+ clearFormat = true;
+ }
+ } else {
+ if (type & DISCONTINUITY_VIDEO_FORMAT) {
+ clearFormat = true;
+ }
+ }
- uint64_t resumeAtPTS;
- if (extra != NULL
- && extra->findInt64(
- IStreamListener::kKeyResumeAtPTS,
- (int64_t *)&resumeAtPTS)) {
- int64_t resumeAtMediaTimeUs =
- mProgram->convertPTSToTimestamp(resumeAtPTS);
+ mQueue->clear(clearFormat);
- extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs);
- }
+ if (type & DISCONTINUITY_TIME) {
+ uint64_t resumeAtPTS;
+ if (extra != NULL
+ && extra->findInt64(
+ IStreamListener::kKeyResumeAtPTS,
+ (int64_t *)&resumeAtPTS)) {
+ int64_t resumeAtMediaTimeUs =
+ mProgram->convertPTSToTimestamp(resumeAtPTS);
- if (mSource != NULL) {
- mSource->queueDiscontinuity(type, extra);
- }
- break;
+ extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs);
}
+ }
- default:
- TRESPASS();
- break;
+ if (mSource != NULL) {
+ mSource->queueDiscontinuity(type, extra);
}
}
@@ -764,10 +792,7 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
switch (type) {
case VIDEO:
{
- if (mStreamType == STREAMTYPE_H264
- || mStreamType == STREAMTYPE_MPEG1_VIDEO
- || mStreamType == STREAMTYPE_MPEG2_VIDEO
- || mStreamType == STREAMTYPE_MPEG4_VIDEO) {
+ if (isVideo()) {
return mSource;
}
break;
@@ -775,9 +800,7 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
case AUDIO:
{
- if (mStreamType == STREAMTYPE_MPEG1_AUDIO
- || mStreamType == STREAMTYPE_MPEG2_AUDIO
- || mStreamType == STREAMTYPE_MPEG2_AUDIO_ATDS) {
+ if (isAudio()) {
return mSource;
}
break;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 388cb54..c8038d1 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -33,9 +33,18 @@ struct MediaSource;
struct ATSParser : public RefBase {
enum DiscontinuityType {
- DISCONTINUITY_NONE,
- DISCONTINUITY_SEEK,
- DISCONTINUITY_FORMATCHANGE
+ DISCONTINUITY_NONE = 0,
+ DISCONTINUITY_TIME = 1,
+ DISCONTINUITY_AUDIO_FORMAT = 2,
+ DISCONTINUITY_VIDEO_FORMAT = 4,
+
+ DISCONTINUITY_SEEK = DISCONTINUITY_TIME,
+
+ // For legacy reasons this also implies a time discontinuity.
+ DISCONTINUITY_FORMATCHANGE =
+ DISCONTINUITY_AUDIO_FORMAT
+ | DISCONTINUITY_VIDEO_FORMAT
+ | DISCONTINUITY_TIME,
};
enum Flags {
@@ -64,21 +73,22 @@ struct ATSParser : public RefBase {
bool PTSTimeDeltaEstablished();
-protected:
- virtual ~ATSParser();
-
-private:
enum {
// From ISO/IEC 13818-1: 2000 (E), Table 2-29
+ STREAMTYPE_RESERVED = 0x00,
STREAMTYPE_MPEG1_VIDEO = 0x01,
STREAMTYPE_MPEG2_VIDEO = 0x02,
STREAMTYPE_MPEG1_AUDIO = 0x03,
STREAMTYPE_MPEG2_AUDIO = 0x04,
- STREAMTYPE_MPEG2_AUDIO_ATDS = 0x0f,
+ STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
STREAMTYPE_H264 = 0x1b,
};
+protected:
+ virtual ~ATSParser();
+
+private:
struct Program;
struct Stream;
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 4a30416..578c669 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
AnotherPacketSource.cpp \
ATSParser.cpp \
ESQueue.cpp \
+ MPEG2PSExtractor.cpp \
MPEG2TSExtractor.cpp \
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index ce07e32..f782ce5 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -29,8 +29,17 @@
namespace android {
AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
- : mFormat(meta),
+ : mIsAudio(false),
+ mFormat(meta),
mEOSResult(OK) {
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp("audio/", mime, 6)) {
+ mIsAudio = true;
+ } else {
+ CHECK(!strncasecmp("video/", mime, 6));
+ }
}
void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
@@ -67,8 +76,7 @@ status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
int32_t discontinuity;
if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)) {
-
- if (discontinuity == ATSParser::DISCONTINUITY_FORMATCHANGE) {
+ if (wasFormatChange(discontinuity)) {
mFormat.clear();
}
@@ -96,7 +104,7 @@ status_t AnotherPacketSource::read(
int32_t discontinuity;
if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
- if (discontinuity == ATSParser::DISCONTINUITY_FORMATCHANGE) {
+ if (wasFormatChange(discontinuity)) {
mFormat.clear();
}
@@ -117,6 +125,15 @@ status_t AnotherPacketSource::read(
return mEOSResult;
}
+bool AnotherPacketSource::wasFormatChange(
+ int32_t discontinuityType) const {
+ if (mIsAudio) {
+ return (discontinuityType & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0;
+ }
+
+ return (discontinuityType & ATSParser::DISCONTINUITY_VIDEO_FORMAT) != 0;
+}
+
void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 439c785..c99f7f2 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -61,10 +61,13 @@ private:
Mutex mLock;
Condition mCondition;
+ bool mIsAudio;
sp<MetaData> mFormat;
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
+ bool wasFormatChange(int32_t discontinuityType) const;
+
DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
};
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index a56da36..b9a4826 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -585,6 +585,8 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() {
return NULL;
}
+ unsigned layer = 4 - ((header >> 17) & 3);
+
sp<ABuffer> accessUnit = new ABuffer(frameSize);
memcpy(accessUnit->data(), data, frameSize);
@@ -601,7 +603,24 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() {
if (mFormat == NULL) {
mFormat = new MetaData;
- mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+
+ switch (layer) {
+ case 1:
+ mFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
+ break;
+ case 2:
+ mFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
+ break;
+ case 3:
+ mFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ break;
+ default:
+ TRESPASS();
+ }
+
mFormat->setInt32(kKeySampleRate, samplingRate);
mFormat->setInt32(kKeyChannelCount, numChannels);
}
diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
new file mode 100644
index 0000000..a089dbf
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MPEG2PSExtractor"
+#include <utils/Log.h>
+
+#include "include/MPEG2PSExtractor.h"
+
+#include "AnotherPacketSource.h"
+#include "ESQueue.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+struct MPEG2PSExtractor::Track : public MediaSource {
+ Track(MPEG2PSExtractor *extractor,
+ unsigned stream_id, unsigned stream_type);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~Track();
+
+private:
+ friend struct MPEG2PSExtractor;
+
+ MPEG2PSExtractor *mExtractor;
+
+ unsigned mStreamID;
+ unsigned mStreamType;
+ ElementaryStreamQueue *mQueue;
+ sp<AnotherPacketSource> mSource;
+
+ status_t appendPESData(
+ unsigned PTS_DTS_flags,
+ uint64_t PTS, uint64_t DTS,
+ const uint8_t *data, size_t size);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Track);
+};
+
+struct MPEG2PSExtractor::WrappedTrack : public MediaSource {
+ WrappedTrack(const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~WrappedTrack();
+
+private:
+ sp<MPEG2PSExtractor> mExtractor;
+ sp<MPEG2PSExtractor::Track> mTrack;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WrappedTrack);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2PSExtractor::MPEG2PSExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mOffset(0),
+ mFinalResult(OK),
+ mBuffer(new ABuffer(0)),
+ mScanning(true),
+ mProgramStreamMapValid(false) {
+ for (size_t i = 0; i < 500; ++i) {
+ if (feedMore() != OK) {
+ break;
+ }
+ }
+
+ // Remove all tracks that were unable to determine their format.
+ for (size_t i = mTracks.size(); i-- > 0;) {
+ if (mTracks.valueAt(i)->getFormat() == NULL) {
+ mTracks.removeItemsAt(i);
+ }
+ }
+
+ mScanning = false;
+}
+
+MPEG2PSExtractor::~MPEG2PSExtractor() {
+}
+
+size_t MPEG2PSExtractor::countTracks() {
+ return mTracks.size();
+}
+
+sp<MediaSource> MPEG2PSExtractor::getTrack(size_t index) {
+ if (index >= mTracks.size()) {
+ return NULL;
+ }
+
+ return new WrappedTrack(this, mTracks.valueAt(index));
+}
+
+sp<MetaData> MPEG2PSExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ if (index >= mTracks.size()) {
+ return NULL;
+ }
+
+ return mTracks.valueAt(index)->getFormat();
+}
+
+sp<MetaData> MPEG2PSExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
+
+ return meta;
+}
+
+uint32_t MPEG2PSExtractor::flags() const {
+ return CAN_PAUSE;
+}
+
+status_t MPEG2PSExtractor::feedMore() {
+ Mutex::Autolock autoLock(mLock);
+
+ // How much data we're reading at a time
+ static const size_t kChunkSize = 8192;
+
+ for (;;) {
+ status_t err = dequeueChunk();
+
+ if (err == -EAGAIN && mFinalResult == OK) {
+ memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
+ mBuffer->setRange(0, mBuffer->size());
+
+ if (mBuffer->size() + kChunkSize > mBuffer->capacity()) {
+ size_t newCapacity = mBuffer->capacity() + kChunkSize;
+ sp<ABuffer> newBuffer = new ABuffer(newCapacity);
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ mBuffer = newBuffer;
+ }
+
+ ssize_t n = mDataSource->readAt(
+ mOffset, mBuffer->data() + mBuffer->size(), kChunkSize);
+
+ if (n < (ssize_t)kChunkSize) {
+ mFinalResult = (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
+ return mFinalResult;
+ }
+
+ mBuffer->setRange(mBuffer->offset(), mBuffer->size() + n);
+ mOffset += n;
+ } else if (err != OK) {
+ mFinalResult = err;
+ return err;
+ } else {
+ return OK;
+ }
+ }
+}
+
+status_t MPEG2PSExtractor::dequeueChunk() {
+ if (mBuffer->size() < 4) {
+ return -EAGAIN;
+ }
+
+ if (memcmp("\x00\x00\x01", mBuffer->data(), 3)) {
+ return ERROR_MALFORMED;
+ }
+
+ unsigned chunkType = mBuffer->data()[3];
+
+ ssize_t res;
+
+ switch (chunkType) {
+ case 0xba:
+ {
+ res = dequeuePack();
+ break;
+ }
+
+ case 0xbb:
+ {
+ res = dequeueSystemHeader();
+ break;
+ }
+
+ default:
+ {
+ res = dequeuePES();
+ break;
+ }
+ }
+
+ if (res > 0) {
+ if (mBuffer->size() < (size_t)res) {
+ return -EAGAIN;
+ }
+
+ mBuffer->setRange(mBuffer->offset() + res, mBuffer->size() - res);
+ res = OK;
+ }
+
+ return res;
+}
+
+ssize_t MPEG2PSExtractor::dequeuePack() {
+ // 32 + 2 + 3 + 1 + 15 + 1 + 15+ 1 + 9 + 1 + 22 + 1 + 1 | +5
+
+ if (mBuffer->size() < 14) {
+ return -EAGAIN;
+ }
+
+ unsigned pack_stuffing_length = mBuffer->data()[13] & 7;
+
+ return pack_stuffing_length + 14;
+}
+
+ssize_t MPEG2PSExtractor::dequeueSystemHeader() {
+ if (mBuffer->size() < 6) {
+ return -EAGAIN;
+ }
+
+ unsigned header_length = U16_AT(mBuffer->data() + 4);
+
+ return header_length + 6;
+}
+
+ssize_t MPEG2PSExtractor::dequeuePES() {
+ if (mBuffer->size() < 6) {
+ return -EAGAIN;
+ }
+
+ unsigned PES_packet_length = U16_AT(mBuffer->data() + 4);
+ CHECK_NE(PES_packet_length, 0u);
+
+ size_t n = PES_packet_length + 6;
+
+ if (mBuffer->size() < n) {
+ return -EAGAIN;
+ }
+
+ ABitReader br(mBuffer->data(), n);
+
+ unsigned packet_startcode_prefix = br.getBits(24);
+
+ LOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
+
+ if (packet_startcode_prefix != 1) {
+ LOGV("Supposedly payload_unit_start=1 unit does not start "
+ "with startcode.");
+
+ return ERROR_MALFORMED;
+ }
+
+ CHECK_EQ(packet_startcode_prefix, 0x000001u);
+
+ unsigned stream_id = br.getBits(8);
+ LOGV("stream_id = 0x%02x", stream_id);
+
+ /* unsigned PES_packet_length = */br.getBits(16);
+
+ if (stream_id == 0xbc) {
+ // program_stream_map
+
+ if (!mScanning) {
+ return n;
+ }
+
+ mStreamTypeByESID.clear();
+
+ /* unsigned current_next_indicator = */br.getBits(1);
+ /* unsigned reserved = */br.getBits(2);
+ /* unsigned program_stream_map_version = */br.getBits(5);
+ /* unsigned reserved = */br.getBits(7);
+ /* unsigned marker_bit = */br.getBits(1);
+ unsigned program_stream_info_length = br.getBits(16);
+
+ size_t offset = 0;
+ while (offset < program_stream_info_length) {
+ if (offset + 2 > program_stream_info_length) {
+ return ERROR_MALFORMED;
+ }
+
+ unsigned descriptor_tag = br.getBits(8);
+ unsigned descriptor_length = br.getBits(8);
+
+ LOGI("found descriptor tag 0x%02x of length %u",
+ descriptor_tag, descriptor_length);
+
+ if (offset + 2 + descriptor_length > program_stream_info_length) {
+ return ERROR_MALFORMED;
+ }
+
+ br.skipBits(8 * descriptor_length);
+
+ offset += 2 + descriptor_length;
+ }
+
+ unsigned elementary_stream_map_length = br.getBits(16);
+
+ offset = 0;
+ while (offset < elementary_stream_map_length) {
+ if (offset + 4 > elementary_stream_map_length) {
+ return ERROR_MALFORMED;
+ }
+
+ unsigned stream_type = br.getBits(8);
+ unsigned elementary_stream_id = br.getBits(8);
+
+ LOGI("elementary stream id 0x%02x has stream type 0x%02x",
+ elementary_stream_id, stream_type);
+
+ mStreamTypeByESID.add(elementary_stream_id, stream_type);
+
+ unsigned elementary_stream_info_length = br.getBits(16);
+
+ if (offset + 4 + elementary_stream_info_length
+ > elementary_stream_map_length) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 4 + elementary_stream_info_length;
+ }
+
+ /* unsigned CRC32 = */br.getBits(32);
+
+ mProgramStreamMapValid = true;
+ } else if (stream_id != 0xbe // padding_stream
+ && stream_id != 0xbf // private_stream_2
+ && stream_id != 0xf0 // ECM
+ && stream_id != 0xf1 // EMM
+ && stream_id != 0xff // program_stream_directory
+ && stream_id != 0xf2 // DSMCC
+ && stream_id != 0xf8) { // H.222.1 type E
+ CHECK_EQ(br.getBits(2), 2u);
+
+ /* unsigned PES_scrambling_control = */br.getBits(2);
+ /* unsigned PES_priority = */br.getBits(1);
+ /* unsigned data_alignment_indicator = */br.getBits(1);
+ /* unsigned copyright = */br.getBits(1);
+ /* unsigned original_or_copy = */br.getBits(1);
+
+ unsigned PTS_DTS_flags = br.getBits(2);
+ LOGV("PTS_DTS_flags = %u", PTS_DTS_flags);
+
+ unsigned ESCR_flag = br.getBits(1);
+ LOGV("ESCR_flag = %u", ESCR_flag);
+
+ unsigned ES_rate_flag = br.getBits(1);
+ LOGV("ES_rate_flag = %u", ES_rate_flag);
+
+ unsigned DSM_trick_mode_flag = br.getBits(1);
+ LOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag);
+
+ unsigned additional_copy_info_flag = br.getBits(1);
+ LOGV("additional_copy_info_flag = %u", additional_copy_info_flag);
+
+ /* unsigned PES_CRC_flag = */br.getBits(1);
+ /* PES_extension_flag = */br.getBits(1);
+
+ unsigned PES_header_data_length = br.getBits(8);
+ LOGV("PES_header_data_length = %u", PES_header_data_length);
+
+ unsigned optional_bytes_remaining = PES_header_data_length;
+
+ uint64_t PTS = 0, DTS = 0;
+
+ if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+ CHECK_GE(optional_bytes_remaining, 5u);
+
+ CHECK_EQ(br.getBits(4), PTS_DTS_flags);
+
+ PTS = ((uint64_t)br.getBits(3)) << 30;
+ CHECK_EQ(br.getBits(1), 1u);
+ PTS |= ((uint64_t)br.getBits(15)) << 15;
+ CHECK_EQ(br.getBits(1), 1u);
+ PTS |= br.getBits(15);
+ CHECK_EQ(br.getBits(1), 1u);
+
+ LOGV("PTS = %llu", PTS);
+ // LOGI("PTS = %.2f secs", PTS / 90000.0f);
+
+ optional_bytes_remaining -= 5;
+
+ if (PTS_DTS_flags == 3) {
+ CHECK_GE(optional_bytes_remaining, 5u);
+
+ CHECK_EQ(br.getBits(4), 1u);
+
+ DTS = ((uint64_t)br.getBits(3)) << 30;
+ CHECK_EQ(br.getBits(1), 1u);
+ DTS |= ((uint64_t)br.getBits(15)) << 15;
+ CHECK_EQ(br.getBits(1), 1u);
+ DTS |= br.getBits(15);
+ CHECK_EQ(br.getBits(1), 1u);
+
+ LOGV("DTS = %llu", DTS);
+
+ optional_bytes_remaining -= 5;
+ }
+ }
+
+ if (ESCR_flag) {
+ CHECK_GE(optional_bytes_remaining, 6u);
+
+ br.getBits(2);
+
+ uint64_t ESCR = ((uint64_t)br.getBits(3)) << 30;
+ CHECK_EQ(br.getBits(1), 1u);
+ ESCR |= ((uint64_t)br.getBits(15)) << 15;
+ CHECK_EQ(br.getBits(1), 1u);
+ ESCR |= br.getBits(15);
+ CHECK_EQ(br.getBits(1), 1u);
+
+ LOGV("ESCR = %llu", ESCR);
+ /* unsigned ESCR_extension = */br.getBits(9);
+
+ CHECK_EQ(br.getBits(1), 1u);
+
+ optional_bytes_remaining -= 6;
+ }
+
+ if (ES_rate_flag) {
+ CHECK_GE(optional_bytes_remaining, 3u);
+
+ CHECK_EQ(br.getBits(1), 1u);
+ /* unsigned ES_rate = */br.getBits(22);
+ CHECK_EQ(br.getBits(1), 1u);
+
+ optional_bytes_remaining -= 3;
+ }
+
+ br.skipBits(optional_bytes_remaining * 8);
+
+ // ES data follows.
+
+ CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+
+ unsigned dataLength =
+ PES_packet_length - 3 - PES_header_data_length;
+
+ if (br.numBitsLeft() < dataLength * 8) {
+ LOGE("PES packet does not carry enough data to contain "
+ "payload. (numBitsLeft = %d, required = %d)",
+ br.numBitsLeft(), dataLength * 8);
+
+ return ERROR_MALFORMED;
+ }
+
+ CHECK_GE(br.numBitsLeft(), dataLength * 8);
+
+ ssize_t index = mTracks.indexOfKey(stream_id);
+ if (index < 0 && mScanning) {
+ unsigned streamType;
+
+ ssize_t streamTypeIndex;
+ if (mProgramStreamMapValid
+ && (streamTypeIndex =
+ mStreamTypeByESID.indexOfKey(stream_id)) >= 0) {
+ streamType = mStreamTypeByESID.valueAt(streamTypeIndex);
+ } else if ((stream_id & ~0x1f) == 0xc0) {
+ // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7
+ // or ISO/IEC 14496-3 audio
+ streamType = ATSParser::STREAMTYPE_MPEG2_AUDIO;
+ } else if ((stream_id & ~0x0f) == 0xe0) {
+ // ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC 14496-2 video
+ streamType = ATSParser::STREAMTYPE_MPEG2_VIDEO;
+ } else {
+ streamType = ATSParser::STREAMTYPE_RESERVED;
+ }
+
+ index = mTracks.add(
+ stream_id, new Track(this, stream_id, streamType));
+ }
+
+ status_t err = OK;
+
+ if (index >= 0) {
+ err =
+ mTracks.editValueAt(index)->appendPESData(
+ PTS_DTS_flags, PTS, DTS, br.data(), dataLength);
+ }
+
+ br.skipBits(dataLength * 8);
+
+ if (err != OK) {
+ return err;
+ }
+ } else if (stream_id == 0xbe) { // padding_stream
+ CHECK_NE(PES_packet_length, 0u);
+ br.skipBits(PES_packet_length * 8);
+ } else {
+ CHECK_NE(PES_packet_length, 0u);
+ br.skipBits(PES_packet_length * 8);
+ }
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2PSExtractor::Track::Track(
+ MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type)
+ : mExtractor(extractor),
+ mStreamID(stream_id),
+ mStreamType(stream_type),
+ mQueue(NULL) {
+ bool supported = true;
+ ElementaryStreamQueue::Mode mode;
+
+ switch (mStreamType) {
+ case ATSParser::STREAMTYPE_H264:
+ mode = ElementaryStreamQueue::H264;
+ break;
+ case ATSParser::STREAMTYPE_MPEG2_AUDIO_ADTS:
+ mode = ElementaryStreamQueue::AAC;
+ break;
+ case ATSParser::STREAMTYPE_MPEG1_AUDIO:
+ case ATSParser::STREAMTYPE_MPEG2_AUDIO:
+ mode = ElementaryStreamQueue::MPEG_AUDIO;
+ break;
+
+ case ATSParser::STREAMTYPE_MPEG1_VIDEO:
+ case ATSParser::STREAMTYPE_MPEG2_VIDEO:
+ mode = ElementaryStreamQueue::MPEG_VIDEO;
+ break;
+
+ case ATSParser::STREAMTYPE_MPEG4_VIDEO:
+ mode = ElementaryStreamQueue::MPEG4_VIDEO;
+ break;
+
+ default:
+ supported = false;
+ break;
+ }
+
+ if (supported) {
+ mQueue = new ElementaryStreamQueue(mode);
+ } else {
+ LOGI("unsupported stream ID 0x%02x", stream_id);
+ }
+}
+
+MPEG2PSExtractor::Track::~Track() {
+ delete mQueue;
+ mQueue = NULL;
+}
+
+status_t MPEG2PSExtractor::Track::start(MetaData *params) {
+ if (mSource == NULL) {
+ return NO_INIT;
+ }
+
+ return mSource->start(params);
+}
+
+status_t MPEG2PSExtractor::Track::stop() {
+ if (mSource == NULL) {
+ return NO_INIT;
+ }
+
+ return mSource->stop();
+}
+
+sp<MetaData> MPEG2PSExtractor::Track::getFormat() {
+ if (mSource == NULL) {
+ return NULL;
+ }
+
+ return mSource->getFormat();
+}
+
+status_t MPEG2PSExtractor::Track::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ if (mSource == NULL) {
+ return NO_INIT;
+ }
+
+ status_t finalResult;
+ while (!mSource->hasBufferAvailable(&finalResult)) {
+ if (finalResult != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ status_t err = mExtractor->feedMore();
+
+ if (err != OK) {
+ mSource->signalEOS(err);
+ }
+ }
+
+ return mSource->read(buffer, options);
+}
+
+status_t MPEG2PSExtractor::Track::appendPESData(
+ unsigned PTS_DTS_flags,
+ uint64_t PTS, uint64_t DTS,
+ const uint8_t *data, size_t size) {
+ if (mQueue == NULL) {
+ return OK;
+ }
+
+ int64_t timeUs;
+ if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+ timeUs = (PTS * 100) / 9;
+ } else {
+ timeUs = 0;
+ }
+
+ status_t err = mQueue->appendData(data, size, timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+
+ sp<ABuffer> accessUnit;
+ while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
+ if (mSource == NULL) {
+ sp<MetaData> meta = mQueue->getFormat();
+
+ if (meta != NULL) {
+ LOGV("Stream ID 0x%02x now has data.", mStreamID);
+
+ mSource = new AnotherPacketSource(meta);
+ mSource->queueAccessUnit(accessUnit);
+ }
+ } else if (mQueue->getFormat() != NULL) {
+ mSource->queueAccessUnit(accessUnit);
+ }
+ }
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2PSExtractor::WrappedTrack::WrappedTrack(
+ const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track)
+ : mExtractor(extractor),
+ mTrack(track) {
+}
+
+MPEG2PSExtractor::WrappedTrack::~WrappedTrack() {
+}
+
+status_t MPEG2PSExtractor::WrappedTrack::start(MetaData *params) {
+ return mTrack->start(params);
+}
+
+status_t MPEG2PSExtractor::WrappedTrack::stop() {
+ return mTrack->stop();
+}
+
+sp<MetaData> MPEG2PSExtractor::WrappedTrack::getFormat() {
+ return mTrack->getFormat();
+}
+
+status_t MPEG2PSExtractor::WrappedTrack::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ return mTrack->read(buffer, options);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffMPEG2PS(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+ uint8_t header[5];
+ if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return false;
+ }
+
+ if (memcmp("\x00\x00\x01\xba", header, 4) || (header[4] >> 6) != 1) {
+ return false;
+ }
+
+ *confidence = 0.25f; // Slightly larger than .mp3 extractor's confidence
+
+ mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 4ecb92f..3f4cdb5 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -34,8 +34,8 @@
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/base64.h>
#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <utils/Vector.h>
@@ -402,43 +402,15 @@ static sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
return csd;
}
-static bool GetClockRate(const AString &desc, uint32_t *clockRate) {
- ssize_t slashPos = desc.find("/");
- if (slashPos < 0) {
- return false;
- }
-
- const char *s = desc.c_str() + slashPos + 1;
-
- char *end;
- unsigned long x = strtoul(s, &end, 10);
-
- if (end == s || (*end != '\0' && *end != '/')) {
- return false;
- }
-
- *clockRate = x;
-
- return true;
-}
-
APacketSource::APacketSource(
const sp<ASessionDescription> &sessionDesc, size_t index)
: mInitCheck(NO_INIT),
- mFormat(new MetaData),
- mEOSResult(OK),
- mIsAVC(false),
- mScanForIDR(true),
- mRTPTimeBase(0),
- mNormalPlayTimeBaseUs(0),
- mLastNormalPlayTimeUs(0) {
+ mFormat(new MetaData) {
unsigned long PT;
AString desc;
AString params;
sessionDesc->getFormatType(index, &PT, &desc, &params);
- CHECK(GetClockRate(desc, &mClockRate));
-
int64_t durationUs;
if (sessionDesc->getDurationUs(&durationUs)) {
mFormat->setInt64(kKeyDuration, durationUs);
@@ -448,8 +420,6 @@ APacketSource::APacketSource(
mInitCheck = OK;
if (!strncmp(desc.c_str(), "H264/", 5)) {
- mIsAVC = true;
-
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t width, height;
@@ -602,137 +572,8 @@ status_t APacketSource::initCheck() const {
return mInitCheck;
}
-status_t APacketSource::start(MetaData *params) {
- return OK;
-}
-
-status_t APacketSource::stop() {
- return OK;
-}
-
sp<MetaData> APacketSource::getFormat() {
return mFormat;
}
-status_t APacketSource::read(
- MediaBuffer **out, const ReadOptions *) {
- *out = NULL;
-
- Mutex::Autolock autoLock(mLock);
- while (mEOSResult == OK && mBuffers.empty()) {
- mCondition.wait(mLock);
- }
-
- if (!mBuffers.empty()) {
- const sp<ABuffer> buffer = *mBuffers.begin();
-
- updateNormalPlayTime_l(buffer);
-
- int64_t timeUs;
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
- mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
-
- *out = mediaBuffer;
-
- mBuffers.erase(mBuffers.begin());
- return OK;
- }
-
- return mEOSResult;
-}
-
-void APacketSource::updateNormalPlayTime_l(const sp<ABuffer> &buffer) {
- uint32_t rtpTime;
- CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
-
- mLastNormalPlayTimeUs =
- (((double)rtpTime - (double)mRTPTimeBase) / mClockRate)
- * 1000000ll
- + mNormalPlayTimeBaseUs;
-}
-
-void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
- int32_t damaged;
- if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
- LOGV("discarding damaged AU");
- return;
- }
-
- if (mScanForIDR && mIsAVC) {
- // This pretty piece of code ensures that the first access unit
- // fed to the decoder after stream-start or seek is guaranteed to
- // be an IDR frame. This is to workaround limitations of a certain
- // hardware h.264 decoder that requires this to be the case.
-
- if (!IsIDR(buffer)) {
- LOGV("skipping AU while scanning for next IDR frame.");
- return;
- }
-
- mScanForIDR = false;
- }
-
- Mutex::Autolock autoLock(mLock);
- mBuffers.push_back(buffer);
- mCondition.signal();
-}
-
-void APacketSource::signalEOS(status_t result) {
- CHECK(result != OK);
-
- Mutex::Autolock autoLock(mLock);
- mEOSResult = result;
- mCondition.signal();
-}
-
-void APacketSource::flushQueue() {
- Mutex::Autolock autoLock(mLock);
- mBuffers.clear();
-
- mScanForIDR = true;
-}
-
-int64_t APacketSource::getNormalPlayTimeUs() {
- Mutex::Autolock autoLock(mLock);
- return mLastNormalPlayTimeUs;
-}
-
-void APacketSource::setNormalPlayTimeMapping(
- uint32_t rtpTime, int64_t normalPlayTimeUs) {
- Mutex::Autolock autoLock(mLock);
-
- mRTPTimeBase = rtpTime;
- mNormalPlayTimeBaseUs = normalPlayTimeUs;
-}
-
-int64_t APacketSource::getQueueDurationUs(bool *eos) {
- Mutex::Autolock autoLock(mLock);
-
- *eos = (mEOSResult != OK);
-
- if (mBuffers.size() < 2) {
- return 0;
- }
-
- const sp<ABuffer> first = *mBuffers.begin();
- const sp<ABuffer> last = *--mBuffers.end();
-
- int64_t firstTimeUs;
- CHECK(first->meta()->findInt64("timeUs", &firstTimeUs));
-
- int64_t lastTimeUs;
- CHECK(last->meta()->findInt64("timeUs", &lastTimeUs));
-
- if (lastTimeUs < firstTimeUs) {
- LOGE("Huh? Time moving backwards? %lld > %lld",
- firstTimeUs, lastTimeUs);
-
- return 0;
- }
-
- return lastTimeUs - firstTimeUs;
-}
-
} // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 7a77fc6..530e537 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -19,63 +19,27 @@
#define A_PACKET_SOURCE_H_
#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/threads.h>
-#include <utils/List.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/RefBase.h>
namespace android {
-struct ABuffer;
struct ASessionDescription;
-struct APacketSource : public MediaSource {
+struct APacketSource : public RefBase {
APacketSource(const sp<ASessionDescription> &sessionDesc, size_t index);
status_t initCheck() const;
- virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
virtual sp<MetaData> getFormat();
- virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
-
- void queueAccessUnit(const sp<ABuffer> &buffer);
- void signalEOS(status_t result);
-
- void flushQueue();
-
- int64_t getNormalPlayTimeUs();
-
- void setNormalPlayTimeMapping(
- uint32_t rtpTime, int64_t normalPlayTimeUs);
-
- int64_t getQueueDurationUs(bool *eos);
-
protected:
virtual ~APacketSource();
private:
status_t mInitCheck;
- Mutex mLock;
- Condition mCondition;
-
sp<MetaData> mFormat;
- List<sp<ABuffer> > mBuffers;
- status_t mEOSResult;
-
- bool mIsAVC;
- bool mScanForIDR;
-
- uint32_t mClockRate;
-
- uint32_t mRTPTimeBase;
- int64_t mNormalPlayTimeBaseUs;
-
- int64_t mLastNormalPlayTimeUs;
-
- void updateNormalPlayTime_l(const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
};
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 47de4e0..cd374e2 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -220,7 +220,7 @@ void ARTPConnection::onRemoveStream(const sp<AMessage> &msg) {
}
if (it == mStreams.end()) {
- TRESPASS();
+ return;
}
mStreams.erase(it);
@@ -274,41 +274,52 @@ void ARTPConnection::onPollStreams() {
}
int res = select(maxSocket + 1, &rs, NULL, NULL, &tv);
- CHECK_GE(res, 0);
if (res > 0) {
- for (List<StreamInfo>::iterator it = mStreams.begin();
- it != mStreams.end(); ++it) {
+ List<StreamInfo>::iterator it = mStreams.begin();
+ while (it != mStreams.end()) {
if ((*it).mIsInjected) {
+ ++it;
continue;
}
+ status_t err = OK;
if (FD_ISSET(it->mRTPSocket, &rs)) {
- receive(&*it, true);
+ err = receive(&*it, true);
}
- if (FD_ISSET(it->mRTCPSocket, &rs)) {
- receive(&*it, false);
+ if (err == OK && FD_ISSET(it->mRTCPSocket, &rs)) {
+ err = receive(&*it, false);
}
+
+ if (err == -ECONNRESET) {
+ // socket failure, this stream is dead, Jim.
+
+ LOGW("failed to receive RTP/RTCP datagram.");
+ it = mStreams.erase(it);
+ continue;
+ }
+
+ ++it;
}
}
- postPollEvent();
-
int64_t nowUs = ALooper::GetNowUs();
if (mLastReceiverReportTimeUs <= 0
|| mLastReceiverReportTimeUs + 5000000ll <= nowUs) {
sp<ABuffer> buffer = new ABuffer(kMaxUDPSize);
- for (List<StreamInfo>::iterator it = mStreams.begin();
- it != mStreams.end(); ++it) {
+ List<StreamInfo>::iterator it = mStreams.begin();
+ while (it != mStreams.end()) {
StreamInfo *s = &*it;
if (s->mIsInjected) {
+ ++it;
continue;
}
if (s->mNumRTCPPacketsReceived == 0) {
// We have never received any RTCP packets on this stream,
// we don't even know where to send a report.
+ ++it;
continue;
}
@@ -327,16 +338,34 @@ void ARTPConnection::onPollStreams() {
if (buffer->size() > 0) {
LOGV("Sending RR...");
- ssize_t n = sendto(
+ ssize_t n;
+ do {
+ n = sendto(
s->mRTCPSocket, buffer->data(), buffer->size(), 0,
(const struct sockaddr *)&s->mRemoteRTCPAddr,
sizeof(s->mRemoteRTCPAddr));
+ } while (n < 0 && errno == EINTR);
+
+ if (n <= 0) {
+ LOGW("failed to send RTCP receiver report (%s).",
+ n == 0 ? "connection gone" : strerror(errno));
+
+ it = mStreams.erase(it);
+ continue;
+ }
+
CHECK_EQ(n, (ssize_t)buffer->size());
mLastReceiverReportTimeUs = nowUs;
}
+
+ ++it;
}
}
+
+ if (!mStreams.empty()) {
+ postPollEvent();
+ }
}
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
@@ -350,16 +379,19 @@ status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
(!receiveRTP && s->mNumRTCPPacketsReceived == 0)
? sizeof(s->mRemoteRTCPAddr) : 0;
- ssize_t nbytes = recvfrom(
+ ssize_t nbytes;
+ do {
+ nbytes = recvfrom(
receiveRTP ? s->mRTPSocket : s->mRTCPSocket,
buffer->data(),
buffer->capacity(),
0,
remoteAddrLen > 0 ? (struct sockaddr *)&s->mRemoteRTCPAddr : NULL,
remoteAddrLen > 0 ? &remoteAddrLen : NULL);
+ } while (nbytes < 0 && errno == EINTR);
- if (nbytes < 0) {
- return -1;
+ if (nbytes <= 0) {
+ return -ECONNRESET;
}
buffer->setRange(0, nbytes);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index bd0e491..4f0363b 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -187,10 +187,13 @@ bool ARTSPConnection::ParseURL(
return true;
}
-static void MakeSocketBlocking(int s, bool blocking) {
+static status_t MakeSocketBlocking(int s, bool blocking) {
// Make socket non-blocking.
int flags = fcntl(s, F_GETFL, 0);
- CHECK_NE(flags, -1);
+
+ if (flags == -1) {
+ return UNKNOWN_ERROR;
+ }
if (blocking) {
flags &= ~O_NONBLOCK;
@@ -198,7 +201,9 @@ static void MakeSocketBlocking(int s, bool blocking) {
flags |= O_NONBLOCK;
}
- CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+ flags = fcntl(s, F_SETFL, flags);
+
+ return flags == -1 ? UNKNOWN_ERROR : OK;
}
void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
@@ -302,27 +307,32 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
reply->post();
}
+void ARTSPConnection::performDisconnect() {
+ if (mUIDValid) {
+ HTTPBase::UnRegisterSocketUserTag(mSocket);
+ }
+ close(mSocket);
+ mSocket = -1;
+
+ flushPendingRequests();
+
+ mUser.clear();
+ mPass.clear();
+ mAuthType = NONE;
+ mNonce.clear();
+
+ mState = DISCONNECTED;
+}
+
void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
if (mState == CONNECTED || mState == CONNECTING) {
- if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- }
- close(mSocket);
- mSocket = -1;
-
- flushPendingRequests();
+ performDisconnect();
}
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
reply->setInt32("result", OK);
- mState = DISCONNECTED;
-
- mUser.clear();
- mPass.clear();
- mAuthType = NONE;
- mNonce.clear();
reply->post();
}
@@ -427,21 +437,25 @@ void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
send(mSocket, request.c_str() + numBytesSent,
request.size() - numBytesSent, 0);
- if (n == 0) {
- // Server closed the connection.
- LOGE("Server unexpectedly closed the connection.");
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
- reply->setInt32("result", ERROR_IO);
- reply->post();
- return;
- } else if (n < 0) {
- if (errno == EINTR) {
- continue;
+ if (n <= 0) {
+ performDisconnect();
+
+ if (n == 0) {
+ // Server closed the connection.
+ LOGE("Server unexpectedly closed the connection.");
+
+ reply->setInt32("result", ERROR_IO);
+ reply->post();
+ } else {
+ LOGE("Error sending rtsp request. (%s)", strerror(errno));
+ reply->setInt32("result", -errno);
+ reply->post();
}
- LOGE("Error sending rtsp request.");
- reply->setInt32("result", -errno);
- reply->post();
return;
}
@@ -512,17 +526,22 @@ status_t ARTSPConnection::receive(void *data, size_t size) {
size_t offset = 0;
while (offset < size) {
ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
- if (n == 0) {
- // Server closed the connection.
- LOGE("Server unexpectedly closed the connection.");
- return ERROR_IO;
- } else if (n < 0) {
- if (errno == EINTR) {
- continue;
- }
- LOGE("Error reading rtsp response.");
- return -errno;
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
+
+ if (n <= 0) {
+ performDisconnect();
+
+ if (n == 0) {
+ // Server closed the connection.
+ LOGE("Server unexpectedly closed the connection.");
+ return ERROR_IO;
+ } else {
+ LOGE("Error reading rtsp response. (%s)", strerror(errno));
+ return -errno;
+ }
}
offset += (size_t)n;
@@ -681,24 +700,8 @@ bool ARTSPConnection::receiveRTSPReponse() {
if (contentLength > 0) {
response->mContent = new ABuffer(contentLength);
- size_t numBytesRead = 0;
- while (numBytesRead < contentLength) {
- ssize_t n = recv(
- mSocket, response->mContent->data() + numBytesRead,
- contentLength - numBytesRead, 0);
-
- if (n == 0) {
- // Server closed the connection.
- TRESPASS();
- } else if (n < 0) {
- if (errno == EINTR) {
- continue;
- }
-
- TRESPASS();
- }
-
- numBytesRead += (size_t)n;
+ if (receive(response->mContent->data(), contentLength) != OK) {
+ return false;
}
}
@@ -765,17 +768,20 @@ bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
send(mSocket, response.c_str() + numBytesSent,
response.size() - numBytesSent, 0);
- if (n == 0) {
- // Server closed the connection.
- LOGE("Server unexpectedly closed the connection.");
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
- return false;
- } else if (n < 0) {
- if (errno == EINTR) {
- continue;
+ if (n <= 0) {
+ if (n == 0) {
+ // Server closed the connection.
+ LOGE("Server unexpectedly closed the connection.");
+ } else {
+ LOGE("Error sending rtsp response (%s).", strerror(errno));
}
- LOGE("Error sending rtsp response.");
+ performDisconnect();
+
return false;
}
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 5cb84fd..68f2d59 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -91,6 +91,8 @@ private:
AString mUserAgent;
+ void performDisconnect();
+
void onConnect(const sp<AMessage> &msg);
void onDisconnect(const sp<AMessage> &msg);
void onCompleteConnection(const sp<AMessage> &msg);
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
deleted file mode 100644
index 2ebae7e..0000000
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#include "ARTSPController.h"
-
-#include "MyHandler.h"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-ARTSPController::ARTSPController(const sp<ALooper> &looper)
- : mState(DISCONNECTED),
- mLooper(looper),
- mUIDValid(false),
- mSeekDoneCb(NULL),
- mSeekDoneCookie(NULL),
- mLastSeekCompletedTimeUs(-1) {
- mReflector = new AHandlerReflector<ARTSPController>(this);
- looper->registerHandler(mReflector);
-}
-
-ARTSPController::~ARTSPController() {
- CHECK_EQ((int)mState, (int)DISCONNECTED);
- mLooper->unregisterHandler(mReflector->id());
-}
-
-void ARTSPController::setUID(uid_t uid) {
- mUIDValid = true;
- mUID = uid;
-}
-
-status_t ARTSPController::connect(const char *url) {
- Mutex::Autolock autoLock(mLock);
-
- if (mState != DISCONNECTED) {
- return ERROR_ALREADY_CONNECTED;
- }
-
- sp<AMessage> msg = new AMessage(kWhatConnectDone, mReflector->id());
-
- mHandler = new MyHandler(url, mLooper, mUIDValid, mUID);
-
- mState = CONNECTING;
-
- mHandler->connect(msg);
-
- while (mState == CONNECTING) {
- mCondition.wait(mLock);
- }
-
- if (mState != CONNECTED) {
- mHandler.clear();
- }
-
- return mConnectionResult;
-}
-
-void ARTSPController::disconnect() {
- Mutex::Autolock autoLock(mLock);
-
- if (mState == CONNECTING) {
- mState = DISCONNECTED;
- mConnectionResult = ERROR_IO;
- mCondition.broadcast();
-
- mHandler.clear();
- return;
- } else if (mState != CONNECTED) {
- return;
- }
-
- sp<AMessage> msg = new AMessage(kWhatDisconnectDone, mReflector->id());
- mHandler->disconnect(msg);
-
- while (mState == CONNECTED) {
- mCondition.wait(mLock);
- }
-
- mHandler.clear();
-}
-
-void ARTSPController::seekAsync(
- int64_t timeUs,
- void (*seekDoneCb)(void *), void *cookie) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(seekDoneCb != NULL);
- CHECK(mSeekDoneCb == NULL);
-
- // Ignore seek requests that are too soon after the previous one has
- // completed, we don't want to swamp the server.
-
- bool tooEarly =
- mLastSeekCompletedTimeUs >= 0
- && ALooper::GetNowUs() < mLastSeekCompletedTimeUs + 500000ll;
-
- if (mState != CONNECTED || tooEarly) {
- (*seekDoneCb)(cookie);
- return;
- }
-
- mSeekDoneCb = seekDoneCb;
- mSeekDoneCookie = cookie;
-
- sp<AMessage> msg = new AMessage(kWhatSeekDone, mReflector->id());
- mHandler->seek(timeUs, msg);
-}
-
-size_t ARTSPController::countTracks() {
- if (mHandler == NULL) {
- return 0;
- }
-
- return mHandler->countTracks();
-}
-
-sp<MediaSource> ARTSPController::getTrack(size_t index) {
- CHECK(mHandler != NULL);
-
- return mHandler->getPacketSource(index);
-}
-
-sp<MetaData> ARTSPController::getTrackMetaData(
- size_t index, uint32_t flags) {
- CHECK(mHandler != NULL);
-
- return mHandler->getPacketSource(index)->getFormat();
-}
-
-void ARTSPController::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatConnectDone:
- {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(msg->findInt32("result", &mConnectionResult));
- mState = (mConnectionResult == OK) ? CONNECTED : DISCONNECTED;
-
- mCondition.signal();
- break;
- }
-
- case kWhatDisconnectDone:
- {
- Mutex::Autolock autoLock(mLock);
- mState = DISCONNECTED;
- mCondition.signal();
- break;
- }
-
- case kWhatSeekDone:
- {
- LOGI("seek done");
-
- mLastSeekCompletedTimeUs = ALooper::GetNowUs();
-
- void (*seekDoneCb)(void *) = mSeekDoneCb;
- mSeekDoneCb = NULL;
-
- (*seekDoneCb)(mSeekDoneCookie);
- break;
- }
-
- default:
- TRESPASS();
- break;
- }
-}
-
-int64_t ARTSPController::getNormalPlayTimeUs() {
- CHECK(mHandler != NULL);
- return mHandler->getNormalPlayTimeUs();
-}
-
-int64_t ARTSPController::getQueueDurationUs(bool *eos) {
- *eos = true;
-
- int64_t minQueuedDurationUs = 0;
- for (size_t i = 0; i < mHandler->countTracks(); ++i) {
- sp<APacketSource> source = mHandler->getPacketSource(i);
-
- bool newEOS;
- int64_t queuedDurationUs = source->getQueueDurationUs(&newEOS);
-
- if (!newEOS) {
- *eos = false;
- }
-
- if (i == 0 || queuedDurationUs < minQueuedDurationUs) {
- minQueuedDurationUs = queuedDurationUs;
- }
- }
-
- return minQueuedDurationUs;
-}
-
-} // namespace android
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 8530ff3..8230347 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -15,7 +15,6 @@ LOCAL_SRC_FILES:= \
ARTPSource.cpp \
ARTPWriter.cpp \
ARTSPConnection.cpp \
- ARTSPController.cpp \
ASessionDescription.cpp \
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 8128813..794c60b 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -44,12 +44,14 @@
// If no access units are received within 5 secs, assume that the rtp
// stream has ended and signal end of stream.
-static int64_t kAccessUnitTimeoutUs = 5000000ll;
+static int64_t kAccessUnitTimeoutUs = 10000000ll;
// If no access units arrive for the first 10 secs after starting the
// stream, assume none ever will and signal EOS or switch transports.
static int64_t kStartupTimeoutUs = 10000000ll;
+static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll;
+
namespace android {
static void MakeUserAgentString(AString *s) {
@@ -94,12 +96,24 @@ static bool GetAttribute(const char *s, const char *key, AString *value) {
}
struct MyHandler : public AHandler {
+ enum {
+ kWhatConnected = 'conn',
+ kWhatDisconnected = 'disc',
+ kWhatSeekDone = 'sdon',
+
+ kWhatAccessUnit = 'accU',
+ kWhatEOS = 'eos!',
+ kWhatSeekDiscontinuity = 'seeD',
+ kWhatNormalPlayTimeMapping = 'nptM',
+ };
+
MyHandler(
- const char *url, const sp<ALooper> &looper,
+ const char *url,
+ const sp<AMessage> &notify,
bool uidValid = false, uid_t uid = 0)
- : mUIDValid(uidValid),
+ : mNotify(notify),
+ mUIDValid(uidValid),
mUID(uid),
- mLooper(looper),
mNetLooper(new ALooper),
mConn(new ARTSPConnection(mUIDValid, mUID)),
mRTPConn(new ARTPConnection),
@@ -118,7 +132,9 @@ struct MyHandler : public AHandler {
mTryFakeRTCP(false),
mReceivedFirstRTCPPacket(false),
mReceivedFirstRTPPacket(false),
- mSeekable(false) {
+ mSeekable(false),
+ mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs),
+ mKeepAliveGeneration(0) {
mNetLooper->setName("rtsp net");
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -145,12 +161,9 @@ struct MyHandler : public AHandler {
mSessionHost = host;
}
- void connect(const sp<AMessage> &doneMsg) {
- mDoneMsg = doneMsg;
-
- mLooper->registerHandler(this);
- mLooper->registerHandler(mConn);
- (1 ? mNetLooper : mLooper)->registerHandler(mRTPConn);
+ void connect() {
+ looper()->registerHandler(mConn);
+ (1 ? mNetLooper : looper())->registerHandler(mRTPConn);
sp<AMessage> notify = new AMessage('biny', id());
mConn->observeBinaryData(notify);
@@ -159,33 +172,16 @@ struct MyHandler : public AHandler {
mConn->connect(mOriginalSessionURL.c_str(), reply);
}
- void disconnect(const sp<AMessage> &doneMsg) {
- mDoneMsg = doneMsg;
-
+ void disconnect() {
(new AMessage('abor', id()))->post();
}
- void seek(int64_t timeUs, const sp<AMessage> &doneMsg) {
+ void seek(int64_t timeUs) {
sp<AMessage> msg = new AMessage('seek', id());
msg->setInt64("time", timeUs);
- msg->setMessage("doneMsg", doneMsg);
msg->post();
}
- int64_t getNormalPlayTimeUs() {
- int64_t maxTimeUs = 0;
- for (size_t i = 0; i < mTracks.size(); ++i) {
- int64_t timeUs = mTracks.editItemAt(i).mPacketSource
- ->getNormalPlayTimeUs();
-
- if (i == 0 || timeUs > maxTimeUs) {
- maxTimeUs = timeUs;
- }
- }
-
- return maxTimeUs;
- }
-
static void addRR(const sp<ABuffer> &buf) {
uint8_t *ptr = buf->data() + buf->size();
ptr[0] = 0x80 | 0;
@@ -379,6 +375,8 @@ struct MyHandler : public AHandler {
case 'disc':
{
+ ++mKeepAliveGeneration;
+
int32_t reconnect;
if (msg->findInt32("reconnect", &reconnect) && reconnect) {
sp<AMessage> reply = new AMessage('conn', id());
@@ -465,8 +463,17 @@ struct MyHandler : public AHandler {
mBaseURL = tmp;
}
- CHECK_GT(mSessionDesc->countTracks(), 1u);
- setupTrack(1);
+ if (mSessionDesc->countTracks() < 2) {
+ // There's no actual tracks in this session.
+ // The first "track" is merely session meta
+ // data.
+
+ LOGW("Session doesn't contain any playable "
+ "tracks. Aborting.");
+ result = ERROR_UNSUPPORTED;
+ } else {
+ setupTrack(1);
+ }
}
}
}
@@ -510,6 +517,34 @@ struct MyHandler : public AHandler {
CHECK_GE(i, 0);
mSessionID = response->mHeaders.valueAt(i);
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ AString timeoutStr;
+ if (GetAttribute(
+ mSessionID.c_str(), "timeout", &timeoutStr)) {
+ char *end;
+ unsigned long timeoutSecs =
+ strtoul(timeoutStr.c_str(), &end, 10);
+
+ if (end == timeoutStr.c_str() || *end != '\0') {
+ LOGW("server specified malformed timeout '%s'",
+ timeoutStr.c_str());
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ } else if (timeoutSecs < 15) {
+ LOGW("server specified too short a timeout "
+ "(%lu secs), using default.",
+ timeoutSecs);
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ } else {
+ mKeepAliveTimeoutUs = timeoutSecs * 1000000ll;
+
+ LOGI("server specified timeout of %lu secs.",
+ timeoutSecs);
+ }
+ }
+
i = mSessionID.find(";");
if (i >= 0) {
// Remove options, i.e. ";timeout=90"
@@ -563,6 +598,9 @@ struct MyHandler : public AHandler {
if (index < mSessionDesc->countTracks()) {
setupTrack(index);
} else if (mSetupTracksSuccessful) {
+ ++mKeepAliveGeneration;
+ postKeepAlive();
+
AString request = "PLAY ";
request.append(mSessionURL);
request.append(" RTSP/1.0\r\n");
@@ -614,12 +652,59 @@ struct MyHandler : public AHandler {
break;
}
+ case 'aliv':
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mKeepAliveGeneration) {
+ // obsolete event.
+ break;
+ }
+
+ AString request;
+ request.append("OPTIONS ");
+ request.append(mSessionURL);
+ request.append(" RTSP/1.0\r\n");
+ request.append("Session: ");
+ request.append(mSessionID);
+ request.append("\r\n");
+ request.append("\r\n");
+
+ sp<AMessage> reply = new AMessage('opts', id());
+ reply->setInt32("generation", mKeepAliveGeneration);
+ mConn->sendRequest(request.c_str(), reply);
+ break;
+ }
+
+ case 'opts':
+ {
+ int32_t result;
+ CHECK(msg->findInt32("result", &result));
+
+ LOGI("OPTIONS completed with result %d (%s)",
+ result, strerror(-result));
+
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mKeepAliveGeneration) {
+ // obsolete event.
+ break;
+ }
+
+ postKeepAlive();
+ break;
+ }
+
case 'abor':
{
for (size_t i = 0; i < mTracks.size(); ++i) {
TrackInfo *info = &mTracks.editItemAt(i);
- info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+ if (!mFirstAccessUnit) {
+ postQueueEOS(i, ERROR_END_OF_STREAM);
+ }
if (!info->mUsingInterleavedTCP) {
mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
@@ -690,11 +775,10 @@ struct MyHandler : public AHandler {
case 'quit':
{
- if (mDoneMsg != NULL) {
- mDoneMsg->setInt32("result", UNKNOWN_ERROR);
- mDoneMsg->post();
- mDoneMsg = NULL;
- }
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatDisconnected);
+ msg->setInt32("result", UNKNOWN_ERROR);
+ msg->post();
break;
}
@@ -708,9 +792,13 @@ struct MyHandler : public AHandler {
}
if (mNumAccessUnitsReceived == 0) {
+#if 1
LOGI("stream ended? aborting.");
(new AMessage('abor', id()))->post();
break;
+#else
+ LOGI("haven't seen an AU in a looong time.");
+#endif
}
mNumAccessUnitsReceived = 0;
@@ -795,17 +883,12 @@ struct MyHandler : public AHandler {
case 'seek':
{
- sp<AMessage> doneMsg;
- CHECK(msg->findMessage("doneMsg", &doneMsg));
-
- if (mSeekPending) {
- doneMsg->post();
- break;
- }
-
if (!mSeekable) {
LOGW("This is a live stream, ignoring seek request.");
- doneMsg->post();
+
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatSeekDone);
+ msg->post();
break;
}
@@ -831,7 +914,6 @@ struct MyHandler : public AHandler {
sp<AMessage> reply = new AMessage('see1', id());
reply->setInt64("time", timeUs);
- reply->setMessage("doneMsg", doneMsg);
mConn->sendRequest(request.c_str(), reply);
break;
}
@@ -842,7 +924,8 @@ struct MyHandler : public AHandler {
for (size_t i = 0; i < mTracks.size(); ++i) {
TrackInfo *info = &mTracks.editItemAt(i);
- info->mPacketSource->flushQueue();
+ postQueueSeekDiscontinuity(i);
+
info->mRTPAnchor = 0;
info->mNTPAnchorUs = -1;
}
@@ -866,11 +949,7 @@ struct MyHandler : public AHandler {
request.append("\r\n");
- sp<AMessage> doneMsg;
- CHECK(msg->findMessage("doneMsg", &doneMsg));
-
sp<AMessage> reply = new AMessage('see2', id());
- reply->setMessage("doneMsg", doneMsg);
mConn->sendRequest(request.c_str(), reply);
break;
}
@@ -915,10 +994,9 @@ struct MyHandler : public AHandler {
mSeekPending = false;
- sp<AMessage> doneMsg;
- CHECK(msg->findMessage("doneMsg", &doneMsg));
-
- doneMsg->post();
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatSeekDone);
+ msg->post();
break;
}
@@ -969,6 +1047,12 @@ struct MyHandler : public AHandler {
}
}
+ void postKeepAlive() {
+ sp<AMessage> msg = new AMessage('aliv', id());
+ msg->setInt32("generation", mKeepAliveGeneration);
+ msg->post((mKeepAliveTimeoutUs * 9) / 10);
+ }
+
void postAccessUnitTimeoutCheck() {
if (mCheckPending) {
return;
@@ -1056,8 +1140,14 @@ struct MyHandler : public AHandler {
LOGV("track #%d: rtpTime=%u <=> npt=%.2f", n, rtpTime, npt1);
- info->mPacketSource->setNormalPlayTimeMapping(
- rtpTime, (int64_t)(npt1 * 1E6));
+ info->mNormalPlayTimeRTP = rtpTime;
+ info->mNormalPlayTimeUs = (int64_t)(npt1 * 1E6);
+
+ if (!mFirstAccessUnit) {
+ postNormalPlayTimeMapping(
+ trackIndex,
+ info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs);
+ }
++n;
}
@@ -1065,11 +1155,15 @@ struct MyHandler : public AHandler {
mSeekable = true;
}
- sp<APacketSource> getPacketSource(size_t index) {
+ sp<MetaData> getTrackFormat(size_t index, int32_t *timeScale) {
CHECK_GE(index, 0u);
CHECK_LT(index, mTracks.size());
- return mTracks.editItemAt(index).mPacketSource;
+ const TrackInfo &info = mTracks.itemAt(index);
+
+ *timeScale = info.mTimeScale;
+
+ return info.mPacketSource->getFormat();
}
size_t countTracks() const {
@@ -1089,6 +1183,9 @@ private:
int64_t mNTPAnchorUs;
int32_t mTimeScale;
+ uint32_t mNormalPlayTimeRTP;
+ int64_t mNormalPlayTimeUs;
+
sp<APacketSource> mPacketSource;
// Stores packets temporarily while no notion of time
@@ -1096,9 +1193,9 @@ private:
List<sp<ABuffer> > mPackets;
};
+ sp<AMessage> mNotify;
bool mUIDValid;
uid_t mUID;
- sp<ALooper> mLooper;
sp<ALooper> mNetLooper;
sp<ARTSPConnection> mConn;
sp<ARTPConnection> mRTPConn;
@@ -1124,11 +1221,11 @@ private:
bool mReceivedFirstRTCPPacket;
bool mReceivedFirstRTPPacket;
bool mSeekable;
+ int64_t mKeepAliveTimeoutUs;
+ int32_t mKeepAliveGeneration;
Vector<TrackInfo> mTracks;
- sp<AMessage> mDoneMsg;
-
void setupTrack(size_t index) {
sp<APacketSource> source =
new APacketSource(mSessionDesc, index);
@@ -1158,6 +1255,8 @@ private:
info->mNewSegment = true;
info->mRTPAnchor = 0;
info->mNTPAnchorUs = -1;
+ info->mNormalPlayTimeRTP = 0;
+ info->mNormalPlayTimeUs = 0ll;
unsigned long PT;
AString formatDesc;
@@ -1283,9 +1382,17 @@ private:
LOGV("onAccessUnitComplete track %d", trackIndex);
if (mFirstAccessUnit) {
- mDoneMsg->setInt32("result", OK);
- mDoneMsg->post();
- mDoneMsg = NULL;
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatConnected);
+ msg->post();
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ TrackInfo *info = &mTracks.editItemAt(i);
+
+ postNormalPlayTimeMapping(
+ i,
+ info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs);
+ }
mFirstAccessUnit = false;
}
@@ -1303,12 +1410,12 @@ private:
track->mPackets.erase(track->mPackets.begin());
if (addMediaTimestamp(trackIndex, track, accessUnit)) {
- track->mPacketSource->queueAccessUnit(accessUnit);
+ postQueueAccessUnit(trackIndex, accessUnit);
}
}
if (addMediaTimestamp(trackIndex, track, accessUnit)) {
- track->mPacketSource->queueAccessUnit(accessUnit);
+ postQueueAccessUnit(trackIndex, accessUnit);
}
}
@@ -1344,6 +1451,39 @@ private:
return true;
}
+ void postQueueAccessUnit(
+ size_t trackIndex, const sp<ABuffer> &accessUnit) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatAccessUnit);
+ msg->setSize("trackIndex", trackIndex);
+ msg->setObject("accessUnit", accessUnit);
+ msg->post();
+ }
+
+ void postQueueEOS(size_t trackIndex, status_t finalResult) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatEOS);
+ msg->setSize("trackIndex", trackIndex);
+ msg->setInt32("finalResult", finalResult);
+ msg->post();
+ }
+
+ void postQueueSeekDiscontinuity(size_t trackIndex) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatSeekDiscontinuity);
+ msg->setSize("trackIndex", trackIndex);
+ msg->post();
+ }
+
+ void postNormalPlayTimeMapping(
+ size_t trackIndex, uint32_t rtpTime, int64_t nptUs) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatNormalPlayTimeMapping);
+ msg->setSize("trackIndex", trackIndex);
+ msg->setInt32("rtpTime", rtpTime);
+ msg->setInt64("nptUs", nptUs);
+ msg->post();
+ }
DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
};
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 20225ba..cfea7e8 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -25,10 +25,12 @@
#include "MtpDataPacket.h"
#include "MtpStringBuffer.h"
+#define MTP_BUFFER_SIZE 16384
+
namespace android {
MtpDataPacket::MtpDataPacket()
- : MtpPacket(16384), // MAX_USBFS_BUFFER_SIZE
+ : MtpPacket(MTP_BUFFER_SIZE), // MAX_USBFS_BUFFER_SIZE
mOffset(MTP_CONTAINER_HEADER_SIZE)
{
}
@@ -345,7 +347,7 @@ void MtpDataPacket::putString(const uint16_t* string) {
#ifdef MTP_DEVICE
int MtpDataPacket::read(int fd) {
- int ret = ::read(fd, mBuffer, mBufferSize);
+ int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
if (ret < MTP_CONTAINER_HEADER_SIZE)
return -1;
mPacketSize = ret;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index dc6c011..1dcfb74 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -1053,11 +1053,14 @@ MtpResponseCode MtpServer::doDeleteObject() {
int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
if (result == MTP_RESPONSE_OK) {
LOGV("deleting %s", (const char *)filePath);
- deletePath((const char *)filePath);
- return mDatabase->deleteFile(handle);
- } else {
- return result;
+ result = mDatabase->deleteFile(handle);
+ // Don't delete the actual files unless the database deletion is allowed
+ if (result == MTP_RESPONSE_OK) {
+ deletePath((const char *)filePath);
+ }
}
+
+ return result;
}
MtpResponseCode MtpServer::doGetObjectPropDesc() {
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 9c45e6e..c9afa19 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,6 +7,8 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib
+
LOCAL_PACKAGE_NAME := mediaframeworktest
include $(BUILD_PACKAGE)
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 3fb2da0..92ac9eb 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -39,6 +39,7 @@ import com.android.mediaframeworktest.functional.videoeditor.VideoEditorExportTe
import com.android.mediaframeworktest.functional.videoeditor.VideoEditorPreviewTest;
import junit.framework.TestSuite;
+import android.os.Bundle;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
@@ -54,6 +55,7 @@ import android.test.InstrumentationTestSuite;
public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
+ public static int mMinCameraFps = 0;
@Override
public TestSuite getAllTests() {
@@ -87,4 +89,16 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
public ClassLoader getLoader() {
return MediaFrameworkTestRunner.class.getClassLoader();
}
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ String minCameraFps = (String) icicle.get("min_camera_fps");
+ System.out.print("min_camera_" + minCameraFps);
+
+ if (minCameraFps != null ) {
+ mMinCameraFps = Integer.parseInt(minCameraFps);
+ }
+ }
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index a203adc..62af3f3 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -47,6 +47,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
addMediaMetadataRetrieverStateUnitTests(suite);
addMediaRecorderStateUnitTests(suite);
addMediaPlayerStateUnitTests(suite);
+ addMediaScannerUnitTests(suite);
return suite;
}
@@ -89,4 +90,8 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaPlayerSetVolumeStateUnitTest.class);
suite.addTestSuite(MediaPlayerMetadataParserTest.class);
}
+
+ private void addMediaScannerUnitTests(TestSuite suite) {
+ suite.addTestSuite(MediaInserterTest.class);
+ }
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
index e3aa8cf..1fa5c0d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
@@ -44,13 +44,7 @@ import java.util.UUID;
*/
public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaBassBoostTest";
- private final static int MIN_ENERGY_RATIO_2 = 3;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private BassBoost mBassBoost = null;
private int mSession = -1;
@@ -184,85 +178,6 @@ public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFr
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual bass boost influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getBassBoost(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mBassBoost.setStrength((short)1000);
- mBassBoost.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with bassboost on.
- assertTrue(msg + ": bass boost has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseBassBoost();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
index ee91bbb..da9089d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
@@ -49,11 +49,6 @@ public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFr
private final static int MAX_BAND_LEVEL = 1500;
private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
private final static int MIN_NUMBER_OF_PRESETS = 4;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Equalizer mEqualizer = null;
private int mSession = -1;
@@ -252,80 +247,6 @@ public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFr
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test that the equalizer actually alters the sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getEqualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(500);
- // measure reference energy around 1kHz
- int refEnergy = probe.capture(1000);
- short band = mEqualizer.getBand(1000000);
- short[] levelRange = mEqualizer.getBandLevelRange();
- mEqualizer.setBandLevel(band, levelRange[0]);
- mEqualizer.setEnabled(true);
- Thread.sleep(500);
- // measure energy around 1kHz with band level at min
- int energy = probe.capture(1000);
- assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseEqualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
-
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
index b74e525..122545f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
@@ -44,13 +44,7 @@ import java.util.UUID;
*/
public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaVirtualizerTest";
- private final static int MIN_ENERGY_RATIO_2 = 2;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Virtualizer mVirtualizer = null;
private int mSession = -1;
@@ -185,89 +179,6 @@ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<Media
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual virtualizer influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getVirtualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mVirtualizer.setStrength((short)1000);
- mVirtualizer.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with virtualizer on.
- // NOTE: this is what is observed with current virtualizer implementation and the test
- // audio file but is not the primary effect of the virtualizer. A better way would
- // be to have a stereo PCM capture and check that a strongly paned input is centered
- // when output. However, we cannot capture stereo with the visualizer.
- assertTrue(msg + ": virtualizer has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseVirtualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
index b5c8c8c..0684946 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.android.mediaframeworktest.MediaProfileReader;
+import com.android.mediaframeworktest.MediaFrameworkTestRunner;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
@@ -115,9 +116,16 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
int audioChannels = highQuality? audioCap.mMaxChannels: audioCap.mMinChannels ;
int audioSamplingRate = highQuality? audioCap.mMaxSampleRate: audioCap.mMinSampleRate;
+ //Overide the fps if the min_camera_fps is set
+ if (MediaFrameworkTestRunner.mMinCameraFps != 0 &&
+ MediaFrameworkTestRunner.mMinCameraFps > videoFps){
+ videoFps = MediaFrameworkTestRunner.mMinCameraFps;
+ }
+
if (videoFps < MIN_VIDEO_FPS) {
videoFps = MIN_VIDEO_FPS;
}
+
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp");
try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/videoeditor/VideoEditorPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/videoeditor/VideoEditorPreviewTest.java
index e848f5f..3c08138 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/videoeditor/VideoEditorPreviewTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/videoeditor/VideoEditorPreviewTest.java
@@ -93,6 +93,7 @@ public class VideoEditorPreviewTest extends
private boolean previewStart;
private boolean previewStop;
+ private boolean previewError;
/* Minimum waiting time for Semaphore to wait for release */
private final long minWaitingTime = 3000;
@@ -141,7 +142,9 @@ public class VideoEditorPreviewTest extends
protected void setPreviewStop() {
previewStop = true;
}
-
+ protected void setPreviewError() {
+ previewError = true;
+ }
protected void validatePreviewProgress(int startMs, int endMs,
boolean loop, long duration) throws Exception {
@@ -149,6 +152,7 @@ public class VideoEditorPreviewTest extends
final Semaphore blockTillPreviewCompletes = new Semaphore(1);
previewStart = false;
previewStop = false;
+ previewError = false;
mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
int i = 0;
public void onProgress(Object item, int action, int progress) {
@@ -187,6 +191,10 @@ public class VideoEditorPreviewTest extends
setPreviewStop();
blockTillPreviewCompletes.release();
}
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ blockTillPreviewCompletes.release();
+ }
});
} catch (Exception e) {
blockTillPreviewCompletes.release();
@@ -196,6 +204,7 @@ public class VideoEditorPreviewTest extends
mVideoEditor.stopPreview();
assertTrue("Preview Failed to start", previewStart);
assertTrue("Preview Failed to stop", previewStop);
+ assertFalse("Preview Error occurred", previewError);
blockTillPreviewCompletes.release();
}
@@ -399,6 +408,7 @@ public class VideoEditorPreviewTest extends
"TransitionSpiral_QVGA.jpg";
previewStart = false;
previewStop = false;
+ previewError = false;
final Semaphore blockTillPreviewCompletes = new Semaphore(1);
@@ -490,6 +500,10 @@ public class VideoEditorPreviewTest extends
setPreviewStop();
blockTillPreviewCompletes.release();
}
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ blockTillPreviewCompletes.release();
+ }
});
} catch (Exception e) {
blockTillPreviewCompletes.release();
@@ -499,6 +513,7 @@ public class VideoEditorPreviewTest extends
blockTillPreviewCompletes.release();
assertTrue("Preview Failed to start", previewStart);
assertTrue("Preview Failed to stop", previewStop);
+ assertFalse("Preview Error occurred", previewError);
assertEquals("Removing Transition " + transition1And2CrossFade.getId(),
transition1And2CrossFade,
@@ -677,6 +692,7 @@ public class VideoEditorPreviewTest extends
final Semaphore blockTillPreviewCompletes = new Semaphore(1);
previewStart = false;
previewStop = false;
+ previewError = false;
final MediaVideoItem mediaVideoItem1 =
mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
@@ -727,6 +743,10 @@ public class VideoEditorPreviewTest extends
setPreviewStop();
blockTillPreviewCompletes.release();
}
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ blockTillPreviewCompletes.release();
+ }
});
} catch (Exception e) {
@@ -736,6 +756,8 @@ public class VideoEditorPreviewTest extends
mVideoEditor.stopPreview();
assertTrue("Preview Failed to start", previewStart);
assertTrue("Preview Failed to stop", previewStop);
+ assertFalse("Preview Error occurred", previewError);
+
blockTillPreviewCompletes.release();
}
@@ -784,6 +806,10 @@ public class VideoEditorPreviewTest extends
setPreviewStop();
blockTillPreviewCompletes.release();
}
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ blockTillPreviewCompletes.release();
+ }
});
} catch (IllegalArgumentException e) {
@@ -1023,7 +1049,10 @@ public class VideoEditorPreviewTest extends
}
public void onStop(VideoEditor videoEditor) {
setPreviewStop();
- }
+ }
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ }
});
}catch (IllegalArgumentException e) {
flagForException = true;
@@ -1089,7 +1118,10 @@ public class VideoEditorPreviewTest extends
}
public void onStop(VideoEditor videoEditor) {
setPreviewStop();
- }
+ }
+ public void onError(VideoEditor videoEditor, int error) {
+ setPreviewError();
+ }
});
}catch (IllegalArgumentException e) {
flagForException = true;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 0b887b9..4f6e7d2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -37,11 +37,13 @@ import android.util.Log;
import android.view.SurfaceHolder;
import java.util.List;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Writer;
import java.io.File;
import java.io.FileWriter;
@@ -68,6 +70,8 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds
private static final String MEDIA_MEMORY_OUTPUT =
"/sdcard/mediaMemOutput.txt";
+ private static final String MEDIA_PROCMEM_OUTPUT =
+ "/sdcard/mediaProcmemOutput.txt";
private static int mStartMemory = 0;
private static int mEndMemory = 0;
@@ -84,6 +88,9 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
private static int DECODER_LIMIT = 150;
private static int CAMERA_LIMIT = 80;
+ private Writer mProcMemWriter;
+ private Writer mMemWriter;
+
private static List<VideoEncoderCap> videoEncoders = MediaProfileReader.getVideoEncoders();
Camera mCamera;
@@ -97,12 +104,21 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
getActivity();
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
+
+ mProcMemWriter = new BufferedWriter(new FileWriter
+ (new File(MEDIA_PROCMEM_OUTPUT), true));
+ mProcMemWriter.write(this.getName() + "\n");
+ mMemWriter = new BufferedWriter(new FileWriter
+ (new File(MEDIA_MEMORY_OUTPUT), true));
+
}
protected void tearDown() throws Exception {
- super.tearDown();
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
+ mProcMemWriter.close();
+ mMemWriter.close();
+ super.tearDown();
}
private void initializeMessageLooper() {
@@ -247,24 +263,39 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
//Write the ps output to the file
- public void getMemoryWriteToLog(Writer output, int writeCount) {
+ public void getMemoryWriteToLog(int writeCount) {
String memusage = null;
try {
if (writeCount == 0) {
mStartMemory = getMediaserverVsize();
- output.write("Start memory : " + mStartMemory + "\n");
+ mMemWriter.write("Start memory : " + mStartMemory + "\n");
}
memusage = captureMediaserverInfo();
- output.write(memusage);
+ mMemWriter.write(memusage);
if (writeCount == NUM_STRESS_LOOP - 1) {
mEndMemory = getMediaserverVsize();
- output.write("End Memory :" + mEndMemory + "\n");
+ mMemWriter.write("End Memory :" + mEndMemory + "\n");
}
} catch (Exception e) {
e.toString();
}
}
+ public void writeProcmemInfo() throws Exception{
+ String cmd = "procmem " + getMediaserverPid();
+ Process p = Runtime.getRuntime().exec(cmd);
+
+ InputStream inStream = p.getInputStream();
+ InputStreamReader inReader = new InputStreamReader(inStream);
+ BufferedReader inBuffer = new BufferedReader(inReader);
+ String s;
+ while ((s = inBuffer.readLine()) != null) {
+ mProcMemWriter.write(s);
+ mProcMemWriter.write("\n");
+ }
+ mProcMemWriter.write("\n\n");
+ }
+
public String captureMediaserverInfo() {
String cm = "ps mediaserver";
String memoryUsage = null;
@@ -306,7 +337,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
return vsizevalue;
}
- public boolean validateMemoryResult(int startPid, int startMemory, Writer output, int limit)
+ public boolean validateMemoryResult(int startPid, int startMemory, int limit)
throws Exception {
// Wait for 10 seconds to make sure the memory settle.
Thread.sleep(10000);
@@ -315,11 +346,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
if (memDiff < 0) {
memDiff = 0;
}
- output.write("The total diff = " + memDiff);
- output.write("\n\n");
+ mMemWriter.write("The total diff = " + memDiff);
+ mMemWriter.write("\n\n");
// mediaserver crash
if (startPid != mEndPid) {
- output.write("mediaserver died. Test failed\n");
+ mMemWriter.write("mediaserver died. Test failed\n");
return false;
}
// memory leak greter than the tolerant
@@ -331,18 +362,16 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testH263VideoPlaybackMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File h263MemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(h263MemoryOut, true));
- output.write("H263 Video Playback Only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("H263 Video Playback Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H263 playback memory test", memoryResult);
}
@@ -350,18 +379,16 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testH264VideoPlaybackMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File h264MemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(h264MemoryOut, true));
- output.write("H264 Video Playback only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("H264 Video Playback only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H264 playback memory test", memoryResult);
}
@@ -369,21 +396,19 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File videoH263RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true));
- output.write("H263 video record only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("H263 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 record only memory test", memoryResult);
}
@@ -391,21 +416,19 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File videoMp4RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true));
- output.write("MPEG4 video record only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("MPEG4 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
assertTrue("MPEG4 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP,
MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("mpeg4 record only memory test", memoryResult);
}
@@ -414,21 +437,19 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testRecordVideoAudioMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File videoRecordAudioMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true));
+ mStartPid = getMediaserverPid();
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
- output.write("Audio and h263 video record\n");
+ mMemWriter.write("Audio and h263 video record\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 audio video record memory test", memoryResult);
}
@@ -436,18 +457,16 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testRecordAudioOnlyMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File audioOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(audioOnlyMemoryOut, true));
- output.write("Audio record only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("Audio record only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressAudioRecord(MediaNames.RECORDER_OUTPUT);
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("audio record only memory test", memoryResult);
}
@@ -455,18 +474,16 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
@LargeTest
public void testCameraPreviewMemoryUsage() throws Exception {
boolean memoryResult = false;
- mStartPid = getMediaserverPid();
- File cameraPreviewMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(cameraPreviewMemoryOut, true));
- output.write("Camera Preview Only\n");
+ mStartPid = getMediaserverPid();
+ mMemWriter.write("Camera Preview Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressCameraPreview();
- getMemoryWriteToLog(output, i);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
}
- output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, CAMERA_LIMIT);
- output.close();
+ mMemWriter.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
assertTrue("camera preview memory test", memoryResult);
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
new file mode 100644
index 0000000..ad3c342
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2011 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 com.android.mediaframeworktest.unit;
+
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.media.MediaInserter;
+import android.net.Uri;
+import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Files;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import dalvik.annotation.TestTargetClass;
+
+import org.easymock.EasyMock;
+import org.easymock.IArgumentMatcher;
+
+@TestTargetClass(MediaInserter.class)
+public class MediaInserterTest extends InstrumentationTestCase {
+
+ private MediaInserter mMediaInserter;
+ private static final int TEST_BUFFER_SIZE = 10;
+ private IContentProvider mMockProvider;
+
+ private int mFilesCounter;
+ private int mAudioCounter;
+ private int mVideoCounter;
+ private int mImagesCounter;
+
+ private static final String sVolumeName = "external";
+ private static final Uri sAudioUri = Audio.Media.getContentUri(sVolumeName);
+ private static final Uri sVideoUri = Video.Media.getContentUri(sVolumeName);
+ private static final Uri sImagesUri = Images.Media.getContentUri(sVolumeName);
+ private static final Uri sFilesUri = Files.getContentUri(sVolumeName);
+
+ private static class MediaUriMatcher implements IArgumentMatcher {
+ private Uri mUri;
+
+ private MediaUriMatcher(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ public boolean matches(Object argument) {
+ if (!(argument instanceof Uri)) {
+ return false;
+ }
+
+ Uri actualUri = (Uri) argument;
+ if (actualUri == mUri) return true;
+ return false;
+ }
+
+ @Override
+ public void appendTo(StringBuffer buffer) {
+ buffer.append("expected a TableUri '").append(mUri).append("'");
+ }
+
+ private static Uri expectMediaUri(Uri in) {
+ EasyMock.reportMatcher(new MediaUriMatcher(in));
+ return null;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMockProvider = EasyMock.createMock(IContentProvider.class);
+ mMediaInserter = new MediaInserter(mMockProvider, TEST_BUFFER_SIZE);
+ mFilesCounter = 0;
+ mAudioCounter = 0;
+ mVideoCounter = 0;
+ mImagesCounter = 0;
+ }
+
+ private ContentValues createFileContent() {
+ ContentValues values = new ContentValues();
+ values.put("_data", "/mnt/sdcard/file" + ++mFilesCounter);
+ return values;
+ }
+
+ private ContentValues createAudioContent() {
+ ContentValues values = new ContentValues();
+ values.put("_data", "/mnt/sdcard/audio" + ++mAudioCounter);
+ return values;
+ }
+
+ private ContentValues createVideoContent() {
+ ContentValues values = new ContentValues();
+ values.put("_data", "/mnt/sdcard/video" + ++mVideoCounter);
+ return values;
+ }
+
+ private ContentValues createImageContent() {
+ ContentValues values = new ContentValues();
+ values.put("_data", "/mnt/sdcard/image" + ++mImagesCounter);
+ return values;
+ }
+
+ private ContentValues createContent(Uri uri) {
+ if (uri == sFilesUri) return createFileContent();
+ else if (uri == sAudioUri) return createAudioContent();
+ else if (uri == sVideoUri) return createVideoContent();
+ else if (uri == sImagesUri) return createImageContent();
+ else throw new IllegalArgumentException("Unknown URL: " + uri.toString());
+ }
+
+ private void fillBuffer(Uri uri, int numberOfFiles) throws Exception {
+ ContentValues values;
+ for (int i = 0; i < numberOfFiles; ++i) {
+ values = createContent(uri);
+ mMediaInserter.insert(uri, values);
+ }
+ }
+
+ @SmallTest
+ public void testInsertContentsLessThanBufferSize() throws Exception {
+ EasyMock.replay(mMockProvider);
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testInsertContentsEqualToBufferSize() throws Exception {
+ EasyMock.expect(mMockProvider.bulkInsert(
+ (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(4);
+ EasyMock.replay(mMockProvider);
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE);
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testInsertContentsMoreThanBufferSize() throws Exception {
+ EasyMock.expect(mMockProvider.bulkInsert(
+ (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(4);
+ EasyMock.replay(mMockProvider);
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testFlushAllWithEmptyContents() throws Exception {
+ EasyMock.replay(mMockProvider);
+
+ mMediaInserter.flushAll();
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testFlushAllWithSomeContents() throws Exception {
+ EasyMock.expect(mMockProvider.bulkInsert(
+ (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(4);
+ EasyMock.replay(mMockProvider);
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
+ mMediaInserter.flushAll();
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testInsertContentsAfterFlushAll() throws Exception {
+ EasyMock.expect(mMockProvider.bulkInsert(
+ (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(8);
+ EasyMock.replay(mMockProvider);
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
+ mMediaInserter.flushAll();
+
+ fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
+ fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
+ fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
+ fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
+
+ EasyMock.verify(mMockProvider);
+ }
+
+ @SmallTest
+ public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
+ EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sFilesUri),
+ (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(1);
+ EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sAudioUri),
+ (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(2);
+ EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sVideoUri),
+ (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sImagesUri),
+ (ContentValues[]) EasyMock.anyObject())).andReturn(1);
+ EasyMock.expectLastCall().times(4);
+ EasyMock.replay(mMockProvider);
+
+ for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
+ fillBuffer(sFilesUri, 1);
+ fillBuffer(sAudioUri, 2);
+ fillBuffer(sVideoUri, 3);
+ fillBuffer(sImagesUri, 4);
+ }
+
+ EasyMock.verify(mMockProvider);
+ }
+}
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
index ed3051b..a6fdeea 100644
--- a/media/tests/players/invoke_mock_media_player.cpp
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -68,7 +68,6 @@ class Player: public MediaPlayerBase
}
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) {return OK;}
- virtual status_t setVideoSurface(const sp<Surface>& surface) {return OK;}
virtual status_t setVideoSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture) {return OK;}
virtual status_t prepare() {return OK;}