diff options
Diffstat (limited to 'media')
17 files changed, 386 insertions, 189 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 25c4200..cd8bb1d 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1717,12 +1717,11 @@ public class AudioManager { /** - * @hide - * CANDIDATE FOR SDK * Registers the remote control client for providing information to display on the remote * controls. - * @param rcClient the remote control client associated responsible - * for providing the information to display on the remote control. + * @param rcClient The remote control client from which remote controls will receive + * information to display. + * @see RemoteControlClient */ public void registerRemoteControlClient(RemoteControlClient rcClient) { if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) { @@ -1741,11 +1740,9 @@ public class AudioManager { } /** - * @hide - * CANDIDATE FOR SDK * Unregisters the remote control client that was providing information to display on the - * remotes. - * @param rcClient the remote control client to unregister + * remote controls. + * @param rcClient The remote control client to unregister. * @see #registerRemoteControlClient(RemoteControlClient) */ public void unregisterRemoteControlClient(RemoteControlClient rcClient) { diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 9d9b6ea..f5e1416 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -343,9 +343,8 @@ public class AudioService extends IAudioService.Stub { readPersistedSettings(); mSettingsObserver = new SettingsObserver(); createStreamStates(); - // Call setMode() to initialize mSetModeDeathHandlers - mMode = AudioSystem.MODE_INVALID; - setMode(AudioSystem.MODE_NORMAL, null); + + mMode = AudioSystem.MODE_NORMAL; mMediaServerOk = true; // Call setRingerModeInt() to apply correct mute @@ -768,37 +767,27 @@ public class AudioService extends IAudioService.Stub { private int mPid; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb) { + SetModeDeathHandler(IBinder cb, int pid) { mCb = cb; - mPid = Binder.getCallingPid(); + mPid = pid; } public void binderDied() { + IBinder newModeOwner = null; synchronized(mSetModeDeathHandlers) { Log.w(TAG, "setMode() client died"); int index = mSetModeDeathHandlers.indexOf(this); if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); } else { - mSetModeDeathHandlers.remove(this); - // If dead client was a the top of client list, - // apply next mode in the stack - if (index == 0) { - // mSetModeDeathHandlers is never empty as the initial entry - // created when AudioService starts is never removed - SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0); - int mode = hdlr.getMode(); - if (AudioService.this.mMode != mode) { - if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { - AudioService.this.mMode = mode; - if (mode != AudioSystem.MODE_NORMAL) { - disconnectBluetoothSco(mCb); - } - } - } - } + newModeOwner = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid); } } + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all + // SCO connections not started by the application changing the mode + if (newModeOwner != null) { + disconnectBluetoothSco(newModeOwner); + } } public int getPid() { @@ -828,60 +817,97 @@ public class AudioService extends IAudioService.Stub { return; } - synchronized (mSettingsLock) { + IBinder newModeOwner = null; + synchronized(mSetModeDeathHandlers) { if (mode == AudioSystem.MODE_CURRENT) { mode = mMode; } - if (mode != mMode) { + newModeOwner = setModeInt(mode, cb, Binder.getCallingPid()); + } + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all + // SCO connections not started by the application changing the mode + if (newModeOwner != null) { + disconnectBluetoothSco(newModeOwner); + } + } - // automatically handle audio focus for mode changes - handleFocusForCalls(mMode, mode, cb); + // must be called synchronized on mSetModeDeathHandlers + // setModeInt() returns a non null IBInder if the audio mode was successfully set to + // any mode other than NORMAL. + IBinder setModeInt(int mode, IBinder cb, int pid) { + IBinder newModeOwner = null; + if (cb == null) { + Log.e(TAG, "setModeInt() called with null binder"); + return newModeOwner; + } - if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { - mMode = mode; + SetModeDeathHandler hdlr = null; + Iterator iter = mSetModeDeathHandlers.iterator(); + while (iter.hasNext()) { + SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); + if (h.getPid() == pid) { + hdlr = h; + // Remove from client list so that it is re-inserted at top of list + iter.remove(); + hdlr.getBinder().unlinkToDeath(hdlr, 0); + break; + } + } + int status = AudioSystem.AUDIO_STATUS_OK; + do { + if (mode == AudioSystem.MODE_NORMAL) { + // get new mode from client at top the list if any + if (!mSetModeDeathHandlers.isEmpty()) { + hdlr = mSetModeDeathHandlers.get(0); + cb = hdlr.getBinder(); + mode = hdlr.getMode(); + } + } else { + if (hdlr == null) { + hdlr = new SetModeDeathHandler(cb, pid); + } + // Register for client death notification + try { + cb.linkToDeath(hdlr, 0); + } catch (RemoteException e) { + // Client has died! + Log.w(TAG, "setMode() could not link to "+cb+" binder death"); + } - synchronized(mSetModeDeathHandlers) { - SetModeDeathHandler hdlr = null; - Iterator iter = mSetModeDeathHandlers.iterator(); - while (iter.hasNext()) { - SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); - if (h.getBinder() == cb) { - hdlr = h; - // Remove from client list so that it is re-inserted at top of list - iter.remove(); - break; - } - } - if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb); - // cb is null when setMode() is called by AudioService constructor - if (cb != null) { - // Register for client death notification - try { - cb.linkToDeath(hdlr, 0); - } catch (RemoteException e) { - // Client has died! - Log.w(TAG, "setMode() could not link to "+cb+" binder death"); - } - } - } - // Last client to call setMode() is always at top of client list - // as required by SetModeDeathHandler.binderDied() - mSetModeDeathHandlers.add(0, hdlr); - hdlr.setMode(mode); - } + // Last client to call setMode() is always at top of client list + // as required by SetModeDeathHandler.binderDied() + mSetModeDeathHandlers.add(0, hdlr); + hdlr.setMode(mode); + } - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode - if (mode != AudioSystem.MODE_NORMAL) { - disconnectBluetoothSco(cb); + if (mode != mMode) { + status = AudioSystem.setPhoneState(mode); + if (status == AudioSystem.AUDIO_STATUS_OK) { + // automatically handle audio focus for mode changes + handleFocusForCalls(mMode, mode, cb); + mMode = mode; + } else { + if (hdlr != null) { + mSetModeDeathHandlers.remove(hdlr); + cb.unlinkToDeath(hdlr, 0); } + // force reading new top of mSetModeDeathHandlers stack + mode = AudioSystem.MODE_NORMAL; } + } else { + status = AudioSystem.AUDIO_STATUS_OK; + } + } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); + + if (status == AudioSystem.AUDIO_STATUS_OK) { + if (mode != AudioSystem.MODE_NORMAL) { + newModeOwner = cb; } int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex; setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false); } + return newModeOwner; } /** pre-condition: oldMode != newMode */ @@ -1345,28 +1371,30 @@ public class AudioService extends IAudioService.Stub { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client process. - if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || - mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) && - (mScoAudioState == SCO_STATE_INACTIVE || - mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { - if (mScoAudioState == SCO_STATE_INACTIVE) { - if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { - if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( - mBluetoothHeadsetDevice)) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - } else { - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + synchronized(mSetModeDeathHandlers) { + if ((mSetModeDeathHandlers.isEmpty() || + mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) && + (mScoAudioState == SCO_STATE_INACTIVE || + mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { + if (mScoAudioState == SCO_STATE_INACTIVE) { + if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { + if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice)) { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + } else { + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + } else if (getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_ACTIVATE_REQ; } - } else if (getBluetoothHeadset()) { - mScoAudioState = SCO_STATE_ACTIVATE_REQ; + } else { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); } } else { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); } - } else { - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); } } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java index 591a8b9..b566653 100644 --- a/media/java/android/media/Metadata.java +++ b/media/java/android/media/Metadata.java @@ -39,6 +39,8 @@ import java.util.TimeZone; The caller is expected to know the type of the metadata and call the right get* method to fetch its value. + + @hide */ public class Metadata { diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index d7b85e4..daa25e0 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -17,7 +17,6 @@ package android.media; import android.content.ComponentName; -import android.content.SharedPreferences.Editor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; @@ -31,15 +30,14 @@ import android.os.RemoteException; import android.util.Log; import java.lang.IllegalArgumentException; -import java.util.HashMap; /** - * @hide - * CANDIDATE FOR SDK * RemoteControlClient enables exposing information meant to be consumed by remote controls - * capable of displaying metadata, album art and media transport control buttons. - * A remote control client object is associated with a media button event receiver - * when registered through + * capable of displaying metadata, artwork and media transport control buttons. + * A remote control client object is associated with a media button event receiver. This + * event receiver must have been previously registered with + * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the + * RemoteControlClient can be registered through * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. */ public class RemoteControlClient @@ -110,7 +108,8 @@ public class RemoteControlClient public final static int PLAYSTATE_ERROR = 9; /** * @hide - * The value of a playback state when none has been declared + * The value of a playback state when none has been declared. + * Intentionally hidden as an application shouldn't set such a playback state value. */ public final static int PLAYSTATE_NONE = 0; @@ -122,7 +121,7 @@ public class RemoteControlClient */ public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0; /** - * Flag indicating a RemoteControlClient makes use of the "rewing" media key. + * Flag indicating a RemoteControlClient makes use of the "rewind" media key. * * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND @@ -173,7 +172,9 @@ public class RemoteControlClient /** * @hide - * The flags for when no media keys are declared supported + * The flags for when no media keys are declared supported. + * Intentionally hidden as an application shouldn't set the transport control flags + * to this value. */ public final static int FLAGS_KEY_MEDIA_NONE = 0; @@ -184,29 +185,29 @@ public class RemoteControlClient public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0; /** * @hide - * FIXME doc not valid * Flag used to signal that the transport control buttons supported by the - * RemoteControlClient have changed. + * RemoteControlClient are requested. * This can for instance happen when playback is at the end of a playlist, and the "next" * operation is not supported anymore. */ public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1; /** * @hide - * FIXME doc not valid - * Flag used to signal that the playback state of the RemoteControlClient has changed. + * Flag used to signal that the playback state of the RemoteControlClient is requested. */ public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2; /** * @hide - * FIXME doc not valid - * Flag used to signal that the album art for the RemoteControlClient has changed. + * Flag used to signal that the album art for the RemoteControlClient is requested. */ public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3; /** * Class constructor. - * @param mediaButtonEventReceiver the receiver for the media button events. + * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have + * been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} + * before this new RemoteControlClient can itself be registered with + * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) * @see AudioManager#registerRemoteControlClient(RemoteControlClient) */ @@ -227,8 +228,11 @@ public class RemoteControlClient /** * Class constructor for a remote control client whose internal event handling * happens on a user-provided Looper. - * @param mediaButtonEventReceiver the receiver for the media button events. - * @param looper the Looper running the event loop. + * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have + * been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} + * before this new RemoteControlClient can itself be registered with + * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. + * @param looper The Looper running the event loop. * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) * @see AudioManager#registerRemoteControlClient(RemoteControlClient) */ @@ -257,11 +261,28 @@ public class RemoteControlClient /** * Class used to modify metadata in a {@link RemoteControlClient} object. + * Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor, + * on which you set the metadata for the RemoteControlClient instance. Once all the information + * has been set, use {@link #apply()} to make it the new metadata that should be displayed + * for the associated client. Once the metadata has been "applied", you cannot reuse this + * instance of the MetadataEditor. */ public class MetadataEditor { + /** + * @hide + */ protected boolean mMetadataChanged; + /** + * @hide + */ protected boolean mArtworkChanged; + /** + * @hide + */ protected Bitmap mEditorArtwork; + /** + * @hide + */ protected Bundle mEditorMetadata; private boolean mApplied = false; @@ -277,13 +298,18 @@ public class RemoteControlClient /** * The metadata key for the content artwork / album art. */ - public final static int METADATA_KEY_ARTWORK = 100; + public final static int BITMAP_KEY_ARTWORK = 100; + /** + * @hide + * TODO(jmtrivi) have lockscreen and music move to the new key name + */ + public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK; /** * Adds textual information to be displayed. * Note that none of the information added after {@link #apply()} has been called, * will be displayed. - * @param key the identifier of a the metadata field to set. Valid values are + * @param key The identifier of a the metadata field to set. Valid values are * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, @@ -294,11 +320,11 @@ public class RemoteControlClient * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}, - * . - * @param value the text for the given key, or {@code null} to signify there is no valid + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. + * @param value The text for the given key, or {@code null} to signify there is no valid * information for the field. - * @return FIXME description + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. */ public synchronized MetadataEditor putString(int key, String value) throws IllegalArgumentException { @@ -315,15 +341,18 @@ public class RemoteControlClient } /** - * FIXME javadoc + * Adds numerical information to be displayed. + * Note that none of the information added after {@link #apply()} has been called, + * will be displayed. * @param key the identifier of a the metadata field to set. Valid values are * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value * expressed in milliseconds), * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. - * @param value FIXME javadoc - * @return FIXME javadoc + * @param value The long value for the given key + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. * @throws IllegalArgumentException */ public synchronized MetadataEditor putLong(int key, long value) @@ -342,9 +371,11 @@ public class RemoteControlClient /** * Sets the album / artwork picture to be displayed on the remote control. - * @param key FIXME description - * @param bitmap the bitmap for the artwork, or null if there isn't any. - * @return FIXME description + * @param key the identifier of the bitmap to set. The only valid value is + * {@link #BITMAP_KEY_ARTWORK} + * @param bitmap The bitmap for the artwork, or null if there isn't any. + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. * @throws IllegalArgumentException * @see android.graphics.Bitmap */ @@ -354,7 +385,7 @@ public class RemoteControlClient Log.e(TAG, "Can't edit a previously applied MetadataEditor"); return this; } - if (key != METADATA_KEY_ARTWORK) { + if (key != BITMAP_KEY_ARTWORK) { throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); } if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) { @@ -369,7 +400,8 @@ public class RemoteControlClient } /** - * FIXME description + * Clears all the metadata that has been set since the MetadataEditor instance was + * created with {@link RemoteControlClient#editMetadata(boolean)}. */ public synchronized void clear() { if (mApplied) { @@ -381,7 +413,10 @@ public class RemoteControlClient } /** - * FIXME description + * Associates all the metadata that has been set since the MetadataEditor instance was + * created with {@link RemoteControlClient#editMetadata(boolean)}, or since + * {@link #clear()} was called, with the RemoteControlClient. Once "applied", + * this MetadataEditor cannot be reused to edit the RemoteControlClient's metadata. */ public synchronized void apply() { if (mApplied) { @@ -408,9 +443,10 @@ public class RemoteControlClient } /** - * FIXME description - * @param startEmpty - * @return + * Creates a {@link MetadataEditor}. + * @param startEmpty Set to false if you want the MetadataEditor to contain the metadata that + * was previously applied to the RemoteControlClient, or true if it is to be created empty. + * @return a new MetadataEditor instance. */ public MetadataEditor editMetadata(boolean startEmpty) { MetadataEditor editor = new MetadataEditor(); @@ -430,7 +466,7 @@ public class RemoteControlClient /** * Sets the current playback state. - * @param state the current playback state, one of the following values: + * @param state The current playback state, one of the following values: * {@link #PLAYSTATE_STOPPED}, * {@link #PLAYSTATE_PAUSED}, * {@link #PLAYSTATE_PLAYING}, @@ -453,7 +489,7 @@ public class RemoteControlClient /** * Sets the flags for the media transport control buttons that this client supports. - * @param a combination of the following flags: + * @param transportControlFlags A combination of the following flags: * {@link #FLAG_KEY_MEDIA_PREVIOUS}, * {@link #FLAG_KEY_MEDIA_REWIND}, * {@link #FLAG_KEY_MEDIA_PLAY}, diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index 4e954fc..ed4e92e 100755 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -2328,6 +2328,10 @@ static int videoEditor_getPixelsList( break; } env->CallVoidMethod(callback, mid, (jint)k); + if (env->ExceptionCheck()) { + err = M4ERR_ALLOC; + break; + } } env->ReleaseIntArrayElements(pixelArray, m_dst32, 0); @@ -2338,7 +2342,7 @@ static int videoEditor_getPixelsList( env->ReleaseStringUTFChars(path, pString); } - if (err != M4NO_ERROR) { + if (err != M4NO_ERROR && !env->ExceptionCheck()) { jniThrowException(env, "java/lang/RuntimeException",\ "ThumbnailGetPixels32 failed"); } diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index 41f8593..19dedfc 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -16,6 +16,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaScanner" +#include <cutils/properties.h> #include <utils/Log.h> #include <media/mediascanner.h> @@ -26,11 +27,14 @@ namespace android { MediaScanner::MediaScanner() - : mLocale(NULL) { + : mLocale(NULL), mSkipList(NULL), mSkipIndex(NULL) { + loadSkipList(); } MediaScanner::~MediaScanner() { setLocale(NULL); + free(mSkipList); + free(mSkipIndex); } void MediaScanner::setLocale(const char *locale) { @@ -47,6 +51,33 @@ const char *MediaScanner::locale() const { return mLocale; } +void MediaScanner::loadSkipList() { + mSkipList = (char *)malloc(PROPERTY_VALUE_MAX * sizeof(char)); + if (mSkipList) { + property_get("testing.mediascanner.skiplist", mSkipList, ""); + } + if (!mSkipList || (strlen(mSkipList) == 0)) { + free(mSkipList); + mSkipList = NULL; + return; + } + mSkipIndex = (int *)malloc(PROPERTY_VALUE_MAX * sizeof(int)); + if (mSkipIndex) { + // dup it because strtok will modify the string + char *skipList = strdup(mSkipList); + if (skipList) { + char * path = strtok(skipList, ","); + int i = 0; + while (path) { + mSkipIndex[i++] = strlen(path); + path = strtok(NULL, ","); + } + mSkipIndex[i] = -1; + free(skipList); + } + } +} + MediaScanResult MediaScanner::processDirectory( const char *path, MediaScannerClient &client) { int pathLength = strlen(path); @@ -75,12 +106,39 @@ MediaScanResult MediaScanner::processDirectory( return result; } +bool MediaScanner::shouldSkipDirectory(char *path) { + if (path && mSkipList && mSkipIndex) { + int len = strlen(path); + int idx = 0; + // track the start position of next path in the comma + // separated list obtained from getprop + int startPos = 0; + while (mSkipIndex[idx] != -1) { + // no point to match path name if strlen mismatch + if ((len == mSkipIndex[idx]) + // pick out the path segment from comma separated list + // to compare against current path parameter + && (strncmp(path, &mSkipList[startPos], len) == 0)) { + return true; + } + startPos += mSkipIndex[idx] + 1; // extra char for the delimiter + idx++; + } + } + return false; +} + MediaScanResult MediaScanner::doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) { // place to copy file or directory name char* fileSpot = path + strlen(path); struct dirent* entry; + if (shouldSkipDirectory(path)) { + LOGD("Skipping: %s", path); + return MEDIA_SCAN_RESULT_OK; + } + // Treat all files as non-media in directories that contain a ".nomedia" file if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { strcpy(fileSpot, ".nomedia"); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 8f213da..bf83849 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -26,6 +26,9 @@ namespace android { +// static +const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; + NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, const sp<AMessage> ¬ify) @@ -43,7 +46,8 @@ NuPlayer::Renderer::Renderer( mHasAudio(false), mHasVideo(false), mSyncQueues(false), - mPaused(false) { + mPaused(false), + mLastPositionUpdateUs(-1ll) { } NuPlayer::Renderer::~Renderer() { @@ -190,7 +194,7 @@ void NuPlayer::Renderer::postDrainAudioQueue() { mDrainAudioQueuePending = true; sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id()); msg->setInt32("generation", mAudioQueueGeneration); - msg->post(10000); + msg->post(); } void NuPlayer::Renderer::signalAudioSinkChanged() { @@ -198,7 +202,6 @@ void NuPlayer::Renderer::signalAudioSinkChanged() { } void NuPlayer::Renderer::onDrainAudioQueue() { - for (;;) { if (mAudioQueue.empty()) { break; @@ -562,6 +565,13 @@ void NuPlayer::Renderer::notifyPosition() { } int64_t nowUs = ALooper::GetNowUs(); + + if (mLastPositionUpdateUs >= 0 + && nowUs < mLastPositionUpdateUs + kMinPositionUpdateDelayUs) { + return; + } + mLastPositionUpdateUs = nowUs; + int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs; sp<AMessage> notify = mNotify->dup(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 2713031..3a641a2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -74,6 +74,8 @@ private: status_t mFinalResult; }; + static const int64_t kMinPositionUpdateDelayUs; + sp<MediaPlayerBase::AudioSink> mAudioSink; sp<AMessage> mNotify; List<QueueEntry> mAudioQueue; @@ -98,6 +100,8 @@ private: bool mPaused; + int64_t mLastPositionUpdateUs; + void onDrainAudioQueue(); void postDrainAudioQueue(); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index e9dc61c..2ba2273 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1131,6 +1131,13 @@ void ACodec::sendFormatChange() { mSentFormat = true; } +void ACodec::signalError(OMX_ERRORTYPE error) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", ACodec::kWhatError); + notify->setInt32("omx-error", error); + notify->post(); +} + //////////////////////////////////////////////////////////////////////////////// ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState) @@ -1252,10 +1259,7 @@ bool ACodec::BaseState::onOMXEvent( LOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1); - sp<AMessage> notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatError); - notify->setInt32("omx-error", data1); - notify->post(); + mCodec->signalError((OMX_ERRORTYPE)data1); return true; } @@ -1548,12 +1552,14 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { && msg->findInt32("render", &render) && render != 0) { // The client wants this buffer to be rendered. - CHECK_EQ(mCodec->mNativeWindow->queueBuffer( + if (mCodec->mNativeWindow->queueBuffer( mCodec->mNativeWindow.get(), - info->mGraphicBuffer.get()), - 0); - - info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; + info->mGraphicBuffer.get()) == OK) { + info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; + } else { + mCodec->signalError(); + info->mStatus = BufferInfo::OWNED_BY_US; + } } else { info->mStatus = BufferInfo::OWNED_BY_US; } @@ -1692,11 +1698,7 @@ void ACodec::UninitializedState::onSetup( if (node == NULL) { LOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str()); - sp<AMessage> notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatError); - notify->setInt32("omx-error", OMX_ErrorComponentNotFound); - notify->post(); - + mCodec->signalError(OMX_ErrorComponentNotFound); return; } @@ -1744,10 +1746,7 @@ void ACodec::LoadedToIdleState::stateEntered() { "(error 0x%08x)", err); - sp<AMessage> notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatError); - notify->setInt32("omx-error", OMX_ErrorUndefined); - notify->post(); + mCodec->signalError(); } } @@ -2063,10 +2062,7 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent( "port reconfiguration (error 0x%08x)", err); - sp<AMessage> notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatError); - notify->setInt32("omx-error", OMX_ErrorUndefined); - notify->post(); + mCodec->signalError(); } return true; diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp index 6313ca3..4e46414 100644 --- a/media/libstagefright/AVIExtractor.cpp +++ b/media/libstagefright/AVIExtractor.cpp @@ -117,14 +117,12 @@ status_t AVIExtractor::AVISource::read( } } - int64_t timeUs = - (mSampleIndex * 1000000ll * mTrack.mRate) / mTrack.mScale; - off64_t offset; size_t size; bool isKey; + int64_t timeUs; status_t err = mExtractor->getSampleInfo( - mTrackIndex, mSampleIndex, &offset, &size, &isKey); + mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs); ++mSampleIndex; @@ -396,6 +394,8 @@ status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) { uint32_t rate = U32LE_AT(&data[20]); uint32_t scale = U32LE_AT(&data[24]); + uint32_t sampleSize = U32LE_AT(&data[44]); + const char *mime = NULL; Track::Kind kind = Track::OTHER; @@ -427,6 +427,7 @@ status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) { track->mMeta = meta; track->mRate = rate; track->mScale = scale; + track->mBytesPerSample = sampleSize; track->mKind = kind; track->mNumSyncSamples = 0; track->mThumbnailSampleSize = 0; @@ -612,11 +613,12 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { off64_t offset; size_t size; bool isKey; - status_t err = getSampleInfo(0, 0, &offset, &size, &isKey); + int64_t timeUs; + status_t err = getSampleInfo(0, 0, &offset, &size, &isKey, &timeUs); if (err != OK) { mOffsetsAreAbsolute = !mOffsetsAreAbsolute; - err = getSampleInfo(0, 0, &offset, &size, &isKey); + err = getSampleInfo(0, 0, &offset, &size, &isKey, &timeUs); if (err != OK) { return err; @@ -630,8 +632,9 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { for (size_t i = 0; i < mTracks.size(); ++i) { Track *track = &mTracks.editItemAt(i); - int64_t durationUs = - (track->mSamples.size() * 1000000ll * track->mRate) / track->mScale; + int64_t durationUs; + CHECK_EQ((status_t)OK, + getSampleTime(i, track->mSamples.size() - 1, &durationUs)); LOGV("track %d duration = %.2f secs", i, durationUs / 1E6); @@ -645,9 +648,10 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { if (!strncasecmp("video/", mime.c_str(), 6) && track->mThumbnailSampleIndex >= 0) { - int64_t thumbnailTimeUs = - (track->mThumbnailSampleIndex * 1000000ll * track->mRate) - / track->mScale; + int64_t thumbnailTimeUs; + CHECK_EQ((status_t)OK, + getSampleTime(i, track->mThumbnailSampleIndex, + &thumbnailTimeUs)); track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); @@ -659,6 +663,21 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { } } } + + if (track->mBytesPerSample != 0) { + // Assume all chunks are the same size for now. + + off64_t offset; + size_t size; + bool isKey; + int64_t sampleTimeUs; + CHECK_EQ((status_t)OK, + getSampleInfo( + i, 0, + &offset, &size, &isKey, &sampleTimeUs)); + + track->mRate *= size / track->mBytesPerSample; + } } mFoundIndex = true; @@ -720,7 +739,9 @@ status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) { off64_t offset; size_t size; bool isKey; - status_t err = getSampleInfo(trackIndex, 0, &offset, &size, &isKey); + int64_t timeUs; + status_t err = + getSampleInfo(trackIndex, 0, &offset, &size, &isKey, &timeUs); if (err != OK) { return err; @@ -762,7 +783,8 @@ status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) { status_t AVIExtractor::getSampleInfo( size_t trackIndex, size_t sampleIndex, - off64_t *offset, size_t *size, bool *isKey) { + off64_t *offset, size_t *size, bool *isKey, + int64_t *sampleTimeUs) { if (trackIndex >= mTracks.size()) { return -ERANGE; } @@ -801,9 +823,20 @@ status_t AVIExtractor::getSampleInfo( *isKey = info.mIsKey; + *sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale; + return OK; } +status_t AVIExtractor::getSampleTime( + size_t trackIndex, size_t sampleIndex, int64_t *sampleTimeUs) { + off64_t offset; + size_t size; + bool isKey; + return getSampleInfo( + trackIndex, sampleIndex, &offset, &size, &isKey, sampleTimeUs); +} + status_t AVIExtractor::getSampleIndexAtTime( size_t trackIndex, int64_t timeUs, MediaSource::ReadOptions::SeekMode mode, @@ -911,7 +944,11 @@ bool SniffAVI( if (!memcmp(tmp, "RIFF", 4) && !memcmp(&tmp[8], "AVI ", 4)) { mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_AVI); - *confidence = 0.2; + + // Just a tad over the mp3 extractor's confidence, since + // these .avi files may contain .mp3 content that otherwise would + // mistakenly lead to us identifying the entire file as a .mp3 file. + *confidence = 0.21; return true; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index fb49d7b..9ab470b 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -50,7 +50,7 @@ namespace android { // Treat time out as an error if we have not received any output // buffers after 3 seconds. -const static int64_t kBufferFilledEventTimeOutUs = 3000000000LL; +const static int64_t kBufferFilledEventTimeOutNs = 3000000000LL; struct CodecInfo { const char *mime; @@ -2325,9 +2325,14 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { { CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)", data1, data2); - CHECK(mFilledBuffers.empty()); if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) { + // There is no need to check whether mFilledBuffers is empty or not + // when the OMX_EventPortSettingsChanged is not meant for reallocating + // the output buffers. + if (data1 == kPortIndexOutput) { + CHECK(mFilledBuffers.empty()); + } onPortSettingsChanged(data1); } else if (data1 == kPortIndexOutput && (data2 == OMX_IndexConfigCommonOutputCrop || @@ -3220,7 +3225,7 @@ status_t OMXCodec::waitForBufferFilled_l() { // for video encoding. return mBufferFilled.wait(mLock); } - status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutUs); + status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs); if (err != OK) { CODEC_LOGE("Timed out waiting for output buffers: %d/%d", countBuffersWeOwn(mPortBuffers[kPortIndexInput]), diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h index 375a94d..b575347 100644 --- a/media/libstagefright/include/AVIExtractor.h +++ b/media/libstagefright/include/AVIExtractor.h @@ -54,6 +54,11 @@ private: uint32_t mRate; uint32_t mScale; + // If bytes per sample == 0, each chunk represents a single sample, + // otherwise each chunk should me a multiple of bytes-per-sample in + // size. + uint32_t mBytesPerSample; + enum Kind { AUDIO, VIDEO, @@ -84,7 +89,11 @@ private: status_t getSampleInfo( size_t trackIndex, size_t sampleIndex, - off64_t *offset, size_t *size, bool *isKey); + off64_t *offset, size_t *size, bool *isKey, + int64_t *sampleTimeUs); + + status_t getSampleTime( + size_t trackIndex, size_t sampleIndex, int64_t *sampleTimeUs); status_t getSampleIndexAtTime( size_t trackIndex, diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java index b1ad315..b1d049e 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java @@ -95,6 +95,25 @@ public class MediaProfileReader return audioEncoderMap.get(audioEncoder); } + public static int getMinFrameRateForCodec(int codec) { + return getMinOrMaxFrameRateForCodec(codec, false); + } + + public static int getMaxFrameRateForCodec(int codec) { + return getMinOrMaxFrameRateForCodec(codec, true); + } + + private static int getMinOrMaxFrameRateForCodec(int codec, boolean max) { + for (VideoEncoderCap cap: videoEncoders) { + if (cap.mCodec == codec) { + if (max) return cap.mMaxFrameRate; + else return cap.mMinFrameRate; + } + } + // Should never reach here + throw new IllegalArgumentException("Unsupported video codec " + codec); + } + private MediaProfileReader() {} // Don't call me private static void initVideoEncoderMap() { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java index 154f691..d5b67aa 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java @@ -657,7 +657,7 @@ public class MediaItemThumbnailTest extends public void testThumbnailListForH264WVGAWithCount() throws Exception { final String videoItemFilename = INPUT_FILE_PATH + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; - final int tnCount = 100; + final int tnCount = 70; final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; final MediaVideoItem mediaVideoItem = mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java index 82df669..796b52c 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java @@ -247,7 +247,9 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra mCamera.unlock(); mRecorder.setCamera(mCamera); Thread.sleep(1000); - recordVideo(15, 352, 288, MediaRecorder.VideoEncoder.H263, + int codec = MediaRecorder.VideoEncoder.H263; + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); + recordVideo(frameRate, 352, 288, codec, MediaRecorder.OutputFormat.THREE_GPP, MediaNames.RECORDED_PORTRAIT_H263, true); mCamera.lock(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index 0f79515..0b887b9 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -365,16 +365,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med assertTrue("H264 playback memory test", memoryResult); } - private int getMaxFrameRateForVideoEncoder(int codec) { - int frameRate = -1; - for (VideoEncoderCap cap: videoEncoders) { - if (cap.mCodec == MediaRecorder.VideoEncoder.H263) { - frameRate = cap.mMaxFrameRate; - } - } - return frameRate; - } - // Test case 4: Capture the memory usage after every 20 video only recorded @LargeTest public void testH263RecordVideoOnlyMemoryUsage() throws Exception { @@ -384,7 +374,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med File videoH263RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true)); output.write("H263 video record only\n"); - int frameRate = getMaxFrameRateForVideoEncoder(MediaRecorder.VideoEncoder.H263); + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263); assertTrue("H263 video recording frame rate", frameRate != -1); for (int i = 0; i < NUM_STRESS_LOOP; i++) { assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263, @@ -406,7 +396,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med File videoMp4RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true)); output.write("MPEG4 video record only\n"); - int frameRate = getMaxFrameRateForVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP); assertTrue("MPEG4 video recording frame rate", frameRate != -1); for (int i = 0; i < NUM_STRESS_LOOP; i++) { assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP, @@ -428,7 +418,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med File videoRecordAudioMemoryOut = new File(MEDIA_MEMORY_OUTPUT); Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true)); - int frameRate = getMaxFrameRateForVideoEncoder(MediaRecorder.VideoEncoder.H263); + int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263); assertTrue("H263 video recording frame rate", frameRate != -1); output.write("Audio and h263 video record\n"); for (int i = 0; i < NUM_STRESS_LOOP; i++) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java index ae0589c..e6177ba 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java @@ -205,7 +205,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filename); - mRecorder.setVideoFrameRate(20); + mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate); mRecorder.setVideoSize(176,144); Log.v(TAG, "setEncoder"); mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); @@ -269,7 +269,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filename); - mRecorder.setVideoFrameRate(20); + mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate); mRecorder.setVideoSize(176,144); Log.v(TAG, "Media recorder setEncoder"); mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); |