diff options
Diffstat (limited to 'media')
171 files changed, 4928 insertions, 818 deletions
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java index e2115e4..f90f1e2 100644 --- a/media/java/android/media/AmrInputStream.java +++ b/media/java/android/media/AmrInputStream.java @@ -16,8 +16,6 @@ package android.media; -import android.util.Log; - import java.io.InputStream; import java.io.IOException; diff --git a/media/java/android/media/AsyncPlayer.java b/media/java/android/media/AsyncPlayer.java index 804528e..14b199e 100644 --- a/media/java/android/media/AsyncPlayer.java +++ b/media/java/android/media/AsyncPlayer.java @@ -22,8 +22,6 @@ import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; -import java.io.IOException; -import java.lang.IllegalStateException; import java.util.LinkedList; /** diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 49f498e..26e9cc5 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -31,7 +31,8 @@ public class AudioFormat { public static final int ENCODING_INVALID = 0; /** Default audio data format */ public static final int ENCODING_DEFAULT = 1; - // These two values must be kept in sync with JNI code for AudioTrack, AudioRecord + + // These two values must be kept in sync with core/jni/android_media_AudioFormat.h /** Audio data format: PCM 16 bit per sample. Guaranteed to be supported by devices. */ public static final int ENCODING_PCM_16BIT = 2; /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index d652cae..8ae06e0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.Intent; import android.media.RemoteController.OnClientUpdateListener; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 461b52f..a4891f8 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -179,13 +179,16 @@ public class AudioRecord /** * Audio session ID */ - private int mSessionId = 0; + private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; //--------------------------------------------------------- // Constructor, Finalize //-------------------- /** * Class constructor. + * Though some invalid parameters will result in an {@link IllegalArgumentException} exception, + * other errors do not. Thus you should call {@link #getState()} immediately after construction + * to confirm that the object is usable. * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for * recording source definitions. * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only @@ -221,7 +224,7 @@ public class AudioRecord // native initialization int[] session = new int[1]; - session[0] = 0; + session[0] = AudioSystem.AUDIO_SESSION_ALLOCATE; //TODO: update native initialization when information about hardware init failure // due to capture device already open is available. int initResult = native_setup( new WeakReference<AudioRecord>(this), @@ -239,7 +242,7 @@ public class AudioRecord // Convenience method for the constructor's parameter checks. - // This is where constructor IllegalArgumentException-s are thrown + // This and audioBuffSizeCheck are where constructor IllegalArgumentException-s are thrown // postconditions: // mRecordSource is valid // mChannelCount is valid @@ -247,7 +250,8 @@ public class AudioRecord // mAudioFormat is valid // mSampleRate is valid private void audioParamCheck(int audioSource, int sampleRateInHz, - int channelConfig, int audioFormat) { + int channelConfig, int audioFormat) + throws IllegalArgumentException { //-------------- // audio source @@ -311,7 +315,7 @@ public class AudioRecord // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT // postcondition: // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) - private void audioBuffSizeCheck(int audioBufferSize) { + private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException { // NB: this section is only valid with PCM data. // To update when supporting compressed formats int frameSizeInBytes = mChannelCount @@ -800,7 +804,7 @@ public class AudioRecord //-------------------- private native final int native_setup(Object audiorecord_this, - int recordSource, int sampleRate, int nbChannels, int audioFormat, + int recordSource, int sampleRate, int channelMask, int audioFormat, int buffSizeInBytes, int[] sessionId); private native final void native_finalize(); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 92474df..46b74da 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -21,20 +21,17 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; -import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -48,12 +45,8 @@ import android.content.res.XmlResourceParser; import android.database.ContentObserver; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; -import android.net.Uri; -import android.net.http.CertificateChainValidator; -import android.net.http.SslError; import android.os.Binder; import android.os.Build; -import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -68,9 +61,6 @@ import android.os.UserHandle; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; -import android.speech.RecognizerIntent; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -83,22 +73,18 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; -import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -import java.util.Stack; /** * The implementation of the volume manager service. @@ -120,8 +106,6 @@ public class AudioService extends IAudioService.Stub { protected static final boolean DEBUG_RC = false; /** Debug volumes */ protected static final boolean DEBUG_VOL = false; - /** Debug cert verification */ - private static final boolean DEBUG_CERTS = false; /** How long to delay before persisting a change in volume/ringer mode. */ private static final int PERSIST_DELAY = 500; @@ -4587,43 +4571,6 @@ public class AudioService extends IAudioService.Stub { } } - public int verifyX509CertChain(int numcerts, byte [] chain, String domain, String authType) { - - if (DEBUG_CERTS) { - Log.v(TAG, "java side verify for " - + numcerts + " certificates (" + chain.length + " bytes" - + ")for "+ domain + "/" + authType); - } - - byte[][] certChain = new byte[numcerts][]; - - ByteBuffer buf = ByteBuffer.wrap(chain); - for (int i = 0; i < numcerts; i++) { - int certlen = buf.getInt(); - if (DEBUG_CERTS) { - Log.i(TAG, "cert " + i +": " + certlen); - } - certChain[i] = new byte[certlen]; - buf.get(certChain[i]); - } - - try { - SslError err = CertificateChainValidator.verifyServerCertificates(certChain, - domain, authType); - if (DEBUG_CERTS) { - Log.i(TAG, "verified: " + err); - } - if (err == null) { - return -1; - } else { - return err.getPrimaryError(); - } - } catch (Exception e) { - Log.e(TAG, "failed to verify chain: " + e); - } - return SslError.SSL_INVALID; - } - //========================================================================================== // Camera shutter sound policy. diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 661b0fd..9c67bae 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -103,6 +103,9 @@ public class AudioSystem /** @deprecated */ @Deprecated public static final int ROUTE_ALL = 0xFFFFFFFF; + // Keep in sync with system/core/include/system/audio.h + public static final int AUDIO_SESSION_ALLOCATE = 0; + /* * Checks whether the specified stream type is active. * diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 01a6fc2..5611efb 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -16,8 +16,13 @@ package android.media; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.nio.ByteBuffer; +import java.nio.NioUtils; +import android.annotation.IntDef; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -145,6 +150,28 @@ public class AudioTrack private final static String TAG = "android.media.AudioTrack"; + /** @hide */ + @IntDef({ + WRITE_BLOCKING, + WRITE_NON_BLOCKING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WriteMode {} + + /** + * @hide CANDIDATE FOR PUBLIC API + * The write mode indicating the write operation will block until all data has been written, + * to be used in {@link #write(ByteBuffer, int, int, int)}. + */ + public final static int WRITE_BLOCKING = 0; + /** + * @hide CANDIDATE FOR PUBLIC API + * The write mode indicating the write operation will return immediately after + * queuing as much audio data for playback as possible without blocking, to be used in + * {@link #write(ByteBuffer, int, int, int)}. + */ + public final static int WRITE_NON_BLOCKING = 1; + //-------------------------------------------------------------------------- // Member variables //-------------------- @@ -211,7 +238,7 @@ public class AudioTrack /** * Audio session ID */ - private int mSessionId = 0; + private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; //-------------------------------- @@ -263,7 +290,7 @@ public class AudioTrack int bufferSizeInBytes, int mode) throws IllegalArgumentException { this(streamType, sampleRateInHz, channelConfig, audioFormat, - bufferSizeInBytes, mode, 0 /*session*/); + bufferSizeInBytes, mode, AudioSystem.AUDIO_SESSION_ALLOCATE); } /** @@ -839,8 +866,12 @@ public class AudioTrack /** - * Similar, except set volume of all channels to same value. - * @hide + * Sets the specified output volume values on all channels of this track. The value is clamped + * to the ({@link #getMinVolume()}, {@link #getMaxVolume()}) interval if outside this range. + * @param volume output attenuation for all channels. A value of 0.0f is silence, + * a value of 1.0f is no attenuation. + * @return error code or success, see {@link #SUCCESS}, + * {@link #ERROR_INVALID_OPERATION} */ public int setVolume(float volume) { return setStereoVolume(volume, volume); @@ -1080,7 +1111,8 @@ public class AudioTrack return ERROR_BAD_VALUE; } - int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat); + int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat, + true /*isBlocking*/); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) @@ -1137,6 +1169,75 @@ public class AudioTrack /** + * @hide CANDIDATE FOR PUBLIC API + * Writes the audio data to the audio sink for playback (streaming mode), + * or copies audio data for later playback (static buffer mode). + * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write + * mode is ignored. + * In streaming mode, the blocking behavior will depend on the write mode. + * @param audioData the buffer that holds the data to play, starting at the position reported + * by <code>audioData.position()</code>. + * <BR>Note that this method will not update the position in this buffer, therefore when + * writing a loop to write all the data in the buffer, you should increment the + * <code>offsetInBytes</code> parameter at each pass by the amount that was previously + * written for this buffer. + * @param offsetInBytes offset to read from in bytes (note this differs from + * <code>audioData.position()</code>). + * @param sizeInBytes number of bytes to read (note this differs from + * <code>audioData.remaining()</code>). + * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no + * effect in static mode. + * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written + * to the audio sink. + * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after + * queuing as much audio data for playback as possible without blocking. + * @return 0 or a positive number of bytes that were written, or + * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION} + */ + public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes, + @WriteMode int writeMode) { + + if (mState == STATE_UNINITIALIZED) { + Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); + return ERROR_INVALID_OPERATION; + } + + if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { + Log.e(TAG, "AudioTrack.write() called with invalid blocking mode"); + return ERROR_BAD_VALUE; + } + + if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) + || (offsetInBytes + sizeInBytes < 0) // detect integer overflow + || (offsetInBytes + sizeInBytes > audioData.remaining())) { + Log.e(TAG, "AudioTrack.write() called with invalid size/offset values"); + return ERROR_BAD_VALUE; + } + + int ret = 0; + if (audioData.isDirect()) { + ret = native_write_native_bytes(audioData, + audioData.position(), + offsetInBytes, sizeInBytes, mAudioFormat, + writeMode == WRITE_BLOCKING); + } else { + ret = native_write_byte(NioUtils.unsafeArray(audioData), + NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes, + sizeInBytes, mAudioFormat, + writeMode == WRITE_BLOCKING); + } + + if ((mDataLoadMode == MODE_STATIC) + && (mState == STATE_NO_STATIC_DATA) + && (ret > 0)) { + // benign race with respect to other APIs that read mState + mState = STATE_INITIALIZED; + } + + return ret; + } + + /** * Notifies the native resource to reuse the audio data already loaded in the native * layer, that is to rewind to start of buffer. * The track's creation mode must be {@link #MODE_STATIC}. @@ -1319,7 +1420,7 @@ public class AudioTrack //-------------------- private native final int native_setup(Object audiotrack_this, - int streamType, int sampleRate, int nbChannels, int audioFormat, + int streamType, int sampleRate, int channelMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId); private native final void native_finalize(); @@ -1335,11 +1436,15 @@ public class AudioTrack private native final void native_flush(); private native final int native_write_byte(byte[] audioData, - int offsetInBytes, int sizeInBytes, int format); + int offsetInBytes, int sizeInBytes, int format, + boolean isBlocking); private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); + private native final int native_write_native_bytes(Object audioData, + int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking); + private native final int native_reload_static(); private native final int native_get_native_frame_count(); diff --git a/media/java/android/media/EncoderCapabilities.java b/media/java/android/media/EncoderCapabilities.java index 71cb1b3..332e360 100644 --- a/media/java/android/media/EncoderCapabilities.java +++ b/media/java/android/media/EncoderCapabilities.java @@ -18,7 +18,6 @@ package android.media; import java.util.List; import java.util.ArrayList; -import android.util.Log; /** * The EncoderCapabilities class is used to retrieve the diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index b5c3631..2f08325 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -35,8 +35,6 @@ import android.view.KeyEvent; */ interface IAudioService { - int verifyX509CertChain(int chainsize, in byte[] chain, String host, String authtype); - void adjustVolume(int direction, int flags, String callingPackage); boolean isLocalOrRemoteMusicActive(); @@ -238,5 +236,4 @@ interface IAudioService { AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); boolean isCameraSoundForced(); - } diff --git a/media/java/android/media/IMediaHTTPConnection.aidl b/media/java/android/media/IMediaHTTPConnection.aidl new file mode 100644 index 0000000..55ffc2e --- /dev/null +++ b/media/java/android/media/IMediaHTTPConnection.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.os.IBinder; + +/** MUST STAY IN SYNC WITH NATIVE CODE at libmedia/IMediaHTTPConnection.{cpp,h} */ + +/** @hide */ +interface IMediaHTTPConnection +{ + IBinder connect(in String uri, in String headers); + void disconnect(); + + int readAt(long offset, int size); + long getSize(); + String getMIMEType(); + String getUri(); +} + diff --git a/media/java/android/media/IMediaHTTPService.aidl b/media/java/android/media/IMediaHTTPService.aidl new file mode 100644 index 0000000..8aaf6b3 --- /dev/null +++ b/media/java/android/media/IMediaHTTPService.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.media.IMediaHTTPConnection; + +/** MUST STAY IN SYNC WITH NATIVE CODE at libmedia/IMediaHTTPService.{cpp,h} */ + +/** @hide */ +interface IMediaHTTPService +{ + IMediaHTTPConnection makeHTTPConnection(); +} diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 23abce7..a346e17 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -21,7 +21,8 @@ import java.lang.AutoCloseable; /** * <p>A single complete image buffer to use with a media source such as a - * {@link MediaCodec}.</p> + * {@link MediaCodec} or a + * {@link android.hardware.camera2.CameraDevice CameraDevice}.</p> * * <p>This class allows for efficient direct application access to the pixel * data of the Image through one or more @@ -82,6 +83,15 @@ public abstract class Image implements AutoCloseable { * plane (4:2:0 subsampling). Each pixel sample in each plane has 8 bits. * Each plane has its own row stride and pixel stride.</td> * </tr> + * <tr> + * <td>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</td> + * <td>1</td> + * <td>A single plane of raw sensor image data, with 16 bits per color + * sample. The details of the layout need to be queried from the source of + * the raw sensor data, such as + * {@link android.hardware.camera2.CameraDevice CameraDevice}. + * </td> + * </tr> * </table> * * @see android.graphics.ImageFormat diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index d454c42..1bd32c4 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -32,7 +32,8 @@ import java.nio.ByteOrder; * rendered into a {@link android.view.Surface}</p> * * <p>Several Android media API classes accept Surface objects as targets to - * render to, including {@link MediaPlayer}, {@link MediaCodec}, and + * render to, including {@link MediaPlayer}, {@link MediaCodec}, + * {@link android.hardware.camera2.CameraDevice}, and * {@link android.renderscript.Allocation RenderScript Allocations}. The image * sizes and formats that can be used with each source vary, and should be * checked in the documentation for the specific API.</p> diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index ddf88df..115786c 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -21,8 +21,12 @@ import android.media.MediaCodecList; import android.media.MediaCrypto; import android.media.MediaFormat; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.view.Surface; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map; @@ -66,8 +70,8 @@ import java.util.Map; * * Each codec maintains a number of input and output buffers that are * referred to by index in API calls. - * The contents of these buffers is represented by the ByteBuffer[] arrays - * accessible through getInputBuffers() and getOutputBuffers(). + * The contents of these buffers are represented by the ByteBuffer[] arrays + * accessible through {@link #getInputBuffers} and {@link #getOutputBuffers}. * * After a successful call to {@link #start} the client "owns" neither * input nor output buffers, subsequent calls to {@link #dequeueInputBuffer} @@ -117,7 +121,18 @@ import java.util.Map; * own any buffers anymore. * Note that the format of the data submitted after a flush must not change, * flush does not support format discontinuities, - * for this a full stop(), configure(), start() cycle is necessary. + * for this a full {@link #stop}, {@link #configure}, {@link #start} + * cycle is necessary. + * + * <p> The factory methods + * {@link #createByCodecName}, + * {@link #createDecoderByType}, + * and {@link #createEncoderByType} + * throw {@link java.io.IOException} on failure which + * the caller must catch or declare to pass up. + * Other methods will throw {@link java.lang.IllegalStateException} + * if the codec is in an Uninitialized, Invalid, or Error state (e.g. not + * initialized properly). Exceptions are thrown elsewhere as noted. </p> * */ final public class MediaCodec { @@ -161,6 +176,34 @@ final public class MediaCodec { */ public static final int BUFFER_FLAG_END_OF_STREAM = 4; + private EventHandler mEventHandler; + private volatile NotificationCallback mNotificationCallback; + + static final int EVENT_NOTIFY = 1; + + private class EventHandler extends Handler { + private MediaCodec mCodec; + + public EventHandler(MediaCodec codec, Looper looper) { + super(looper); + mCodec = codec; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_NOTIFY: + { + NotificationCallback cb = mNotificationCallback; + if (cb != null) { + cb.onCodecNotify(mCodec); + } + break; + } + } + } + } + /** * Instantiate a decoder supporting input data of the given mime type. * @@ -181,16 +224,22 @@ final public class MediaCodec { * </ul> * * @param type The mime type of the input data. + * @throws IOException if the codec cannot be created. + * @throws IllegalArgumentException if type is null. */ - public static MediaCodec createDecoderByType(String type) { + public static MediaCodec createDecoderByType(String type) + throws IOException { return new MediaCodec(type, true /* nameIsType */, false /* encoder */); } /** * Instantiate an encoder supporting output data of the given mime type. * @param type The desired mime type of the output data. + * @throws IOException if the codec cannot be created. + * @throws IllegalArgumentException if type is null. */ - public static MediaCodec createEncoderByType(String type) { + public static MediaCodec createEncoderByType(String type) + throws IOException { return new MediaCodec(type, true /* nameIsType */, true /* encoder */); } @@ -199,14 +248,26 @@ final public class MediaCodec { * use this method to instantiate it. Use with caution. * Likely to be used with information obtained from {@link android.media.MediaCodecList} * @param name The name of the codec to be instantiated. + * @throws IOException if the codec cannot be created. + * @throws IllegalArgumentException if name is null. */ - public static MediaCodec createByCodecName(String name) { + public static MediaCodec createByCodecName(String name) + throws IOException { return new MediaCodec( name, false /* nameIsType */, false /* unused */); } private MediaCodec( String name, boolean nameIsType, boolean encoder) { + Looper looper; + if ((looper = Looper.myLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else if ((looper = Looper.getMainLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else { + mEventHandler = null; + } + native_setup(name, nameIsType, encoder); } @@ -272,6 +333,10 @@ final public class MediaCodec { * <p> * The application is responsible for calling release() on the Surface when * done. + * <p> + * The Surface must be rendered with a hardware-accelerated API, such as OpenGL ES. + * {@link android.view.Surface#lockCanvas(android.graphics.Rect)} may fail or produce + * unexpected results. */ public native final Surface createInputSurface(); @@ -287,7 +352,15 @@ final public class MediaCodec { * To ensure that it is available to other client call {@link #release} * and don't just rely on garbage collection to eventually do this for you. */ - public native final void stop(); + public final void stop() { + native_stop(); + + if (mEventHandler != null) { + mEventHandler.removeMessages(EVENT_NOTIFY); + } + } + + private native final void native_stop(); /** * Flush both input and output ports of the component, all indices @@ -618,6 +691,22 @@ final public class MediaCodec { setParameters(keys, values); } + public void setNotificationCallback(NotificationCallback cb) { + mNotificationCallback = cb; + } + + public interface NotificationCallback { + void onCodecNotify(MediaCodec codec); + } + + private void postEventFromNative( + int what, int arg1, int arg2, Object obj) { + if (mEventHandler != null) { + Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj); + mEventHandler.sendMessage(msg); + } + } + private native final void setParameters(String[] keys, Object[] values); /** diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index f5a703b..7170ffb 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -16,7 +16,6 @@ package android.media; -import android.media.MediaDrmException; import java.lang.ref.WeakReference; import java.util.UUID; import java.util.HashMap; @@ -24,7 +23,6 @@ import java.util.List; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.Bundle; import android.os.Parcel; import android.util.Log; diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index c3e5035..f2753ee 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -21,7 +21,9 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.MediaCodec; import android.media.MediaFormat; +import android.media.MediaHTTPService; import android.net.Uri; +import android.os.IBinder; import java.io.FileDescriptor; import java.io.IOException; @@ -137,11 +139,19 @@ final public class MediaExtractor { ++i; } } - setDataSource(path, keys, values); + + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + keys, + values); } - private native final void setDataSource( - String path, String[] keys, String[] values) throws IOException; + private native final void nativeSetDataSource( + IBinder httpServiceBinder, + String path, + String[] keys, + String[] values) throws IOException; /** * Sets the data source (file-path or http URL) to use. @@ -156,7 +166,11 @@ final public class MediaExtractor { * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. */ public final void setDataSource(String path) throws IOException { - setDataSource(path, null, null); + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + null, + null); } /** diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 761ecca..526656a 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -16,17 +16,12 @@ package android.media; -import android.content.ContentValues; -import android.provider.MediaStore.Audio; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; import android.media.DecoderCapabilities; import android.media.DecoderCapabilities.VideoDecoder; import android.media.DecoderCapabilities.AudioDecoder; import android.mtp.MtpConstants; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index 34008bb..b155cda 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -51,7 +51,6 @@ import android.util.Log; import android.util.Slog; import android.view.KeyEvent; -import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; @@ -263,7 +262,7 @@ public class MediaFocusControl implements OnFinished { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForServer di = - (DisplayInfoForServer) displayIterator.next(); + displayIterator.next(); if (di.mClientNotifListComp != null) { boolean wasEnabled = di.mEnabled; di.mEnabled = isComponentInStringArray(di.mClientNotifListComp, @@ -539,7 +538,7 @@ public class MediaFocusControl implements OnFinished { // evaluated it, traversal order doesn't matter here) Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { - FocusRequester fr = (FocusRequester)stackIterator.next(); + FocusRequester fr = stackIterator.next(); if(fr.hasSameClient(clientToRemove)) { Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " + clientToRemove); @@ -563,7 +562,7 @@ public class MediaFocusControl implements OnFinished { // evaluated it, traversal order doesn't matter here) Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { - FocusRequester fr = (FocusRequester)stackIterator.next(); + FocusRequester fr = stackIterator.next(); if(fr.hasSameBinder(cb)) { Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " + cb); stackIterator.remove(); @@ -931,33 +930,11 @@ public class MediaFocusControl implements OnFinished { } } - protected static boolean isMediaKeyCode(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_MEDIA_CLOSE: - case KeyEvent.KEYCODE_MEDIA_EJECT: - case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: - return true; - default: - return false; - } - } - private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) { if (keyEvent == null) { return false; } - return MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode()); + return KeyEvent.isMediaKey(keyEvent.getKeyCode()); } /** @@ -1384,7 +1361,7 @@ public class MediaFocusControl implements OnFinished { synchronized(mRCStack) { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); pw.println(" IRCD: " + di.mRcDisplay + " -- w:" + di.mArtworkExpectedWidth + " -- h:" + di.mArtworkExpectedHeight + @@ -1411,7 +1388,7 @@ public class MediaFocusControl implements OnFinished { // (using an iterator on the stack so we can safely remove an entry after having // evaluated it, traversal order doesn't matter here) while(stackIterator.hasNext()) { - RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); + RemoteControlStackEntry rcse = stackIterator.next(); if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) { // a stack entry is from the package being removed, remove it from the stack stackIterator.remove(); @@ -2076,7 +2053,7 @@ public class MediaFocusControl implements OnFinished { // remove the display from the list final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); if (di.mRcDisplay == mRcDisplay) { if (DEBUG_RC) Log.w(TAG, " RCD removed from list"); displayIterator.remove(); @@ -2100,7 +2077,7 @@ public class MediaFocusControl implements OnFinished { private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); try { rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth, di.mArtworkExpectedHeight); @@ -2138,7 +2115,7 @@ public class MediaFocusControl implements OnFinished { private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { return true; } @@ -2217,7 +2194,7 @@ public class MediaFocusControl implements OnFinished { boolean displayWasPluggedIn = false; final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext() && !displayWasPluggedIn) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { displayWasPluggedIn = true; di.release(); @@ -2259,7 +2236,7 @@ public class MediaFocusControl implements OnFinished { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); boolean artworkSizeUpdate = false; while (displayIterator.hasNext() && !artworkSizeUpdate) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) { di.mArtworkExpectedWidth = w; @@ -2306,7 +2283,7 @@ public class MediaFocusControl implements OnFinished { // (display stack traversal order doesn't matter). final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { - final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); + final DisplayInfoForServer di = displayIterator.next(); if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { di.mWantsPositionSync = wantsSync; rcdRegistered = true; diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java new file mode 100644 index 0000000..67680a8 --- /dev/null +++ b/media/java/android/media/MediaHTTPConnection.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.os.IBinder; +import android.os.StrictMode; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.URL; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; + +/** @hide */ +public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { + private static final String TAG = "MediaHTTPConnection"; + private static final boolean VERBOSE = false; + + private long mCurrentOffset = -1; + private URL mURL = null; + private Map<String, String> mHeaders = null; + private HttpURLConnection mConnection = null; + private long mTotalSize = -1; + private InputStream mInputStream = null; + + public MediaHTTPConnection() { + if (CookieHandler.getDefault() == null) { + CookieHandler.setDefault(new CookieManager()); + } + + native_setup(); + } + + @Override + public IBinder connect(String uri, String headers) { + if (VERBOSE) { + Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers); + } + + try { + disconnect(); + mURL = new URL(uri); + mHeaders = convertHeaderStringToMap(headers); + } catch (MalformedURLException e) { + return null; + } + + return native_getIMemory(); + } + + private Map<String, String> convertHeaderStringToMap(String headers) { + HashMap<String, String> map = new HashMap<String, String>(); + + String[] pairs = headers.split("\r\n"); + for (String pair : pairs) { + int colonPos = pair.indexOf(":"); + if (colonPos >= 0) { + String key = pair.substring(0, colonPos); + String val = pair.substring(colonPos + 1); + + map.put(key, val); + } + } + + return map; + } + + @Override + public void disconnect() { + teardownConnection(); + mHeaders = null; + mURL = null; + } + + private void teardownConnection() { + if (mConnection != null) { + mInputStream = null; + + mConnection.disconnect(); + mConnection = null; + + mCurrentOffset = -1; + } + } + + private void seekTo(long offset) throws IOException { + teardownConnection(); + + try { + mConnection = (HttpURLConnection)mURL.openConnection(); + + if (mHeaders != null) { + for (Map.Entry<String, String> entry : mHeaders.entrySet()) { + mConnection.setRequestProperty( + entry.getKey(), entry.getValue()); + } + } + + if (offset > 0) { + mConnection.setRequestProperty( + "Range", "bytes=" + offset + "-"); + } + + int response = mConnection.getResponseCode(); + // remember the current, possibly redirected URL + mURL = mConnection.getURL(); + + if (response == HttpURLConnection.HTTP_PARTIAL) { + // Partial content, we cannot just use getContentLength + // because what we want is not just the length of the range + // returned but the size of the full content if available. + + String contentRange = + mConnection.getHeaderField("Content-Range"); + + mTotalSize = -1; + if (contentRange != null) { + // format is "bytes xxx-yyy/zzz + // where "zzz" is the total number of bytes of the + // content or '*' if unknown. + + int lastSlashPos = contentRange.lastIndexOf('/'); + if (lastSlashPos >= 0) { + String total = + contentRange.substring(lastSlashPos + 1); + + try { + mTotalSize = Long.parseLong(total); + } catch (NumberFormatException e) { + } + } + } + } else if (response != HttpURLConnection.HTTP_OK) { + throw new IOException(); + } else { + mTotalSize = mConnection.getContentLength(); + } + + if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) { + // Some servers simply ignore "Range" requests and serve + // data from the start of the content. + throw new IOException(); + } + + mInputStream = + new BufferedInputStream(mConnection.getInputStream()); + + mCurrentOffset = offset; + } catch (IOException e) { + mTotalSize = -1; + mInputStream = null; + mConnection = null; + mCurrentOffset = -1; + + throw e; + } + } + + @Override + public int readAt(long offset, int size) { + return native_readAt(offset, size); + } + + private int readAt(long offset, byte[] data, int size) { + StrictMode.ThreadPolicy policy = + new StrictMode.ThreadPolicy.Builder().permitAll().build(); + + StrictMode.setThreadPolicy(policy); + + try { + if (offset != mCurrentOffset) { + seekTo(offset); + } + + int n = mInputStream.read(data, 0, size); + + if (n == -1) { + // InputStream signals EOS using a -1 result, our semantics + // are to return a 0-length read. + n = 0; + } + + mCurrentOffset += n; + + if (VERBOSE) { + Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); + } + + return n; + } catch (IOException e) { + if (VERBOSE) { + Log.d(TAG, "readAt " + offset + " / " + size + " => -1"); + } + return -1; + } catch (Exception e) { + if (VERBOSE) { + Log.d(TAG, "unknown exception " + e); + Log.d(TAG, "readAt " + offset + " / " + size + " => -1"); + } + return -1; + } + } + + @Override + public long getSize() { + if (mConnection == null) { + try { + seekTo(0); + } catch (IOException e) { + return -1; + } + } + + return mTotalSize; + } + + @Override + public String getMIMEType() { + if (mConnection == null) { + try { + seekTo(0); + } catch (IOException e) { + return "application/octet-stream"; + } + } + + return mConnection.getContentType(); + } + + @Override + public String getUri() { + return mURL.toString(); + } + + @Override + protected void finalize() { + native_finalize(); + } + + private static native final void native_init(); + private native final void native_setup(); + private native final void native_finalize(); + + private native final IBinder native_getIMemory(); + private native final int native_readAt(long offset, int size); + + static { + System.loadLibrary("media_jni"); + native_init(); + } + + private int mNativeContext; + +} diff --git a/media/java/android/media/MediaHTTPService.java b/media/java/android/media/MediaHTTPService.java new file mode 100644 index 0000000..3b4703d --- /dev/null +++ b/media/java/android/media/MediaHTTPService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +/** @hide */ +public class MediaHTTPService extends IMediaHTTPService.Stub { + private static final String TAG = "MediaHTTPService"; + + public MediaHTTPService() { + } + + public IMediaHTTPConnection makeHTTPConnection() { + return new MediaHTTPConnection(); + } + + /* package private */static IBinder createHttpServiceBinderIfNecessary( + String path) { + if (path.startsWith("http://") + || path.startsWith("https://") + || path.startsWith("widevine://")) { + return (new MediaHTTPService()).asBinder(); + } + + return null; + } +} diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java index 373ba11..3bfdb5a 100644 --- a/media/java/android/media/MediaMetadataEditor.java +++ b/media/java/android/media/MediaMetadataEditor.java @@ -18,7 +18,6 @@ package android.media; import android.graphics.Bitmap; import android.os.Bundle; -import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import android.util.SparseIntArray; diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index db27d09..9a69c06 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; +import android.os.IBinder; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -100,11 +101,16 @@ public class MediaMetadataRetriever values[i] = entry.getValue(); ++i; } - _setDataSource(uri, keys, values); + + _setDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(uri), + uri, + keys, + values); } private native void _setDataSource( - String uri, String[] keys, String[] values) + IBinder httpServiceBinder, String uri, String[] keys, String[] values) throws IllegalArgumentException; /** diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index e5c97e7..f518ab2 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -17,13 +17,11 @@ package android.media; import android.media.MediaCodec.BufferInfo; - import dalvik.system.CloseGuard; -import java.io.File; import java.io.FileDescriptor; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.Map; @@ -79,6 +77,7 @@ final public class MediaMuxer { private OutputFormat() {} /** MPEG4 media file format*/ public static final int MUXER_OUTPUT_MPEG_4 = 0; + public static final int MUXER_OUTPUT_WEBM = 1; }; // All the native functions are listed here. @@ -120,20 +119,22 @@ final public class MediaMuxer { if (path == null) { throw new IllegalArgumentException("path must not be null"); } - if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) { + if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 && + format != OutputFormat.MUXER_OUTPUT_WEBM) { throw new IllegalArgumentException("format is invalid"); } - FileOutputStream fos = null; + // Use RandomAccessFile so we can open the file with RW access; + // RW access allows the native writer to memory map the output file. + RandomAccessFile file = null; try { - File file = new File(path); - fos = new FileOutputStream(file); - FileDescriptor fd = fos.getFD(); + file = new RandomAccessFile(path, "rws"); + FileDescriptor fd = file.getFD(); mNativeObject = nativeSetup(fd, format); mState = MUXER_STATE_INITIALIZED; mCloseGuard.open("release"); } finally { - if (fos != null) { - fos.close(); + if (file != null) { + file.close(); } } } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 41ba5d6..e20a4af 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -22,27 +22,23 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.AssetFileDescriptor; -import android.net.Proxy; -import android.net.ProxyProperties; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; -import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.PowerManager; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; -import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.media.AudioManager; import android.media.MediaFormat; import android.media.MediaTimeProvider; -import android.media.MediaTimeProvider.OnMediaTimeListener; import android.media.SubtitleController; import android.media.SubtitleData; @@ -884,8 +880,6 @@ public class MediaPlayer implements SubtitleController.Listener */ public void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { - disableProxyListener(); - String scheme = uri.getScheme(); if(scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); @@ -919,11 +913,6 @@ public class MediaPlayer implements SubtitleController.Listener Log.d(TAG, "Couldn't open file on client side, trying server side"); setDataSource(uri.toString(), headers); - - if (scheme.equalsIgnoreCase("http") - || scheme.equalsIgnoreCase("https")) { - setupProxyListener(context); - } } /** @@ -974,8 +963,6 @@ public class MediaPlayer implements SubtitleController.Listener private void setDataSource(String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { - disableProxyListener(); - final Uri uri = Uri.parse(path); if ("file".equals(uri.getScheme())) { path = uri.getPath(); @@ -992,8 +979,18 @@ public class MediaPlayer implements SubtitleController.Listener } } - private native void _setDataSource( + private void _setDataSource( String path, String[] keys, String[] values) + throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + keys, + values); + } + + private native void nativeSetDataSource( + IBinder httpServiceBinder, String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; /** @@ -1021,7 +1018,6 @@ public class MediaPlayer implements SubtitleController.Listener */ public void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException { - disableProxyListener(); _setDataSource(fd, offset, length); } @@ -1393,8 +1389,6 @@ public class MediaPlayer implements SubtitleController.Listener if (mEventHandler != null) { mEventHandler.removeCallbacksAndMessages(null); } - - disableProxyListener(); } private native void _reset(); @@ -2742,59 +2736,6 @@ public class MediaPlayer implements SubtitleController.Listener mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); } - private Context mProxyContext = null; - private ProxyReceiver mProxyReceiver = null; - - private void setupProxyListener(Context context) { - IntentFilter filter = new IntentFilter(); - filter.addAction(Proxy.PROXY_CHANGE_ACTION); - mProxyReceiver = new ProxyReceiver(); - mProxyContext = context; - - Intent currentProxy = - context.getApplicationContext().registerReceiver(mProxyReceiver, filter); - - if (currentProxy != null) { - handleProxyBroadcast(currentProxy); - } - } - - private void disableProxyListener() { - if (mProxyReceiver == null) { - return; - } - - Context appContext = mProxyContext.getApplicationContext(); - if (appContext != null) { - appContext.unregisterReceiver(mProxyReceiver); - } - - mProxyReceiver = null; - mProxyContext = null; - } - - private void handleProxyBroadcast(Intent intent) { - ProxyProperties props = - (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO); - - if (props == null || props.getHost() == null) { - updateProxyConfig(null); - } else { - updateProxyConfig(props); - } - } - - private class ProxyReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) { - handleProxyBroadcast(intent); - } - } - } - - private native void updateProxyConfig(ProxyProperties props); - /** @hide */ static class TimeProvider implements MediaPlayer.OnSeekCompleteListener, MediaTimeProvider { diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 5a9d577..21e2f4b 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -124,6 +124,18 @@ public class MediaRecorder public native void setCamera(Camera c); /** + * Gets the surface to record from when using SURFACE video source. + * <p> + * Should only be called after prepare(). Frames rendered before start() + * will be discarded. + * </p> + * @throws IllegalStateException if it is called before prepare(), after + * stop() or is called when VideoSource is not set to SURFACE. + * @see android.media.MediaRecorder.VideoSource + */ + public native Surface getSurface(); + + /** * Sets a Surface to show a preview of recorded media (video). Calls this * before prepare() to make sure that the desirable preview display is * set. If {@link #setCamera(Camera)} is used and the surface has been @@ -225,10 +237,23 @@ public class MediaRecorder */ private VideoSource() {} public static final int DEFAULT = 0; - /** Camera video source */ + /** Camera video source + * <p> + * Using android.hardware.Camera as video source. + * </p> + */ public static final int CAMERA = 1; - /** @hide */ - public static final int GRALLOC_BUFFER = 2; + /** Surface video source + * <p> + * Using a Surface as video source. + * </p><p> + * This flag must be used when recording from an + * android.hardware.camera2.CameraDevice source. + * </p><p> + * When using this video source type, use {@link MediaRecorder#getSurface()} + * to retrieve the surface created by MediaRecorder. + */ + public static final int SURFACE = 2; } /** @@ -392,8 +417,8 @@ public class MediaRecorder setParameter("time-lapse-enable=1"); double timeBetweenFrameCapture = 1 / fps; - int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture); - setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs); + long timeBetweenFrameCaptureUs = (long) (1000000 * timeBetweenFrameCapture); + setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureUs); } /** diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 53835e2..72f3e1a 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -55,6 +55,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; @@ -1400,27 +1401,60 @@ public class MediaScanner return false; } - public static boolean isNoMediaPath(String path) { - if (path == null) return false; + private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>(); + private static HashMap<String,String> mMediaPaths = new HashMap<String,String>(); + /* MediaProvider calls this when a .nomedia file is added or removed */ + public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) { + synchronized (MediaScanner.class) { + if (clearMediaPaths) { + mMediaPaths.clear(); + } + if (clearNoMediaPaths) { + mNoMediaPaths.clear(); + } + } + } + + public static boolean isNoMediaPath(String path) { + if (path == null) { + return false; + } // return true if file or any parent directory has name starting with a dot - if (path.indexOf("/.") >= 0) return true; - - // now check to see if any parent directories have a ".nomedia" file - // start from 1 so we don't bother checking in the root directory - int offset = 1; - while (offset >= 0) { - int slashIndex = path.indexOf('/', offset); - if (slashIndex > offset) { - slashIndex++; // move past slash - File file = new File(path.substring(0, slashIndex) + ".nomedia"); - if (file.exists()) { - // we have a .nomedia in one of the parent directories - return true; + if (path.indexOf("/.") >= 0) { + return true; + } + + int firstSlash = path.lastIndexOf('/'); + if (firstSlash <= 0) { + return false; + } + String parent = path.substring(0, firstSlash); + + synchronized (MediaScanner.class) { + if (mNoMediaPaths.containsKey(parent)) { + return true; + } else if (!mMediaPaths.containsKey(parent)) { + // check to see if any parent directories have a ".nomedia" file + // start from 1 so we don't bother checking in the root directory + int offset = 1; + while (offset >= 0) { + int slashIndex = path.indexOf('/', offset); + if (slashIndex > offset) { + slashIndex++; // move past slash + File file = new File(path.substring(0, slashIndex) + ".nomedia"); + if (file.exists()) { + // we have a .nomedia in one of the parent directories + mNoMediaPaths.put(parent, ""); + return true; + } + } + offset = slashIndex; } + mMediaPaths.put(parent, ""); } - offset = slashIndex; } + return isNoMediaFile(path); } diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java index b566653..eb543b4 100644 --- a/media/java/android/media/Metadata.java +++ b/media/java/android/media/Metadata.java @@ -16,7 +16,6 @@ package android.media; -import android.graphics.Bitmap; import android.os.Parcel; import android.util.Log; diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java index 63b149c..23c3652 100644 --- a/media/java/android/media/MiniThumbFile.java +++ b/media/java/android/media/MiniThumbFile.java @@ -16,7 +16,6 @@ package android.media; -import android.graphics.Bitmap; import android.net.Uri; import android.os.Environment; import android.util.Log; diff --git a/media/java/android/media/Rating.java b/media/java/android/media/Rating.java index 82c0392..f4fbe2c 100644 --- a/media/java/android/media/Rating.java +++ b/media/java/android/media/Rating.java @@ -16,7 +16,6 @@ package android.media; -import android.graphics.Bitmap; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -30,8 +29,13 @@ import android.util.Log; * through one of the factory methods. */ public final class Rating implements Parcelable { - private final static String TAG = "Rating"; + /** + * Indicates a rating style is not supported. A Rating will never have this + * type, but can be used by other classes to indicate they do not support + * Rating. + */ + public final static int RATING_NONE = 0; /** * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 0c00aba..c2c61d3 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -24,13 +24,11 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; -import android.media.MediaMetadataRetriever; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 910b24c..3711585 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -16,7 +16,6 @@ package android.media; -import android.Manifest; import android.app.ActivityManager; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; @@ -29,8 +28,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Log; @@ -267,7 +264,7 @@ public final class RemoteController * @throws IllegalArgumentException */ public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException { - if (!MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode())) { + if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { throw new IllegalArgumentException("not a media key event"); } final PendingIntent pi; diff --git a/media/java/android/media/ResampleInputStream.java b/media/java/android/media/ResampleInputStream.java index b025e25..80919f7 100644 --- a/media/java/android/media/ResampleInputStream.java +++ b/media/java/android/media/ResampleInputStream.java @@ -16,8 +16,6 @@ package android.media; -import android.util.Log; - import java.io.InputStream; import java.io.IOException; diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 8e4004b..e211b99 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -23,7 +23,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.app.Activity; import android.content.ContentUris; import android.content.Context; -import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.Environment; diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index fbfc574..f1b256e 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -18,7 +18,6 @@ package android.media; import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.lang.ref.WeakReference; import android.content.Context; diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java index f552e82..3e6f6f9 100644 --- a/media/java/android/media/SubtitleData.java +++ b/media/java/android/media/SubtitleData.java @@ -17,7 +17,6 @@ package android.media; import android.os.Parcel; -import android.util.Log; /** * @hide diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index 756638c..daa5fa5 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -17,9 +17,6 @@ package android.media; import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -29,15 +26,12 @@ import android.media.MediaMetadataRetriever; import android.media.MediaFile.MediaFileType; import android.net.Uri; import android.os.ParcelFileDescriptor; -import android.provider.BaseColumns; import android.provider.MediaStore.Images; -import android.provider.MediaStore.Images.Thumbnails; import android.util.Log; import java.io.FileInputStream; import java.io.FileDescriptor; import java.io.IOException; -import java.io.OutputStream; /** * Thumbnail generation routines for media provider. diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java index 4dec081..58d3520 100644 --- a/media/java/android/media/WebVttRenderer.java +++ b/media/java/android/media/WebVttRenderer.java @@ -1136,11 +1136,15 @@ class WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.Rendering } public WebVttRenderingWidget(Context context, AttributeSet attrs) { - this(context, null, 0); + this(context, attrs, 0); } - public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); @@ -1521,6 +1525,8 @@ class WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.Rendering if (DEBUG) { setBackgroundColor(DEBUG_REGION_BACKGROUND); + } else { + setBackgroundColor(captionStyle.windowColor); } } @@ -1533,6 +1539,8 @@ class WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.Rendering final CueLayout cueBox = mRegionCueBoxes.get(i); cueBox.setCaptionStyle(captionStyle, fontSize); } + + setBackgroundColor(captionStyle.windowColor); } /** diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java index cc121a3..9b381cc 100644 --- a/media/java/android/media/audiofx/AudioEffect.java +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -22,7 +22,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; -import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.ByteOrder; import java.nio.ByteBuffer; diff --git a/media/java/android/media/audiofx/BassBoost.java b/media/java/android/media/audiofx/BassBoost.java index 91459ed..a46cc22 100644 --- a/media/java/android/media/audiofx/BassBoost.java +++ b/media/java/android/media/audiofx/BassBoost.java @@ -16,16 +16,9 @@ package android.media.audiofx; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.os.Bundle; import android.util.Log; -import java.nio.ByteOrder; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.util.StringTokenizer; diff --git a/media/java/android/media/audiofx/EnvironmentalReverb.java b/media/java/android/media/audiofx/EnvironmentalReverb.java index f1f582e..ef1c4c3 100644 --- a/media/java/android/media/audiofx/EnvironmentalReverb.java +++ b/media/java/android/media/audiofx/EnvironmentalReverb.java @@ -16,15 +16,7 @@ package android.media.audiofx; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.os.Bundle; -import android.util.Log; - -import java.nio.ByteOrder; -import java.nio.ByteBuffer; import java.util.StringTokenizer; /** diff --git a/media/java/android/media/audiofx/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java index 7f38955..7abada0 100644 --- a/media/java/android/media/audiofx/Equalizer.java +++ b/media/java/android/media/audiofx/Equalizer.java @@ -16,16 +16,9 @@ package android.media.audiofx; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.os.Bundle; import android.util.Log; -import java.nio.ByteOrder; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.util.StringTokenizer; diff --git a/media/java/android/media/audiofx/PresetReverb.java b/media/java/android/media/audiofx/PresetReverb.java index 7a89ae7..ef91667 100644 --- a/media/java/android/media/audiofx/PresetReverb.java +++ b/media/java/android/media/audiofx/PresetReverb.java @@ -16,15 +16,7 @@ package android.media.audiofx; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.os.Bundle; -import android.util.Log; - -import java.nio.ByteOrder; -import java.nio.ByteBuffer; import java.util.StringTokenizer; diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java index 68a7b88..6b20006 100644 --- a/media/java/android/media/audiofx/Virtualizer.java +++ b/media/java/android/media/audiofx/Virtualizer.java @@ -16,16 +16,9 @@ package android.media.audiofx; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.os.Bundle; import android.util.Log; -import java.nio.ByteOrder; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.util.StringTokenizer; diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java index ff04201..24c74ac 100644 --- a/media/java/android/media/audiofx/Visualizer.java +++ b/media/java/android/media/audiofx/Visualizer.java @@ -18,7 +18,6 @@ package android.media.audiofx; import android.util.Log; import java.lang.ref.WeakReference; -import java.io.IOException; import android.os.Handler; import android.os.Looper; import android.os.Message; diff --git a/media/java/android/media/session/IMediaController.aidl b/media/java/android/media/session/IMediaController.aidl new file mode 100644 index 0000000..d34e973 --- /dev/null +++ b/media/java/android/media/session/IMediaController.aidl @@ -0,0 +1,51 @@ +/* Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.content.Intent; +import android.media.Rating; +import android.media.session.IMediaControllerCallback; +import android.media.session.MediaMetadata; +import android.media.session.PlaybackState; +import android.os.Bundle; +import android.os.ResultReceiver; +import android.view.KeyEvent; + +/** + * Interface to a MediaSession in the system. + * @hide + */ +interface IMediaController { + void sendCommand(String command, in Bundle extras, in ResultReceiver cb); + void sendMediaButton(in KeyEvent mediaButton); + void registerCallbackListener(in IMediaControllerCallback cb); + void unregisterCallbackListener(in IMediaControllerCallback cb); + boolean isTransportControlEnabled(); + + // These commands are for the TransportController + void play(); + void pause(); + void stop(); + void next(); + void previous(); + void fastForward(); + void rewind(); + void seekTo(long pos); + void rate(in Rating rating); + MediaMetadata getMetadata(); + PlaybackState getPlaybackState(); + int getRatingType(); +}
\ No newline at end of file diff --git a/media/java/android/media/session/IMediaControllerCallback.aidl b/media/java/android/media/session/IMediaControllerCallback.aidl new file mode 100644 index 0000000..3651f1b --- /dev/null +++ b/media/java/android/media/session/IMediaControllerCallback.aidl @@ -0,0 +1,32 @@ +/* Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.media.session.MediaMetadata; +import android.media.session.PlaybackState; +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IMediaControllerCallback { + void onEvent(String event, in Bundle extras); + void onRouteChanged(in Bundle route); + + // These callbacks are for the TransportController + void onPlaybackStateChanged(in PlaybackState state); + void onMetadataChanged(in MediaMetadata metadata); +}
\ No newline at end of file diff --git a/media/java/android/media/session/IMediaSession.aidl b/media/java/android/media/session/IMediaSession.aidl new file mode 100644 index 0000000..aed7641 --- /dev/null +++ b/media/java/android/media/session/IMediaSession.aidl @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.media.session.IMediaController; +import android.media.session.MediaMetadata; +import android.media.session.PlaybackState; +import android.os.Bundle; + +/** + * Interface to a MediaSession in the system. + * @hide + */ +interface IMediaSession { + void sendEvent(String event, in Bundle data); + IMediaController getMediaController(); + void setTransportPerformerEnabled(); + void setRouteState(in Bundle routeState); + void setRoute(in Bundle mediaRouteDescriptor); + List<String> getSupportedInterfaces(); + void publish(); + void destroy(); + + // These commands are for the TransportPerformer + void setMetadata(in MediaMetadata metadata); + void setPlaybackState(in PlaybackState state); + void setRatingType(int type); +}
\ No newline at end of file diff --git a/media/java/android/media/session/IMediaSessionCallback.aidl b/media/java/android/media/session/IMediaSessionCallback.aidl new file mode 100644 index 0000000..7c183e0 --- /dev/null +++ b/media/java/android/media/session/IMediaSessionCallback.aidl @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.media.Rating; +import android.content.Intent; +import android.os.Bundle; +import android.os.ResultReceiver; + +/** + * @hide + */ +oneway interface IMediaSessionCallback { + void onCommand(String command, in Bundle extras, in ResultReceiver cb); + void onMediaButton(in Intent mediaRequestIntent); + void onRequestRouteChange(in Bundle route); + + // These callbacks are for the TransportPerformer + void onPlay(); + void onPause(); + void onStop(); + void onNext(); + void onPrevious(); + void onFastForward(); + void onRewind(); + void onSeekTo(long pos); + void onRate(in Rating rating); +}
\ No newline at end of file diff --git a/media/java/android/media/session/IMediaSessionManager.aidl b/media/java/android/media/session/IMediaSessionManager.aidl new file mode 100644 index 0000000..0b4328e --- /dev/null +++ b/media/java/android/media/session/IMediaSessionManager.aidl @@ -0,0 +1,28 @@ +/* Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.media.session.IMediaSession; +import android.media.session.IMediaSessionCallback; +import android.os.Bundle; + +/** + * Interface to the MediaSessionManagerService + * @hide + */ +interface IMediaSessionManager { + IMediaSession createSession(String packageName, in IMediaSessionCallback cb, String tag); +}
\ No newline at end of file diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java new file mode 100644 index 0000000..afd8b11 --- /dev/null +++ b/media/java/android/media/session/MediaController.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Allows an app to interact with an ongoing media session. Media buttons and + * other commands can be sent to the session. A callback may be registered to + * receive updates from the session, such as metadata and play state changes. + * <p> + * A MediaController can be created through {@link MediaSessionManager} if you + * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if + * you have a {@link MediaSessionToken} from the session owner. + * <p> + * MediaController objects are thread-safe. + */ +public final class MediaController { + private static final String TAG = "MediaController"; + + private static final int MSG_EVENT = 1; + private static final int MESSAGE_PLAYBACK_STATE = 2; + private static final int MESSAGE_METADATA = 3; + private static final int MSG_ROUTE = 4; + + private final IMediaController mSessionBinder; + + private final CallbackStub mCbStub = new CallbackStub(this); + private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>(); + private final Object mLock = new Object(); + + private boolean mCbRegistered = false; + + private TransportController mTransportController; + + private MediaController(IMediaController sessionBinder) { + mSessionBinder = sessionBinder; + } + + /** + * @hide + */ + public static MediaController fromBinder(IMediaController sessionBinder) { + MediaController controller = new MediaController(sessionBinder); + try { + controller.mSessionBinder.registerCallbackListener(controller.mCbStub); + if (controller.mSessionBinder.isTransportControlEnabled()) { + controller.mTransportController = new TransportController(sessionBinder); + } + } catch (RemoteException e) { + Log.wtf(TAG, "MediaController created with expired token", e); + controller = null; + } + return controller; + } + + /** + * Get a new MediaController for a MediaSessionToken. If successful the + * controller returned will be connected to the session that generated the + * token. + * + * @param token The session token to use + * @return A controller for the session or null + */ + public static MediaController fromToken(MediaSessionToken token) { + return fromBinder(token.getBinder()); + } + + /** + * Get a TransportController if the session supports it. If it is not + * supported null will be returned. + * + * @return A TransportController or null + */ + public TransportController getTransportController() { + return mTransportController; + } + + /** + * Send the specified media button to the session. Only media keys can be + * sent using this method. + * + * @param keycode The media button keycode, such as + * {@link KeyEvent#KEYCODE_MEDIA_PLAY}. + */ + public void sendMediaButton(int keycode) { + if (!KeyEvent.isMediaKey(keycode)) { + throw new IllegalArgumentException("May only send media buttons through " + + "sendMediaButton"); + } + // TODO do something better than key down/up events + KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keycode); + try { + mSessionBinder.sendMediaButton(event); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in sendMediaButton", e); + } + } + + /** + * Adds a callback to receive updates from the Session. Updates will be + * posted on the caller's thread. + * + * @param cb The callback object, must not be null + */ + public void addCallback(Callback cb) { + addCallback(cb, null); + } + + /** + * Adds a callback to receive updates from the session. Updates will be + * posted on the specified handler's thread. + * + * @param cb Cannot be null. + * @param handler The handler to post updates on. If null the callers thread + * will be used + */ + public void addCallback(Callback cb, Handler handler) { + if (handler == null) { + handler = new Handler(); + } + synchronized (mLock) { + addCallbackLocked(cb, handler); + } + } + + /** + * Stop receiving updates on the specified callback. If an update has + * already been posted you may still receive it after calling this method. + * + * @param cb The callback to remove + */ + public void removeCallback(Callback cb) { + synchronized (mLock) { + removeCallbackLocked(cb); + } + } + + /** + * Sends a generic command to the session. It is up to the session creator + * to decide what commands and parameters they will support. As such, + * commands should only be sent to sessions that the controller owns. + * + * @param command The command to send + * @param params Any parameters to include with the command + * @param cb The callback to receive the result on + */ + public void sendCommand(String command, Bundle params, ResultReceiver cb) { + if (TextUtils.isEmpty(command)) { + throw new IllegalArgumentException("command cannot be null or empty"); + } + try { + mSessionBinder.sendCommand(command, params, cb); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in sendCommand.", e); + } + } + + /* + * @hide + */ + IMediaController getSessionBinder() { + return mSessionBinder; + } + + private void addCallbackLocked(Callback cb, Handler handler) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + if (handler == null) { + throw new IllegalArgumentException("Handler cannot be null"); + } + if (getHandlerForCallbackLocked(cb) != null) { + Log.w(TAG, "Callback is already added, ignoring"); + return; + } + MessageHandler holder = new MessageHandler(handler.getLooper(), cb); + mCallbacks.add(holder); + + if (!mCbRegistered) { + try { + mSessionBinder.registerCallbackListener(mCbStub); + mCbRegistered = true; + } catch (RemoteException e) { + Log.d(TAG, "Dead object in registerCallback", e); + } + } + } + + private boolean removeCallbackLocked(Callback cb) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + mCallbacks.remove(i); + return true; + } + } + return false; + } + + private MessageHandler getHandlerForCallbackLocked(Callback cb) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + return handler; + } + } + return null; + } + + private void postEvent(String event, Bundle extras) { + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_EVENT, event, extras); + } + } + } + + private void postRouteChanged(Bundle routeDescriptor) { + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_ROUTE, null, routeDescriptor); + } + } + } + + /** + * Callback for receiving updates on from the session. A Callback can be + * registered using {@link #addCallback} + */ + public static abstract class Callback { + /** + * Override to handle custom events sent by the session owner without a + * specified interface. Controllers should only handle these for + * sessions they own. + * + * @param event + */ + public void onEvent(String event, Bundle extras) { + } + + /** + * Override to handle route changes for this session. + * + * @param route + */ + public void onRouteChanged(Bundle route) { + } + } + + private final static class CallbackStub extends IMediaControllerCallback.Stub { + private final WeakReference<MediaController> mController; + + public CallbackStub(MediaController controller) { + mController = new WeakReference<MediaController>(controller); + } + + @Override + public void onEvent(String event, Bundle extras) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postEvent(event, extras); + } + } + + @Override + public void onRouteChanged(Bundle mediaRouteDescriptor) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postRouteChanged(mediaRouteDescriptor); + } + } + + @Override + public void onPlaybackStateChanged(PlaybackState state) { + MediaController controller = mController.get(); + if (controller != null) { + TransportController tc = controller.getTransportController(); + if (tc != null) { + tc.postPlaybackStateChanged(state); + } + } + } + + @Override + public void onMetadataChanged(MediaMetadata metadata) { + MediaController controller = mController.get(); + if (controller != null) { + TransportController tc = controller.getTransportController(); + if (tc != null) { + tc.postMetadataChanged(metadata); + } + } + } + + } + + private final static class MessageHandler extends Handler { + private final MediaController.Callback mCallback; + + public MessageHandler(Looper looper, MediaController.Callback cb) { + super(looper, null, true); + mCallback = cb; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EVENT: + mCallback.onEvent((String) msg.obj, msg.getData()); + break; + case MSG_ROUTE: + mCallback.onRouteChanged(msg.getData()); + } + } + + public void post(int what, Object obj, Bundle data) { + obtainMessage(what, obj).sendToTarget(); + } + } + +} diff --git a/media/java/android/media/session/MediaMetadata.aidl b/media/java/android/media/session/MediaMetadata.aidl new file mode 100644 index 0000000..4431d9d --- /dev/null +++ b/media/java/android/media/session/MediaMetadata.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media.session; + +parcelable MediaMetadata; diff --git a/media/java/android/media/session/MediaMetadata.java b/media/java/android/media/session/MediaMetadata.java new file mode 100644 index 0000000..e2330f7 --- /dev/null +++ b/media/java/android/media/session/MediaMetadata.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.graphics.Bitmap; +import android.media.Rating; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.Log; + +/** + * Contains metadata about an item, such as the title, artist, etc. + */ +public final class MediaMetadata implements Parcelable { + private static final String TAG = "MediaMetadata"; + + /** + * The title of the media. + */ + public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; + + /** + * The artist of the media. + */ + public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; + + /** + * The duration of the media in ms. A duration of 0 is the default. + */ + public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; + + /** + * The album title for the media. + */ + public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; + + /** + * The author of the media. + */ + public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; + + /** + * The writer of the media. + */ + public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; + + /** + * The composer of the media. + */ + public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; + + /** + * The date the media was created or published as TODO determine format. + */ + public static final String METADATA_KEY_DATE = "android.media.metadata.DATE"; + + /** + * The year the media was created or published as a numeric String. + */ + public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; + + /** + * The genre of the media. + */ + public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; + + /** + * The track number for the media. + */ + public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; + + /** + * The number of tracks in the media's original source. + */ + public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; + + /** + * The disc number for the media's original source. + */ + public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; + + /** + * The artist for the album of the media's original source. + */ + public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; + + /** + * The artwork for the media as a {@link Bitmap}. + */ + public static final String METADATA_KEY_ART = "android.media.metadata.ART"; + + /** + * The artwork for the media as a Uri style String. + */ + public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; + + /** + * The artwork for the album of the media's original source as a + * {@link Bitmap}. + */ + public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; + + /** + * The artwork for the album of the media's original source as a Uri style + * String. + */ + public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; + + /** + * The user's rating for the media. + * + * @see Rating + */ + public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; + + /** + * The overall rating for the media. + * + * @see Rating + */ + public static final String METADATA_KEY_RATING = "android.media.metadata.RATING"; + + private static final int METADATA_TYPE_INVALID = -1; + private static final int METADATA_TYPE_LONG = 0; + private static final int METADATA_TYPE_STRING = 1; + private static final int METADATA_TYPE_BITMAP = 2; + private static final int METADATA_TYPE_RATING = 3; + private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE; + + static { + METADATA_KEYS_TYPE = new ArrayMap<String, Integer>(); + METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_STRING); + METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING); + METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING); + } + private final Bundle mBundle; + + private MediaMetadata(Bundle bundle) { + mBundle = new Bundle(bundle); + } + + private MediaMetadata(Parcel in) { + mBundle = in.readBundle(); + } + + /** + * Returns the value associated with the given key, or null if no mapping of + * the desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key The key the value is stored under + * @return a String value, or null + */ + public String getString(String key) { + return mBundle.getString(key); + } + + /** + * Returns the value associated with the given key, or 0L if no long exists + * for the given key. + * + * @param key The key the value is stored under + * @return a long value + */ + public long getLong(String key) { + return mBundle.getLong(key); + } + + /** + * Return a {@link Rating} for the given key or null if no rating exists for + * the given key. + * + * @param key The key the value is stored under + * @return A {@link Rating} or null + */ + public Rating getRating(String key) { + Rating rating = null; + try { + rating = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a bitmap + Log.d(TAG, "Failed to retrieve a key as Rating.", e); + } + return rating; + } + + /** + * Return a {@link Bitmap} for the given key or null if no bitmap exists for + * the given key. + * + * @param key The key the value is stored under + * @return A {@link Bitmap} or null + */ + public Bitmap getBitmap(String key) { + Bitmap bmp = null; + try { + bmp = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a bitmap + Log.d(TAG, "Failed to retrieve a key as Bitmap.", e); + } + return bmp; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBundle(mBundle); + } + + public static final Parcelable.Creator<MediaMetadata> CREATOR + = new Parcelable.Creator<MediaMetadata>() { + @Override + public MediaMetadata createFromParcel(Parcel in) { + return new MediaMetadata(in); + } + + @Override + public MediaMetadata[] newArray(int size) { + return new MediaMetadata[size]; + } + }; + + /** + * Use to build MediaMetadata objects. The system defined metadata keys must + * use the appropriate data type. + */ + public static final class Builder { + private final Bundle mBundle; + + /** + * Create an empty Builder. Any field that should be included in the + * {@link MediaMetadata} must be added. + */ + public Builder() { + mBundle = new Bundle(); + } + + /** + * Create a Builder using a {@link MediaMetadata} instance to set the + * initial values. All fields in the source metadata will be included in + * the new metadata. Fields can be overwritten by adding the same key. + * + * @param source + */ + public Builder(MediaMetadata source) { + mBundle = new Bundle(source.mBundle); + } + + /** + * Put a String value into the metadata. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + * <ul> + * <li>{@link #METADATA_KEY_TITLE}</li> + * <li>{@link #METADATA_KEY_ARTIST}</li> + * <li>{@link #METADATA_KEY_ALBUM}</li> + * <li>{@link #METADATA_KEY_AUTHOR}</li> + * <li>{@link #METADATA_KEY_WRITER}</li> + * <li>{@link #METADATA_KEY_COMPOSER}</li> + * <li>{@link #METADATA_KEY_DATE}</li> + * <li>{@link #METADATA_KEY_YEAR}</li> + * <li>{@link #METADATA_KEY_GENRE}</li> + * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>li> + * <li>{@link #METADATA_KEY_ART_URI}</li>li> + * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The String value to store + * @return The Builder to allow chaining + */ + public Builder putString(String key, String value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_STRING) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a String"); + } + } + mBundle.putString(key, value); + return this; + } + + /** + * Put a long value into the metadata. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + * <ul> + * <li>{@link #METADATA_KEY_DURATION}</li> + * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li> + * <li>{@link #METADATA_KEY_NUM_TRACKS}</li> + * <li>{@link #METADATA_KEY_DISC_NUMBER}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The String value to store + * @return The Builder to allow chaining + */ + public Builder putLong(String key, long value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a long"); + } + } + mBundle.putLong(key, value); + return this; + } + + /** + * Put a {@link Rating} into the metadata. Custom keys may be used, but + * if the METADATA_KEYs defined in this class are used they may only be + * one of the following: + * <ul> + * <li>{@link #METADATA_KEY_RATING}</li> + * <li>{@link #METADATA_KEY_USER_RATING}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The String value to store + * @return The Builder to allow chaining + */ + public Builder putRating(String key, Rating value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a Rating"); + } + } + mBundle.putParcelable(key, value); + return this; + } + + /** + * Put a {@link Bitmap} into the metadata. Custom keys may be used, but + * if the METADATA_KEYs defined in this class are used they may only be + * one of the following: + * <ul> + * <li>{@link #METADATA_KEY_ART}</li> + * <li>{@link #METADATA_KEY_ALBUM_ART}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The Bitmap to store + * @return The Builder to allow chaining + */ + public Builder putBitmap(String key, Bitmap value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a Bitmap"); + } + } + mBundle.putParcelable(key, value); + return this; + } + + /** + * Creates a {@link MediaMetadata} instance with the specified fields. + * + * @return The new MediaMetadata instance + */ + public MediaMetadata build() { + return new MediaMetadata(mBundle); + } + } + +} diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java new file mode 100644 index 0000000..23c3035 --- /dev/null +++ b/media/java/android/media/session/MediaSession.java @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.content.Intent; +import android.media.Rating; +import android.media.session.IMediaController; +import android.media.session.IMediaSession; +import android.media.session.IMediaSessionCallback; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Allows interaction with media controllers, media routes, volume keys, media + * buttons, and transport controls. + * <p> + * A MediaSession should be created when an app wants to publish media playback + * information or negotiate with a media route. In general an app only needs one + * session for all playback, though multiple sessions can be created for sending + * media to multiple routes or to provide finer grain controls of media. + * <p> + * A MediaSession is created by calling + * {@link MediaSessionManager#createSession(String)}. Once a session is created + * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the + * session through {@link MediaSessionManager#getActiveSessions()}. The owner of + * the session may also use {@link #getSessionToken()} to allow apps without + * this permission to create a {@link MediaController} to interact with this + * session. + * <p> + * To receive commands, media keys, and other events a Callback must be set with + * {@link #addCallback(Callback)}. + * <p> + * When an app is finished performing playback it must call {@link #release()} + * to clean up the session and notify any controllers. + * <p> + * MediaSession objects are thread safe + */ +public final class MediaSession { + private static final String TAG = "MediaSession"; + + private static final int MSG_MEDIA_BUTTON = 1; + private static final int MSG_COMMAND = 2; + private static final int MSG_ROUTE_CHANGE = 3; + + private static final String KEY_COMMAND = "command"; + private static final String KEY_EXTRAS = "extras"; + private static final String KEY_CALLBACK = "callback"; + + private final Object mLock = new Object(); + + private final MediaSessionToken mSessionToken; + private final IMediaSession mBinder; + private final CallbackStub mCbStub; + + private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>(); + // TODO route interfaces + private final ArrayMap<String, RouteInterface.Stub> mInterfaces + = new ArrayMap<String, RouteInterface.Stub>(); + + private TransportPerformer mPerformer; + + private boolean mPublished = false;; + + /** + * @hide + */ + public MediaSession(IMediaSession binder, CallbackStub cbStub) { + mBinder = binder; + mCbStub = cbStub; + IMediaController controllerBinder = null; + try { + controllerBinder = mBinder.getMediaController(); + } catch (RemoteException e) { + throw new RuntimeException("Dead object in MediaSessionController constructor: ", e); + } + mSessionToken = new MediaSessionToken(controllerBinder); + } + + /** + * Set the callback to receive updates on. + * + * @param callback The callback object + */ + public void addCallback(Callback callback) { + addCallback(callback, null); + } + + public void addCallback(Callback callback, Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + synchronized (mLock) { + if (getHandlerForCallbackLocked(callback) != null) { + Log.w(TAG, "Callback is already added, ignoring"); + return; + } + if (handler == null) { + handler = new Handler(); + } + MessageHandler msgHandler = new MessageHandler(handler.getLooper(), callback); + mCallbacks.add(msgHandler); + } + } + + public void removeCallback(Callback callback) { + synchronized (mLock) { + removeCallbackLocked(callback); + } + } + + /** + * Start using a TransportPerformer with this media session. This must be + * called before calling publish and cannot be called more than once. + * Calling this will allow MediaControllers to retrieve a + * TransportController. + * + * @see TransportController + * @return The TransportPerformer created for this session + */ + public TransportPerformer setTransportPerformerEnabled() { + if (mPerformer != null) { + throw new IllegalStateException("setTransportPerformer can only be called once."); + } + if (mPublished) { + throw new IllegalStateException("setTransportPerformer cannot be called after publish"); + } + + mPerformer = new TransportPerformer(mBinder); + try { + mBinder.setTransportPerformerEnabled(); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setTransportPerformerEnabled.", e); + } + return mPerformer; + } + + /** + * Retrieves the TransportPerformer used by this session. If called before + * {@link #setTransportPerformerEnabled} null will be returned. + * + * @return The TransportPerformer associated with this session or null + */ + public TransportPerformer getTransportPerformer() { + return mPerformer; + } + + /** + * Call after you have finished setting up the session. This will make it + * available to listeners and begin pushing updates to MediaControllers. + * This can only be called once. + */ + public void publish() { + if (mPublished) { + throw new RuntimeException("publish() may only be called once."); + } + try { + mBinder.publish(); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in publish.", e); + } + mPublished = true; + } + + /** + * Add an interface that can be used by MediaSessions. TODO make this a + * route provider api + * + * @see RouteInterface + * @param iface The interface to add + * @hide + */ + public void addInterface(RouteInterface.Stub iface) { + if (iface == null) { + throw new IllegalArgumentException("Stub cannot be null"); + } + String name = iface.getName(); + if (TextUtils.isEmpty(name)) { + throw new IllegalArgumentException("Stub must return a valid name"); + } + if (mInterfaces.containsKey(iface)) { + throw new IllegalArgumentException("Interface is already added"); + } + synchronized (mLock) { + mInterfaces.put(iface.getName(), iface); + } + } + + /** + * Send a proprietary event to all MediaControllers listening to this + * Session. It's up to the Controller/Session owner to determine the meaning + * of any events. + * + * @param event The name of the event to send + * @param extras Any extras included with the event + */ + public void sendEvent(String event, Bundle extras) { + if (TextUtils.isEmpty(event)) { + throw new IllegalArgumentException("event cannot be null or empty"); + } + try { + mBinder.sendEvent(event, extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error sending event", e); + } + } + + /** + * This must be called when an app has finished performing playback. If + * playback is expected to start again shortly the session can be left open, + * but it must be released if your activity or service is being destroyed. + */ + public void release() { + try { + mBinder.destroy(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error releasing session: ", e); + } + } + + /** + * Retrieve a token object that can be used by apps to create a + * {@link MediaController} for interacting with this session. The owner of + * the session is responsible for deciding how to distribute these tokens. + * + * @return A token that can be used to create a MediaController for this + * session + */ + public MediaSessionToken getSessionToken() { + return mSessionToken; + } + + private MessageHandler getHandlerForCallbackLocked(Callback cb) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + return handler; + } + } + return null; + } + + private boolean removeCallbackLocked(Callback cb) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + mCallbacks.remove(i); + return true; + } + } + return false; + } + + private void postCommand(String command, Bundle extras, ResultReceiver resultCb) { + Command cmd = new Command(command, extras, resultCb); + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_COMMAND, cmd); + } + } + } + + private void postMediaButton(Intent mediaButtonIntent) { + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_MEDIA_BUTTON, mediaButtonIntent); + } + } + } + + private void postRequestRouteChange(Bundle mediaRouteDescriptor) { + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_ROUTE_CHANGE, mediaRouteDescriptor); + } + } + } + + /** + * Receives commands or updates from controllers and routes. An app can + * specify what commands and buttons it supports by setting them on the + * MediaSession (TODO). + */ + public abstract static class Callback { + + public Callback() { + } + + /** + * Called when a media button is pressed and this session has the + * highest priority or a controller sends a media button event to the + * session. TODO determine if using Intents identical to the ones + * RemoteControlClient receives is useful + * <p> + * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a + * KeyEvent in {@link Intent#EXTRA_KEY_EVENT} + * + * @param mediaButtonIntent an intent containing the KeyEvent as an + * extra + */ + public void onMediaButton(Intent mediaButtonIntent) { + } + + /** + * Called when a controller has sent a custom command to this session. + * The owner of the session may handle custom commands but is not + * required to. + * + * @param command + * @param extras optional + */ + public void onCommand(String command, Bundle extras, ResultReceiver cb) { + } + + /** + * Called when the user has selected a different route to connect to. + * The app is responsible for connecting to the new route and migrating + * ongoing playback if necessary. + * + * @param descriptor + */ + public void onRequestRouteChange(Bundle descriptor) { + } + } + + /** + * @hide + */ + public static class CallbackStub extends IMediaSessionCallback.Stub { + private WeakReference<MediaSession> mMediaSession; + + public void setMediaSession(MediaSession session) { + mMediaSession = new WeakReference<MediaSession>(session); + } + + @Override + public void onCommand(String command, Bundle extras, ResultReceiver cb) + throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.postCommand(command, extras, cb); + } + } + + @Override + public void onMediaButton(Intent mediaButtonIntent) throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.postMediaButton(mediaButtonIntent); + } + } + + @Override + public void onRequestRouteChange(Bundle mediaRouteDescriptor) throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.postRequestRouteChange(mediaRouteDescriptor); + } + } + + @Override + public void onPlay() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onPlay(); + } + } + } + + @Override + public void onPause() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onPause(); + } + } + } + + @Override + public void onStop() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onStop(); + } + } + } + + @Override + public void onNext() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onNext(); + } + } + } + + @Override + public void onPrevious() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onPrevious(); + } + } + } + + @Override + public void onFastForward() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onFastForward(); + } + } + } + + @Override + public void onRewind() throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onRewind(); + } + } + } + + @Override + public void onSeekTo(long pos) throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onSeekTo(pos); + } + } + } + + @Override + public void onRate(Rating rating) throws RemoteException { + MediaSession session = mMediaSession.get(); + if (session != null) { + TransportPerformer tp = session.getTransportPerformer(); + if (tp != null) { + tp.onRate(rating); + } + } + } + + } + + private class MessageHandler extends Handler { + private MediaSession.Callback mCallback; + + public MessageHandler(Looper looper, MediaSession.Callback callback) { + super(looper, null, true); + mCallback = callback; + } + + @Override + public void handleMessage(Message msg) { + synchronized (mLock) { + if (mCallback == null) { + return; + } + switch (msg.what) { + case MSG_MEDIA_BUTTON: + mCallback.onMediaButton((Intent) msg.obj); + break; + case MSG_COMMAND: + Command cmd = (Command) msg.obj; + mCallback.onCommand(cmd.command, cmd.extras, cmd.stub); + break; + case MSG_ROUTE_CHANGE: + mCallback.onRequestRouteChange((Bundle) msg.obj); + break; + } + } + msg.recycle(); + } + + public void post(int what, Object obj) { + obtainMessage(what, obj).sendToTarget(); + } + } + + private static final class Command { + public final String command; + public final Bundle extras; + public final ResultReceiver stub; + + public Command(String command, Bundle extras, ResultReceiver stub) { + this.command = command; + this.extras = extras; + this.stub = stub; + } + } +} diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java new file mode 100644 index 0000000..e3f2d9c --- /dev/null +++ b/media/java/android/media/session/MediaSessionManager.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.content.Context; +import android.media.session.IMediaSessionManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * MediaSessionManager allows the creation and control of MediaSessions in the + * system. A MediaSession enables publishing information about ongoing media and + * interacting with MediaControllers and MediaRoutes. + * <p> + * Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to + * get an instance of this class. + * <p> + * + * @see MediaSession + * @see MediaController + */ +public final class MediaSessionManager { + private static final String TAG = "MediaSessionManager"; + + private final IMediaSessionManager mService; + + private Context mContext; + + /** + * @hide + */ + public MediaSessionManager(Context context) { + // Consider rewriting like DisplayManagerGlobal + // Decide if we need context + mContext = context; + IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE); + mService = IMediaSessionManager.Stub.asInterface(b); + } + + /** + * Creates a new session. + * + * @param tag A short name for debugging purposes + * @return a {@link MediaSession} for the new session + */ + public MediaSession createSession(String tag) { + try { + MediaSession.CallbackStub cbStub = new MediaSession.CallbackStub(); + MediaSession session = new MediaSession(mService + .createSession(mContext.getPackageName(), cbStub, tag), cbStub); + cbStub.setMediaSession(session); + + return session; + } catch (RemoteException e) { + Log.e(TAG, "Failed to create session: ", e); + return null; + } + } + + /** + * Get a list of controllers for all ongoing sessions. This requires the + * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by + * the calling app. + * + * @return a list of controllers for ongoing sessions + */ + public List<MediaController> getActiveSessions() { + // TODO + return new ArrayList<MediaController>(); + } +} diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/MediaSessionToken.aidl new file mode 100644 index 0000000..5812682 --- /dev/null +++ b/media/java/android/media/session/MediaSessionToken.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media.session; + +parcelable MediaSessionToken; diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java new file mode 100644 index 0000000..dbb4964 --- /dev/null +++ b/media/java/android/media/session/MediaSessionToken.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.media.session.IMediaController; +import android.os.Parcel; +import android.os.Parcelable; + +public class MediaSessionToken implements Parcelable { + private IMediaController mBinder; + + /** + * @hide + */ + MediaSessionToken(IMediaController binder) { + mBinder = binder; + } + + private MediaSessionToken(Parcel in) { + mBinder = IMediaController.Stub.asInterface(in.readStrongBinder()); + } + + /** + * @hide + */ + IMediaController getBinder() { + return mBinder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mBinder.asBinder()); + } + + public static final Parcelable.Creator<MediaSessionToken> CREATOR + = new Parcelable.Creator<MediaSessionToken>() { + @Override + public MediaSessionToken createFromParcel(Parcel in) { + return new MediaSessionToken(in); + } + + @Override + public MediaSessionToken[] newArray(int size) { + return new MediaSessionToken[size]; + } + }; +} diff --git a/media/java/android/media/session/PlaybackState.aidl b/media/java/android/media/session/PlaybackState.aidl new file mode 100644 index 0000000..0876ebd --- /dev/null +++ b/media/java/android/media/session/PlaybackState.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media.session; + +parcelable PlaybackState; diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java new file mode 100644 index 0000000..b3506b3 --- /dev/null +++ b/media/java/android/media/session/PlaybackState.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.media.RemoteControlClient; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Playback state for a {@link MediaSession}. This includes a state like + * {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position, + * and the current control capabilities. + */ +public final class PlaybackState implements Parcelable { + /** + * Indicates this performer supports the stop command. + * + * @see #setActions + */ + public static final long ACTION_STOP = 1 << 0; + + /** + * Indicates this performer supports the pause command. + * + * @see #setActions + */ + public static final long ACTION_PAUSE = 1 << 1; + + /** + * Indicates this performer supports the play command. + * + * @see #setActions + */ + public static final long ACTION_PLAY = 1 << 2; + + /** + * Indicates this performer supports the rewind command. + * + * @see #setActions + */ + public static final long ACTION_REWIND = 1 << 3; + + /** + * Indicates this performer supports the previous command. + * + * @see #setActions + */ + public static final long ACTION_PREVIOUS_ITEM = 1 << 4; + + /** + * Indicates this performer supports the next command. + * + * @see #setActions + */ + public static final long ACTION_NEXT_ITEM = 1 << 5; + + /** + * Indicates this performer supports the fast forward command. + * + * @see #setActions + */ + public static final long ACTION_FASTFORWARD = 1 << 6; + + /** + * Indicates this performer supports the set rating command. + * + * @see #setActions + */ + public static final long ACTION_RATING = 1 << 7; + + /** + * Indicates this performer supports the seek to command. + * + * @see #setActions + */ + public static final long ACTION_SEEK_TO = 1 << 8; + + /** + * This is the default playback state and indicates that no media has been + * added yet, or the performer has been reset and has no content to play. + * + * @see #setState + */ + public final static int PLAYSTATE_NONE = 0; + + /** + * State indicating this item is currently stopped. + * + * @see #setState + */ + public final static int PLAYSTATE_STOPPED = 1; + + /** + * State indicating this item is currently paused. + * + * @see #setState + */ + public final static int PLAYSTATE_PAUSED = 2; + + /** + * State indicating this item is currently playing. + * + * @see #setState + */ + public final static int PLAYSTATE_PLAYING = 3; + + /** + * State indicating this item is currently fast forwarding. + * + * @see #setState + */ + public final static int PLAYSTATE_FAST_FORWARDING = 4; + + /** + * State indicating this item is currently rewinding. + * + * @see #setState + */ + public final static int PLAYSTATE_REWINDING = 5; + + /** + * State indicating this item is currently buffering and will begin playing + * when enough data has buffered. + * + * @see #setState + */ + public final static int PLAYSTATE_BUFFERING = 6; + + /** + * State indicating this item is currently in an error state. The error + * message should also be set when entering this state. + * + * @see #setState + */ + public final static int PLAYSTATE_ERROR = 7; + + private int mState; + private long mPosition; + private long mBufferPosition; + private float mSpeed; + private long mCapabilities; + private String mErrorMessage; + + /** + * Create an empty PlaybackState. At minimum a state and actions should be + * set before publishing a PlaybackState. + */ + public PlaybackState() { + } + + /** + * Create a new PlaybackState from an existing PlaybackState. All fields + * will be copied to the new state. + * + * @param from The PlaybackState to duplicate + */ + public PlaybackState(PlaybackState from) { + this.setState(from.getState()); + this.setPosition(from.getPosition()); + this.setBufferPosition(from.getBufferPosition()); + this.setSpeed(from.getSpeed()); + this.setActions(from.getActions()); + this.setErrorMessage(from.getErrorMessage()); + } + + private PlaybackState(Parcel in) { + this.setState(in.readInt()); + this.setPosition(in.readLong()); + this.setBufferPosition(in.readLong()); + this.setSpeed(in.readFloat()); + this.setActions(in.readLong()); + this.setErrorMessage(in.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getState()); + dest.writeLong(getPosition()); + dest.writeLong(getBufferPosition()); + dest.writeFloat(getSpeed()); + dest.writeLong(getActions()); + dest.writeString(getErrorMessage()); + } + + /** + * Get the current state of playback. One of the following: + * <ul> + * <li> {@link PlaybackState#PLAYSTATE_NONE}</li> + * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li> + * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li> + * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li> + * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li> + * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li> + * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li> + * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li> + */ + public int getState() { + return mState; + } + + /** + * Set the current state of playback. One of the following: + * <ul> + * <li> {@link PlaybackState#PLAYSTATE_NONE}</li> + * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li> + * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li> + * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li> + * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li> + * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li> + * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li> + * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li> + */ + public void setState(int mState) { + this.mState = mState; + } + + /** + * Get the current playback position in ms. + */ + public long getPosition() { + return mPosition; + } + + /** + * Set the current playback position in ms. + */ + public void setPosition(long position) { + mPosition = position; + } + + /** + * Get the current buffer position in ms. This is the farthest playback + * point that can be reached from the current position using only buffered + * content. + */ + public long getBufferPosition() { + return mBufferPosition; + } + + /** + * Set the current buffer position in ms. This is the farthest playback + * point that can be reached from the current position using only buffered + * content. + */ + public void setBufferPosition(long bufferPosition) { + mBufferPosition = bufferPosition; + } + + /** + * Get the current playback speed as a multiple of normal playback. This + * should be negative when rewinding. A value of 1 means normal playback and + * 0 means paused. + */ + public float getSpeed() { + return mSpeed; + } + + /** + * Set the current playback speed as a multiple of normal playback. This + * should be negative when rewinding. A value of 1 means normal playback and + * 0 means paused. + */ + public void setSpeed(float speed) { + mSpeed = speed; + } + + /** + * Get the current actions available on this session. This should use a + * bitmask of the available actions. + * <ul> + * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li> + * <li> {@link PlaybackState#ACTION_REWIND}</li> + * <li> {@link PlaybackState#ACTION_PLAY}</li> + * <li> {@link PlaybackState#ACTION_PAUSE}</li> + * <li> {@link PlaybackState#ACTION_STOP}</li> + * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li> + * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li> + * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> + * <li> {@link PlaybackState#ACTION_RATING}</li> + * </ul> + */ + public long getActions() { + return mCapabilities; + } + + /** + * Set the current capabilities available on this session. This should use a + * bitmask of the available capabilities. + * <ul> + * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li> + * <li> {@link PlaybackState#ACTION_REWIND}</li> + * <li> {@link PlaybackState#ACTION_PLAY}</li> + * <li> {@link PlaybackState#ACTION_PAUSE}</li> + * <li> {@link PlaybackState#ACTION_STOP}</li> + * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li> + * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li> + * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> + * <li> {@link PlaybackState#ACTION_RATING}</li> + * </ul> + */ + public void setActions(long capabilities) { + mCapabilities = capabilities; + } + + /** + * Get a user readable error message. This should be set when the state is + * {@link PlaybackState#PLAYSTATE_ERROR}. + */ + public String getErrorMessage() { + return mErrorMessage; + } + + /** + * Set a user readable error message. This should be set when the state is + * {@link PlaybackState#PLAYSTATE_ERROR}. + */ + public void setErrorMessage(String errorMessage) { + mErrorMessage = errorMessage; + } + + public static final Parcelable.Creator<PlaybackState> CREATOR + = new Parcelable.Creator<PlaybackState>() { + @Override + public PlaybackState createFromParcel(Parcel in) { + return new PlaybackState(in); + } + + @Override + public PlaybackState[] newArray(int size) { + return new PlaybackState[size]; + } + }; +} diff --git a/media/java/android/media/session/RouteInterface.java b/media/java/android/media/session/RouteInterface.java new file mode 100644 index 0000000..2391f27 --- /dev/null +++ b/media/java/android/media/session/RouteInterface.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Parcelable; +import android.os.ResultReceiver; + +/** + * Routes can support multiple interfaces for MediaSessions to interact with. To + * add a standard interface you should implement that interface's RouteInterface + * Stub and register it with the session. The set of supported commands is + * dependent on the specific interface's implementation. + * <p> + * A MediaInterface can be registered by calling TODO. Once added an interface + * will be used by Sessions to decide how they communicate with a session and + * cannot be removed, so all interfaces that you plan to support should be added + * when the route is created. + * + * @see RouteTransportControls + */ +public final class RouteInterface { + private static final String TAG = "MediaInterface"; + + private static final String KEY_RESULT = "result"; + + private final MediaController mController; + private final String mIface; + + /** + * @hide + */ + RouteInterface(MediaController controller, String iface) { + mController = controller; + mIface = iface; + } + + public void sendCommand(String command, Bundle params, ResultReceiver cb) { + // TODO + } + + public void addListener(EventListener listener) { + addListener(listener, null); + } + + public void addListener(EventListener listener, Handler handler) { + // TODO See MediaController for add/remove pattern + } + + public void removeListener(EventListener listener) { + // TODO + } + + // TODO decide on list of supported types + private static Bundle writeResultToBundle(Object v) { + Bundle b = new Bundle(); + if (v == null) { + // Don't send anything if null + } else if (v instanceof String) { + b.putString(KEY_RESULT, (String) v); + } else if (v instanceof Integer) { + b.putInt(KEY_RESULT, (Integer) v); + } else if (v instanceof Bundle) { + // Must be before Parcelable + b.putBundle(KEY_RESULT, (Bundle) v); + } else if (v instanceof Parcelable) { + b.putParcelable(KEY_RESULT, (Parcelable) v); + } else if (v instanceof Short) { + b.putShort(KEY_RESULT, (Short) v); + } else if (v instanceof Long) { + b.putLong(KEY_RESULT, (Long) v); + } else if (v instanceof Float) { + b.putFloat(KEY_RESULT, (Float) v); + } else if (v instanceof Double) { + b.putDouble(KEY_RESULT, (Double) v); + } else if (v instanceof Boolean) { + b.putBoolean(KEY_RESULT, (Boolean) v); + } else if (v instanceof CharSequence) { + // Must be after String + b.putCharSequence(KEY_RESULT, (CharSequence) v); + } else if (v instanceof boolean[]) { + b.putBooleanArray(KEY_RESULT, (boolean[]) v); + } else if (v instanceof byte[]) { + b.putByteArray(KEY_RESULT, (byte[]) v); + } else if (v instanceof String[]) { + b.putStringArray(KEY_RESULT, (String[]) v); + } else if (v instanceof CharSequence[]) { + // Must be after String[] and before Object[] + b.putCharSequenceArray(KEY_RESULT, (CharSequence[]) v); + } else if (v instanceof IBinder) { + b.putBinder(KEY_RESULT, (IBinder) v); + } else if (v instanceof Parcelable[]) { + b.putParcelableArray(KEY_RESULT, (Parcelable[]) v); + } else if (v instanceof int[]) { + b.putIntArray(KEY_RESULT, (int[]) v); + } else if (v instanceof long[]) { + b.putLongArray(KEY_RESULT, (long[]) v); + } else if (v instanceof Byte) { + b.putByte(KEY_RESULT, (Byte) v); + } + return b; + } + + public abstract static class Stub { + + /** + * The name of an interface should be a fully qualified name to prevent + * namespace collisions. Example: "com.myproject.MyPlaybackInterface" + * + * @return The name of this interface + */ + public abstract String getName(); + + /** + * This is called when a command is received that matches the interface + * you registered. Commands can come from any app with a MediaController + * reference to the session. + * + * @see MediaController + * @see MediaSession + * @param command The command or method to invoke. + * @param args Any args that were included with the command. May be + * null. + * @param cb The callback provided to send a response on. May be null. + */ + public abstract void onCommand(String command, Bundle args, ResultReceiver cb); + + public final void sendEvent(MediaSession session, String event, Bundle extras) { + // TODO + } + } + + /** + * An EventListener can be registered by an app with TODO to handle events + * sent by the session on a specific interface. + */ + public static abstract class EventListener { + /** + * This is called when an event is received from the interface. Events + * are sent by the session owner and will be delivered to all + * controllers that are listening to the interface. + * + * @param event The event that occurred. + * @param args Any extras that were included with the event. May be + * null. + */ + public abstract void onEvent(String event, Bundle args); + } + + private static final class EventHandler extends Handler { + + private final RouteInterface.EventListener mListener; + + public EventHandler(Looper looper, RouteInterface.EventListener cb) { + super(looper, null, true); + mListener = cb; + } + + @Override + public void handleMessage(Message msg) { + mListener.onEvent((String) msg.obj, msg.getData()); + } + + public void postEvent(String event, Bundle args) { + Message msg = obtainMessage(0, event); + msg.setData(args); + msg.sendToTarget(); + } + } +} diff --git a/media/java/android/media/session/RouteTransportControls.java b/media/java/android/media/session/RouteTransportControls.java new file mode 100644 index 0000000..665fd10 --- /dev/null +++ b/media/java/android/media/session/RouteTransportControls.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.media.RemoteControlClient; +import android.os.Bundle; +import android.os.Handler; +import android.os.ResultReceiver; +import android.text.TextUtils; +import android.util.Log; + +/** + * A standard media control interface for Routes. Routes can support multiple + * interfaces for MediaSessions to interact with. TODO rewrite for routes + */ +public final class RouteTransportControls { + private static final String TAG = "RouteTransportControls"; + public static final String NAME = "android.media.session.RouteTransportControls"; + + private static final String KEY_VALUE1 = "value1"; + + private static final String METHOD_FAST_FORWARD = "fastForward"; + private static final String METHOD_GET_CURRENT_POSITION = "getCurrentPosition"; + private static final String METHOD_GET_CAPABILITIES = "getCapabilities"; + + private static final String EVENT_PLAYSTATE_CHANGE = "playstateChange"; + private static final String EVENT_METADATA_CHANGE = "metadataChange"; + + private final MediaController mController; + private final RouteInterface mIface; + + private RouteTransportControls(RouteInterface iface, MediaController controller) { + mIface = iface; + mController = controller; + } + + public static RouteTransportControls from(MediaController controller) { +// MediaInterface iface = controller.getInterface(NAME); +// if (iface != null) { +// return new RouteTransportControls(iface, controller); +// } + return null; + } + + /** + * Send a play command to the route. TODO rename resume() and use messaging + * protocol, not KeyEvent + */ + public void play() { + // TODO + } + + /** + * Send a pause command to the session. + */ + public void pause() { + // TODO + } + + /** + * Set the rate at which to fastforward. Valid values are in the range [0,1] + * with actual rates depending on the implementation. + * + * @param rate + */ + public void fastForward(float rate) { + if (rate < 0 || rate > 1) { + throw new IllegalArgumentException("Rate must be between 0 and 1 inclusive"); + } + Bundle b = new Bundle(); + b.putFloat(KEY_VALUE1, rate); + mIface.sendCommand(METHOD_FAST_FORWARD, b, null); + } + + public void getCurrentPosition(ResultReceiver cb) { + mIface.sendCommand(METHOD_GET_CURRENT_POSITION, null, cb); + } + + public void getCapabilities(ResultReceiver cb) { + mIface.sendCommand(METHOD_GET_CAPABILITIES, null, cb); + } + + public void addListener(Listener listener) { + mIface.addListener(listener.mListener); + } + + public void addListener(Listener listener, Handler handler) { + mIface.addListener(listener.mListener, handler); + } + + public void removeListener(Listener listener) { + mIface.removeListener(listener.mListener); + } + + public static abstract class Stub extends RouteInterface.Stub { + private final MediaSession mSession; + + public Stub(MediaSession session) { + mSession = session; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void onCommand(String method, Bundle extras, ResultReceiver cb) { + if (TextUtils.isEmpty(method)) { + return; + } + Bundle result; + if (METHOD_FAST_FORWARD.equals(method)) { + fastForward(extras.getFloat(KEY_VALUE1, -1)); + } else if (METHOD_GET_CURRENT_POSITION.equals(method)) { + if (cb != null) { + result = new Bundle(); + result.putLong(KEY_VALUE1, getCurrentPosition()); + cb.send(0, result); + } + } else if (METHOD_GET_CAPABILITIES.equals(method)) { + if (cb != null) { + result = new Bundle(); + result.putLong(KEY_VALUE1, getCapabilities()); + cb.send(0, result); + } + } + } + + /** + * Override to handle fast forwarding. Valid values are [0,1] inclusive. + * The interpretation of the rate is up to the implementation. If no + * rate was included with the command a rate of -1 will be used by + * default. + * + * @param rate The rate at which to fast forward as a multiplier + */ + public void fastForward(float rate) { + Log.w(TAG, "fastForward is not supported."); + } + + /** + * Override to handle getting the current position of playback in + * millis. + * + * @return The current position in millis or -1 + */ + public long getCurrentPosition() { + Log.w(TAG, "getCurrentPosition is not supported"); + return -1; + } + + /** + * Override to handle getting the set of capabilities currently + * available. + * + * @return A bit mask of the supported capabilities + */ + public long getCapabilities() { + Log.w(TAG, "getCapabilities is not supported"); + return 0; + } + + /** + * Publish the current playback state to the system and any controllers. + * Valid values are defined in {@link RemoteControlClient}. TODO move + * play states somewhere else. + * + * @param state + */ + public final void updatePlaybackState(int state) { + Bundle extras = new Bundle(); + extras.putInt(KEY_VALUE1, state); + sendEvent(mSession, EVENT_PLAYSTATE_CHANGE, extras); + } + } + + /** + * Register this event listener using TODO to receive + * TransportControlInterface events from a session. + * + * @see RouteInterface.EventListener + */ + public static abstract class Listener { + + private RouteInterface.EventListener mListener = new RouteInterface.EventListener() { + @Override + public final void onEvent(String event, Bundle args) { + if (EVENT_PLAYSTATE_CHANGE.equals(event)) { + onPlaybackStateChange(args.getInt(KEY_VALUE1)); + } else if (EVENT_METADATA_CHANGE.equals(event)) { + onMetadataUpdate(args); + } + } + }; + + /** + * Override to handle updates to the playback state. Valid values are in + * {@link TransportPerformer}. TODO put playstate values somewhere more + * generic. + * + * @param state + */ + public void onPlaybackStateChange(int state) { + } + + /** + * Override to handle metadata changes for this session's media. The + * default supported fields are those in {@link MediaMetadata}. + * + * @param metadata + */ + public void onMetadataUpdate(Bundle metadata) { + } + } + +} diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java new file mode 100644 index 0000000..15b11f3 --- /dev/null +++ b/media/java/android/media/session/TransportController.java @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.media.Rating; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; + +/** + * Interface for controlling media playback on a session. This allows an app to + * request changes in playback, retrieve the current playback state and + * metadata, and listen for changes to the playback state and metadata. + */ +public final class TransportController { + private static final String TAG = "TransportController"; + + private final Object mLock = new Object(); + private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>(); + private final IMediaController mBinder; + + /** + * @hide + */ + public TransportController(IMediaController binder) { + mBinder = binder; + } + + /** + * Start listening to changes in playback state. + */ + public void addStateListener(TransportStateListener listener) { + addStateListener(listener, null); + } + + public void addStateListener(TransportStateListener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + synchronized (mLock) { + if (getHandlerForListenerLocked(listener) != null) { + Log.w(TAG, "Listener is already added, ignoring"); + return; + } + if (handler == null) { + handler = new Handler(); + } + + MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener); + mListeners.add(msgHandler); + } + } + + /** + * Stop listening to changes in playback state. + */ + public void removeStateListener(TransportStateListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + synchronized (mLock) { + removeStateListenerLocked(listener); + } + } + + /** + * Request that the player start its playback at its current position. + */ + public void play() { + try { + mBinder.play(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling play.", e); + } + } + + /** + * Request that the player pause its playback and stay at its current + * position. + */ + public void pause() { + try { + mBinder.pause(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling pause.", e); + } + } + + /** + * Request that the player stop its playback; it may clear its state in + * whatever way is appropriate. + */ + public void stop() { + try { + mBinder.stop(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling stop.", e); + } + } + + /** + * Move to a new location in the media stream. + * + * @param pos Position to move to, in milliseconds. + */ + public void seekTo(long pos) { + try { + mBinder.seekTo(pos); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling seekTo.", e); + } + } + + /** + * Start fast forwarding. If playback is already fast forwarding this may + * increase the rate. + */ + public void fastForward() { + try { + mBinder.fastForward(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling fastForward.", e); + } + } + + /** + * Skip to the next item. + */ + public void next() { + try { + mBinder.next(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling next.", e); + } + } + + /** + * Start rewinding. If playback is already rewinding this may increase the + * rate. + */ + public void rewind() { + try { + mBinder.rewind(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling rewind.", e); + } + } + + /** + * Skip to the previous item. + */ + public void previous() { + try { + mBinder.previous(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling previous.", e); + } + } + + /** + * Rate the current content. This will cause the rating to be set for the + * current user. The Rating type must match the type returned by + * {@link #getRatingType()}. + * + * @param rating The rating to set for the current content + */ + public void rate(Rating rating) { + try { + mBinder.rate(rating); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling rate.", e); + } + } + + /** + * Get the rating type supported by the session. One of: + * <ul> + * <li>{@link Rating#RATING_NONE}</li> + * <li>{@link Rating#RATING_HEART}</li> + * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li> + * <li>{@link Rating#RATING_3_STARS}</li> + * <li>{@link Rating#RATING_4_STARS}</li> + * <li>{@link Rating#RATING_5_STARS}</li> + * <li>{@link Rating#RATING_PERCENTAGE}</li> + * </ul> + * + * @return The supported rating type + */ + public int getRatingType() { + try { + return mBinder.getRatingType(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getRatingType.", e); + return Rating.RATING_NONE; + } + } + + /** + * Get the current playback state for this session. + * + * @return The current PlaybackState or null + */ + public PlaybackState getPlaybackState() { + try { + return mBinder.getPlaybackState(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getPlaybackState.", e); + return null; + } + } + + /** + * Get the current metadata for this session. + * + * @return The current MediaMetadata or null. + */ + public MediaMetadata getMetadata() { + try { + return mBinder.getMetadata(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getMetadata.", e); + return null; + } + } + + /** + * @hide + */ + public final void postPlaybackStateChanged(PlaybackState state) { + synchronized (mLock) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state); + } + } + } + + /** + * @hide + */ + public final void postMetadataChanged(MediaMetadata metadata) { + synchronized (mLock) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).post(MessageHandler.MSG_UPDATE_METADATA, + metadata); + } + } + } + + private MessageHandler getHandlerForListenerLocked(TransportStateListener listener) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + MessageHandler handler = mListeners.get(i); + if (listener == handler.mListener) { + return handler; + } + } + return null; + } + + private boolean removeStateListenerLocked(TransportStateListener listener) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + if (listener == mListeners.get(i).mListener) { + mListeners.remove(i); + return true; + } + } + return false; + } + + /** + * Register using {@link #addStateListener} to receive updates when there + * are playback changes on the session. + */ + public static abstract class TransportStateListener { + private MessageHandler mHandler; + /** + * Override to handle changes in playback state. + * + * @param state The new playback state of the session + */ + public void onPlaybackStateChanged(PlaybackState state) { + } + + /** + * Override to handle changes to the current metadata. + * + * @see MediaMetadata + * @param metadata The current metadata for the session or null + */ + public void onMetadataChanged(MediaMetadata metadata) { + } + + private void setHandler(Handler handler) { + mHandler = new MessageHandler(handler.getLooper(), this); + } + } + + private static class MessageHandler extends Handler { + private static final int MSG_UPDATE_PLAYBACK_STATE = 1; + private static final int MSG_UPDATE_METADATA = 2; + + private TransportStateListener mListener; + + public MessageHandler(Looper looper, TransportStateListener cb) { + super(looper, null, true); + mListener = cb; + } + + public void post(int msg, Object obj) { + obtainMessage(msg, obj).sendToTarget(); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_PLAYBACK_STATE: + mListener.onPlaybackStateChanged((PlaybackState) msg.obj); + break; + case MSG_UPDATE_METADATA: + mListener.onMetadataChanged((MediaMetadata) msg.obj); + break; + } + } + } + +} diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java new file mode 100644 index 0000000..b96db20 --- /dev/null +++ b/media/java/android/media/session/TransportPerformer.java @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.session; + +import android.media.AudioManager; +import android.media.Rating; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Allows broadcasting of playback changes. + */ +public final class TransportPerformer { + private static final String TAG = "TransportPerformer"; + private final Object mLock = new Object(); + private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>(); + + private IMediaSession mBinder; + + /** + * @hide + */ + public TransportPerformer(IMediaSession binder) { + mBinder = binder; + } + + /** + * Add a listener to receive updates on. + * + * @param listener The callback object + */ + public void addListener(Listener listener) { + addListener(listener, null); + } + + /** + * Add a listener to receive updates on. The updates will be posted to the + * specified handler. If no handler is provided they will be posted to the + * caller's thread. + * + * @param listener The listener to receive updates on + * @param handler The handler to post the updates on + */ + public void addListener(Listener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + synchronized (mLock) { + if (getHandlerForListenerLocked(listener) != null) { + Log.w(TAG, "Listener is already added, ignoring"); + } + if (handler == null) { + handler = new Handler(); + } + MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener); + mListeners.add(msgHandler); + } + } + + /** + * Stop receiving updates on the specified handler. If an update has already + * been posted you may still receive it after this call returns. + * + * @param listener The listener to stop receiving updates on + */ + public void removeListener(Listener listener) { + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + synchronized (mLock) { + removeListenerLocked(listener); + } + } + + /** + * Update the current playback state. + * + * @param state The current state of playback + */ + public final void setPlaybackState(PlaybackState state) { + try { + mBinder.setPlaybackState(state); + } catch (RemoteException e) { + Log.wtf(TAG, "Dead object in setPlaybackState.", e); + } + } + + /** + * Update the current metadata. New metadata can be created using + * {@link MediaMetadata.Builder}. + * + * @param metadata The new metadata + */ + public final void setMetadata(MediaMetadata metadata) { + try { + mBinder.setMetadata(metadata); + } catch (RemoteException e) { + Log.wtf(TAG, "Dead object in setPlaybackState.", e); + } + } + + /** + * @hide + */ + public final void onPlay() { + post(MessageHandler.MESSAGE_PLAY); + } + + /** + * @hide + */ + public final void onPause() { + post(MessageHandler.MESSAGE_PAUSE); + } + + /** + * @hide + */ + public final void onStop() { + post(MessageHandler.MESSAGE_STOP); + } + + /** + * @hide + */ + public final void onNext() { + post(MessageHandler.MESSAGE_NEXT); + } + + /** + * @hide + */ + public final void onPrevious() { + post(MessageHandler.MESSAGE_PREVIOUS); + } + + /** + * @hide + */ + public final void onFastForward() { + post(MessageHandler.MESSAGE_FAST_FORWARD); + } + + /** + * @hide + */ + public final void onRewind() { + post(MessageHandler.MESSAGE_REWIND); + } + + /** + * @hide + */ + public final void onSeekTo(long pos) { + post(MessageHandler.MESSAGE_SEEK_TO, pos); + } + + /** + * @hide + */ + public final void onRate(Rating rating) { + post(MessageHandler.MESSAGE_RATE, rating); + } + + private MessageHandler getHandlerForListenerLocked(Listener listener) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + MessageHandler handler = mListeners.get(i); + if (listener == handler.mListener) { + return handler; + } + } + return null; + } + + private boolean removeListenerLocked(Listener listener) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + if (listener == mListeners.get(i).mListener) { + mListeners.remove(i); + return true; + } + } + return false; + } + + private void post(int what, Object obj) { + synchronized (mLock) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).post(what, obj); + } + } + } + + private void post(int what) { + post(what, null); + } + + /** + * Extend Listener to handle transport controls. Listeners can be registered + * using {@link #addListener}. + */ + public static abstract class Listener { + + /** + * Override to handle requests to begin playback. + */ + public void onPlay() { + } + + /** + * Override to handle requests to pause playback. + */ + public void onPause() { + } + + /** + * Override to handle requests to skip to the next media item. + */ + public void onNext() { + } + + /** + * Override to handle requests to skip to the previous media item. + */ + public void onPrevious() { + } + + /** + * Override to handle requests to fast forward. + */ + public void onFastForward() { + } + + /** + * Override to handle requests to rewind. + */ + public void onRewind() { + } + + /** + * Override to handle requests to stop playback. + */ + public void onStop() { + } + + /** + * Override to handle requests to seek to a specific position in ms. + * + * @param pos New position to move to, in milliseconds. + */ + public void onSeekTo(long pos) { + } + + /** + * Override to handle the item being rated. + * + * @param rating + */ + public void onRate(Rating rating) { + } + + /** + * Report that audio focus has changed on the app. This only happens if + * you have indicated you have started playing with + * {@link #setPlaybackState}. TODO figure out route focus apis/handling. + * + * @param focusChange The type of focus change, TBD. The default + * implementation will deliver a call to {@link #onPause} + * when focus is lost. + */ + public void onRouteFocusChange(int focusChange) { + switch (focusChange) { + case AudioManager.AUDIOFOCUS_LOSS: + onPause(); + break; + } + } + } + + private class MessageHandler extends Handler { + private static final int MESSAGE_PLAY = 1; + private static final int MESSAGE_PAUSE = 2; + private static final int MESSAGE_STOP = 3; + private static final int MESSAGE_NEXT = 4; + private static final int MESSAGE_PREVIOUS = 5; + private static final int MESSAGE_FAST_FORWARD = 6; + private static final int MESSAGE_REWIND = 7; + private static final int MESSAGE_SEEK_TO = 8; + private static final int MESSAGE_RATE = 9; + + private Listener mListener; + + public MessageHandler(Looper looper, Listener cb) { + super(looper); + mListener = cb; + } + + public void post(int what, Object obj) { + obtainMessage(what, obj).sendToTarget(); + } + + public void post(int what) { + post(what, null); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_PLAY: + mListener.onPlay(); + break; + case MESSAGE_PAUSE: + mListener.onPause(); + break; + case MESSAGE_STOP: + mListener.onStop(); + break; + case MESSAGE_NEXT: + mListener.onNext(); + break; + case MESSAGE_PREVIOUS: + mListener.onPrevious(); + break; + case MESSAGE_FAST_FORWARD: + mListener.onFastForward(); + break; + case MESSAGE_REWIND: + mListener.onRewind(); + break; + case MESSAGE_SEEK_TO: + mListener.onSeekTo((Long) msg.obj); + break; + case MESSAGE_RATE: + mListener.onRate((Rating) msg.obj); + break; + } + } + } +} diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java index 2b0b3e2..2d3de85 100644 --- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java +++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java @@ -28,7 +28,6 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Rect; import android.graphics.Matrix; import android.media.videoeditor.VideoEditor.ExportProgressListener; import android.media.videoeditor.VideoEditor.PreviewProgressListener; diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 9ceefc3..15ae238 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -16,21 +16,23 @@ package android.mtp; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContentValues; import android.content.IContentProvider; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.media.MediaScanner; import android.net.Uri; -import android.os.Environment; +import android.os.BatteryManager; +import android.os.BatteryStats; import android.os.RemoteException; import android.provider.MediaStore; import android.provider.MediaStore.Audio; import android.provider.MediaStore.Files; -import android.provider.MediaStore.Images; import android.provider.MediaStore.MediaColumns; import android.util.Log; import android.view.Display; @@ -115,11 +117,35 @@ public class MtpDatabase { + Files.FileColumns.PARENT + "=?"; private final MediaScanner mMediaScanner; + private MtpServer mServer; + + // read from native code + private int mBatteryLevel; + private int mBatteryScale; static { System.loadLibrary("media_jni"); } + private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); + int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + if (newLevel != mBatteryLevel) { + mBatteryLevel = newLevel; + if (mServer != null) { + // send device property changed event + mServer.sendDevicePropertyChanged( + MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL); + } + } + } + } + }; + public MtpDatabase(Context context, String volumeName, String storagePath, String[] subDirectories) { native_setup(); @@ -173,6 +199,18 @@ public class MtpDatabase { initDeviceProperties(context); } + public void setServer(MtpServer server) { + mServer = server; + + // register for battery notifications when we are connected + if (server != null) { + mContext.registerReceiver(mBatteryReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } else { + mContext.unregisterReceiver(mBatteryReceiver); + } + } + @Override protected void finalize() throws Throwable { try { @@ -558,6 +596,11 @@ public class MtpDatabase { MtpConstants.PROPERTY_DURATION, MtpConstants.PROPERTY_GENRE, MtpConstants.PROPERTY_COMPOSER, + MtpConstants.PROPERTY_AUDIO_WAVE_CODEC, + MtpConstants.PROPERTY_BITRATE_TYPE, + MtpConstants.PROPERTY_AUDIO_BITRATE, + MtpConstants.PROPERTY_NUMBER_OF_CHANNELS, + MtpConstants.PROPERTY_SAMPLE_RATE, }; static final int[] VIDEO_PROPERTIES = { @@ -665,6 +708,7 @@ public class MtpDatabase { MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE, + MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL, }; } @@ -821,6 +865,8 @@ public class MtpDatabase { outStringValue[imageSize.length()] = 0; return MtpConstants.RESPONSE_OK; + // DEVICE_PROPERTY_BATTERY_LEVEL is implemented in the JNI code + default: return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED; } diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java index 8310579..72dcaa8 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -18,8 +18,6 @@ package android.mtp; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; -import android.os.ParcelFileDescriptor; -import android.util.Log; /** * This class represents an MTP or PTP device connected on the USB host bus. An application can diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index 48da40f..781988d 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -20,7 +20,6 @@ import android.content.IContentProvider; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; -import android.provider.MediaStore; import android.provider.MediaStore.Audio; import android.provider.MediaStore.Files; import android.provider.MediaStore.Images; diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index 266f78e..3814630 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -30,6 +30,7 @@ public class MtpServer implements Runnable { public MtpServer(MtpDatabase database, boolean usePtp) { native_setup(database, usePtp); + database.setServer(this); } public void start() { @@ -51,6 +52,10 @@ public class MtpServer implements Runnable { native_send_object_removed(handle); } + public void sendDevicePropertyChanged(int property) { + native_send_device_property_changed(property); + } + public void addStorage(MtpStorage storage) { native_add_storage(storage); } @@ -64,6 +69,7 @@ public class MtpServer implements Runnable { private native final void native_cleanup(); private native final void native_send_object_added(int handle); private native final void native_send_object_removed(int handle); + private native final void native_send_device_property_changed(int property); private native final void native_add_storage(MtpStorage storage); private native final void native_remove_storage(int storageId); } diff --git a/media/jni/Android.mk b/media/jni/Android.mk index dea971e..ed98b96 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES:= \ android_media_MediaCodecList.cpp \ android_media_MediaDrm.cpp \ android_media_MediaExtractor.cpp \ + android_media_MediaHTTPConnection.cpp \ android_media_MediaMuxer.cpp \ android_media_MediaPlayer.cpp \ android_media_MediaRecorder.cpp \ @@ -37,6 +38,7 @@ LOCAL_SHARED_LIBRARIES := \ libcamera_client \ libmtp \ libusbhost \ + libjhead \ libexif \ libstagefright_amrnb_common \ @@ -60,8 +62,7 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, libhardware)/hardware \ system/media/camera/include \ $(PV_INCLUDES) \ - $(JNI_H_INCLUDE) \ - $(call include-path-for, corecg graphics) + $(JNI_H_INCLUDE) LOCAL_CFLAGS += diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index d475eee..fcd425e 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -86,8 +86,8 @@ public: void setCpuConsumer(const sp<CpuConsumer>& consumer) { mConsumer = consumer; } CpuConsumer* getCpuConsumer() { return mConsumer.get(); } - void setBufferQueue(const sp<BufferQueue>& bq) { mBufferQueue = bq; } - BufferQueue* getBufferQueue() { return mBufferQueue.get(); } + void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; } + IGraphicBufferProducer* getProducer() { return mProducer.get(); } void setBufferFormat(int format) { mFormat = format; } int getBufferFormat() { return mFormat; } @@ -104,7 +104,7 @@ private: List<CpuConsumer::LockedBuffer*> mBuffers; sp<CpuConsumer> mConsumer; - sp<BufferQueue> mBufferQueue; + sp<IGraphicBufferProducer> mProducer; jobject mWeakThiz; jclass mClazz; int mFormat; @@ -222,7 +222,7 @@ static CpuConsumer* ImageReader_getCpuConsumer(JNIEnv* env, jobject thiz) return ctx->getCpuConsumer(); } -static BufferQueue* ImageReader_getBufferQueue(JNIEnv* env, jobject thiz) +static IGraphicBufferProducer* ImageReader_getProducer(JNIEnv* env, jobject thiz) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); @@ -230,7 +230,7 @@ static BufferQueue* ImageReader_getBufferQueue(JNIEnv* env, jobject thiz) jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); return NULL; } - return ctx->getBufferQueue(); + return ctx->getProducer(); } static void ImageReader_setNativeContext(JNIEnv* env, @@ -613,8 +613,10 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, nativeFormat = Image_getPixelFormat(env, format); - sp<BufferQueue> bq = new BufferQueue(); - sp<CpuConsumer> consumer = new CpuConsumer(bq, maxImages, + sp<IGraphicBufferProducer> gbProducer; + sp<IGraphicBufferConsumer> gbConsumer; + BufferQueue::createBufferQueue(&gbProducer, &gbConsumer); + sp<CpuConsumer> consumer = new CpuConsumer(gbConsumer, maxImages, /*controlledByApp*/true); // TODO: throw dvm exOutOfMemoryError? if (consumer == NULL) { @@ -629,7 +631,7 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, } sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages)); ctx->setCpuConsumer(consumer); - ctx->setBufferQueue(bq); + ctx->setProducer(gbProducer); consumer->setFrameAvailableListener(ctx); ImageReader_setNativeContext(env, thiz, ctx); ctx->setBufferFormat(nativeFormat); @@ -794,14 +796,14 @@ static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) { ALOGV("%s: ", __FUNCTION__); - BufferQueue* bq = ImageReader_getBufferQueue(env, thiz); - if (bq == NULL) { + IGraphicBufferProducer* gbp = ImageReader_getProducer(env, thiz); + if (gbp == NULL) { jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); return NULL; } // Wrap the IGBP in a Java-language Surface. - return android_view_Surface_createFromIGraphicBufferProducer(env, bq); + return android_view_Surface_createFromIGraphicBufferProducer(env, gbp); } static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx) diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 221ea57..b2fb2df 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -51,6 +51,10 @@ enum { DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, }; +enum { + EVENT_NOTIFY = 1, +}; + struct CryptoErrorCodes { jint cryptoErrorNoKey; jint cryptoErrorKeyExpired; @@ -59,6 +63,7 @@ struct CryptoErrorCodes { struct fields_t { jfieldID context; + jmethodID postEventFromNativeID; jfieldID cryptoInfoNumSubSamplesID; jfieldID cryptoInfoNumBytesOfClearDataID; jfieldID cryptoInfoNumBytesOfEncryptedDataID; @@ -75,7 +80,9 @@ JMediaCodec::JMediaCodec( JNIEnv *env, jobject thiz, const char *name, bool nameIsType, bool encoder) : mClass(NULL), - mObject(NULL) { + mObject(NULL), + mGeneration(1), + mRequestedActivityNotification(false) { jclass clazz = env->GetObjectClass(thiz); CHECK(clazz != NULL); @@ -87,7 +94,7 @@ JMediaCodec::JMediaCodec( mLooper->start( false, // runOnCallingThread - false, // canCallJava + true, // canCallJava PRIORITY_FOREGROUND); if (nameIsType) { @@ -101,12 +108,39 @@ status_t JMediaCodec::initCheck() const { return mCodec != NULL ? OK : NO_INIT; } -JMediaCodec::~JMediaCodec() { +void JMediaCodec::registerSelf() { + mLooper->registerHandler(this); +} + +void JMediaCodec::release() { if (mCodec != NULL) { mCodec->release(); mCodec.clear(); } + if (mLooper != NULL) { + mLooper->unregisterHandler(id()); + mLooper->stop(); + mLooper.clear(); + } +} + +JMediaCodec::~JMediaCodec() { + if (mCodec != NULL || mLooper != NULL) { + /* MediaCodec and looper should have been released explicitly already + * in setMediaCodec() (see comments in setMediaCodec()). + * + * Otherwise JMediaCodec::~JMediaCodec() might be called from within the + * message handler, doing release() there risks deadlock as MediaCodec:: + * release() post synchronous message to the same looper. + * + * Print a warning and try to proceed with releasing. + */ + ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()..."); + release(); + ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec()."); + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mObject); @@ -122,7 +156,8 @@ status_t JMediaCodec::configure( int flags) { sp<Surface> client; if (bufferProducer != NULL) { - mSurfaceTextureClient = new Surface(bufferProducer, true /* controlledByApp */); + mSurfaceTextureClient = + new Surface(bufferProducer, true /* controlledByApp */); } else { mSurfaceTextureClient.clear(); } @@ -136,13 +171,32 @@ status_t JMediaCodec::createInputSurface( } status_t JMediaCodec::start() { - return mCodec->start(); + status_t err = mCodec->start(); + + if (err != OK) { + return err; + } + + mActivityNotification = new AMessage(kWhatActivityNotify, id()); + mActivityNotification->setInt32("generation", mGeneration); + + requestActivityNotification(); + + return err; } status_t JMediaCodec::stop() { mSurfaceTextureClient.clear(); - return mCodec->stop(); + status_t err = mCodec->stop(); + + sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, id()); + sp<AMessage> response; + msg->postAndAwaitResponse(&response); + + mActivityNotification.clear(); + + return err; } status_t JMediaCodec::flush() { @@ -174,7 +228,11 @@ status_t JMediaCodec::queueSecureInputBuffer( } status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { - return mCodec->dequeueInputBuffer(index, timeoutUs); + status_t err = mCodec->dequeueInputBuffer(index, timeoutUs); + + requestActivityNotification(); + + return err; } status_t JMediaCodec::dequeueOutputBuffer( @@ -182,9 +240,12 @@ status_t JMediaCodec::dequeueOutputBuffer( size_t size, offset; int64_t timeUs; uint32_t flags; - status_t err; - if ((err = mCodec->dequeueOutputBuffer( - index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) { + status_t err = mCodec->dequeueOutputBuffer( + index, &offset, &size, &timeUs, &flags, timeoutUs); + + requestActivityNotification(); + + if (err != OK) { return err; } @@ -320,6 +381,67 @@ void JMediaCodec::setVideoScalingMode(int mode) { } } +void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatRequestActivityNotifications: + { + if (mRequestedActivityNotification) { + break; + } + + mCodec->requestActivityNotification(mActivityNotification); + mRequestedActivityNotification = true; + break; + } + + case kWhatActivityNotify: + { + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mGeneration) { + // stale + break; + } + + mRequestedActivityNotification = false; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod( + mObject, + gFields.postEventFromNativeID, + EVENT_NOTIFY, + 0 /* arg1 */, + 0 /* arg2 */, + NULL /* obj */); + + break; + } + + case kWhatStopActivityNotifications: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + ++mGeneration; + mRequestedActivityNotification = false; + + sp<AMessage> response = new AMessage; + response->postReply(replyID); + break; + } + + default: + TRESPASS(); + } +} + +void JMediaCodec::requestActivityNotification() { + (new AMessage(kWhatRequestActivityNotifications, id()))->post(); +} + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -333,6 +455,12 @@ static sp<JMediaCodec> setMediaCodec( codec->incStrong(thiz); } if (old != NULL) { + /* release MediaCodec and stop the looper now before decStrong. + * otherwise JMediaCodec::~JMediaCodec() could be called from within + * its message handler, doing release() from there will deadlock + * (as MediaCodec::release() post synchronous message to the same looper) + */ + old->release(); old->decStrong(thiz); } env->SetLongField(thiz, gFields.context, (jlong)codec.get()); @@ -888,6 +1016,12 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); CHECK(gFields.context != NULL); + gFields.postEventFromNativeID = + env->GetMethodID( + clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V"); + + CHECK(gFields.postEventFromNativeID != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo")); CHECK(clazz.get() != NULL); @@ -961,6 +1095,8 @@ static void android_media_MediaCodec_native_setup( return; } + codec->registerSelf(); + setMediaCodec(env,thiz, codec); } @@ -981,7 +1117,7 @@ static JNINativeMethod gMethods[] = { (void *)android_media_MediaCodec_createInputSurface }, { "start", "()V", (void *)android_media_MediaCodec_start }, - { "stop", "()V", (void *)android_media_MediaCodec_stop }, + { "native_stop", "()V", (void *)android_media_MediaCodec_stop }, { "flush", "()V", (void *)android_media_MediaCodec_flush }, { "queueInputBuffer", "(IIIJI)V", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 2fbbd72..2f2ea96 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -21,8 +21,8 @@ #include <media/hardware/CryptoAPI.h> #include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AHandler.h> #include <utils/Errors.h> -#include <utils/RefBase.h> namespace android { @@ -34,13 +34,16 @@ struct IGraphicBufferProducer; struct MediaCodec; class Surface; -struct JMediaCodec : public RefBase { +struct JMediaCodec : public AHandler { JMediaCodec( JNIEnv *env, jobject thiz, const char *name, bool nameIsType, bool encoder); status_t initCheck() const; + void registerSelf(); + void release(); + status_t configure( const sp<AMessage> &format, const sp<IGraphicBufferProducer> &bufferProducer, @@ -94,7 +97,15 @@ struct JMediaCodec : public RefBase { protected: virtual ~JMediaCodec(); + virtual void onMessageReceived(const sp<AMessage> &msg); + private: + enum { + kWhatActivityNotify, + kWhatRequestActivityNotifications, + kWhatStopActivityNotifications, + }; + jclass mClass; jweak mObject; sp<Surface> mSurfaceTextureClient; @@ -102,6 +113,12 @@ private: sp<ALooper> mLooper; sp<MediaCodec> mCodec; + sp<AMessage> mActivityNotification; + int32_t mGeneration; + bool mRequestedActivityNotification; + + void requestActivityNotification(); + DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec); }; diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index 543cb6c..a78f16d 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -26,6 +26,7 @@ #include "jni.h" #include "JNIHelp.h" +#include <media/IMediaHTTPService.h> #include <media/hardware/CryptoAPI.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -35,6 +36,8 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/NuMediaExtractor.h> +#include "android_util_Binder.h" + namespace android { struct fields_t { @@ -135,8 +138,10 @@ JMediaExtractor::~JMediaExtractor() { } status_t JMediaExtractor::setDataSource( - const char *path, const KeyedVector<String8, String8> *headers) { - return mImpl->setDataSource(path, headers); + const sp<IMediaHTTPService> &httpService, + const char *path, + const KeyedVector<String8, String8> *headers) { + return mImpl->setDataSource(httpService, path, headers); } status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { @@ -661,7 +666,10 @@ static void android_media_MediaExtractor_native_setup( static void android_media_MediaExtractor_setDataSource( JNIEnv *env, jobject thiz, - jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) { + jobject httpServiceBinderObj, + jstring pathObj, + jobjectArray keysArray, + jobjectArray valuesArray) { sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); if (extractor == NULL) { @@ -686,7 +694,13 @@ static void android_media_MediaExtractor_setDataSource( return; } - status_t err = extractor->setDataSource(path, &headers); + sp<IMediaHTTPService> httpService; + if (httpServiceBinderObj != NULL) { + sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast<IMediaHTTPService>(binder); + } + + status_t err = extractor->setDataSource(httpService, path, &headers); env->ReleaseStringUTFChars(pathObj, path); path = NULL; @@ -839,8 +853,9 @@ static JNINativeMethod gMethods[] = { { "native_finalize", "()V", (void *)android_media_MediaExtractor_native_finalize }, - { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;" - "[Ljava/lang/String;)V", + { "nativeSetDataSource", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;" + "[Ljava/lang/String;)V", (void *)android_media_MediaExtractor_setDataSource }, { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V", diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h index ccbad8c..e5a0c16e 100644 --- a/media/jni/android_media_MediaExtractor.h +++ b/media/jni/android_media_MediaExtractor.h @@ -29,6 +29,7 @@ namespace android { +struct IMediaHTTPService; struct MetaData; struct NuMediaExtractor; @@ -36,6 +37,7 @@ struct JMediaExtractor : public RefBase { JMediaExtractor(JNIEnv *env, jobject thiz); status_t setDataSource( + const sp<IMediaHTTPService> &httpService, const char *path, const KeyedVector<String8, String8> *headers); diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp new file mode 100644 index 0000000..c48af11 --- /dev/null +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaHTTPConnection-JNI" +#include <utils/Log.h> + +#include <binder/MemoryDealer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "android_media_MediaHTTPConnection.h" +#include "android_util_Binder.h" + +#include "android_runtime/AndroidRuntime.h" +#include "jni.h" +#include "JNIHelp.h" + +namespace android { + +JMediaHTTPConnection::JMediaHTTPConnection(JNIEnv *env, jobject thiz) + : mClass(NULL), + mObject(NULL), + mByteArrayObj(NULL) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); + + mDealer = new MemoryDealer(kBufferSize, "MediaHTTPConnection"); + mMemory = mDealer->allocate(kBufferSize); + + ScopedLocalRef<jbyteArray> tmp( + env, env->NewByteArray(JMediaHTTPConnection::kBufferSize)); + + mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get()); +} + +JMediaHTTPConnection::~JMediaHTTPConnection() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteGlobalRef(mByteArrayObj); + mByteArrayObj = NULL; + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +sp<IMemory> JMediaHTTPConnection::getIMemory() { + return mMemory; +} + +jbyteArray JMediaHTTPConnection::getByteArrayObj() { + return mByteArrayObj; +} + +} // namespace android + +using namespace android; + +struct fields_t { + jfieldID context; + + jmethodID readAtMethodID; +}; + +static fields_t gFields; + +static sp<JMediaHTTPConnection> setObject( + JNIEnv *env, jobject thiz, const sp<JMediaHTTPConnection> &conn) { + sp<JMediaHTTPConnection> old = + (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context); + + if (conn != NULL) { + conn->incStrong(thiz); + } + if (old != NULL) { + old->decStrong(thiz); + } + env->SetIntField(thiz, gFields.context, (int)conn.get()); + + return old; +} + +static sp<JMediaHTTPConnection> getObject(JNIEnv *env, jobject thiz) { + return (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context); +} + +static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { + ScopedLocalRef<jclass> clazz( + env, env->FindClass("android/media/MediaHTTPConnection")); + CHECK(clazz.get() != NULL); + + gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I"); + CHECK(gFields.context != NULL); + + gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I"); +} + +static void android_media_MediaHTTPConnection_native_setup( + JNIEnv *env, jobject thiz) { + sp<JMediaHTTPConnection> conn = new JMediaHTTPConnection(env, thiz); + + setObject(env, thiz, conn); +} + +static void android_media_MediaHTTPConnection_native_finalize( + JNIEnv *env, jobject thiz) { + setObject(env, thiz, NULL); +} + +static jobject android_media_MediaHTTPConnection_native_getIMemory( + JNIEnv *env, jobject thiz) { + sp<JMediaHTTPConnection> conn = getObject(env, thiz); + + return javaObjectForIBinder(env, conn->getIMemory()->asBinder()); +} + +static jint android_media_MediaHTTPConnection_native_readAt( + JNIEnv *env, jobject thiz, jlong offset, jint size) { + sp<JMediaHTTPConnection> conn = getObject(env, thiz); + + if (size > JMediaHTTPConnection::kBufferSize) { + size = JMediaHTTPConnection::kBufferSize; + } + + jbyteArray byteArrayObj = conn->getByteArrayObj(); + + jint n = env->CallIntMethod( + thiz, gFields.readAtMethodID, offset, byteArrayObj, size); + + if (n > 0) { + env->GetByteArrayRegion( + byteArrayObj, + 0, + n, + (jbyte *)conn->getIMemory()->pointer()); + } + + return n; +} + +static JNINativeMethod gMethods[] = { + { "native_getIMemory", "()Landroid/os/IBinder;", + (void *)android_media_MediaHTTPConnection_native_getIMemory }, + + { "native_readAt", "(JI)I", + (void *)android_media_MediaHTTPConnection_native_readAt }, + + { "native_init", "()V", + (void *)android_media_MediaHTTPConnection_native_init }, + + { "native_setup", "()V", + (void *)android_media_MediaHTTPConnection_native_setup }, + + { "native_finalize", "()V", + (void *)android_media_MediaHTTPConnection_native_finalize }, +}; + +int register_android_media_MediaHTTPConnection(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/media/MediaHTTPConnection", gMethods, NELEM(gMethods)); +} + diff --git a/media/jni/android_media_MediaHTTPConnection.h b/media/jni/android_media_MediaHTTPConnection.h new file mode 100644 index 0000000..62ff678 --- /dev/null +++ b/media/jni/android_media_MediaHTTPConnection.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ +#define _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ + +#include "jni.h" + +#include <media/stagefright/foundation/ABase.h> +#include <utils/RefBase.h> + +namespace android { + +struct IMemory; +struct MemoryDealer; + +struct JMediaHTTPConnection : public RefBase { + enum { + kBufferSize = 32768, + }; + + JMediaHTTPConnection(JNIEnv *env, jobject thiz); + + sp<IMemory> getIMemory(); + + jbyteArray getByteArrayObj(); + +protected: + virtual ~JMediaHTTPConnection(); + +private: + jclass mClass; + jweak mObject; + jbyteArray mByteArrayObj; + + sp<MemoryDealer> mDealer; + sp<IMemory> mMemory; + + DISALLOW_EVIL_CONSTRUCTORS(JMediaHTTPConnection); +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 6176f0f..4e42ae3 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -21,7 +21,8 @@ #include <assert.h> #include <utils/Log.h> #include <utils/threads.h> -#include <core/SkBitmap.h> +#include <SkBitmap.h> +#include <media/IMediaHTTPService.h> #include <media/mediametadataretriever.h> #include <private/media/VideoFrame.h> @@ -29,6 +30,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include "android_media_Utils.h" +#include "android_util_Binder.h" using namespace android; @@ -80,7 +82,7 @@ static void setRetriever(JNIEnv* env, jobject thiz, MediaMetadataRetriever* retr static void android_media_MediaMetadataRetriever_setDataSourceAndHeaders( - JNIEnv *env, jobject thiz, jstring path, + JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path, jobjectArray keys, jobjectArray values) { ALOGV("setDataSource"); @@ -122,10 +124,19 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( env, keys, values, &headersVector)) { return; } + + sp<IMediaHTTPService> httpService; + if (httpServiceBinderObj != NULL) { + sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast<IMediaHTTPService>(binder); + } + process_media_retriever_call( env, retriever->setDataSource( - pathStr.string(), headersVector.size() > 0 ? &headersVector : NULL), + httpService, + pathStr.string(), + headersVector.size() > 0 ? &headersVector : NULL), "java/lang/RuntimeException", "setDataSource failed"); @@ -442,7 +453,7 @@ static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobje static JNINativeMethod nativeMethods[] = { { "_setDataSource", - "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders }, diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index 3561b06..3fef446f 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -164,7 +164,7 @@ static void android_media_MediaMuxer_setOrientationHint( } static void android_media_MediaMuxer_setLocation( - JNIEnv *env, jclass clazz, jint nativeObject, jint latitude, jint longitude) { + JNIEnv *env, jclass clazz, jlong nativeObject, jint latitude, jint longitude) { MediaMuxer* muxer = reinterpret_cast<MediaMuxer *>(nativeObject); status_t res = muxer->setLocation(latitude, longitude); diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 9d0d5a6..dc3ae5b 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -20,6 +20,7 @@ #include "utils/Log.h" #include <media/mediaplayer.h> +#include <media/IMediaHTTPService.h> #include <media/MediaPlayerInterface.h> #include <stdio.h> #include <assert.h> @@ -45,6 +46,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include "android_util_Binder.h" // ---------------------------------------------------------------------------- using namespace android; @@ -183,7 +185,7 @@ static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStat static void android_media_MediaPlayer_setDataSourceAndHeaders( - JNIEnv *env, jobject thiz, jstring path, + JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path, jobjectArray keys, jobjectArray values) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); @@ -214,8 +216,15 @@ android_media_MediaPlayer_setDataSourceAndHeaders( return; } + sp<IMediaHTTPService> httpService; + if (httpServiceBinderObj != NULL) { + sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast<IMediaHTTPService>(binder); + } + status_t opStatus = mp->setDataSource( + httpService, pathStr, headersVector.size() > 0? &headersVector : NULL); @@ -726,7 +735,8 @@ static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz } static jint -android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply) +android_media_MediaPlayer_pullBatteryData( + JNIEnv *env, jobject /* thiz */, jobject java_reply) { sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player")); sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); @@ -806,58 +816,13 @@ android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject ; } -static void -android_media_MediaPlayer_updateProxyConfig( - JNIEnv *env, jobject thiz, jobject proxyProps) -{ - ALOGV("updateProxyConfig"); - sp<MediaPlayer> thisplayer = getMediaPlayer(env, thiz); - if (thisplayer == NULL) { - return; - } - - if (proxyProps == NULL) { - thisplayer->updateProxyConfig( - NULL /* host */, 0 /* port */, NULL /* exclusionList */); - } else { - jstring hostObj = (jstring)env->CallObjectMethod( - proxyProps, fields.proxyConfigGetHost); - - const char *host = env->GetStringUTFChars(hostObj, NULL); - - int port = env->CallIntMethod(proxyProps, fields.proxyConfigGetPort); - - jstring exclusionListObj = (jstring)env->CallObjectMethod( - proxyProps, fields.proxyConfigGetExclusionList); - - if (host != NULL && exclusionListObj != NULL) { - const char *exclusionList = env->GetStringUTFChars(exclusionListObj, NULL); - - if (exclusionList != NULL) { - thisplayer->updateProxyConfig(host, port, exclusionList); - - env->ReleaseStringUTFChars(exclusionListObj, exclusionList); - exclusionList = NULL; - } else { - thisplayer->updateProxyConfig(host, port, ""); - } - } else if (host != NULL) { - thisplayer->updateProxyConfig(host, port, ""); - } - - if (host != NULL) { - env->ReleaseStringUTFChars(hostObj, host); - host = NULL; - } - } -} - // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { { - "_setDataSource", - "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + "nativeSetDataSource", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;" + "[Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSourceAndHeaders }, @@ -893,7 +858,6 @@ static JNINativeMethod gMethods[] = { {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint}, {"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer}, - {"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig}, }; static const char* const kClassPathName = "android/media/MediaPlayer"; @@ -911,6 +875,7 @@ extern int register_android_media_Drm(JNIEnv *env); extern int register_android_media_MediaCodec(JNIEnv *env); extern int register_android_media_MediaExtractor(JNIEnv *env); extern int register_android_media_MediaCodecList(JNIEnv *env); +extern int register_android_media_MediaHTTPConnection(JNIEnv *env); extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); extern int register_android_media_MediaMuxer(JNIEnv *env); extern int register_android_media_MediaRecorder(JNIEnv *env); @@ -922,7 +887,7 @@ extern int register_android_mtp_MtpDatabase(JNIEnv *env); extern int register_android_mtp_MtpDevice(JNIEnv *env); extern int register_android_mtp_MtpServer(JNIEnv *env); -jint JNI_OnLoad(JavaVM* vm, void* reserved) +jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { JNIEnv* env = NULL; jint result = -1; @@ -1018,6 +983,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_media_MediaHTTPConnection(env) < 0) { + ALOGE("ERROR: MediaHTTPConnection native registration failed"); + goto bail; + } + /* success -- return valid version number */ result = JNI_VERSION_1_4; diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 0cfd2ff..fd69cad 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -344,6 +344,26 @@ android_media_MediaRecorder_native_getMaxAmplitude(JNIEnv *env, jobject thiz) return (jint) result; } +static jobject +android_media_MediaRecorder_getSurface(JNIEnv *env, jobject thiz) +{ + ALOGV("getSurface"); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + + sp<IGraphicBufferProducer> bufferProducer = mr->querySurfaceMediaSourceFromMediaServer(); + if (bufferProducer == NULL) { + jniThrowException( + env, + "java/lang/IllegalStateException", + "failed to get surface"); + return NULL; + } + + // Wrap the IGBP in a Java-language Surface. + return android_view_Surface_createFromIGraphicBufferProducer(env, + bufferProducer); +} + static void android_media_MediaRecorder_start(JNIEnv *env, jobject thiz) { @@ -470,6 +490,7 @@ static JNINativeMethod gMethods[] = { {"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration}, {"setMaxFileSize", "(J)V", (void *)android_media_MediaRecorder_setMaxFileSize}, {"_prepare", "()V", (void *)android_media_MediaRecorder_prepare}, + {"getSurface", "()Landroid/view/Surface;", (void *)android_media_MediaRecorder_getSurface}, {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, {"start", "()V", (void *)android_media_MediaRecorder_start}, {"stop", "()V", (void *)android_media_MediaRecorder_stop}, diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 6b0bd0d..82c6a80 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -68,6 +68,8 @@ static jmethodID method_sessionStarted; static jmethodID method_sessionEnded; static jfieldID field_context; +static jfieldID field_batteryLevel; +static jfieldID field_batteryScale; // MtpPropertyList fields static jfieldID field_mCount; @@ -527,68 +529,75 @@ MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle, MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet) { - int type; + JNIEnv* env = AndroidRuntime::getJNIEnv(); - if (!getDevicePropertyInfo(property, type)) - return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; + if (property == MTP_DEVICE_PROPERTY_BATTERY_LEVEL) { + // special case - implemented here instead of Java + packet.putUInt8((uint8_t)env->GetIntField(mDatabase, field_batteryLevel)); + return MTP_RESPONSE_OK; + } else { + int type; + + if (!getDevicePropertyInfo(property, type)) + return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; + + jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty, + (jint)property, mLongBuffer, mStringBuffer); + if (result != MTP_RESPONSE_OK) { + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return result; + } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty, - (jint)property, mLongBuffer, mStringBuffer); - if (result != MTP_RESPONSE_OK) { - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return result; - } + jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); + jlong longValue = longValues[0]; + env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); - jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); - jlong longValue = longValues[0]; - env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); + switch (type) { + case MTP_TYPE_INT8: + packet.putInt8(longValue); + break; + case MTP_TYPE_UINT8: + packet.putUInt8(longValue); + break; + case MTP_TYPE_INT16: + packet.putInt16(longValue); + break; + case MTP_TYPE_UINT16: + packet.putUInt16(longValue); + break; + case MTP_TYPE_INT32: + packet.putInt32(longValue); + break; + case MTP_TYPE_UINT32: + packet.putUInt32(longValue); + break; + case MTP_TYPE_INT64: + packet.putInt64(longValue); + break; + case MTP_TYPE_UINT64: + packet.putUInt64(longValue); + break; + case MTP_TYPE_INT128: + packet.putInt128(longValue); + break; + case MTP_TYPE_UINT128: + packet.putInt128(longValue); + break; + case MTP_TYPE_STR: + { + jchar* str = env->GetCharArrayElements(mStringBuffer, 0); + packet.putString(str); + env->ReleaseCharArrayElements(mStringBuffer, str, 0); + break; + } + default: + ALOGE("unsupported type in getDevicePropertyValue\n"); + return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT; + } - switch (type) { - case MTP_TYPE_INT8: - packet.putInt8(longValue); - break; - case MTP_TYPE_UINT8: - packet.putUInt8(longValue); - break; - case MTP_TYPE_INT16: - packet.putInt16(longValue); - break; - case MTP_TYPE_UINT16: - packet.putUInt16(longValue); - break; - case MTP_TYPE_INT32: - packet.putInt32(longValue); - break; - case MTP_TYPE_UINT32: - packet.putUInt32(longValue); - break; - case MTP_TYPE_INT64: - packet.putInt64(longValue); - break; - case MTP_TYPE_UINT64: - packet.putUInt64(longValue); - break; - case MTP_TYPE_INT128: - packet.putInt128(longValue); - break; - case MTP_TYPE_UINT128: - packet.putInt128(longValue); - break; - case MTP_TYPE_STR: - { - jchar* str = env->GetCharArrayElements(mStringBuffer, 0); - packet.putString(str); - env->ReleaseCharArrayElements(mStringBuffer, str, 0); - break; - } - default: - ALOGE("unsupported type in getDevicePropertyValue\n"); - return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT; + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return MTP_RESPONSE_OK; } - - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return MTP_RESPONSE_OK; } MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property, @@ -923,6 +932,7 @@ static const PropertyTableEntry kDevicePropertyTable[] = { { MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR }, { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR }, { MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR }, + { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 }, }; bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) { @@ -990,6 +1000,22 @@ MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle, MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, MtpObjectFormat format) { + static const int channelEnum[] = { + 1, // mono + 2, // stereo + 3, // 2.1 + 4, // 3 + 5, // 3.1 + 6, // 4 + 7, // 4.1 + 8, // 5 + 9, // 5.1 + }; + static const int bitrateEnum[] = { + 1, // fixed rate + 2, // variable rate + }; + MtpProperty* result = NULL; switch (property) { case MTP_PROPERTY_OBJECT_FORMAT: @@ -1003,6 +1029,7 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, case MTP_PROPERTY_STORAGE_ID: case MTP_PROPERTY_PARENT_OBJECT: case MTP_PROPERTY_DURATION: + case MTP_PROPERTY_AUDIO_WAVE_CODEC: result = new MtpProperty(property, MTP_TYPE_UINT32); break; case MTP_PROPERTY_OBJECT_SIZE: @@ -1031,6 +1058,22 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, // We allow renaming files and folders result = new MtpProperty(property, MTP_TYPE_STR, true); break; + case MTP_PROPERTY_BITRATE_TYPE: + result = new MtpProperty(property, MTP_TYPE_UINT16); + result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0])); + break; + case MTP_PROPERTY_AUDIO_BITRATE: + result = new MtpProperty(property, MTP_TYPE_UINT32); + result->setFormRange(1, 1536000, 1); + break; + case MTP_PROPERTY_NUMBER_OF_CHANNELS: + result = new MtpProperty(property, MTP_TYPE_UINT16); + result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0])); + break; + case MTP_PROPERTY_SAMPLE_RATE: + result = new MtpProperty(property, MTP_TYPE_UINT32); + result->setFormRange(8000, 48000, 1); + break; } return result; @@ -1046,7 +1089,7 @@ MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: writable = true; // fall through - case MTP_DEVICE_PROPERTY_IMAGE_SIZE: + case MTP_DEVICE_PROPERTY_IMAGE_SIZE: { result = new MtpProperty(property, MTP_TYPE_STR, writable); // get current value @@ -1063,6 +1106,12 @@ MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { ALOGE("unable to read device property, response: %04X", ret); } break; + } + case MTP_DEVICE_PROPERTY_BATTERY_LEVEL: + result = new MtpProperty(property, MTP_TYPE_UINT8); + result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1); + result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel); + break; } checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -1234,6 +1283,16 @@ int register_android_mtp_MtpDatabase(JNIEnv *env) ALOGE("Can't find MtpDatabase.mNativeContext"); return -1; } + field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); + if (field_batteryLevel == NULL) { + ALOGE("Can't find MtpDatabase.mBatteryLevel"); + return -1; + } + field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I"); + if (field_batteryScale == NULL) { + ALOGE("Can't find MtpDatabase.mBatteryScale"); + return -1; + } // now set up fields for MtpPropertyList class clazz = env->FindClass("android/mtp/MtpPropertyList"); diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 9d7f1c2..2f90dfe 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -118,6 +118,18 @@ android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle } static void +android_mtp_MtpServer_send_device_property_changed(JNIEnv *env, jobject thiz, jint property) +{ + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) + server->sendDevicePropertyChanged(property); + else + ALOGE("server is null in send_object_removed"); +} + +static void android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) { Mutex::Autolock autoLock(sMutex); @@ -174,6 +186,8 @@ static JNINativeMethod gMethods[] = { {"native_cleanup", "()V", (void *)android_mtp_MtpServer_cleanup}, {"native_send_object_added", "(I)V", (void *)android_mtp_MtpServer_send_object_added}, {"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed}, + {"native_send_device_property_changed", "(I)V", + (void *)android_mtp_MtpServer_send_device_property_changed}, {"native_add_storage", "(Landroid/mtp/MtpStorage;)V", (void *)android_mtp_MtpServer_add_storage}, {"native_remove_storage", "(I)V", (void *)android_mtp_MtpServer_remove_storage}, diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk index 6be7fdd..76e8346 100644 --- a/media/jni/mediaeditor/Android.mk +++ b/media/jni/mediaeditor/Android.mk @@ -35,7 +35,6 @@ LOCAL_C_INCLUDES += \ $(TOP)/frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/base/media/libstagefright/rtsp \ $(JNI_H_INCLUDE) \ - $(call include-path-for, corecg graphics) \ $(TOP)/frameworks/native/include/media/editor \ $(TOP)/frameworks/base/core/jni/mediaeditor \ $(TOP)/frameworks/av/libvideoeditor/vss/inc \ diff --git a/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java b/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java index 6cfc0e8..dc9dd79 100644 --- a/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java +++ b/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java @@ -16,7 +16,6 @@ package com.android.media.remotedisplay; -import android.media.MediaRouter; import android.media.RemoteDisplayState.RemoteDisplayInfo; import android.text.TextUtils; diff --git a/media/mca/effect/java/android/media/effect/EffectContext.java b/media/mca/effect/java/android/media/effect/EffectContext.java index ef03229..a11b9c4 100644 --- a/media/mca/effect/java/android/media/effect/EffectContext.java +++ b/media/mca/effect/java/android/media/effect/EffectContext.java @@ -19,10 +19,7 @@ package android.media.effect; import android.filterfw.core.CachedFrameManager; import android.filterfw.core.FilterContext; -import android.filterfw.core.FilterFactory; import android.filterfw.core.GLEnvironment; -import android.filterfw.core.GLFrame; -import android.filterfw.core.FrameManager; import android.opengl.GLES20; /** diff --git a/media/mca/effect/java/android/media/effect/EffectFactory.java b/media/mca/effect/java/android/media/effect/EffectFactory.java index 4330279..f6fcba7 100644 --- a/media/mca/effect/java/android/media/effect/EffectFactory.java +++ b/media/mca/effect/java/android/media/effect/EffectFactory.java @@ -18,7 +18,6 @@ package android.media.effect; import java.lang.reflect.Constructor; -import java.util.HashMap; /** * <p>The EffectFactory class defines the list of available Effects, and provides functionality to diff --git a/media/mca/effect/java/android/media/effect/FilterEffect.java b/media/mca/effect/java/android/media/effect/FilterEffect.java index d7c319e..34b3549 100644 --- a/media/mca/effect/java/android/media/effect/FilterEffect.java +++ b/media/mca/effect/java/android/media/effect/FilterEffect.java @@ -17,10 +17,7 @@ package android.media.effect; -import android.filterfw.core.CachedFrameManager; import android.filterfw.core.FilterContext; -import android.filterfw.core.FilterFactory; -import android.filterfw.core.GLEnvironment; import android.filterfw.core.GLFrame; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; diff --git a/media/mca/effect/java/android/media/effect/FilterGraphEffect.java b/media/mca/effect/java/android/media/effect/FilterGraphEffect.java index b18bea8..80c695b 100644 --- a/media/mca/effect/java/android/media/effect/FilterGraphEffect.java +++ b/media/mca/effect/java/android/media/effect/FilterGraphEffect.java @@ -19,17 +19,13 @@ package android.media.effect; import android.filterfw.core.Filter; import android.filterfw.core.FilterGraph; import android.filterfw.core.GraphRunner; -import android.filterfw.core.SimpleScheduler; import android.filterfw.core.SyncRunner; -import android.media.effect.Effect; import android.media.effect.FilterEffect; import android.media.effect.EffectContext; import android.filterfw.io.GraphIOException; import android.filterfw.io.GraphReader; import android.filterfw.io.TextGraphReader; -import android.util.Log; - /** * Effect subclass for effects based on a single Filter. Subclasses need only invoke the * constructor with the correct arguments to obtain an Effect implementation. diff --git a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java index 6f85861..47900df 100644 --- a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java +++ b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java @@ -21,11 +21,8 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterFactory; import android.filterfw.core.FilterFunction; import android.filterfw.core.Frame; -import android.media.effect.Effect; import android.media.effect.EffectContext; -import android.util.Log; - /** * Effect subclass for effects based on a single Filter. Subclasses need only invoke the * constructor with the correct arguments to obtain an Effect implementation. diff --git a/media/mca/effect/java/android/media/effect/SizeChangeEffect.java b/media/mca/effect/java/android/media/effect/SizeChangeEffect.java index 4d27bae..1bf7d40 100644 --- a/media/mca/effect/java/android/media/effect/SizeChangeEffect.java +++ b/media/mca/effect/java/android/media/effect/SizeChangeEffect.java @@ -16,15 +16,9 @@ package android.media.effect; -import android.filterfw.core.Filter; -import android.filterfw.core.FilterFactory; -import android.filterfw.core.FilterFunction; import android.filterfw.core.Frame; -import android.media.effect.Effect; import android.media.effect.EffectContext; -import android.util.Log; - /** * Effect subclass for effects based on a single Filter with output size differnet * from input. Subclasses need only invoke the constructor with the correct arguments diff --git a/media/mca/effect/java/android/media/effect/effects/CropEffect.java b/media/mca/effect/java/android/media/effect/effects/CropEffect.java index 3e8d78a..7e1c495 100644 --- a/media/mca/effect/java/android/media/effect/effects/CropEffect.java +++ b/media/mca/effect/java/android/media/effect/effects/CropEffect.java @@ -19,7 +19,6 @@ package android.media.effect.effects; import android.media.effect.EffectContext; import android.media.effect.SizeChangeEffect; -import android.media.effect.SingleFilterEffect; import android.filterpacks.imageproc.CropRectFilter; /** diff --git a/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java b/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java index 3f36d98..feaf6e8 100644 --- a/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java +++ b/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java @@ -20,7 +20,6 @@ package android.filterfw; import android.filterfw.core.Filter; import android.filterfw.core.FilterFactory; import android.filterfw.core.FilterFunction; -import android.filterfw.core.Frame; import android.filterfw.core.FrameManager; /** diff --git a/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java b/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java index 70cbad4..819774a 100644 --- a/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java +++ b/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java @@ -18,17 +18,9 @@ package android.filterfw.core; import android.os.AsyncTask; -import android.os.Handler; import android.util.Log; -import java.lang.InterruptedException; -import java.lang.Runnable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.TimeUnit; - /** * @hide */ diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java index 73b009d..062b6ba 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Filter.java +++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java @@ -33,7 +33,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; -import java.util.LinkedList; import java.util.Set; /** diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java index ef8c542..7dd0783 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Frame.java +++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java @@ -20,7 +20,6 @@ package android.filterfw.core; import android.filterfw.core.FrameFormat; import android.filterfw.core.FrameManager; import android.graphics.Bitmap; -import android.util.Log; import java.nio.ByteBuffer; diff --git a/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java b/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java index bfd09ba..7d1553f 100644 --- a/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java +++ b/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java @@ -24,8 +24,6 @@ import android.filterfw.core.GLFrame; import android.filterfw.core.NativeBuffer; import android.graphics.Bitmap; -import android.util.Log; - import java.nio.ByteBuffer; /** diff --git a/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java b/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java index dbc8d16..83c475f 100644 --- a/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java +++ b/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java @@ -18,7 +18,6 @@ package android.filterfw.core; import android.filterfw.core.Filter; -import android.filterfw.core.Scheduler; import android.filterfw.core.RoundRobinScheduler; import android.util.Log; diff --git a/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java b/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java index f493fd2..35ba04f 100644 --- a/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java +++ b/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java @@ -20,7 +20,6 @@ package android.filterfw.core; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.FrameManager; -import android.filterfw.core.NativeBuffer; import android.filterfw.format.ObjectFormat; import android.graphics.Bitmap; @@ -28,10 +27,7 @@ import java.io.InputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.OptionalDataException; import java.io.OutputStream; -import java.io.StreamCorruptedException; -import java.lang.reflect.Constructor; import java.nio.ByteBuffer; /** diff --git a/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java b/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java index 534a30d..7ddd1d4 100644 --- a/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java +++ b/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java @@ -20,11 +20,9 @@ package android.filterfw.core; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.FrameManager; -import android.filterfw.core.NativeBuffer; import android.filterfw.format.ObjectFormat; import android.graphics.Bitmap; -import java.lang.reflect.Constructor; import java.nio.ByteBuffer; /** diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp index 84dad8c..f092af8 100644 --- a/media/mca/filterfw/native/core/gl_env.cpp +++ b/media/mca/filterfw/native/core/gl_env.cpp @@ -162,9 +162,11 @@ bool GLEnv::InitWithNewContext() { } // Create dummy surface using a GLConsumer - sp<BufferQueue> bq = new BufferQueue(); - surfaceTexture_ = new GLConsumer(bq, 0); - window_ = new Surface(static_cast<sp<IGraphicBufferProducer> >(bq)); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + surfaceTexture_ = new GLConsumer(consumer, 0); + window_ = new Surface(producer); surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL); if (CheckEGLError("eglCreateWindowSurface")) return false; diff --git a/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java index 4185343..4a47fa4 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java @@ -20,13 +20,8 @@ package android.filterpacks.base; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.os.Handler; import android.os.Looper; diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java index 6b8cbc7..f909c3f 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java @@ -22,7 +22,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.KeyValueMap; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java index 518b837..87d8f0b 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java @@ -24,8 +24,6 @@ import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java index 3aadaac..05ac50d 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java @@ -20,7 +20,6 @@ package android.filterpacks.base; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; /** diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java index 1776820..af61d9a 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java @@ -23,11 +23,8 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.MutableFrameFormat; import android.filterfw.format.ImageFormat; -import java.util.Set; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java index b2285cd..91bb417 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java @@ -23,11 +23,8 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.MutableFrameFormat; import android.filterfw.format.ImageFormat; -import java.util.Set; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java index 6c22ee7..0ef9055 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java @@ -23,7 +23,6 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.format.PrimitiveFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java index f3e08e4..73434d4 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java @@ -19,8 +19,6 @@ package android.filterpacks.base; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java index d511e44..78e2b50 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java @@ -17,15 +17,12 @@ package android.filterpacks.base; -import java.util.Set; - import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.MutableFrameFormat; import android.filterfw.format.ObjectFormat; /** diff --git a/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java index 3d3d0f1..fd65a9d 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java @@ -20,7 +20,6 @@ package android.filterpacks.base; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import java.io.OutputStream; diff --git a/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java index 254167a..0e988820 100644 --- a/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java @@ -21,9 +21,7 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; /** diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java index 473369c..c3cc282 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java @@ -17,18 +17,9 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; -import android.filterfw.format.ImageFormat; - -import java.util.Set; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java index c71c1c9..ac83db2 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java @@ -21,13 +21,10 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.NativeProgram; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - public class AutoFixFilter extends Filter { @GenerateFieldPort(name = "tile_size", hasDefault = true) diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java index e4bb6cf..92b177c 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java @@ -21,17 +21,11 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.filterpacks.imageproc.ImageCombineFilter; import android.graphics.Bitmap; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java index 978fc94..89e8723 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java @@ -17,17 +17,11 @@ package android.filterpacks.imageproc; -import android.content.Context; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; -import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeFrame; import android.filterfw.format.ImageFormat; import android.graphics.Bitmap; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java index 20b02d2..38221b4 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java index 29bc8a3..aff5e9e 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java @@ -16,18 +16,9 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; -import android.filterfw.format.ImageFormat; - -import java.util.Set; /** * The filter linearly blends "left" and "right" frames. The blending weight is diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java index 046e69d..bc62e19 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java @@ -17,13 +17,8 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java index 7488980..1d408be 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java @@ -21,13 +21,9 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; public class ColorTemperatureFilter extends Filter { diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java index 70e987f..7043c72 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java @@ -17,18 +17,11 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; -import java.util.Set; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java index 5222d9c..0ef323c 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java @@ -22,19 +22,13 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; -import android.filterfw.geometry.Point; import android.filterfw.geometry.Quad; import android.filterfw.format.ImageFormat; import android.filterfw.format.ObjectFormat; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java index d423d06..010ee21 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java @@ -21,14 +21,9 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java index e0514f8..d565e65 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java @@ -21,15 +21,10 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - public class CrossProcessFilter extends Filter { @GenerateFieldPort(name = "tile_size", hasDefault = true) diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java index f93a82c..72745c0 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java index 3f1711e..d10a6ef 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java @@ -21,17 +21,11 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.GLFrame; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.geometry.Quad; import android.filterfw.format.ImageFormat; import android.filterfw.format.ObjectFormat; -import android.opengl.GLES20; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java index 83c9348..b288e6e 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java @@ -23,9 +23,6 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.geometry.Quad; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java index d8c88ee..ef82ee9 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java index fc917a1..c7fb55d 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java @@ -21,10 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.GenerateFinalPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java index 5d7929f..2ff6588 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java @@ -22,17 +22,11 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - import java.lang.Math; -import java.util.Set; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java index 3d319ea..340f308 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java @@ -22,7 +22,6 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.filterfw.geometry.Point; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java index f8b857b..68c760f 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java @@ -21,10 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java index 577243a..528eaa2 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java @@ -21,14 +21,9 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.filterfw.geometry.Quad; -import android.filterfw.geometry.Point; import java.util.Date; import java.util.Random; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java index 858489b..c9a6956 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java @@ -21,16 +21,10 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; -import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java index a5405cb..e8bf482 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java @@ -17,21 +17,16 @@ package android.filterpacks.imageproc; -import android.content.Context; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.format.ImageFormat; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; -import android.util.Log; - import java.io.OutputStream; -import java.io.IOException; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java index b996eb8..8bf80b2 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java @@ -20,9 +20,7 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java index 20aba91..5e3d15b 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java @@ -20,16 +20,12 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - public class ImageStitcher extends Filter { @GenerateFieldPort(name = "xSlices") diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java index 400fd5d..881e30f 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java @@ -17,12 +17,8 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java index 726ffff..4e53f92 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java index 440d6a6..f66fc23 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java index bc2e553..864d7e2 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java index 8618804..48b2fdf 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; @@ -31,8 +28,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.PointF; -import android.util.Log; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java index 411e061..c79c11b 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java @@ -23,10 +23,7 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java index 3da7939..43d8d6c 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java @@ -22,16 +22,11 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.filterfw.geometry.Quad; import android.filterfw.geometry.Point; -import android.util.Log; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java index b83af39..757fac1 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java index 7a83fdf..a9f4e2c 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java index 256b769..a290996 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java @@ -21,15 +21,10 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import java.util.Set; - public class SharpenFilter extends Filter { @GenerateFieldPort(name = "scale", hasDefault = true) diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java index f4fc271..afe92de 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java @@ -21,16 +21,10 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; -import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java index c9f097d..9db296b 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java @@ -22,16 +22,11 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.filterfw.geometry.Quad; import android.filterfw.geometry.Point; -import android.util.Log; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java index 0da54a5..2b140ba 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java index 00e7bf4..760ce3a 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java @@ -17,23 +17,14 @@ package android.filterpacks.imageproc; -import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - -import java.lang.reflect.Field; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java index bc4a65e..3c121d0 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java @@ -27,8 +27,6 @@ import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - import java.lang.Math; /** * @hide diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java index ab4814f..f0084fa 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java @@ -21,16 +21,11 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; -import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java index 9258502..bbb0fc3 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java @@ -21,16 +21,11 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; -import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java index 715fec6..249cc6f 100644 --- a/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java @@ -21,9 +21,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; diff --git a/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java index cc33b89..2fe519f 100644 --- a/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java @@ -22,8 +22,6 @@ import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; import android.filterfw.format.ObjectFormat; /** diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java index a5c1ccb..ba88736 100644 --- a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java @@ -26,19 +26,11 @@ import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLEnvironment; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; -import android.view.Surface; import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import android.graphics.Rect; import android.util.Log; diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java index 308d168..05cb81b 100644 --- a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java @@ -25,19 +25,11 @@ import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLEnvironment; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import android.graphics.Rect; import android.util.Log; diff --git a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java index 78f7f3e..a31ac2c 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java @@ -24,7 +24,6 @@ import android.filterfw.core.Frame; import android.filterfw.core.GLFrame; import android.filterfw.core.FrameFormat; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.opengl.GLES20; @@ -32,7 +31,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; -import java.lang.ArrayIndexOutOfBoundsException; import java.lang.Math; import java.util.Arrays; import java.nio.ByteBuffer; diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java index 8bb653b..d034051 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java @@ -17,32 +17,23 @@ package android.filterpacks.videosink; -import android.content.Context; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; -import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.filterfw.geometry.Point; import android.filterfw.geometry.Quad; -import android.os.ConditionVariable; import android.media.MediaRecorder; import android.media.CamcorderProfile; import android.filterfw.core.GLEnvironment; import java.io.IOException; import java.io.FileDescriptor; -import java.util.List; -import java.util.Set; import android.util.Log; diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java index dbf9768..ce7a8c1 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java @@ -18,7 +18,6 @@ package android.filterpacks.videosink; import java.lang.RuntimeException; -import android.util.Log; /** @hide **/ public class MediaRecorderStopException extends RuntimeException { diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java index 2c474ab..d260684 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java @@ -17,29 +17,22 @@ package android.filterpacks.videosrc; -import android.content.Context; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; -import android.os.ConditionVariable; import android.opengl.Matrix; import java.io.IOException; import java.util.List; -import java.util.Set; import android.util.Log; diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java index 0be6c62..53a39a7 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java @@ -23,28 +23,20 @@ import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.net.Uri; -import android.os.ConditionVariable; import android.opengl.Matrix; import android.view.Surface; import java.io.IOException; -import java.io.FileDescriptor; import java.lang.IllegalArgumentException; -import java.util.List; -import java.util.Set; import android.util.Log; diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java index 37fa242..6595baa 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java @@ -16,31 +16,20 @@ package android.filterpacks.videosrc; -import android.content.Context; -import android.content.res.AssetFileDescriptor; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.graphics.SurfaceTexture; -import android.media.MediaPlayer; import android.os.ConditionVariable; import android.opengl.Matrix; -import java.io.IOException; -import java.io.FileDescriptor; -import java.lang.IllegalArgumentException; -import java.util.List; -import java.util.Set; - import android.util.Log; /** <p>A filter that converts textures from a SurfaceTexture object into frames for diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java index b6d9f94..5d03627 100644 --- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java +++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java @@ -19,29 +19,19 @@ package android.filterpacks.videosrc; import android.filterfw.core.Filter; import android.filterfw.core.FilterContext; -import android.filterfw.core.FilterSurfaceView; import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.GenerateFieldPort; import android.filterfw.core.GenerateFinalPort; import android.filterfw.core.GLEnvironment; import android.filterfw.core.GLFrame; -import android.filterfw.core.KeyValueMap; import android.filterfw.core.MutableFrameFormat; -import android.filterfw.core.NativeProgram; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; import android.filterfw.format.ImageFormat; import android.filterfw.geometry.Quad; import android.filterfw.geometry.Point; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.util.Log; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index ee867ff..d01f4ec 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -137,6 +137,8 @@ public class MediaNames { public static final String INVALD_VIDEO_PATH = "/sdcard/media_api/filepathdoesnotexist" + "/filepathdoesnotexist/temp.3gp"; + public static final String RECORDED_SURFACE_3GP = "/sdcard/surface.3gp"; + public static final long RECORDED_TIME = 5000; public static final long VALID_VIDEO_DURATION = 2000; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java index abe8b8c..54c8add 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java @@ -88,7 +88,8 @@ public class MediaProfileReader if (audioEncoder != MediaRecorder.AudioEncoder.AMR_NB && audioEncoder != MediaRecorder.AudioEncoder.AMR_WB && audioEncoder != MediaRecorder.AudioEncoder.AAC && - audioEncoder != MediaRecorder.AudioEncoder.HE_AAC) { + audioEncoder != MediaRecorder.AudioEncoder.HE_AAC && + audioEncoder != MediaRecorder.AudioEncoder.AAC_ELD) { throw new IllegalArgumentException("Unsupported audio encodeer " + audioEncoder); } return audioEncoderMap.get(audioEncoder); @@ -128,5 +129,6 @@ public class MediaProfileReader audioEncoderMap.put(MediaRecorder.AudioEncoder.AMR_WB, "amrwb"); audioEncoderMap.put(MediaRecorder.AudioEncoder.AAC, "aac"); audioEncoderMap.put(MediaRecorder.AudioEncoder.HE_AAC, "heaac"); + audioEncoderMap.put(MediaRecorder.AudioEncoder.AAC_ELD, "aaceld"); } } 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 8e6d5cb..d7069cac 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 @@ -22,6 +22,10 @@ import com.android.mediaframeworktest.MediaNames; import java.io.*; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; @@ -30,6 +34,7 @@ import android.media.EncoderCapabilities.VideoEncoderCap; import android.media.EncoderCapabilities.AudioEncoderCap; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import com.android.mediaframeworktest.MediaProfileReader; @@ -41,27 +46,28 @@ import java.util.List; /** - * Junit / Instrumentation test case for the media recorder api - */ + * Junit / Instrumentation test case for the media recorder api + */ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { private String TAG = "MediaRecorderTest"; private int mOutputDuration =0; private int mOutputVideoWidth = 0; private int mOutputVideoHeight= 0 ; - + private SurfaceHolder mSurfaceHolder = null; private MediaRecorder mRecorder; private int MIN_VIDEO_FPS = 5; + private int HIGH_SPEED_FPS = 120; private static final int CAMERA_ID = 0; Context mContext; Camera mCamera; - + public MediaRecorderTest() { - super("com.android.mediaframeworktest", MediaFrameworkTest.class); - + super(MediaFrameworkTest.class); + } protected void setUp() throws Exception { @@ -69,8 +75,8 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra mRecorder = new MediaRecorder(); super.setUp(); } - - private void recordVideo(int frameRate, int width, int height, + + private void recordVideo(int frameRate, int width, int height, int videoFormat, int outFormat, String outFile, boolean videoOnly) { Log.v(TAG,"startPreviewAndPrepareRecording"); try { @@ -80,7 +86,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra } mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(outFormat); - Log.v(TAG, "output format " + outFormat); + Log.v(TAG, "output format " + outFormat); mRecorder.setOutputFile(outFile); mRecorder.setVideoFrameRate(frameRate); mRecorder.setVideoSize(width, height); @@ -105,7 +111,186 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra mRecorder.release(); } } - + + private boolean validateGetSurface(boolean useSurface) { + Log.v(TAG,"validateGetSurface, useSurface=" + useSurface); + MediaRecorder recorder = new MediaRecorder(); + Surface surface; + boolean success = true; + try { + /* initialization */ + if (!useSurface) { + mCamera = Camera.open(CAMERA_ID); + Camera.Parameters parameters = mCamera.getParameters(); + parameters.setPreviewSize(352, 288); + parameters.set("orientation", "portrait"); + mCamera.setParameters(parameters); + mCamera.unlock(); + recorder.setCamera(mCamera); + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + recorder.setPreviewDisplay(mSurfaceHolder.getSurface()); + } + + recorder.setAudioSource(MediaRecorder.AudioSource.MIC); + int videoSource = useSurface ? + MediaRecorder.VideoSource.SURFACE : + MediaRecorder.VideoSource.CAMERA; + recorder.setVideoSource(videoSource); + recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + recorder.setOutputFile(MediaNames.RECORDED_SURFACE_3GP); + recorder.setVideoFrameRate(30); + recorder.setVideoSize(352, 288); + recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + + /* Test: getSurface() before prepare() + * should throw IllegalStateException + */ + try { + surface = recorder.getSurface(); + throw new Exception("getSurface failed to throw IllegalStateException"); + } catch (IllegalStateException e) { + // OK + } + + recorder.prepare(); + + /* Test: getSurface() after prepare() + * should succeed for surface source + * should fail for camera source + */ + try { + surface = recorder.getSurface(); + if (!useSurface) { + throw new Exception("getSurface failed to throw IllegalStateException"); + } + } catch (IllegalStateException e) { + if (useSurface) { + throw new Exception("getSurface failed to throw IllegalStateException"); + } + } + + recorder.start(); + + /* Test: getSurface() after start() + * should succeed for surface source + * should fail for camera source + */ + try { + surface = recorder.getSurface(); + if (!useSurface) { + throw new Exception("getSurface failed to throw IllegalStateException"); + } + } catch (IllegalStateException e) { + if (useSurface) { + throw new Exception("getSurface failed to throw IllegalStateException"); + } + } + + try { + recorder.stop(); + } catch (Exception e) { + // stop() could fail if the recording is empty, as we didn't render anything. + // ignore any failure in stop, we just want it stopped. + } + + /* Test: getSurface() after stop() + * should throw IllegalStateException + */ + try { + surface = recorder.getSurface(); + throw new Exception("getSurface failed to throw IllegalStateException"); + } catch (IllegalStateException e) { + // OK + } + } catch (Exception e) { + // fail + success = false; + } + + try { + if (mCamera != null) { + mCamera.lock(); + mCamera.release(); + mCamera = null; + } + recorder.release(); + } catch (Exception e) { + success = false; + } + + return success; + } + + private boolean recordVideoFromSurface( + int frameRate, int captureRate, int width, int height, + int videoFormat, int outFormat, String outFile, boolean videoOnly) { + Log.v(TAG,"recordVideoFromSurface"); + MediaRecorder recorder = new MediaRecorder(); + int sleepTime = 33; // normal capture at 33ms / frame + try { + if (!videoOnly) { + recorder.setAudioSource(MediaRecorder.AudioSource.MIC); + } + recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + recorder.setOutputFormat(outFormat); + recorder.setOutputFile(outFile); + recorder.setVideoFrameRate(frameRate); + if (captureRate > 0) { + recorder.setCaptureRate(captureRate); + sleepTime = 1000 / captureRate; + } + recorder.setVideoSize(width, height); + recorder.setVideoEncoder(videoFormat); + if (!videoOnly) { + recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + } + recorder.prepare(); + Surface surface = recorder.getSurface(); + + Paint paint = new Paint(); + paint.setTextSize(16); + paint.setColor(Color.RED); + int i; + + /* Test: draw 10 frames at 30fps before start + * these should be dropped and not causing malformed stream. + */ + for(i = 0; i < 10; i++) { + Canvas canvas = surface.lockCanvas(null); + int background = (i * 255 / 99); + canvas.drawARGB(255, background, background, background); + String text = "Frame #" + i; + canvas.drawText(text, 100, 100, paint); + surface.unlockCanvasAndPost(canvas); + Thread.sleep(sleepTime); + } + + Log.v(TAG, "start"); + recorder.start(); + + /* Test: draw another 90 frames at 30fps after start */ + for(i = 10; i < 100; i++) { + Canvas canvas = surface.lockCanvas(null); + int background = (i * 255 / 99); + canvas.drawARGB(255, background, background, background); + String text = "Frame #" + i; + canvas.drawText(text, 100, 100, paint); + surface.unlockCanvasAndPost(canvas); + Thread.sleep(sleepTime); + } + + Log.v(TAG, "stop"); + recorder.stop(); + recorder.release(); + } catch (Exception e) { + Log.v("record video failed ", e.toString()); + recorder.release(); + return false; + } + return true; + } + private boolean recordVideoWithPara(VideoEncoderCap videoCap, AudioEncoderCap audioCap, boolean highQuality){ boolean recordSuccess = false; int videoEncoder = videoCap.mCodec; @@ -171,7 +356,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra return recordSuccess; } - private boolean invalidRecordSetting(int frameRate, int width, int height, + private boolean invalidRecordSetting(int frameRate, int width, int height, int videoFormat, int outFormat, String outFile, boolean videoOnly) { try { if (!videoOnly) { @@ -180,7 +365,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra } mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(outFormat); - Log.v(TAG, "output format " + outFormat); + Log.v(TAG, "output format " + outFormat); mRecorder.setOutputFile(outFile); mRecorder.setVideoFrameRate(frameRate); mRecorder.setVideoSize(width, height); @@ -208,7 +393,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra } return false; } - + private void getOutputVideoProperty(String outputFilePath) { MediaPlayer mediaPlayer = new MediaPlayer(); try { @@ -223,13 +408,13 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra Thread.sleep(1000); mOutputVideoHeight = mediaPlayer.getVideoHeight(); mOutputVideoWidth = mediaPlayer.getVideoWidth(); - mediaPlayer.release(); + mediaPlayer.release(); } catch (Exception e) { Log.v(TAG, e.toString()); mediaPlayer.release(); - } + } } - + private boolean validateVideo(String filePath, int width, int height) { boolean validVideo = false; getOutputVideoProperty(filePath); @@ -260,7 +445,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra int codec = MediaRecorder.VideoEncoder.H263; int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); recordVideo(frameRate, 352, 288, codec, - MediaRecorder.OutputFormat.THREE_GPP, + MediaRecorder.OutputFormat.THREE_GPP, MediaNames.RECORDED_PORTRAIT_H263, true); mCamera.lock(); mCamera.release(); @@ -271,15 +456,15 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra } assertTrue("PortraitH263", videoRecordedResult); } - + @LargeTest - public void testInvalidVideoPath() throws Exception { + public void testInvalidVideoPath() throws Exception { boolean isTestInvalidVideoPathSuccessful = false; - isTestInvalidVideoPathSuccessful = invalidRecordSetting(15, 176, 144, MediaRecorder.VideoEncoder.H263, - MediaRecorder.OutputFormat.THREE_GPP, MediaNames.INVALD_VIDEO_PATH, false); + isTestInvalidVideoPathSuccessful = invalidRecordSetting(15, 176, 144, MediaRecorder.VideoEncoder.H263, + MediaRecorder.OutputFormat.THREE_GPP, MediaNames.INVALD_VIDEO_PATH, false); assertTrue("Invalid outputFile Path", isTestInvalidVideoPathSuccessful); } - + @LargeTest //test cases for the new codec public void testDeviceSpecificCodec() throws Exception { @@ -309,4 +494,83 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra assertTrue("testDeviceSpecificCodec", false); } } + + // Test MediaRecorder.getSurface() api with surface or camera source + public void testGetSurfaceApi() { + boolean success = false; + int noOfFailure = 0; + try { + for (int k = 0; k < 2; k++) { + success = validateGetSurface( + k == 0 ? true : false /* useSurface */); + if (!success) { + noOfFailure++; + } + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + assertTrue("testGetSurfaceApi", noOfFailure == 0); + } + + // Test recording from surface source with/without audio + public void testSurfaceRecording() { + boolean success = false; + int noOfFailure = 0; + try { + int codec = MediaRecorder.VideoEncoder.H264; + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); + for (int k = 0; k < 2; k++) { + String filename = "/sdcard/surface_" + + (k==0?"video_only":"with_audio") + ".3gp"; + + success = recordVideoFromSurface(frameRate, 0, 352, 288, codec, + MediaRecorder.OutputFormat.THREE_GPP, filename, + k == 0 ? true : false /* videoOnly */); + if (success) { + success = validateVideo(filename, 352, 288); + } + if (!success) { + noOfFailure++; + } + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + assertTrue("testSurfaceRecording", noOfFailure == 0); + } + + // Test recording from surface source with/without audio + public void testSurfaceRecordingTimeLapse() { + boolean success = false; + int noOfFailure = 0; + try { + int codec = MediaRecorder.VideoEncoder.H264; + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); + for (int k = 0; k < 2; k++) { + // k==0: time lapse test, set capture rate to MIN_VIDEO_FPS + // k==1: slow motion test, set capture rate to HIGH_SPEED_FPS + String filename = "/sdcard/surface_" + + (k==0 ? "time_lapse" : "slow_motion") + ".3gp"; + + // always set videoOnly=false, MediaRecorder should disable + // audio automatically with time lapse/slow motion + success = recordVideoFromSurface(frameRate, + k==0 ? MIN_VIDEO_FPS : HIGH_SPEED_FPS, + 352, 288, codec, + MediaRecorder.OutputFormat.THREE_GPP, + filename, false /* videoOnly */); + if (success) { + success = validateVideo(filename, 352, 288); + } + if (!success) { + noOfFailure++; + } + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + assertTrue("testSurfaceRecordingTimeLapse", noOfFailure == 0); + } + } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java index 8c76421..eb1a589 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java @@ -85,8 +85,8 @@ public class MediaInserterTest extends InstrumentationTestCase { super.setUp(); mMockProvider = EasyMock.createMock(IContentProvider.class); mMediaInserter = new MediaInserter(mMockProvider, - mPackageName, TEST_BUFFER_SIZE); - mPackageName = getInstrumentation().getContext().getPackageName(); + mPackageName, TEST_BUFFER_SIZE); + mPackageName = getInstrumentation().getContext().getPackageName(); mFilesCounter = 0; mAudioCounter = 0; mVideoCounter = 0; @@ -224,19 +224,19 @@ public class MediaInserterTest extends InstrumentationTestCase { @SmallTest public void testInsertContentsWithDifferentSizePerContentType() throws Exception { EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sFilesUri), + MediaUriMatcher.expectMediaUri(sFilesUri), (ContentValues[]) EasyMock.anyObject())).andReturn(1); EasyMock.expectLastCall().times(1); EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sAudioUri), + MediaUriMatcher.expectMediaUri(sAudioUri), (ContentValues[]) EasyMock.anyObject())).andReturn(1); EasyMock.expectLastCall().times(2); EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sVideoUri), + MediaUriMatcher.expectMediaUri(sVideoUri), (ContentValues[]) EasyMock.anyObject())).andReturn(1); EasyMock.expectLastCall().times(3); EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sImagesUri), + MediaUriMatcher.expectMediaUri(sImagesUri), (ContentValues[]) EasyMock.anyObject())).andReturn(1); EasyMock.expectLastCall().times(4); EasyMock.replay(mMockProvider); diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk index ad874c8..95ae33b 100644 --- a/media/tests/omxjpegdecoder/Android.mk +++ b/media/tests/omxjpegdecoder/Android.mk @@ -34,11 +34,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES := \ $(TOP)/external/jpeg \ - $(TOP)/external/skia/include/config \ - $(TOP)/external/skia/include/core \ - $(TOP)/external/skia/include/images \ - $(TOP)/external/skia/include/utils \ - $(TOP)/external/skia/include/effects \ $(TOP)/frameworks/base/media/libstagefright \ $(TOP)/frameworks/base/include/ \ $(TOP)/frameworks/base/ \ diff --git a/media/tests/omxjpegdecoder/StreamSource.h b/media/tests/omxjpegdecoder/StreamSource.h index 6c34cbd..9807385 100644 --- a/media/tests/omxjpegdecoder/StreamSource.h +++ b/media/tests/omxjpegdecoder/StreamSource.h @@ -20,7 +20,7 @@ #include <stdio.h> -#include <core/SkStream.h> +#include <SkStream.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaErrors.h> #include <utils/threads.h> diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp index a94fedb..d1fed7bb 100644 --- a/media/tests/players/invoke_mock_media_player.cpp +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -27,6 +27,7 @@ using android::INVALID_OPERATION; using android::Surface; using android::IGraphicBufferProducer; +using android::IMediaHTTPService; using android::MediaPlayerBase; using android::OK; using android::Parcel; @@ -57,6 +58,7 @@ class Player: public MediaPlayerBase virtual bool hardwareOutput() {return true;} virtual status_t setDataSource( + const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *) { ALOGV("setDataSource %s", url); |
