summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioManager.java76
-rw-r--r--media/java/android/media/AudioManagerInternal.java2
-rw-r--r--media/java/android/media/AudioRecord.java7
-rw-r--r--media/java/android/media/AudioSystem.java47
-rw-r--r--media/java/android/media/AudioTrack.java34
-rw-r--r--media/java/android/media/MediaCodec.java30
-rw-r--r--media/java/android/media/MediaCodecInfo.java80
-rw-r--r--media/java/android/media/MediaPlayer.java67
-rw-r--r--media/java/android/media/MediaRecorder.java24
-rw-r--r--media/java/android/media/MediaSync.java82
-rw-r--r--media/java/android/media/RemoteDisplay.java13
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java6
-rw-r--r--media/java/android/media/audiofx/Visualizer.java8
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java35
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicy.java24
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicyConfig.java8
-rw-r--r--media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl3
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.java7
-rw-r--r--media/java/android/media/midi/MidiReceiver.java4
-rw-r--r--media/java/android/media/tv/ITvInputSessionWrapper.java15
-rw-r--r--media/java/android/media/tv/TvContentRating.java98
-rw-r--r--media/java/android/media/tv/TvInputManager.java4
-rw-r--r--media/java/android/media/tv/TvInputService.java16
-rw-r--r--media/java/android/media/tv/TvView.java2
-rw-r--r--media/jni/android_media_MediaCodec.cpp218
-rw-r--r--media/jni/android_media_MediaCodec.h5
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp9
-rw-r--r--media/jni/android_media_MediaPlayer.cpp105
-rw-r--r--media/jni/android_media_MediaRecorder.cpp33
-rw-r--r--media/jni/android_media_MediaSync.cpp242
-rw-r--r--media/jni/android_media_MediaSync.h16
-rw-r--r--media/jni/android_media_SyncSettings.cpp12
-rw-r--r--media/jni/android_media_SyncSettings.h7
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp10
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp11
-rw-r--r--media/jni/soundpool/SoundPool.cpp9
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java22
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java108
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java62
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java25
-rw-r--r--media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4bin0 -> 2100454 bytes
42 files changed, 1233 insertions, 354 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index eeac69a..6eaf812 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -57,7 +57,8 @@ import java.util.Iterator;
*/
public class AudioManager {
- private final Context mApplicationContext;
+ private Context mOriginalContext;
+ private Context mApplicationContext;
private long mVolumeKeyUpTime;
private final boolean mUseVolumeKeySounds;
private final boolean mUseFixedVolume;
@@ -621,14 +622,33 @@ public class AudioManager {
* @hide
*/
public AudioManager(Context context) {
- mApplicationContext = context;
- mUseVolumeKeySounds = mApplicationContext.getResources().getBoolean(
+ setContext(context);
+ mUseVolumeKeySounds = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
- mUseFixedVolume = mApplicationContext.getResources().getBoolean(
+ mUseFixedVolume = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
sAudioPortEventHandler.init();
}
+ private Context getContext() {
+ if (mApplicationContext == null) {
+ setContext(mOriginalContext);
+ }
+ if (mApplicationContext != null) {
+ return mApplicationContext;
+ }
+ return mOriginalContext;
+ }
+
+ private void setContext(Context context) {
+ mApplicationContext = context.getApplicationContext();
+ if (mApplicationContext != null) {
+ mOriginalContext = null;
+ } else {
+ mOriginalContext = context;
+ }
+ }
+
private static IAudioService getService()
{
if (sService != null) {
@@ -663,7 +683,7 @@ public class AudioManager {
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
*/
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
helper.sendMediaButtonEvent(keyEvent, false);
}
@@ -709,7 +729,7 @@ public class AudioManager {
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
if (event.getRepeatCount() == 0) {
- MediaSessionLegacyHelper.getHelper(mApplicationContext)
+ MediaSessionLegacyHelper.getHelper(getContext())
.sendVolumeKeyEvent(event, false);
}
break;
@@ -737,7 +757,7 @@ public class AudioManager {
mVolumeKeyUpTime = SystemClock.uptimeMillis();
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
- MediaSessionLegacyHelper.getHelper(mApplicationContext)
+ MediaSessionLegacyHelper.getHelper(getContext())
.sendVolumeKeyEvent(event, false);
break;
}
@@ -783,7 +803,7 @@ public class AudioManager {
IAudioService service = getService();
try {
service.adjustStreamVolume(streamType, direction, flags,
- mApplicationContext.getOpPackageName());
+ getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustStreamVolume", e);
}
@@ -813,7 +833,7 @@ public class AudioManager {
* @see #isVolumeFixed()
*/
public void adjustVolume(int direction, int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
}
@@ -842,7 +862,7 @@ public class AudioManager {
* @see #isVolumeFixed()
*/
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
}
@@ -850,7 +870,7 @@ public class AudioManager {
public void setMasterMute(boolean mute, int flags) {
IAudioService service = getService();
try {
- service.setMasterMute(mute, flags, mApplicationContext.getOpPackageName());
+ service.setMasterMute(mute, flags, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMasterMute", e);
}
@@ -997,7 +1017,7 @@ public class AudioManager {
}
IAudioService service = getService();
try {
- service.setRingerModeExternal(ringerMode, mApplicationContext.getOpPackageName());
+ service.setRingerModeExternal(ringerMode, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setRingerMode", e);
}
@@ -1018,7 +1038,7 @@ public class AudioManager {
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
- service.setStreamVolume(streamType, index, flags, mApplicationContext.getOpPackageName());
+ service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
@@ -1331,7 +1351,7 @@ public class AudioManager {
* @see #startBluetoothSco()
*/
public boolean isBluetoothScoAvailableOffCall() {
- return mApplicationContext.getResources().getBoolean(
+ return getContext().getResources().getBoolean(
com.android.internal.R.bool.config_bluetooth_sco_off_call);
}
@@ -1384,7 +1404,7 @@ public class AudioManager {
IAudioService service = getService();
try {
service.startBluetoothSco(mICallBack,
- mApplicationContext.getApplicationInfo().targetSdkVersion);
+ getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in startBluetoothSco", e);
}
@@ -1532,7 +1552,7 @@ public class AudioManager {
public void setMicrophoneMute(boolean on){
IAudioService service = getService();
try {
- service.setMicrophoneMute(on, mApplicationContext.getOpPackageName());
+ service.setMicrophoneMute(on, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMicrophoneMute", e);
}
@@ -1963,7 +1983,7 @@ public class AudioManager {
* Settings has an in memory cache, so this is fast.
*/
private boolean querySoundEffectsEnabled(int user) {
- return Settings.System.getIntForUser(mApplicationContext.getContentResolver(),
+ return Settings.System.getIntForUser(getContext().getContentResolver(),
Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
}
@@ -2375,7 +2395,7 @@ public class AudioManager {
try {
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
- mApplicationContext.getOpPackageName() /* package name */, flags,
+ getContext().getOpPackageName() /* package name */, flags,
ap != null ? ap.cb() : null);
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);
@@ -2400,7 +2420,7 @@ public class AudioManager {
.setInternalLegacyStreamType(streamType).build(),
durationHint, mICallBack, null,
AudioSystem.IN_VOICE_COMM_FOCUS_ID,
- mApplicationContext.getOpPackageName(),
+ getContext().getOpPackageName(),
AUDIOFOCUS_FLAG_LOCK,
null /* policy token */);
} catch (RemoteException e) {
@@ -2469,7 +2489,7 @@ public class AudioManager {
if (eventReceiver == null) {
return;
}
- if (!eventReceiver.getPackageName().equals(mApplicationContext.getPackageName())) {
+ if (!eventReceiver.getPackageName().equals(getContext().getPackageName())) {
Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
"receiver and context package names don't match");
return;
@@ -2478,7 +2498,7 @@ public class AudioManager {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext,
+ PendingIntent pi = PendingIntent.getBroadcast(getContext(),
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
registerMediaButtonIntent(pi, eventReceiver);
}
@@ -2512,8 +2532,8 @@ public class AudioManager {
Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
return;
}
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
- helper.addMediaButtonListener(pi, eventReceiver, mApplicationContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+ helper.addMediaButtonListener(pi, eventReceiver, getContext());
}
/**
@@ -2531,7 +2551,7 @@ public class AudioManager {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext,
+ PendingIntent pi = PendingIntent.getBroadcast(getContext(),
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
unregisterMediaButtonIntent(pi);
}
@@ -2554,7 +2574,7 @@ public class AudioManager {
* @hide
*/
public void unregisterMediaButtonIntent(PendingIntent pi) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
helper.removeMediaButtonListener(pi);
}
@@ -2571,7 +2591,7 @@ public class AudioManager {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext));
+ rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
}
/**
@@ -2586,7 +2606,7 @@ public class AudioManager {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext));
+ rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
}
/**
@@ -3280,7 +3300,7 @@ public class AudioManager {
*/
public void setRingerModeInternal(int ringerMode) {
try {
- getService().setRingerModeInternal(ringerMode, mApplicationContext.getOpPackageName());
+ getService().setRingerModeInternal(ringerMode, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Error calling setRingerModeInternal", e);
}
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index abb4257..a721923 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -51,5 +51,7 @@ public abstract class AudioManagerInternal {
/** Called when internal ringer mode is evaluated, returns the new external ringer mode */
int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeExternal, VolumePolicy policy);
+
+ boolean canVolumeDownEnterSilent();
}
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 201a796..472da02 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -26,6 +26,7 @@ import java.util.Iterator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityThread;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -378,7 +379,7 @@ public class AudioRecord
int initResult = native_setup( new WeakReference<AudioRecord>(this),
mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
- session);
+ session, ActivityThread.currentOpPackageName());
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
return; // with mState == STATE_UNINITIALIZED
@@ -1321,7 +1322,6 @@ public class AudioRecord
return native_set_pos_update_period(periodInFrames);
}
-
//--------------------------------------------------------------------------
// Explicit Routing
//--------------------
@@ -1451,7 +1451,7 @@ public class AudioRecord
private native final int native_setup(Object audiorecord_this,
Object /*AudioAttributes*/ attributes,
int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int[] sessionId);
+ int buffSizeInBytes, int[] sessionId, String opPackageName);
// TODO remove: implementation calls directly into implementation of native_release()
private native final void native_finalize();
@@ -1499,5 +1499,4 @@ public class AudioRecord
private static void loge(String msg) {
Log.e(TAG, msg);
}
-
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 25e6594..3dae543 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -19,6 +19,7 @@ package android.media;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.audiopolicy.AudioMix;
+import android.util.Log;
import java.util.ArrayList;
@@ -32,6 +33,7 @@ import java.util.ArrayList;
*/
public class AudioSystem
{
+ private static final String TAG = "AudioSystem";
/* These values must be kept in sync with system/audio.h */
/*
* If these are modified, please also update Settings.System.VOLUME_SETTINGS
@@ -224,6 +226,48 @@ public class AudioSystem
}
}
+ /**
+ * Handles events for the audio policy manager about dynamic audio policies
+ * @see android.media.audiopolicy.AudioPolicy
+ */
+ public interface DynamicPolicyCallback
+ {
+ void onDynamicPolicyMixStateUpdate(String regId, int state);
+ }
+
+ //keep in sync with include/media/AudioPolicy.h
+ private final static int DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE = 0;
+
+ private static DynamicPolicyCallback sDynPolicyCallback;
+
+ public static void setDynamicPolicyCallback(DynamicPolicyCallback cb)
+ {
+ synchronized (AudioSystem.class) {
+ sDynPolicyCallback = cb;
+ native_register_dynamic_policy_callback();
+ }
+ }
+
+ private static void dynamicPolicyCallbackFromNative(int event, String regId, int val)
+ {
+ DynamicPolicyCallback cb = null;
+ synchronized (AudioSystem.class) {
+ if (sDynPolicyCallback != null) {
+ cb = sDynPolicyCallback;
+ }
+ }
+ if (cb != null) {
+ switch(event) {
+ case DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE:
+ cb.onDynamicPolicyMixStateUpdate(regId, val);
+ break;
+ default:
+ Log.e(TAG, "dynamicPolicyCallbackFromNative: unknown event " + event);
+ }
+ }
+ }
+
+
/*
* Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
* Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
@@ -580,6 +624,9 @@ public class AudioSystem
public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
public static native int setAudioPortConfig(AudioPortConfig config);
+ // declare this instance as having a dynamic policy callback handler
+ private static native final void native_register_dynamic_policy_callback();
+
// must be kept in sync with value in include/system/audio.h
public static final int AUDIO_HW_SYNC_INVALID = 0;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6f1fd24..cb05cc5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -920,13 +920,7 @@ public class AudioTrack
* @throws IllegalStateException if track is not initialized.
*/
public @NonNull PlaybackSettings getPlaybackSettings() {
- float[] floatArray = new float[2];
- int[] intArray = new int[2];
- native_get_playback_settings(floatArray, intArray);
- return new PlaybackSettings()
- .setSpeed(floatArray[0])
- .setPitch(floatArray[1])
- .setAudioFallbackMode(intArray[0]);
+ return native_get_playback_settings();
}
/**
@@ -1340,21 +1334,7 @@ public class AudioTrack
if (settings == null) {
throw new IllegalArgumentException("settings is null");
}
- float[] floatArray;
- int[] intArray;
- try {
- floatArray = new float[] {
- settings.getSpeed(),
- settings.getPitch(),
- };
- intArray = new int[] {
- settings.getAudioFallbackMode(),
- PlaybackSettings.AUDIO_STRETCH_MODE_DEFAULT,
- };
- } catch (IllegalStateException e) {
- throw new IllegalArgumentException(e);
- }
- native_set_playback_settings(floatArray, intArray);
+ native_set_playback_settings(settings);
}
@@ -2353,14 +2333,8 @@ public class AudioTrack
private native final int native_set_playback_rate(int sampleRateInHz);
private native final int native_get_playback_rate();
- // floatArray must be a non-null array of length >= 2
- // [0] is speed
- // [1] is pitch
- // intArray must be a non-null array of length >= 2
- // [0] is audio fallback mode
- // [1] is audio stretch mode
- private native final void native_set_playback_settings(float[] floatArray, int[] intArray);
- private native final void native_get_playback_settings(float[] floatArray, int[] intArray);
+ private native final void native_set_playback_settings(@NonNull PlaybackSettings settings);
+ private native final @NonNull PlaybackSettings native_get_playback_settings();
private native final int native_set_marker_pos(int marker);
private native final int native_get_marker_pos();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d47bedc..6f7b583 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -664,11 +664,11 @@ final public class MediaCodec {
if (!mHasSurface) {
throw new IllegalStateException("codec was not configured for an output surface");
}
-
- // TODO implement this
- throw new IllegalArgumentException("codec does not support this surface");
+ native_setSurface(surface);
}
+ private native void native_setSurface(@NonNull Surface surface);
+
/**
* Create a persistent input surface that can be used with codecs that normally have an input
* surface, such as video encoders. A persistent input can be reused by subsequent
@@ -681,12 +681,20 @@ final public class MediaCodec {
*/
@NonNull
public static Surface createPersistentInputSurface() {
- // TODO implement this
- return new PersistentSurface();
+ return native_createPersistentInputSurface();
}
static class PersistentSurface extends Surface {
- PersistentSurface() {}
+ @SuppressWarnings("unused")
+ PersistentSurface() {} // used by native
+
+ @Override
+ public void release() {
+ native_releasePersistentInputSurface(this);
+ super.release();
+ }
+
+ private long mPersistentObject;
};
/**
@@ -700,9 +708,17 @@ final public class MediaCodec {
* {@link #createPersistentInputSurface}.
*/
public void usePersistentInputSurface(@NonNull Surface surface) {
- throw new IllegalArgumentException("not implemented");
+ if (!(surface instanceof PersistentSurface)) {
+ throw new IllegalArgumentException("not a PersistentSurface");
+ }
+ native_usePersistentInputSurface(surface);
}
+ @NonNull
+ private static native final PersistentSurface native_createPersistentInputSurface();
+ private static native final void native_releasePersistentInputSurface(@NonNull Surface surface);
+ private native final void native_usePersistentInputSurface(@NonNull Surface surface);
+
private native final void native_setCallback(@Nullable Callback cb);
private native final void native_configure(
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 974c9af..89d419a 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1733,6 +1733,72 @@ public final class MediaCodecInfo {
maxBlocks, maxBlocksPerSecond,
16 /* blockWidth */, 16 /* blockHeight */,
1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2)) {
+ int maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ boolean supported = true;
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.MPEG2ProfileSimple:
+ switch (profileLevel.level) {
+ case CodecProfileLevel.MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
+ default:
+ Log.w(TAG, "Unrecognized profile/level "
+ + profileLevel.profile + "/"
+ + profileLevel.level + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ break;
+ case CodecProfileLevel.MPEG2ProfileMain:
+ switch (profileLevel.level) {
+ case CodecProfileLevel.MPEG2LevelLL:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
+ case CodecProfileLevel.MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
+ case CodecProfileLevel.MPEG2LevelH14:
+ FR = 60; W = 90; H = 68; MBPS = 367200; FS = 6120; BR = 60000; break;
+ case CodecProfileLevel.MPEG2LevelHL:
+ FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
+ default:
+ Log.w(TAG, "Unrecognized profile/level "
+ + profileLevel.profile + "/"
+ + profileLevel.level + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ break;
+ case CodecProfileLevel.MPEG2Profile422:
+ case CodecProfileLevel.MPEG2ProfileSNR:
+ case CodecProfileLevel.MPEG2ProfileSpatial:
+ case CodecProfileLevel.MPEG2ProfileHigh:
+ Log.i(TAG, "Unsupported profile "
+ + profileLevel.profile + " for " + mime);
+ errors |= ERROR_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
+ maxBlocks = Math.max(FS, maxBlocks);
+ maxBps = Math.max(BR * 1000, maxBps);
+ maxWidth = Math.max(W, maxWidth);
+ maxHeight = Math.max(H, maxHeight);
+ maxRate = Math.max(FR, maxRate);
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
int maxWidth = 11, maxHeight = 9, maxRate = 15;
maxBlocks = 99;
@@ -2343,6 +2409,20 @@ public final class MediaCodecInfo {
public static final int MPEG4Level4a = 0x40;
public static final int MPEG4Level5 = 0x80;
+ // from OMX_VIDEO_MPEG2PROFILETYPE
+ public static final int MPEG2ProfileSimple = 0x00;
+ public static final int MPEG2ProfileMain = 0x01;
+ public static final int MPEG2Profile422 = 0x02;
+ public static final int MPEG2ProfileSNR = 0x03;
+ public static final int MPEG2ProfileSpatial = 0x04;
+ public static final int MPEG2ProfileHigh = 0x05;
+
+ // from OMX_VIDEO_MPEG2LEVELTYPE
+ public static final int MPEG2LevelLL = 0x00;
+ public static final int MPEG2LevelML = 0x01;
+ public static final int MPEG2LevelH14 = 0x02;
+ public static final int MPEG2LevelHL = 0x03;
+
// from OMX_AUDIO_AACPROFILETYPE
public static final int AACObjectMain = 1;
public static final int AACObjectLC = 2;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index a33fa59..6ec10c7 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -42,6 +42,7 @@ import android.system.OsConstants;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
+import android.widget.VideoView;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaFormat;
@@ -1953,21 +1954,16 @@ public class MediaPlayer implements SubtitleController.Listener
TrackInfo(Parcel in) {
mTrackType = in.readInt();
- // TODO: parcel in the full MediaFormat
+ // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat
+ // even for audio/video tracks, meaning we only set the mime and language.
+ String mime = in.readString();
String language = in.readString();
+ mFormat = MediaFormat.createSubtitleFormat(mime, language);
- if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
- mFormat = MediaFormat.createSubtitleFormat(
- MEDIA_MIMETYPE_TEXT_SUBRIP, language);
- } else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- String mime = in.readString();
- mFormat = MediaFormat.createSubtitleFormat(mime, language);
+ if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
- } else {
- mFormat = new MediaFormat();
- mFormat.setString(MediaFormat.KEY_LANGUAGE, language);
}
}
@@ -2126,6 +2122,43 @@ public class MediaPlayer implements SubtitleController.Listener
mSubtitleController.setAnchor(anchor);
}
+ /**
+ * The private version of setSubtitleAnchor is used internally to set mSubtitleController if
+ * necessary when clients don't provide their own SubtitleControllers using the public version
+ * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
+ */
+ private synchronized void setSubtitleAnchor() {
+ if (mSubtitleController == null) {
+ final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
+ thread.start();
+ Handler handler = new Handler(thread.getLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ Context context = ActivityThread.currentApplication();
+ mSubtitleController = new SubtitleController(context, mTimeProvider, MediaPlayer.this);
+ mSubtitleController.setAnchor(new Anchor() {
+ @Override
+ public void setSubtitleWidget(RenderingWidget subtitleWidget) {
+ }
+
+ @Override
+ public Looper getSubtitleLooper() {
+ return Looper.getMainLooper();
+ }
+ });
+ thread.getLooper().quitSafely();
+ }
+ });
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG, "failed to join SetSubtitleAnchorThread");
+ }
+ }
+ }
+
private final Object mInbandSubtitleLock = new Object();
private SubtitleTrack[] mInbandSubtitleTracks;
private int mSelectedSubtitleTrackIndex = -1;
@@ -2386,24 +2419,14 @@ public class MediaPlayer implements SubtitleController.Listener
fFormat.setString(MediaFormat.KEY_MIME, mime);
fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1);
- Context context = ActivityThread.currentApplication();
// A MediaPlayer created by a VideoView should already have its mSubtitleController set.
if (mSubtitleController == null) {
- mSubtitleController = new SubtitleController(context, mTimeProvider, this);
- mSubtitleController.setAnchor(new Anchor() {
- @Override
- public void setSubtitleWidget(RenderingWidget subtitleWidget) {
- }
-
- @Override
- public Looper getSubtitleLooper() {
- return Looper.getMainLooper();
- }
- });
+ setSubtitleAnchor();
}
if (!mSubtitleController.hasRendererFor(fFormat)) {
// test and add not atomic
+ Context context = ActivityThread.currentApplication();
mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
}
final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 78fd9f0..206c171 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.hardware.Camera;
@@ -111,7 +112,8 @@ public class MediaRecorder
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
- native_setup(new WeakReference<MediaRecorder>(this), packageName);
+ native_setup(new WeakReference<MediaRecorder>(this), packageName,
+ ActivityThread.currentOpPackageName());
}
/**
@@ -141,22 +143,28 @@ public class MediaRecorder
/**
* Configures the recorder to use a persistent surface when using SURFACE video source.
- * <p> May only be called after {@link #prepare} in lieu of {@link #getSurface}.
- * Frames rendered to the Surface before {@link #start} will be discarded.</p>
+ * <p> May only be called before {@link #prepare}. If called, {@link #getSurface} should
+ * not be used and will throw IllegalStateException. Frames rendered to the Surface
+ * before {@link #start} will be discarded.</p>
* @param surface a persistent input surface created by
* {@link MediaCodec#createPersistentInputSurface}
- * @throws IllegalStateException if it is called before {@link #prepare}, after
- * {@link #stop}, or is called when VideoSource is not set to SURFACE.
+ * @throws IllegalStateException if it is called after {@link #prepare} and before
+ * {@link #stop}.
* @throws IllegalArgumentException if the surface was not created by
* {@link MediaCodec#createPersistentInputSurface}.
* @see MediaCodec#createPersistentInputSurface
* @see MediaRecorder.VideoSource
*/
- public void usePersistentSurface(Surface surface) {
- throw new IllegalArgumentException("not implemented");
+ public void usePersistentSurface(@NonNull Surface surface) {
+ if (!(surface instanceof MediaCodec.PersistentSurface)) {
+ throw new IllegalArgumentException("not a PersistentSurface");
+ }
+ native_usePersistentSurface(surface);
}
+ private native final void native_usePersistentSurface(@NonNull Surface surface);
+
/**
* Sets a Surface to show a preview of recorded media (video). Calls this
* before prepare() to make sure that the desirable preview display is
@@ -1080,7 +1088,7 @@ public class MediaRecorder
private static native final void native_init();
private native final void native_setup(Object mediarecorder_this,
- String clientName) throws IllegalStateException;
+ String clientName, String opPackageName) throws IllegalStateException;
private native final void native_finalize();
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index dc6760d..a5b0d39 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -199,6 +199,7 @@ final public class MediaSync {
private final Object mAudioLock = new Object();
private AudioTrack mAudioTrack = null;
private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>();
+ // this is only used for paused/running decisions, so it is not affected by clock drift
private float mPlaybackRate = 0.0f;
private long mNativeContext;
@@ -311,13 +312,13 @@ final public class MediaSync {
* @throws IllegalArgumentException if the surface has been released, is invalid,
* or can not be connected.
* @throws IllegalStateException if setting the surface is not supported, e.g.
- * not in the Initialized state, or another surface has already been configured.
+ * not in the Initialized state, or another surface has already been set.
*/
public void setSurface(@Nullable Surface surface) {
- native_configureSurface(surface);
+ native_setSurface(surface);
}
- private native final void native_configureSurface(@Nullable Surface surface);
+ private native final void native_setSurface(@Nullable Surface surface);
/**
* Sets the audio track for MediaSync.
@@ -327,21 +328,17 @@ final public class MediaSync {
* @param audioTrack Specify an AudioTrack through which to render the audio data.
* @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
* @throws IllegalStateException if setting the audio track is not supported, e.g.
- * not in the Initialized state, or another audio track has already been configured.
+ * not in the Initialized state, or another audio track has already been set.
*/
public void setAudioTrack(@Nullable AudioTrack audioTrack) {
- // AudioTrack has sanity check for configured sample rate.
- int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
-
- native_configureAudioTrack(audioTrack, nativeSampleRateInHz);
+ native_setAudioTrack(audioTrack);
mAudioTrack = audioTrack;
if (audioTrack != null && mAudioThread == null) {
createAudioThread();
}
}
- private native final void native_configureAudioTrack(
- @Nullable AudioTrack audioTrack, int nativeSampleRateInHz);
+ private native final void native_setAudioTrack(@Nullable AudioTrack audioTrack);
/**
* Requests a Surface to use as the input. This may only be called after
@@ -349,7 +346,7 @@ final public class MediaSync {
* <p>
* The application is responsible for calling release() on the Surface when
* done.
- * @throws IllegalStateException if not configured, or another input surface has
+ * @throws IllegalStateException if not set, or another input surface has
* already been created.
*/
@NonNull
@@ -459,36 +456,11 @@ final public class MediaSync {
* @throws IllegalArgumentException if the settings are not supported.
*/
public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
- float rate;
- try {
- rate = settings.getSpeed();
-
- // rate is specified
- if (mAudioTrack != null) {
- try {
- if (rate == 0.0) {
- mAudioTrack.pause();
- } else {
- mAudioTrack.setPlaybackSettings(settings);
- mAudioTrack.play();
- }
- } catch (IllegalStateException e) {
- throw e;
- }
- }
-
- synchronized(mAudioLock) {
- mPlaybackRate = rate;
- }
- if (mPlaybackRate != 0.0 && mAudioThread != null) {
- postRenderAudio(0);
- }
- native_setPlaybackRate(mPlaybackRate);
- } catch (IllegalStateException e) {
- // rate is not specified; still, propagate settings to audio track
- if (mAudioTrack != null) {
- mAudioTrack.setPlaybackSettings(settings);
- }
+ synchronized(mAudioLock) {
+ mPlaybackRate = native_setPlaybackSettings(settings);;
+ }
+ if (mPlaybackRate != 0.0 && mAudioThread != null) {
+ postRenderAudio(0);
}
}
@@ -501,18 +473,9 @@ final public class MediaSync {
* been initialized.
*/
@NonNull
- public PlaybackSettings getPlaybackSettings() {
- if (mAudioTrack != null) {
- return mAudioTrack.getPlaybackSettings();
- } else {
- PlaybackSettings settings = new PlaybackSettings();
- settings.allowDefaults();
- settings.setSpeed(mPlaybackRate);
- return settings;
- }
- }
+ public native PlaybackSettings getPlaybackSettings();
- private native final void native_setPlaybackRate(float rate);
+ private native float native_setPlaybackSettings(@NonNull PlaybackSettings settings);
/**
* Sets A/V sync mode.
@@ -523,7 +486,16 @@ final public class MediaSync {
* initialized.
* @throws IllegalArgumentException if settings are not supported.
*/
- public native void setSyncSettings(@NonNull SyncSettings settings);
+ public void setSyncSettings(@NonNull SyncSettings settings) {
+ synchronized(mAudioLock) {
+ mPlaybackRate = native_setSyncSettings(settings);;
+ }
+ if (mPlaybackRate != 0.0 && mAudioThread != null) {
+ postRenderAudio(0);
+ }
+ }
+
+ private native float native_setSyncSettings(@NonNull SyncSettings settings);
/**
* Gets the A/V sync mode.
@@ -598,7 +570,7 @@ final public class MediaSync {
* @param sizeInBytes number of bytes to queue.
* @param presentationTimeUs the presentation timestamp in microseconds for the first frame
* in the buffer.
- * @throws IllegalStateException if audio track is not configured or internal configureation
+ * @throws IllegalStateException if audio track is not set or internal configureation
* has not been done correctly.
*/
public void queueAudio(
@@ -606,7 +578,7 @@ final public class MediaSync {
long presentationTimeUs) {
if (mAudioTrack == null || mAudioThread == null) {
throw new IllegalStateException(
- "AudioTrack is NOT configured or audio thread is not created");
+ "AudioTrack is NOT set or audio thread is not created");
}
synchronized(mAudioLock) {
diff --git a/media/java/android/media/RemoteDisplay.java b/media/java/android/media/RemoteDisplay.java
index 4e937a5..5add65a 100644
--- a/media/java/android/media/RemoteDisplay.java
+++ b/media/java/android/media/RemoteDisplay.java
@@ -37,17 +37,19 @@ public final class RemoteDisplay {
private final CloseGuard mGuard = CloseGuard.get();
private final Listener mListener;
private final Handler mHandler;
+ private final String mOpPackageName;
private long mPtr;
- private native long nativeListen(String iface);
+ private native long nativeListen(String iface, String opPackageName);
private native void nativeDispose(long ptr);
private native void nativePause(long ptr);
private native void nativeResume(long ptr);
- private RemoteDisplay(Listener listener, Handler handler) {
+ private RemoteDisplay(Listener listener, Handler handler, String opPackageName) {
mListener = listener;
mHandler = handler;
+ mOpPackageName = opPackageName;
}
@Override
@@ -66,7 +68,8 @@ public final class RemoteDisplay {
* @param listener The listener to invoke when displays are connected or disconnected.
* @param handler The handler on which to invoke the listener.
*/
- public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
+ public static RemoteDisplay listen(String iface, Listener listener, Handler handler,
+ String opPackageName) {
if (iface == null) {
throw new IllegalArgumentException("iface must not be null");
}
@@ -77,7 +80,7 @@ public final class RemoteDisplay {
throw new IllegalArgumentException("handler must not be null");
}
- RemoteDisplay display = new RemoteDisplay(listener, handler);
+ RemoteDisplay display = new RemoteDisplay(listener, handler, opPackageName);
display.startListening(iface);
return display;
}
@@ -113,7 +116,7 @@ public final class RemoteDisplay {
}
private void startListening(String iface) {
- mPtr = nativeListen(iface);
+ mPtr = nativeListen(iface, mOpPackageName);
if (mPtr == 0) {
throw new IllegalStateException("Could not start listening for "
+ "remote display connection on \"" + iface + "\"");
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index a8b9686..b94a7e6 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -18,6 +18,7 @@ package android.media.audiofx;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityThread;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -395,7 +396,7 @@ public class AudioEffect {
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this),
type.toString(), uuid.toString(), priority, audioSession, id,
- desc);
+ desc, ActivityThread.currentOpPackageName());
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
Log.e(TAG, "Error code " + initResult
+ " when initializing AudioEffect.");
@@ -1217,7 +1218,8 @@ public class AudioEffect {
private static native final void native_init();
private native final int native_setup(Object audioeffect_this, String type,
- String uuid, int priority, int audioSession, int[] id, Object[] desc);
+ String uuid, int priority, int audioSession, int[] id, Object[] desc,
+ String opPackageName);
private native final void native_finalize();
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 24c74ac..0fe7246 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -16,6 +16,7 @@
package android.media.audiofx;
+import android.app.ActivityThread;
import android.util.Log;
import java.lang.ref.WeakReference;
import android.os.Handler;
@@ -206,7 +207,8 @@ public class Visualizer {
synchronized (mStateLock) {
mState = STATE_UNINITIALIZED;
// native initialization
- int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id);
+ int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
+ ActivityThread.currentOpPackageName());
if (result != SUCCESS && result != ALREADY_EXISTS) {
Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
switch (result) {
@@ -716,7 +718,8 @@ public class Visualizer {
private native final int native_setup(Object audioeffect_this,
int audioSession,
- int[] id);
+ int[] id,
+ String opPackageName);
private native final void native_finalize();
@@ -765,6 +768,5 @@ public class Visualizer {
}
}
-
}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 6aa4d8a..4ffac6d 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -36,20 +36,30 @@ public class AudioMix {
private int mRouteFlags;
private String mRegistrationId;
private int mMixType = MIX_TYPE_INVALID;
- private int mMixState = MIX_STATE_DISABLED;
+ int mMixState = MIX_STATE_DISABLED;
+ int mCallbackFlags;
/**
* All parameters are guaranteed valid through the Builder.
*/
- private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags) {
+ private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags) {
mRule = rule;
mFormat = format;
mRouteFlags = routeFlags;
mRegistrationId = null;
mMixType = rule.getTargetMixType();
+ mCallbackFlags = callbackFlags;
}
- // ROUTE_FLAG_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
+ // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined
+ // in frameworks/av/include/media/AudioPolicy.h
+ /** @hide */
+ public final static int CALLBACK_FLAG_NOTIFY_ACTIVITY = 0x1;
+ // when adding new MIX_FLAG_* flags, add them to this mask of authorized masks:
+ private final static int CALLBACK_FLAGS_ALL = CALLBACK_FLAG_NOTIFY_ACTIVITY;
+
+ // ROUTE_FLAG_* values: keep in sync with MIX_ROUTE_FLAG_* values defined
+ // in frameworks/av/include/media/AudioPolicy.h
/**
* An audio mix behavior where the output of the mix is sent to the original destination of
* the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
@@ -161,6 +171,7 @@ public class AudioMix {
private AudioMixingRule mRule = null;
private AudioFormat mFormat = null;
private int mRouteFlags = 0;
+ private int mCallbackFlags = 0;
/**
* @hide
@@ -199,6 +210,22 @@ public class AudioMix {
}
/**
+ * @hide
+ * Only used by AudioPolicyConfig, not a public API.
+ * @param callbackFlags which callbacks are called from native
+ * @return the same Builder instance.
+ * @throws IllegalArgumentException
+ */
+ public Builder setCallbackFlags(int flags) throws IllegalArgumentException {
+ if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
+ throw new IllegalArgumentException("Illegal callback flags 0x"
+ + Integer.toHexString(flags).toUpperCase());
+ }
+ mCallbackFlags = flags;
+ return this;
+ }
+
+ /**
* Sets the {@link AudioFormat} for the mix.
* @param format a non-null {@link AudioFormat} instance.
* @return the same Builder instance.
@@ -256,7 +283,7 @@ public class AudioMix {
}
mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
}
- return new AudioMix(mRule, mFormat, mRouteFlags);
+ return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags);
}
}
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index f128044..423b467 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -189,6 +189,12 @@ public class AudioPolicy {
@SystemApi
public AudioPolicy build() {
+ if (mStatusListener != null) {
+ // the AudioPolicy status listener includes updates on each mix activity state
+ for (AudioMix mix : mMixes) {
+ mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
+ }
+ }
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
mFocusListener, mStatusListener);
}
@@ -432,6 +438,18 @@ public class AudioPolicy {
+ afi.getClientId() + "wasNotified=" + wasNotified);
}
}
+
+ public void notifyMixStateUpdate(String regId, int state) {
+ for (AudioMix mix : mConfig.getMixes()) {
+ if (mix.getRegistration().equals(regId)) {
+ mix.mMixState = state;
+ sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/);
+ if (DEBUG) {
+ Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state);
+ }
+ }
+ }
+ }
};
//==================================================
@@ -440,6 +458,7 @@ public class AudioPolicy {
private final static int MSG_POLICY_STATUS_CHANGE = 0;
private final static int MSG_FOCUS_GRANT = 1;
private final static int MSG_FOCUS_LOSS = 2;
+ private final static int MSG_MIX_STATE_UPDATE = 3;
private class EventHandler extends Handler {
public EventHandler(AudioPolicy ap, Looper looper) {
@@ -464,6 +483,11 @@ public class AudioPolicy {
(AudioFocusInfo) msg.obj, msg.arg1 != 0);
}
break;
+ case MSG_MIX_STATE_UPDATE:
+ if (mStatusListener != null) {
+ mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
+ }
+ break;
default:
Log.e(TAG, "Unknown event " + msg.what);
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 917e07b..252f5f4 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -59,6 +59,10 @@ public class AudioPolicyConfig implements Parcelable {
mMixes.add(mix);
}
+ public ArrayList<AudioMix> getMixes() {
+ return mMixes;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mMixes);
@@ -75,6 +79,8 @@ public class AudioPolicyConfig implements Parcelable {
for (AudioMix mix : mMixes) {
// write mix route flags
dest.writeInt(mix.getRouteFlags());
+ // write callback flags
+ dest.writeInt(mix.mCallbackFlags);
// write mix format
dest.writeInt(mix.getFormat().getSampleRate());
dest.writeInt(mix.getFormat().getEncoding());
@@ -96,6 +102,8 @@ public class AudioPolicyConfig implements Parcelable {
// read mix route flags
int routeFlags = in.readInt();
mixBuilder.setRouteFlags(routeFlags);
+ // read callback flags
+ mixBuilder.setCallbackFlags(in.readInt());
// read mix format
int sampleRate = in.readInt();
int encoding = in.readInt();
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index c777c58..ad8af15 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -25,4 +25,7 @@ oneway interface IAudioPolicyCallback {
// callbacks for audio focus
void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult);
void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified);
+
+ // callback for mix activity status update
+ void notifyMixStateUpdate(in String regId, int state);
}
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index af108eb..35374ed 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -69,6 +69,13 @@ public final class MidiDeviceInfo implements Parcelable {
public static final String PROPERTY_PRODUCT = "product";
/**
+ * Bundle key for the device's version property.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ * Matches the USB device version number for USB MIDI devices.
+ */
+ public static final String PROPERTY_VERSION = "version";
+
+ /**
* Bundle key for the device's serial number property.
* Used with the {@link android.os.Bundle} returned by {@link #getProperties}
* Matches the USB device serial number for USB MIDI devices.
diff --git a/media/java/android/media/midi/MidiReceiver.java b/media/java/android/media/midi/MidiReceiver.java
index d069075..f8799d4 100644
--- a/media/java/android/media/midi/MidiReceiver.java
+++ b/media/java/android/media/midi/MidiReceiver.java
@@ -23,6 +23,10 @@ import java.io.IOException;
*/
abstract public class MidiReceiver {
/**
+ * Although public, this method should be considered a private implementation
+ * detail. Client code should call {@link #send} or {@link #sendWithTimestamp}
+ * instead.
+ *
* Called to pass MIDI data to the receiver.
* May fail if count exceeds {@link #getMaxMessageSize}.
*
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index a3442e3..95aaa7f 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -41,8 +41,9 @@ import com.android.internal.os.SomeArgs;
public class ITvInputSessionWrapper extends ITvInputSession.Stub implements HandlerCaller.Callback {
private static final String TAG = "TvInputSessionWrapper";
- private static final int MESSAGE_HANDLING_DURATION_THRESHOLD_MILLIS = 50;
- private static final int MESSAGE_TUNE_DURATION_THRESHOLD_MILLIS = 2000;
+ private static final int EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS = 50;
+ private static final int EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS = 2000;
+ private static final int EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS = 5 * 1000;
private static final int DO_RELEASE = 1;
private static final int DO_SET_MAIN = 2;
@@ -184,14 +185,18 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
}
}
long duration = System.currentTimeMillis() - startTime;
- if (duration > MESSAGE_HANDLING_DURATION_THRESHOLD_MILLIS) {
+ if (duration > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration="
+ duration + "ms)");
- if (msg.what == DO_TUNE && duration > MESSAGE_TUNE_DURATION_THRESHOLD_MILLIS) {
+ if (msg.what == DO_TUNE && duration > EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS) {
throw new RuntimeException("Too much time to handle tune request. (" + duration
- + "ms > " + MESSAGE_TUNE_DURATION_THRESHOLD_MILLIS + "ms) "
+ + "ms > " + EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS + "ms) "
+ "Consider handling the tune request in a separate thread.");
}
+ if (duration > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
+ throw new RuntimeException("Too much time to handle a request. (type=" + msg.what +
+ ", " + duration + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
+ }
}
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 966e41a..043b80e 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -171,6 +171,14 @@ import java.util.Objects;
* <td>TV content rating system for Brazil</td>
* </tr>
* <tr>
+ * <td>CA_TV_EN</td>
+ * <td>TV content rating system for Canada (English)</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR</td>
+ * <td>TV content rating system for Canada (French)</td>
+ * </tr>
+ * <tr>
* <td>DVB</td>
* <td>DVB content rating system</td>
* </tr>
@@ -195,6 +203,10 @@ import java.util.Objects;
* <td>TV content rating system for Singapore</td>
* </tr>
* <tr>
+ * <td>US_MV</td>
+ * <td>Movie content rating system for the United States</td>
+ * </tr>
+ * <tr>
* <td>US_TV</td>
* <td>TV content rating system for the United States</td>
* </tr>
@@ -290,6 +302,60 @@ import java.util.Objects;
* <td>Content suitable for viewers over the age of 18</td>
* </tr>
* <tr>
+ * <td valign="top" rowspan="7">CA_TV_EN</td>
+ * <td>CA_TV_EN_EXEMPT</td>
+ * <td>Exempt from ratings</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_C</td>
+ * <td>Suitable for children ages 2&#8211;7</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_C8</td>
+ * <td>Suitable for children ages 8 and older</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_G</td>
+ * <td>Suitable for the entire family</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_PG</td>
+ * <td>May contain moderate violence, profanity, nudity, and sexual references</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_14</td>
+ * <td>Intended for viewers ages 14 and older</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_EN_18</td>
+ * <td>Intended for viewers ages 18 and older</td>
+ * </tr>
+ * <tr>
+ * <td valign="top" rowspan="6">CA_TV_FR</td>
+ * <td>CA_TV_FR_E</td>
+ * <td>Exempt from ratings</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR_G</td>
+ * <td>Appropriate for all ages</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR_8</td>
+ * <td>Appropriate for children 8</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR_13</td>
+ * <td>Suitable for children 13</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR_16</td>
+ * <td>Recommended for children over the age of 16</td>
+ * </tr>
+ * <tr>
+ * <td>CA_TV_FR_18</td>
+ * <td>Only to be viewed by adults</td>
+ * </tr>
+ * <tr>
* <td valign="top" rowspan="15">DVB</td>
* <td>DVB_4</td>
* <td>Recommended for ages 4 and over</td>
@@ -608,6 +674,27 @@ import java.util.Objects;
* <td>Suitable for adults aged 21 and above</td>
* </tr>
* <tr>
+ * <td valign="top" rowspan="5">US_MV</td>
+ * <td>US_MV_G</td>
+ * <td>General audiences</td>
+ * </tr>
+ * <tr>
+ * <td>US_MV_PG</td>
+ * <td>Parental guidance suggested</td>
+ * </tr>
+ * <tr>
+ * <td>US_MV_PG13</td>
+ * <td>Parents strongly cautioned</td>
+ * </tr>
+ * <tr>
+ * <td>US_MV_R</td>
+ * <td>Restricted, under 17 requires accompanying parent or adult guardian</td>
+ * </tr>
+ * <tr>
+ * <td>US_MV_NC17</td>
+ * <td>No one 17 and under admitted</td>
+ * </tr>
+ * <tr>
* <td valign="top" rowspan="6">US_TV</td>
* <td>US_TV_Y</td>
* <td>This program is designed to be appropriate for all children</td>
@@ -696,10 +783,15 @@ public final class TvContentRating {
private final int mHashCode;
/**
- * Rating constant denoting unrated content.
+ * Rating constant denoting unrated content. Used to handle the case where the content rating
+ * information is missing.
+ *
+ * <p>TV input services can call {@link TvInputManager#isRatingBlocked} with this constant to
+ * determine whether they should block unrated content. The subsequent call to
+ * {@link TvInputService.Session#notifyContentBlocked} with the same constant notifies
+ * applications that the current program content is blocked by parental controls.
*/
- public static final TvContentRating UNRATED = new TvContentRating("com.android.tv", "",
- "UNRATED", null);
+ public static final TvContentRating UNRATED = new TvContentRating("null", "null", "null", null);
/**
* Creates a {@code TvContentRating} object with predefined content rating strings.
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 601fa45..dca0631 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1035,7 +1035,7 @@ public final class TvInputManager {
/**
* Checks whether a given TV content rating is blocked by the user.
*
- * @param rating The TV content rating to check.
+ * @param rating The TV content rating to check. Can be {@link TvContentRating#UNRATED}.
* @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
*/
public boolean isRatingBlocked(@NonNull TvContentRating rating) {
@@ -1696,6 +1696,8 @@ public final class TvInputManager {
* @param rate The ratio between desired playback rate and normal one.
* @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_DEFAULT}
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH}
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
*/
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 5156ae8..c1035b0 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -528,11 +528,12 @@ public abstract class TvInputService extends Service {
* TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
* service should block the content or not is determined by invoking
* {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)}
- * with the content rating for the current program. Then the {@link TvInputManager} makes a
- * judgment based on the user blocked ratings stored in the secure settings and returns the
- * result. If the rating in question turns out to be blocked, the TV input service must
- * immediately block the content and call this method with the content rating of the current
- * program to prompt the PIN verification screen.
+ * with the content rating for the current program or {@link TvContentRating#UNRATED} in
+ * case the rating information is missing. Then the {@link TvInputManager} makes a judgment
+ * based on the user blocked ratings stored in the secure settings and returns the result.
+ * If the rating in question turns out to be blocked, the TV input service must immediately
+ * block the content and call this method with the content rating of the current program to
+ * prompt the PIN verification screen.
*
* <p>Each TV input service also needs to continuously listen to any changes made to the
* parental controls settings by registering a broadcast receiver to receive
@@ -540,7 +541,8 @@ public abstract class TvInputService extends Service {
* {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
* reevaluate the current program with the new parental controls settings.
*
- * @param rating The content rating for the current TV program.
+ * @param rating The content rating for the current TV program. Can be
+ * {@link TvContentRating#UNRATED}.
* @see #notifyContentAllowed
* @see TvInputManager
*/
@@ -891,6 +893,8 @@ public abstract class TvInputService extends Service {
* @param rate The ratio between desired playback rate and normal one.
* @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_DEFAULT}
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH}
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
* @see #onTimeShiftResume
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index d248b12..7e64b17 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -457,6 +457,8 @@ public class TvView extends ViewGroup {
* @param rate The ratio between desired playback rate and normal one.
* @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_DEFAULT}
+ * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH}
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
*/
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5f586a9..f808c0d 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -39,7 +39,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/PersistentSurface.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/window.h>
@@ -75,6 +75,14 @@ static struct ExceptionReason {
jint reasonReclaimed;
} gExceptionReason;
+static struct {
+ jclass clazz;
+ jfieldID mLock;
+ jfieldID mPersistentObject;
+ jmethodID ctor;
+ jmethodID setNativeObjectLocked;
+} gPersistentSurfaceClassInfo;
+
struct fields_t {
jfieldID context;
jmethodID postEventFromNativeID;
@@ -87,6 +95,7 @@ struct fields_t {
};
static fields_t gFields;
+static const void *sRefBaseOwner;
////////////////////////////////////////////////////////////////////////////////
@@ -245,11 +254,29 @@ status_t JMediaCodec::configure(
return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
}
+status_t JMediaCodec::setSurface(
+ const sp<IGraphicBufferProducer> &bufferProducer) {
+ sp<Surface> client;
+ if (bufferProducer != NULL) {
+ client = new Surface(bufferProducer, true /* controlledByApp */);
+ }
+ status_t err = mCodec->setSurface(client);
+ if (err == OK) {
+ mSurfaceTextureClient = client;
+ }
+ return err;
+}
+
status_t JMediaCodec::createInputSurface(
sp<IGraphicBufferProducer>* bufferProducer) {
return mCodec->createInputSurface(bufferProducer);
}
+status_t JMediaCodec::usePersistentInputSurface(
+ const sp<PersistentSurface> &surface) {
+ return mCodec->usePersistentInputSurface(surface);
+}
+
status_t JMediaCodec::start() {
return mCodec->start();
}
@@ -592,8 +619,8 @@ static jthrowable createCodecException(
break;
}
- // TODO: propagate reason from MediaCodec.
- int reason = gExceptionReason.reasonHardware;
+ int reason =
+ (err == DEAD_OBJECT) ? gExceptionReason.reasonReclaimed : gExceptionReason.reasonHardware;
return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get(), reason);
}
@@ -797,6 +824,10 @@ static jint throwExceptionAsNecessary(
jniThrowException(env, "java/lang/IllegalStateException", msg);
return 0;
+ case BAD_VALUE:
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+ return 0;
+
default:
if (isCryptoError(err)) {
throwCryptoException(env, err, msg);
@@ -869,6 +900,149 @@ static void android_media_MediaCodec_native_configure(
throwExceptionAsNecessary(env, err);
}
+static void android_media_MediaCodec_native_setSurface(
+ JNIEnv *env,
+ jobject thiz,
+ jobject jsurface) {
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ if (jsurface != NULL) {
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
+ if (surface != NULL) {
+ bufferProducer = surface->getIGraphicBufferProducer();
+ } else {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ "The surface has been released");
+ return;
+ }
+ }
+
+ status_t err = codec->setSurface(bufferProducer);
+ throwExceptionAsNecessary(env, err);
+}
+
+sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
+ JNIEnv* env, jobject object) {
+ sp<PersistentSurface> persistentSurface;
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ persistentSurface = reinterpret_cast<PersistentSurface *>(
+ env->GetLongField(object,
+ gPersistentSurfaceClassInfo.mPersistentObject));
+ env->MonitorExit(lock);
+ }
+ env->DeleteLocalRef(lock);
+
+ return persistentSurface;
+}
+
+static jobject android_media_MediaCodec_createPersistentInputSurface(
+ JNIEnv* env, jclass /* clazz */) {
+ ALOGV("android_media_MediaCodec_createPersistentInputSurface");
+ sp<PersistentSurface> persistentSurface =
+ MediaCodec::CreatePersistentInputSurface();
+
+ if (persistentSurface == NULL) {
+ return NULL;
+ }
+
+ sp<Surface> surface = new Surface(
+ persistentSurface->getBufferProducer(), true);
+ if (surface == NULL) {
+ return NULL;
+ }
+
+ jobject object = env->NewObject(
+ gPersistentSurfaceClassInfo.clazz,
+ gPersistentSurfaceClassInfo.ctor);
+
+ if (object == NULL) {
+ if (env->ExceptionCheck()) {
+ ALOGE("Could not create PersistentSurface.");
+ env->ExceptionClear();
+ }
+ return NULL;
+ }
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ env->CallVoidMethod(
+ object,
+ gPersistentSurfaceClassInfo.setNativeObjectLocked,
+ (jlong)surface.get());
+ env->SetLongField(
+ object,
+ gPersistentSurfaceClassInfo.mPersistentObject,
+ (jlong)persistentSurface.get());
+ env->MonitorExit(lock);
+ } else {
+ env->DeleteLocalRef(object);
+ object = NULL;
+ }
+ env->DeleteLocalRef(lock);
+
+ if (object != NULL) {
+ surface->incStrong(&sRefBaseOwner);
+ persistentSurface->incStrong(&sRefBaseOwner);
+ }
+
+ return object;
+}
+
+static void android_media_MediaCodec_releasePersistentInputSurface(
+ JNIEnv* env, jclass /* clazz */, jobject object) {
+ sp<PersistentSurface> persistentSurface;
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ persistentSurface = reinterpret_cast<PersistentSurface *>(
+ env->GetLongField(
+ object, gPersistentSurfaceClassInfo.mPersistentObject));
+ env->SetLongField(
+ object,
+ gPersistentSurfaceClassInfo.mPersistentObject,
+ (jlong)0);
+ env->MonitorExit(lock);
+ }
+ env->DeleteLocalRef(lock);
+
+ if (persistentSurface != NULL) {
+ persistentSurface->decStrong(&sRefBaseOwner);
+ }
+ // no need to release surface as it will be released by Surface's jni
+}
+
+static void android_media_MediaCodec_usePersistentInputSurface(
+ JNIEnv* env, jobject thiz, jobject object) {
+ ALOGV("android_media_MediaCodec_usePersistentInputSurface");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<PersistentSurface> persistentSurface =
+ android_media_MediaCodec_getPersistentInputSurface(env, object);
+
+ status_t err = codec->usePersistentInputSurface(persistentSurface);
+ if (err != NO_ERROR) {
+ throwExceptionAsNecessary(env, err);
+ }
+}
+
static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
jobject thiz) {
ALOGV("android_media_MediaCodec_createInputSurface");
@@ -1471,6 +1645,29 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) {
CHECK(field != NULL);
gExceptionReason.reasonReclaimed =
env->GetStaticIntField(clazz.get(), field);
+
+ clazz.reset(env->FindClass("android/view/Surface"));
+ CHECK(clazz.get() != NULL);
+
+ field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+ CHECK(field != NULL);
+ gPersistentSurfaceClassInfo.mLock = field;
+
+ jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V");
+ CHECK(method != NULL);
+ gPersistentSurfaceClassInfo.setNativeObjectLocked = method;
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface"));
+ CHECK(clazz.get() != NULL);
+ gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ method = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(method != NULL);
+ gPersistentSurfaceClassInfo.ctor = method;
+
+ field = env->GetFieldID(clazz.get(), "mPersistentObject", "J");
+ CHECK(field != NULL);
+ gPersistentSurfaceClassInfo.mPersistentObject = field;
}
static void android_media_MediaCodec_native_setup(
@@ -1521,6 +1718,17 @@ static JNINativeMethod gMethods[] = {
{ "native_reset", "()V", (void *)android_media_MediaCodec_reset },
+ { "native_releasePersistentInputSurface",
+ "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_releasePersistentInputSurface},
+
+ { "native_createPersistentInputSurface",
+ "()Landroid/media/MediaCodec$PersistentSurface;",
+ (void *)android_media_MediaCodec_createPersistentInputSurface },
+
+ { "native_usePersistentInputSurface", "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_usePersistentInputSurface },
+
{ "native_setCallback",
"(Landroid/media/MediaCodec$Callback;)V",
(void *)android_media_MediaCodec_native_setCallback },
@@ -1530,6 +1738,10 @@ static JNINativeMethod gMethods[] = {
"Landroid/media/MediaCrypto;I)V",
(void *)android_media_MediaCodec_native_configure },
+ { "native_setSurface",
+ "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_native_setSurface },
+
{ "createInputSurface", "()Landroid/view/Surface;",
(void *)android_media_MediaCodec_createInputSurface },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 9f2785a..bf61f42 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -33,6 +33,7 @@ struct AString;
struct ICrypto;
struct IGraphicBufferProducer;
struct MediaCodec;
+struct PersistentSurface;
class Surface;
struct JMediaCodec : public AHandler {
@@ -53,7 +54,11 @@ struct JMediaCodec : public AHandler {
const sp<ICrypto> &crypto,
int flags);
+ status_t setSurface(
+ const sp<IGraphicBufferProducer> &surface);
+
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+ status_t usePersistentInputSurface(const sp<PersistentSurface> &surface);
status_t start();
status_t stop();
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 88a6771..59fb6d6 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -299,15 +299,16 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env,
return NULL;
}
- SkBitmap *bitmap = GraphicsJNI::getSkBitmap(env, jBitmap);
+ SkBitmap bitmap;
+ GraphicsJNI::getSkBitmap(env, jBitmap, &bitmap);
- bitmap->lockPixels();
- rotate((uint16_t*)bitmap->getPixels(),
+ bitmap.lockPixels();
+ rotate((uint16_t*)bitmap.getPixels(),
(uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
videoFrame->mWidth,
videoFrame->mHeight,
videoFrame->mRotationAngle);
- bitmap->unlockPixels();
+ bitmap.unlockPixels();
if (videoFrame->mDisplayWidth != videoFrame->mWidth ||
videoFrame->mDisplayHeight != videoFrame->mHeight) {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2c61779..5b55a61 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -174,6 +174,8 @@ static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStat
} else { // Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ } else if ( opStatus == (status_t) BAD_VALUE ) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
@@ -442,8 +444,33 @@ android_media_MediaPlayer_setPlaybackSettings(JNIEnv *env, jobject thiz, jobject
pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode,
pbs.audioStretchModeSet, pbs.audioRate.mStretchMode);
- // TODO: pass playback settings to mediaplayer when audiotrack supports it
- process_media_player_call(env, thiz, mp->setPlaybackRate(pbs.audioRate.mSpeed), NULL, NULL);
+ AudioPlaybackRate rate;
+ status_t err = mp->getPlaybackSettings(&rate);
+ if (err == OK) {
+ bool updatedRate = false;
+ if (pbs.speedSet) {
+ rate.mSpeed = pbs.audioRate.mSpeed;
+ updatedRate = true;
+ }
+ if (pbs.pitchSet) {
+ rate.mPitch = pbs.audioRate.mPitch;
+ updatedRate = true;
+ }
+ if (pbs.audioFallbackModeSet) {
+ rate.mFallbackMode = pbs.audioRate.mFallbackMode;
+ updatedRate = true;
+ }
+ if (pbs.audioStretchModeSet) {
+ rate.mStretchMode = pbs.audioRate.mStretchMode;
+ updatedRate = true;
+ }
+ if (updatedRate) {
+ err = mp->setPlaybackSettings(rate);
+ }
+ }
+ process_media_player_call(
+ env, thiz, err,
+ "java/lang/IllegalStateException", "unexpected error");
}
static jobject
@@ -457,15 +484,9 @@ android_media_MediaPlayer_getPlaybackSettings(JNIEnv *env, jobject thiz)
PlaybackSettings pbs;
AudioPlaybackRate &audioRate = pbs.audioRate;
-
- audioRate.mSpeed = 1.0f;
- audioRate.mPitch = 1.0f;
- audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_DEFAULT;
- audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_DEFAULT;
-
- // TODO: get this from mediaplayer when audiotrack supports it
- // process_media_player_call(
- // env, thiz, mp->getPlaybackSettings(&audioRate), NULL, NULL);
+ process_media_player_call(
+ env, thiz, mp->getPlaybackSettings(&audioRate),
+ "java/lang/IllegalStateException", "unexpected error");
ALOGV("getPlaybackSettings: %f %f %d %d",
audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
@@ -489,13 +510,35 @@ android_media_MediaPlayer_setSyncSettings(JNIEnv *env, jobject thiz, jobject set
SyncSettings scs;
scs.fillFromJobject(env, gSyncSettingsFields, settings);
ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
- scs.syncSourceSet, scs.syncSource,
- scs.audioAdjustModeSet, scs.audioAdjustMode,
- scs.toleranceSet, scs.tolerance,
- scs.frameRateSet, scs.frameRate);
-
- // TODO: pass sync settings to mediaplayer when it supports it
- // process_media_player_call(env, thiz, mp->setSyncSettings(scs), NULL, NULL);
+ scs.syncSourceSet, scs.sync.mSource,
+ scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode,
+ scs.toleranceSet, scs.sync.mTolerance,
+ scs.frameRateSet, scs.frameRate);
+
+ AVSyncSettings avsync;
+ float videoFrameRate;
+ status_t err = mp->getSyncSettings(&avsync, &videoFrameRate);
+ if (err == OK) {
+ bool updatedSync = scs.frameRateSet;
+ if (scs.syncSourceSet) {
+ avsync.mSource = scs.sync.mSource;
+ updatedSync = true;
+ }
+ if (scs.audioAdjustModeSet) {
+ avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode;
+ updatedSync = true;
+ }
+ if (scs.toleranceSet) {
+ avsync.mTolerance = scs.sync.mTolerance;
+ updatedSync = true;
+ }
+ if (updatedSync) {
+ err = mp->setSyncSettings(avsync, scs.frameRateSet ? scs.frameRate : -1.f);
+ }
+ }
+ process_media_player_call(
+ env, thiz, err,
+ "java/lang/IllegalStateException", "unexpected error");
}
static jobject
@@ -508,21 +551,27 @@ android_media_MediaPlayer_getSyncSettings(JNIEnv *env, jobject thiz)
}
SyncSettings scs;
- scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
- scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
- scs.tolerance = 0.f;
- scs.frameRate = 0.f;
-
- // TODO: get this from mediaplayer when it supports it
- // process_media_player_call(
- // env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+ scs.frameRate = -1.f;
+ process_media_player_call(
+ env, thiz, mp->getSyncSettings(&scs.sync, &scs.frameRate),
+ "java/lang/IllegalStateException", "unexpected error");
+
ALOGV("getSyncSettings: %d %d %f %f",
- scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+ scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate);
+
+ // sanity check settings
+ if (scs.sync.mSource >= AVSYNC_SOURCE_MAX
+ || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+ || scs.sync.mTolerance < 0.f
+ || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
scs.syncSourceSet = true;
scs.audioAdjustModeSet = true;
scs.toleranceSet = true;
- scs.frameRateSet = false;
+ scs.frameRateSet = scs.frameRate >= 0.f;
return scs.asJobject(env, gSyncSettingsFields);
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 8b7d40d..0044bed 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -29,8 +29,11 @@
#include <camera/ICameraService.h>
#include <camera/Camera.h>
#include <media/mediarecorder.h>
+#include <media/stagefright/PersistentSurface.h>
#include <utils/threads.h>
+#include <ScopedUtfChars.h>
+
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
@@ -46,6 +49,8 @@ using namespace android;
// helper function to extract a native Camera object from a Camera Java object
extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
+extern sp<PersistentSurface>
+android_media_MediaCodec_getPersistentInputSurface(JNIEnv* env, jobject object);
struct fields_t {
jfieldID context;
@@ -113,6 +118,12 @@ static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
return android_view_Surface_getSurface(env, clazz);
}
+static sp<PersistentSurface> get_persistentSurface(JNIEnv* env, jobject object)
+{
+ ALOGV("get_persistentSurface");
+ return android_media_MediaCodec_getPersistentInputSurface(env, object);
+}
+
// Returns true if it throws an exception.
static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
{
@@ -444,11 +455,13 @@ android_media_MediaRecorder_native_init(JNIEnv *env)
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring packageName)
+ jstring packageName, jstring opPackageName)
{
ALOGV("setup");
- sp<MediaRecorder> mr = new MediaRecorder();
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+
+ sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
if (mr == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
@@ -483,6 +496,18 @@ android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz)
android_media_MediaRecorder_release(env, thiz);
}
+void android_media_MediaRecorder_usePersistentSurface(
+ JNIEnv* env, jobject thiz, jobject object) {
+ ALOGV("android_media_MediaRecorder_usePersistentSurface");
+
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+
+ sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
+
+ process_media_recorder_call(env, mr->usePersistentSurface(persistentSurface),
+ "java/lang/IllegalArgumentException", "native_usePersistentSurface failed.");
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -506,8 +531,10 @@ static JNINativeMethod gMethods[] = {
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V", (void *)android_media_MediaRecorder_native_setup},
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
+ (void *)android_media_MediaRecorder_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
+ {"native_usePersistentSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_usePersistentSurface },
};
// This function only registers the native methods, and is called from
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index f192262..5c18901 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -21,6 +21,7 @@
#include "android_media_MediaSync.h"
#include "android_media_AudioTrack.h"
+#include "android_media_PlaybackSettings.h"
#include "android_media_SyncSettings.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
@@ -29,6 +30,7 @@
#include <gui/Surface.h>
+#include <media/AudioResamplerPublic.h>
#include <media/AudioTrack.h>
#include <media/stagefright/MediaClock.h>
#include <media/stagefright/MediaSync.h>
@@ -47,6 +49,7 @@ struct fields_t {
};
static fields_t gFields;
+static PlaybackSettings::fields_t gPlaybackSettingsFields;
static SyncSettings::fields_t gSyncSettingsFields;
////////////////////////////////////////////////////////////////////////////////
@@ -58,14 +61,12 @@ JMediaSync::JMediaSync() {
JMediaSync::~JMediaSync() {
}
-status_t JMediaSync::configureSurface(const sp<IGraphicBufferProducer> &bufferProducer) {
- return mSync->configureSurface(bufferProducer);
+status_t JMediaSync::setSurface(const sp<IGraphicBufferProducer> &bufferProducer) {
+ return mSync->setSurface(bufferProducer);
}
-status_t JMediaSync::configureAudioTrack(
- const sp<AudioTrack> &audioTrack,
- int32_t nativeSampleRateInHz) {
- return mSync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
+status_t JMediaSync::setAudioTrack(const sp<AudioTrack> &audioTrack) {
+ return mSync->setAudioTrack(audioTrack);
}
status_t JMediaSync::createInputSurface(
@@ -73,14 +74,34 @@ status_t JMediaSync::createInputSurface(
return mSync->createInputSurface(bufferProducer);
}
-status_t JMediaSync::setPlaybackRate(float rate) {
- return mSync->setPlaybackRate(rate);
-}
-
sp<const MediaClock> JMediaSync::getMediaClock() {
return mSync->getMediaClock();
}
+status_t JMediaSync::setPlaybackSettings(const AudioPlaybackRate& rate) {
+ return mSync->setPlaybackSettings(rate);
+}
+
+void JMediaSync::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+ mSync->getPlaybackSettings(rate);
+}
+
+status_t JMediaSync::setSyncSettings(const AVSyncSettings& syncSettings) {
+ return mSync->setSyncSettings(syncSettings);
+}
+
+void JMediaSync::getSyncSettings(AVSyncSettings* syncSettings /* nonnull */) {
+ mSync->getSyncSettings(syncSettings);
+}
+
+status_t JMediaSync::setVideoFrameRateHint(float rate) {
+ return mSync->setVideoFrameRateHint(rate);
+}
+
+float JMediaSync::getVideoFrameRate() {
+ return mSync->getVideoFrameRate();
+}
+
status_t JMediaSync::updateQueuedAudioData(
int sizeInBytes, int64_t presentationTimeUs) {
return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
@@ -142,9 +163,9 @@ static void throwExceptionAsNecessary(
}
}
-static void android_media_MediaSync_native_configureSurface(
+static void android_media_MediaSync_native_setSurface(
JNIEnv *env, jobject thiz, jobject jsurface) {
- ALOGV("android_media_MediaSync_configureSurface");
+ ALOGV("android_media_MediaSync_setSurface");
sp<JMediaSync> sync = getMediaSync(env, thiz);
if (sync == NULL) {
@@ -163,7 +184,7 @@ static void android_media_MediaSync_native_configureSurface(
}
}
- status_t err = sync->configureSurface(bufferProducer);
+ status_t err = sync->setSurface(bufferProducer);
if (err == INVALID_OPERATION) {
throwExceptionAsNecessary(
@@ -175,9 +196,9 @@ static void android_media_MediaSync_native_configureSurface(
}
}
-static void android_media_MediaSync_native_configureAudioTrack(
- JNIEnv *env, jobject thiz, jobject jaudioTrack, jint nativeSampleRateInHz) {
- ALOGV("android_media_MediaSync_configureAudioTrack");
+static void android_media_MediaSync_native_setAudioTrack(
+ JNIEnv *env, jobject thiz, jobject jaudioTrack) {
+ ALOGV("android_media_MediaSync_setAudioTrack");
sp<JMediaSync> sync = getMediaSync(env, thiz);
if (sync == NULL) {
@@ -194,7 +215,7 @@ static void android_media_MediaSync_native_configureAudioTrack(
}
}
- status_t err = sync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
+ status_t err = sync->setAudioTrack(audioTrack);
if (err == INVALID_OPERATION) {
throwExceptionAsNecessary(
@@ -287,29 +308,132 @@ static jlong android_media_MediaSync_native_getPlayTimeForPendingAudioFrames(
return (jlong)playTimeUs;
}
-static void
-android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
-{
+static jfloat android_media_MediaSync_setPlaybackSettings(
+ JNIEnv *env, jobject thiz, jobject settings) {
sp<JMediaSync> sync = getMediaSync(env, thiz);
if (sync == NULL) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
- return;
+ return (jfloat)0.f;
+ }
+
+ PlaybackSettings pbs;
+ pbs.fillFromJobject(env, gPlaybackSettingsFields, settings);
+ ALOGV("setPlaybackSettings: %d:%f %d:%f %d:%u %d:%u",
+ pbs.speedSet, pbs.audioRate.mSpeed,
+ pbs.pitchSet, pbs.audioRate.mPitch,
+ pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode,
+ pbs.audioStretchModeSet, pbs.audioRate.mStretchMode);
+
+ AudioPlaybackRate rate;
+ sync->getPlaybackSettings(&rate);
+ bool updatedRate = false;
+ if (pbs.speedSet) {
+ rate.mSpeed = pbs.audioRate.mSpeed;
+ updatedRate = true;
+ }
+ if (pbs.pitchSet) {
+ rate.mPitch = pbs.audioRate.mPitch;
+ updatedRate = true;
+ }
+ if (pbs.audioFallbackModeSet) {
+ rate.mFallbackMode = pbs.audioRate.mFallbackMode;
+ updatedRate = true;
+ }
+ if (pbs.audioStretchModeSet) {
+ rate.mStretchMode = pbs.audioRate.mStretchMode;
+ updatedRate = true;
+ }
+ if (updatedRate) {
+ status_t err = sync->setPlaybackSettings(rate);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ return (jfloat)0.f;
+ }
+ }
+
+ sp<const MediaClock> mediaClock = sync->getMediaClock();
+ if (mediaClock == NULL) {
+ return (jfloat)0.f;
+ }
+
+ return (jfloat)mediaClock->getPlaybackRate();
+}
+
+static jobject android_media_MediaSync_getPlaybackSettings(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return NULL;
+ }
+
+ PlaybackSettings pbs;
+ AudioPlaybackRate &audioRate = pbs.audioRate;
+ sync->getPlaybackSettings(&audioRate);
+ ALOGV("getPlaybackSettings: %f %f %d %d",
+ audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
+
+ pbs.speedSet = true;
+ pbs.pitchSet = true;
+ pbs.audioFallbackModeSet = true;
+ pbs.audioStretchModeSet = true;
+
+ return pbs.asJobject(env, gPlaybackSettingsFields);
+}
+
+static jfloat android_media_MediaSync_setSyncSettings(
+ JNIEnv *env, jobject thiz, jobject settings) {
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return (jfloat)0.f;
}
SyncSettings scs;
scs.fillFromJobject(env, gSyncSettingsFields, settings);
ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
- scs.syncSourceSet, scs.syncSource,
- scs.audioAdjustModeSet, scs.audioAdjustMode,
- scs.toleranceSet, scs.tolerance,
+ scs.syncSourceSet, scs.sync.mSource,
+ scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode,
+ scs.toleranceSet, scs.sync.mTolerance,
scs.frameRateSet, scs.frameRate);
- // TODO: pass sync settings to mediasync when it supports it
+ AVSyncSettings avsync;
+ sync->getSyncSettings(&avsync);
+ bool updatedSync = false;
+ status_t err = OK;
+ if (scs.syncSourceSet) {
+ avsync.mSource = scs.sync.mSource;
+ updatedSync = true;
+ }
+ if (scs.audioAdjustModeSet) {
+ avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode;
+ updatedSync = true;
+ }
+ if (scs.toleranceSet) {
+ avsync.mTolerance = scs.sync.mTolerance;
+ updatedSync = true;
+ }
+ if (updatedSync) {
+ err = sync->setSyncSettings(avsync);
+ }
+
+ if (scs.frameRateSet && err == OK) {
+ err = sync->setVideoFrameRateHint(scs.frameRate);
+ }
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ return (jfloat)0.f;
+ }
+
+ sp<const MediaClock> mediaClock = sync->getMediaClock();
+ if (mediaClock == NULL) {
+ return (jfloat)0.f;
+ }
+
+ return (jfloat)mediaClock->getPlaybackRate();
}
-static jobject
-android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz)
-{
+static jobject android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) {
sp<JMediaSync> sync = getMediaSync(env, thiz);
if (sync == NULL) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
@@ -317,21 +441,25 @@ android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz)
}
SyncSettings scs;
- scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
- scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
- scs.tolerance = 0.f;
- scs.frameRate = 0.f;
-
- // TODO: get this from mediaplayer when it supports it
- // process_media_player_call(
- // env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+ sync->getSyncSettings(&scs.sync);
+ scs.frameRate = sync->getVideoFrameRate();
+
ALOGV("getSyncSettings: %d %d %f %f",
- scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+ scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate);
+
+ // sanity check settings
+ if (scs.sync.mSource >= AVSYNC_SOURCE_MAX
+ || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+ || scs.sync.mTolerance < 0.f
+ || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return NULL;
+ }
scs.syncSourceSet = true;
scs.audioAdjustModeSet = true;
scs.toleranceSet = true;
- scs.frameRateSet = false;
+ scs.frameRateSet = scs.frameRate >= 0.f;
return scs.asJobject(env, gSyncSettingsFields);
}
@@ -359,6 +487,7 @@ static void android_media_MediaSync_native_init(JNIEnv *env) {
CHECK(gFields.mediaTimestampClockRateID != NULL);
gSyncSettingsFields.init(env);
+ gPlaybackSettingsFields.init(env);
}
static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
@@ -367,33 +496,18 @@ static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
setMediaSync(env, thiz, sync);
}
-static void android_media_MediaSync_native_setPlaybackRate(
- JNIEnv *env, jobject thiz, jfloat rate) {
- sp<JMediaSync> sync = getMediaSync(env, thiz);
- if (sync == NULL) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
- return;
- }
-
- status_t err = sync->setPlaybackRate(rate);
- if (err != NO_ERROR) {
- throwExceptionAsNecessary(env, err);
- return;
- }
-}
-
static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) {
android_media_MediaSync_release(env, thiz);
}
static JNINativeMethod gMethods[] = {
- { "native_configureSurface",
+ { "native_setSurface",
"(Landroid/view/Surface;)V",
- (void *)android_media_MediaSync_native_configureSurface },
+ (void *)android_media_MediaSync_native_setSurface },
- { "native_configureAudioTrack",
- "(Landroid/media/AudioTrack;I)V",
- (void *)android_media_MediaSync_native_configureAudioTrack },
+ { "native_setAudioTrack",
+ "(Landroid/media/AudioTrack;)V",
+ (void *)android_media_MediaSync_native_setAudioTrack },
{ "createInputSurface", "()Landroid/view/Surface;",
(void *)android_media_MediaSync_createInputSurface },
@@ -416,11 +530,17 @@ static JNINativeMethod gMethods[] = {
{ "native_release", "()V", (void *)android_media_MediaSync_release },
- { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate },
+ { "native_setPlaybackSettings", "(Landroid/media/PlaybackSettings;)F",
+ (void *)android_media_MediaSync_setPlaybackSettings },
+
+ { "getPlaybackSettings", "()Landroid/media/PlaybackSettings;",
+ (void *)android_media_MediaSync_getPlaybackSettings },
- { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings},
+ { "native_setSyncSettings", "(Landroid/media/SyncSettings;)F",
+ (void *)android_media_MediaSync_setSyncSettings },
- { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings},
+ { "getSyncSettings", "()Landroid/media/SyncSettings;",
+ (void *)android_media_MediaSync_getSyncSettings },
{ "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize },
};
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index cf81a72..fa5e5e0 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -18,11 +18,13 @@
#define _ANDROID_MEDIA_MEDIASYNC_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaSync.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
namespace android {
+struct AudioPlaybackRate;
class AudioTrack;
struct IGraphicBufferProducer;
struct MediaClock;
@@ -31,18 +33,22 @@ class MediaSync;
struct JMediaSync : public RefBase {
JMediaSync();
- status_t configureSurface(const sp<IGraphicBufferProducer> &bufferProducer);
- status_t configureAudioTrack(
- const sp<AudioTrack> &audioTrack, int32_t nativeSampleRateInHz);
+ status_t setSurface(const sp<IGraphicBufferProducer> &bufferProducer);
+ status_t setAudioTrack(const sp<AudioTrack> &audioTrack);
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
status_t updateQueuedAudioData(int sizeInBytes, int64_t presentationTimeUs);
- status_t setPlaybackRate(float rate);
-
status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+ status_t setPlaybackSettings(const AudioPlaybackRate& rate);
+ void getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+ status_t setSyncSettings(const AVSyncSettings& syncSettings);
+ void getSyncSettings(AVSyncSettings* syncSettings /* nonnull */);
+ status_t setVideoFrameRateHint(float rate);
+ float getVideoFrameRate();
+
sp<const MediaClock> getMediaClock();
protected:
diff --git a/media/jni/android_media_SyncSettings.cpp b/media/jni/android_media_SyncSettings.cpp
index 2f0605e..5da35e7 100644
--- a/media/jni/android_media_SyncSettings.cpp
+++ b/media/jni/android_media_SyncSettings.cpp
@@ -57,9 +57,9 @@ void SyncSettings::fields_t::exit(JNIEnv *env) {
}
void SyncSettings::fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) {
- syncSource = env->GetIntField(settings, fields.sync_source);
- audioAdjustMode = env->GetIntField(settings, fields.audio_adjust_mode);
- tolerance = env->GetFloatField(settings, fields.tolerance);
+ sync.mSource = (AVSyncSource)env->GetIntField(settings, fields.sync_source);
+ sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)env->GetIntField(settings, fields.audio_adjust_mode);
+ sync.mTolerance = env->GetFloatField(settings, fields.tolerance);
frameRate = env->GetFloatField(settings, fields.frame_rate);
int set = env->GetIntField(settings, fields.set);
@@ -74,9 +74,9 @@ jobject SyncSettings::asJobject(JNIEnv *env, const fields_t& fields) {
if (settings == NULL) {
return NULL;
}
- env->SetIntField(settings, fields.sync_source, (jint)syncSource);
- env->SetIntField(settings, fields.audio_adjust_mode, (jint)audioAdjustMode);
- env->SetFloatField(settings, fields.tolerance, (jfloat)tolerance);
+ env->SetIntField(settings, fields.sync_source, (jint)sync.mSource);
+ env->SetIntField(settings, fields.audio_adjust_mode, (jint)sync.mAudioAdjustMode);
+ env->SetFloatField(settings, fields.tolerance, (jfloat)sync.mTolerance);
env->SetFloatField(settings, fields.frame_rate, (jfloat)frameRate);
env->SetIntField(
settings, fields.set,
diff --git a/media/jni/android_media_SyncSettings.h b/media/jni/android_media_SyncSettings.h
index 586533f..23530db 100644
--- a/media/jni/android_media_SyncSettings.h
+++ b/media/jni/android_media_SyncSettings.h
@@ -19,13 +19,12 @@
#include "jni.h"
+#include <media/stagefright/MediaSync.h>
+
namespace android {
struct SyncSettings {
- // keep this here until it is implemented
- int syncSource;
- int audioAdjustMode;
- float tolerance;
+ AVSyncSettings sync;
float frameRate;
bool syncSourceSet;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index c364d46..96b72a2 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -25,6 +25,8 @@
#include <android_runtime/AndroidRuntime.h>
#include "media/AudioEffect.h"
+#include <ScopedUtfChars.h>
+
using namespace android;
#define AUDIOEFFECT_SUCCESS 0
@@ -249,7 +251,8 @@ android_media_AudioEffect_native_init(JNIEnv *env)
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
+ jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId,
+ jobjectArray javadesc, jstring opPackageName)
{
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
@@ -267,6 +270,8 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
jstring jdescName;
jstring jdescImplementor;
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
@@ -312,6 +317,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
+ String16(opPackageNameStr.c_str()),
uuidStr,
priority,
effectCallback,
@@ -868,7 +874,7 @@ android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz _
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 460277f..abc681e 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -26,6 +26,8 @@
#include <utils/threads.h>
#include "media/Visualizer.h"
+#include <ScopedUtfChars.h>
+
using namespace android;
#define VISUALIZER_SUCCESS 0
@@ -331,7 +333,7 @@ static void android_media_visualizer_effect_callback(int32_t event,
static jint
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint sessionId, jintArray jId)
+ jint sessionId, jintArray jId, jstring opPackageName)
{
ALOGV("android_media_visualizer_native_setup");
visualizerJniStorage* lpJniStorage = NULL;
@@ -339,6 +341,8 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
Visualizer* lpVisualizer = NULL;
jint* nId = NULL;
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+
lpJniStorage = new visualizerJniStorage();
if (lpJniStorage == NULL) {
ALOGE("setup: Error creating JNI Storage");
@@ -362,7 +366,8 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
}
// create the native Visualizer object
- lpVisualizer = new Visualizer(0,
+ lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()),
+ 0,
android_media_visualizer_effect_callback,
lpJniStorage,
sessionId);
@@ -662,7 +667,7 @@ android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_visualizer_native_init},
- {"native_setup", "(Ljava/lang/Object;I[I)I",
+ {"native_setup", "(Ljava/lang/Object;I[ILjava/lang/String;)I",
(void *)android_media_visualizer_native_setup},
{"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
{"native_release", "()V", (void *)android_media_visualizer_native_release},
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 25c6154..84ae3b4 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -514,10 +514,11 @@ static status_t decode(int fd, int64_t offset, int64_t length,
if (strncmp(mime, "audio/", 6) == 0) {
AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
- if (AMediaCodec_configure(codec, format,
- NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
- || AMediaCodec_start(codec) != AMEDIA_OK
- || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
+ if (codec == NULL
+ || AMediaCodec_configure(codec, format,
+ NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
+ || AMediaCodec_start(codec) != AMEDIA_OK
+ || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
AMediaExtractor_delete(ex);
AMediaCodec_delete(codec);
AMediaFormat_delete(format);
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 8d194e5..63a8da7 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -47,6 +47,7 @@ import java.util.UUID;
public final class BluetoothMidiDevice {
private static final String TAG = "BluetoothMidiDevice";
+ private static final boolean DEBUG = false;
private static final int MAX_PACKET_SIZE = 20;
@@ -152,8 +153,10 @@ public final class BluetoothMidiDevice {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
-// logByteArray("Received ", characteristic.getValue(), 0,
-// characteristic.getValue().length);
+ if (DEBUG) {
+ logByteArray("Received ", characteristic.getValue(), 0,
+ characteristic.getValue().length);
+ }
mPacketDecoder.decodePacket(characteristic.getValue(), mOutputReceiver);
}
};
@@ -182,8 +185,10 @@ public final class BluetoothMidiDevice {
byte[] writeBuffer = mWriteBuffers[count];
System.arraycopy(buffer, 0, writeBuffer, 0, count);
mCharacteristic.setValue(writeBuffer);
-// logByteArray("Sent ", mCharacteristic.getValue(), 0,
-// mCharacteristic.getValue().length);
+ if (DEBUG) {
+ logByteArray("Sent ", mCharacteristic.getValue(), 0,
+ mCharacteristic.getValue().length);
+ }
mBluetoothGatt.writeCharacteristic(mCharacteristic);
}
}
@@ -259,14 +264,7 @@ public final class BluetoothMidiDevice {
private static void logByteArray(String prefix, byte[] value, int offset, int count) {
StringBuilder builder = new StringBuilder(prefix);
for (int i = offset; i < count; i++) {
- String hex = Integer.toHexString(value[i]);
- int length = hex.length();
- if (length == 1) {
- hex = "0x" + hex;
- } else {
- hex = hex.substring(length - 2, length);
- }
- builder.append(hex);
+ builder.append(String.format("0x%02X", value[i]));
if (i != value.length - 1) {
builder.append(", ");
}
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
index 463edcf..99ea353 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
@@ -44,7 +44,7 @@ public class BluetoothPacketEncoder extends PacketEncoder {
// timestamp for first message in current packet
private int mPacketTimestamp;
// current running status, or zero if none
- private int mRunningStatus;
+ private byte mRunningStatus;
private boolean mWritePending;
@@ -56,12 +56,28 @@ public class BluetoothPacketEncoder extends PacketEncoder {
public void onReceive(byte[] msg, int offset, int count, long timestamp)
throws IOException {
- int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
- int status = msg[0] & 0xFF;
-
synchronized (mLock) {
+ int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
+ byte status = msg[offset];
+ boolean isSysExStart = (status == MidiConstants.STATUS_SYSTEM_EXCLUSIVE);
+ boolean isSysExContinuation = ((status & 0x80) == 0);
+
+ int bytesNeeded;
+ if (isSysExStart || isSysExContinuation) {
+ // SysEx messages can be split into multiple packets
+ bytesNeeded = 1;
+ } else {
+ bytesNeeded = count;
+ }
+
boolean needsTimestamp = (milliTimestamp != mPacketTimestamp);
- int bytesNeeded = count;
+ if (isSysExStart) {
+ // SysEx start byte must be preceded by a timestamp
+ needsTimestamp = true;
+ } else if (isSysExContinuation) {
+ // SysEx continuation packets must not have timestamp byte
+ needsTimestamp = false;
+ }
if (needsTimestamp) bytesNeeded++; // add one for timestamp byte
if (status == mRunningStatus) bytesNeeded--; // subtract one for status byte
@@ -71,15 +87,12 @@ public class BluetoothPacketEncoder extends PacketEncoder {
flushLocked(true);
}
- // write header if we are starting a new packet
- if (mAccumulatedBytes == 0) {
- // header byte with timestamp bits 7 - 12
- mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | (milliTimestamp >> 7));
- mPacketTimestamp = milliTimestamp;
- needsTimestamp = true;
+ // write the header if necessary
+ if (appendHeader(milliTimestamp)) {
+ needsTimestamp = !isSysExContinuation;
}
- // write new timestamp byte and status byte if necessary
+ // write new timestamp byte if necessary
if (needsTimestamp) {
// timestamp byte with bits 0 - 6 of timestamp
mAccumulationBuffer[mAccumulatedBytes++] =
@@ -87,20 +100,55 @@ public class BluetoothPacketEncoder extends PacketEncoder {
mPacketTimestamp = milliTimestamp;
}
- if (status != mRunningStatus) {
- mAccumulationBuffer[mAccumulatedBytes++] = (byte)status;
- if (MidiConstants.allowRunningStatus(status)) {
- mRunningStatus = status;
- } else if (MidiConstants.allowRunningStatus(status)) {
- mRunningStatus = 0;
+ if (isSysExStart || isSysExContinuation) {
+ // MidiFramer will end the packet with SysEx End if there is one in the buffer
+ boolean hasSysExEnd =
+ (msg[offset + count - 1] == MidiConstants.STATUS_END_SYSEX);
+ int remaining = (hasSysExEnd ? count - 1 : count);
+
+ while (remaining > 0) {
+ if (mAccumulatedBytes == mAccumulationBuffer.length) {
+ // write out our data if there is no more room
+ // if necessary, block until previous packet is sent
+ flushLocked(true);
+ appendHeader(milliTimestamp);
+ }
+
+ int copy = mAccumulationBuffer.length - mAccumulatedBytes;
+ if (copy > remaining) copy = remaining;
+ System.arraycopy(msg, offset, mAccumulationBuffer, mAccumulatedBytes, copy);
+ mAccumulatedBytes += copy;
+ offset += copy;
+ remaining -= copy;
}
- }
- // now copy data bytes
- int dataLength = count - 1;
- System.arraycopy(msg, 1, mAccumulationBuffer, mAccumulatedBytes, dataLength);
- // FIXME - handle long SysEx properly
- mAccumulatedBytes += dataLength;
+ if (hasSysExEnd) {
+ // SysEx End command must be preceeded by a timestamp byte
+ if (mAccumulatedBytes + 2 > mAccumulationBuffer.length) {
+ // write out our data if there is no more room
+ // if necessary, block until previous packet is sent
+ flushLocked(true);
+ appendHeader(milliTimestamp);
+ }
+ mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | (milliTimestamp & 0x7F));
+ mAccumulationBuffer[mAccumulatedBytes++] = MidiConstants.STATUS_END_SYSEX;
+ }
+ } else {
+ // Non-SysEx message
+ if (status != mRunningStatus) {
+ mAccumulationBuffer[mAccumulatedBytes++] = status;
+ if (MidiConstants.allowRunningStatus(status)) {
+ mRunningStatus = status;
+ } else if (MidiConstants.cancelsRunningStatus(status)) {
+ mRunningStatus = 0;
+ }
+ }
+
+ // now copy data bytes
+ int dataLength = count - 1;
+ System.arraycopy(msg, offset + 1, mAccumulationBuffer, mAccumulatedBytes, dataLength);
+ mAccumulatedBytes += dataLength;
+ }
// write the packet if possible, but do not block
flushLocked(false);
@@ -108,6 +156,18 @@ public class BluetoothPacketEncoder extends PacketEncoder {
}
};
+ private boolean appendHeader(int milliTimestamp) {
+ // write header if we are starting a new packet
+ if (mAccumulatedBytes == 0) {
+ // header byte with timestamp bits 7 - 12
+ mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | ((milliTimestamp >> 7) & 0x3F));
+ mPacketTimestamp = milliTimestamp;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
// MidiFramer for normalizing incoming data
private final MidiFramer mMidiFramer = new MidiFramer(mFramedDataReceiver);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 86c23c7..16b4c43 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -60,6 +60,7 @@ public class MediaNames {
public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp";
public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp";
public static final String VIDEO_HEVC_AAC = "/sdcard/media_api/video/HEVC_320_AAC_128.mp4";
+ public static final String VIDEO_MPEG2_AAC = "/sdcard/media_api/video/MPEG2_1500_AAC_128.mp4";
public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
public static final String VIDEO_WEBM = "/sdcard/media_api/video/big-buck-bunny_trailer.webm";
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 e730329..563b0f3 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
@@ -27,6 +27,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.Camera;
+import android.media.MediaCodec;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
@@ -225,10 +226,12 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
private boolean recordVideoFromSurface(
int frameRate, int captureRate, int width, int height,
- int videoFormat, int outFormat, String outFile, boolean videoOnly) {
+ int videoFormat, int outFormat, String outFile, boolean videoOnly,
+ Surface persistentSurface) {
Log.v(TAG,"recordVideoFromSurface");
MediaRecorder recorder = new MediaRecorder();
int sleepTime = 33; // normal capture at 33ms / frame
+ Surface surface = null;
try {
if (!videoOnly) {
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -246,8 +249,15 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
if (!videoOnly) {
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
+ if (persistentSurface != null) {
+ Log.v(TAG, "using persistent surface");
+ surface = persistentSurface;
+ recorder.usePersistentSurface(surface);
+ }
recorder.prepare();
- Surface surface = recorder.getSurface();
+ if (persistentSurface == null) {
+ surface = recorder.getSurface();
+ }
Paint paint = new Paint();
paint.setTextSize(16);
@@ -283,11 +293,15 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
Log.v(TAG, "stop");
recorder.stop();
- recorder.release();
} catch (Exception e) {
- Log.v("record video failed ", e.toString());
- recorder.release();
+ Log.v(TAG, "record video failed: " + e.toString());
return false;
+ } finally {
+ recorder.release();
+ // release surface if not using persistent surface
+ if (persistentSurface == null && surface != null) {
+ surface.release();
+ }
}
return true;
}
@@ -550,7 +564,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
MediaRecorder.OutputFormat.THREE_GPP, filename,
- k == 0 ? true : false /* videoOnly */);
+ k == 0 ? true : false /* videoOnly */, null);
if (success) {
success = validateVideo(filename, 352, 288);
}
@@ -564,6 +578,40 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
assertTrue("testSurfaceRecording", noOfFailure == 0);
}
+ public void testPersistentSurfaceRecording() {
+ boolean success = false;
+ int noOfFailure = 0;
+ Surface surface = null;
+ try {
+ int codec = MediaRecorder.VideoEncoder.H264;
+ int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
+ surface = MediaCodec.createPersistentInputSurface();
+ for (int k = 0; k < 2; k++) {
+ String filename = "/sdcard/surface_persistent" + k + ".3gp";
+
+ Log.v(TAG, "test persistent surface - round " + k);
+ success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
+ MediaRecorder.OutputFormat.THREE_GPP, filename,
+ true /* videoOnly */, surface);
+ if (success) {
+ success = validateVideo(filename, 352, 288);
+ }
+ if (!success) {
+ noOfFailure++;
+ }
+ }
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ } finally {
+ if (surface != null) {
+ Log.v(TAG, "releasing persistent surface");
+ surface.release();
+ surface = null;
+ }
+ }
+ assertTrue("testPersistentSurfaceRecording", noOfFailure == 0);
+ }
+
// Test recording from surface source with/without audio
public void testSurfaceRecordingTimeLapse() {
boolean success = false;
@@ -583,7 +631,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra
success = recordVideoFromSurface(
frameRate, captureRate, 352, 288, codec,
MediaRecorder.OutputFormat.THREE_GPP,
- filename, false /* videoOnly */);
+ filename, false /* videoOnly */, null);
if (success) {
success = validateVideo(filename, 352, 288);
if (success) {
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 244b07f..c528165 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -432,7 +432,22 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
assertTrue("HEVC playback memory test", memoryResult);
}
- // Test case 4: Capture the memory usage after every 20 video only recorded
+ // Test case 4: Capture the memory usage after every 20 mpeg2 playback
+ @LargeTest
+ public void testMPEG2VideoPlaybackMemoryUsage() throws Exception {
+ boolean memoryResult = false;
+
+ mStartPid = getMediaserverPid();
+ for (int i = 0; i < NUM_STRESS_LOOP; i++) {
+ mediaStressPlayback(MediaNames.VIDEO_MPEG2_AAC);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
+ }
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
+ assertTrue("MPEG2 playback memory test", memoryResult);
+ }
+
+ // Test case 5: Capture the memory usage after every 20 video only recorded
@LargeTest
public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
if (mCamcorderProfile != null) {
@@ -453,7 +468,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
}
- // Test case 5: Capture the memory usage after every 20 video only recorded
+ // Test case 6: Capture the memory usage after every 20 video only recorded
@LargeTest
public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
if (mCamcorderProfile != null) {
@@ -474,7 +489,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
}
- // Test case 6: Capture the memory usage after every 20 video and audio
+ // Test case 7: Capture the memory usage after every 20 video and audio
// recorded
@LargeTest
public void testRecordVideoAudioMemoryUsage() throws Exception {
@@ -496,7 +511,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
}
- // Test case 7: Capture the memory usage after every 20 audio only recorded
+ // Test case 8: Capture the memory usage after every 20 audio only recorded
@LargeTest
public void testRecordAudioOnlyMemoryUsage() throws Exception {
boolean memoryResult = false;
@@ -511,7 +526,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
assertTrue("audio record only memory test", memoryResult);
}
- // Test case 8: Capture the memory usage after every 20 camera preview
+ // Test case 9: Capture the memory usage after every 20 camera preview
@LargeTest
public void testCameraPreviewMemoryUsage() throws Exception {
boolean memoryResult = false;
diff --git a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
new file mode 100644
index 0000000..33f66a0
--- /dev/null
+++ b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
Binary files differ