diff options
Diffstat (limited to 'media')
210 files changed, 13333 insertions, 4107 deletions
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java index d40ca5a..bc68472 100644 --- a/media/java/android/media/AmrInputStream.java +++ b/media/java/android/media/AmrInputStream.java @@ -16,7 +16,6 @@ package android.media; -import android.util.Config; import android.util.Log; import java.io.InputStream; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index cc2ffa0..e1daede 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -50,8 +50,7 @@ public class AudioManager { private long mVolumeKeyUpTime; private int mVolumeControlStream = -1; private static String TAG = "AudioManager"; - private static boolean DEBUG = false; - private static boolean localLOGV = DEBUG || android.util.Config.LOGV; + private static boolean localLOGV = false; /** * Broadcast intent, a hint for applications that audio is about to become diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 9d0cba3..504cfde 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -111,6 +111,7 @@ public class AudioService extends IAudioService.Stub { private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8; private static final int MSG_LOAD_SOUND_EFFECTS = 9; private static final int MSG_SET_FORCE_USE = 10; + private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11; private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; @@ -355,6 +356,12 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); context.registerReceiver(mReceiver, intentFilter); + // Register for package removal intent broadcasts for media button receiver persistence + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + pkgFilter.addDataScheme("package"); + context.registerReceiver(mReceiver, pkgFilter); + // Register for media button intent broadcasts. intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); @@ -444,6 +451,9 @@ public class AudioService extends IAudioService.Stub { // Broadcast vibrate settings broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); + + // Restore the default media button receiver from the system settings + restoreMediaButtonReceiver(); } private void setStreamVolumeIndex(int stream, int index) { @@ -1912,6 +1922,11 @@ public class AudioService extends IAudioService.Stub { } } + private void persistMediaButtonReceiver(ComponentName receiver) { + Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, + receiver == null ? "" : receiver.flattenToString()); + } + private void cleanupPlayer(MediaPlayer mp) { if (mp != null) { try { @@ -2022,6 +2037,10 @@ public class AudioService extends IAudioService.Stub { case MSG_SET_FORCE_USE: setForceUse(msg.arg1, msg.arg2); break; + + case MSG_PERSIST_MEDIABUTTONRECEIVER: + persistMediaButtonReceiver( (ComponentName) msg.obj ); + break; } } } @@ -2354,6 +2373,14 @@ public class AudioService extends IAudioService.Stub { newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); mContext.sendStickyBroadcast(newIntent); + } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + // a package is being removed, not replaced + String packageName = intent.getData().getSchemeSpecificPart(); + if (packageName != null) { + removeMediaButtonReceiverForPackage(packageName); + } + } } } } @@ -2469,7 +2496,7 @@ public class AudioService extends IAudioService.Stub { if(fse.mClientId.equals(clientToRemove)) { Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " + fse.mClientId); - mFocusStack.remove(fse); + stackIterator.remove(); } } } @@ -2489,7 +2516,7 @@ public class AudioService extends IAudioService.Stub { if(fse.mSourceRef.equals(cb)) { Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " + fse.mClientId); - mFocusStack.remove(fse); + stackIterator.remove(); } } if (isTopOfStackForClientToRemove) { @@ -2701,6 +2728,56 @@ public class AudioService extends IAudioService.Stub { /** * Helper function: + * Remove any entry in the remote control stack that has the same package name as packageName + * Pre-condition: packageName != null + */ + private void removeMediaButtonReceiverForPackage(String packageName) { + synchronized(mRCStack) { + if (mRCStack.empty()) { + return; + } else { + RemoteControlStackEntry oldTop = mRCStack.peek(); + Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); + // iterate over the stack entries + while(stackIterator.hasNext()) { + RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); + if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) { + // a stack entry is from the package being removed, remove it from the stack + stackIterator.remove(); + } + } + if (mRCStack.empty()) { + // no saved media button receiver + mAudioHandler.sendMessage( + mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, + null)); + return; + } else if (oldTop != mRCStack.peek()) { + // the top of the stack has changed, save it in the system settings + // by posting a message to persist it + mAudioHandler.sendMessage( + mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, + mRCStack.peek().mReceiverComponent)); + } + } + } + } + + /** + * Helper function: + * Restore remote control receiver from the system settings + */ + private void restoreMediaButtonReceiver() { + String receiverName = Settings.System.getString(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER); + if ((null != receiverName) && !receiverName.isEmpty()) { + ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName); + registerMediaButtonEventReceiver(receiverComponentName); + } + } + + /** + * Helper function: * Set the new remote control receiver at the top of the RC focus stack */ private void pushMediaButtonReceiver(ComponentName newReceiver) { @@ -2712,11 +2789,15 @@ public class AudioService extends IAudioService.Stub { while(stackIterator.hasNext()) { RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); if(rcse.mReceiverComponent.equals(newReceiver)) { - mRCStack.remove(rcse); + stackIterator.remove(); break; } } mRCStack.push(new RemoteControlStackEntry(newReceiver)); + + // post message to persist the default media button receiver + mAudioHandler.sendMessage( mAudioHandler.obtainMessage( + MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) ); } /** @@ -2728,7 +2809,7 @@ public class AudioService extends IAudioService.Stub { while(stackIterator.hasNext()) { RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); if(rcse.mReceiverComponent.equals(newReceiver)) { - mRCStack.remove(rcse); + stackIterator.remove(); break; } } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 2492d47..95d93b2 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -64,44 +64,20 @@ public class AudioSystem /* * Sets the microphone mute on or off. * - * param on set <var>true</var> to mute the microphone; + * @param on set <var>true</var> to mute the microphone; * <var>false</var> to turn mute off - * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR + * @return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR */ public static native int muteMicrophone(boolean on); /* * Checks whether the microphone mute is on or off. * - * return true if microphone is muted, false if it's not + * @return true if microphone is muted, false if it's not */ public static native boolean isMicrophoneMuted(); - /* - * Sets the audio mode. - * - * param mode the requested audio mode (NORMAL, RINGTONE, or IN_CALL). - * Informs the HAL about the current audio state so that - * it can route the audio appropriately. - * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR - */ - /** @deprecated use {@link #setPhoneState(int)} */ - public static int setMode(int mode) { - return AUDIO_STATUS_ERROR; - } - /* - * Returns the current audio mode. - * - * return the current audio mode (NORMAL, RINGTONE, or IN_CALL). - * Returns the current current audio state from the HAL. - * - */ - /** @deprecated Do not use. */ - public static int getMode() { - return MODE_INVALID; - } - - /* modes for setPhoneState */ + /* modes for setPhoneState, must match AudioSystem.h audio_mode */ public static final int MODE_INVALID = -2; public static final int MODE_CURRENT = -1; public static final int MODE_NORMAL = 0; @@ -111,7 +87,7 @@ public class AudioSystem public static final int NUM_MODES = 4; - /* Routing bits for setRouting/getRouting API */ + /* Routing bits for the former setRouting/getRouting API */ /** @deprecated */ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0); /** @deprecated */ @@ -128,33 +104,6 @@ public class AudioSystem @Deprecated public static final int ROUTE_ALL = 0xFFFFFFFF; /* - * Sets the audio routing for a specified mode - * - * param mode audio mode to change route. E.g., MODE_RINGTONE. - * param routes bit vector of routes requested, created from one or - * more of ROUTE_xxx types. Set bits indicate that route should be on - * param mask bit vector of routes to change, created from one or more of - * ROUTE_xxx types. Unset bits indicate the route should be left unchanged - * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR - */ - /** @deprecated use {@link #setDeviceConnectionState(int,int,String)} */ - public static int setRouting(int mode, int routes, int mask) { - return AUDIO_STATUS_ERROR; - } - - /* - * Returns the current audio routing bit vector for a specified mode. - * - * param mode audio mode to change route (e.g., MODE_RINGTONE) - * return an audio route bit vector that can be compared with ROUTE_xxx - * bits - */ - /** @deprecated use {@link #getDeviceConnectionState(int,String)} */ - public static int getRouting(int mode) { - return 0; - } - - /* * Checks whether the specified stream type is active. * * return true if any track playing on this stream is active. @@ -163,7 +112,7 @@ public class AudioSystem /* * Sets a group generic audio configuration parameters. The use of these parameters - * are platform dependant, see libaudio + * are platform dependent, see libaudio * * param keyValuePairs list of parameters key value pairs in the form: * key1=value1;key2=value2;... @@ -172,7 +121,7 @@ public class AudioSystem /* * Gets a group generic audio configuration parameters. The use of these parameters - * are platform dependant, see libaudio + * are platform dependent, see libaudio * * param keys list of parameters * return value: list of parameters key value pairs in the form: @@ -180,15 +129,7 @@ public class AudioSystem */ public static native String getParameters(String keys); - /* - private final static String TAG = "audio"; - - private void log(String msg) { - Log.d(TAG, "[AudioSystem] " + msg); - } - */ - - // These match the enum in libs/android_runtime/android_media_AudioSystem.cpp + // These match the enum AudioError in frameworks/base/core/jni/android_media_AudioSystem.cpp /* Command sucessful or Media server restarted. see ErrorCallback */ public static final int AUDIO_STATUS_OK = 0; /* Command failed or unspecified audio error. see ErrorCallback */ @@ -215,7 +156,7 @@ public class AudioSystem /* * Registers a callback to be invoked when an error occurs. - * param cb the callback to run + * @param cb the callback to run */ public static void setErrorCallback(ErrorCallback cb) { @@ -272,16 +213,17 @@ public class AudioSystem public static final int DEVICE_IN_AUX_DIGITAL = 0x800000; public static final int DEVICE_IN_DEFAULT = 0x80000000; - // device states + // device states, must match AudioSystem::device_connection_state public static final int DEVICE_STATE_UNAVAILABLE = 0; public static final int DEVICE_STATE_AVAILABLE = 1; + private static final int NUM_DEVICE_STATES = 1; - // phone state + // phone state, match audio_mode??? public static final int PHONE_STATE_OFFCALL = 0; public static final int PHONE_STATE_RINGING = 1; public static final int PHONE_STATE_INCALL = 2; - // config for setForceUse + // device categories config for setForceUse, must match AudioSystem::forced_config public static final int FORCE_NONE = 0; public static final int FORCE_SPEAKER = 1; public static final int FORCE_HEADPHONES = 2; @@ -292,13 +234,15 @@ public class AudioSystem public static final int FORCE_BT_DESK_DOCK = 7; public static final int FORCE_ANALOG_DOCK = 8; public static final int FORCE_DIGITAL_DOCK = 9; + private static final int NUM_FORCE_CONFIG = 10; public static final int FORCE_DEFAULT = FORCE_NONE; - // usage for serForceUse + // usage for setForceUse, must match AudioSystem::force_use public static final int FOR_COMMUNICATION = 0; public static final int FOR_MEDIA = 1; public static final int FOR_RECORD = 2; public static final int FOR_DOCK = 3; + private static final int NUM_FORCE_USE = 4; public static native int setDeviceConnectionState(int device, int state, String device_address); public static native int getDeviceConnectionState(int device, String device_address); diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index a027bc6..a54cf28 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -66,8 +66,9 @@ public class MediaFile { public static final int FILE_TYPE_ASF = 26; public static final int FILE_TYPE_MKV = 27; public static final int FILE_TYPE_MP2TS = 28; + public static final int FILE_TYPE_AVI = 29; private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4; - private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MP2TS; + private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_AVI; // Image file types public static final int FILE_TYPE_JPEG = 31; @@ -175,6 +176,7 @@ public class MediaFile { addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG); addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG); addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC); + addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts", MtpConstants.FORMAT_AAC); addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska"); addFileType("MID", FILE_TYPE_MID, "audio/midi"); @@ -185,6 +187,7 @@ public class MediaFile { addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); addFileType("RTX", FILE_TYPE_MID, "audio/midi"); addFileType("OTA", FILE_TYPE_MID, "audio/midi"); + addFileType("MXMF", FILE_TYPE_MID, "audio/midi"); addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG); addFileType("MPG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG); @@ -197,6 +200,7 @@ public class MediaFile { addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska"); addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska"); addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts"); + addFileType("AVI", FILE_TYPE_AVI, "video/avi"); if (isWMVEnabled()) { addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV); diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 77e939e..02d6b66 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -26,6 +26,8 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Map; + /** * MediaMetadataRetriever class provides a unified interface for retrieving * frame and meta data from an input media file. @@ -56,7 +58,33 @@ public class MediaMetadataRetriever * @throws IllegalArgumentException If the path is invalid. */ public native void setDataSource(String path) throws IllegalArgumentException; - + + /** + * Sets the data source (URI) to use. Call this + * method before the rest of the methods in this class. This method may be + * time-consuming. + * + * @param uri The URI of the input media. + * @param headers the headers to be sent together with the request for the data + * @throws IllegalArgumentException If the URI is invalid. + */ + public void setDataSource(String uri, Map<String, String> headers) + throws IllegalArgumentException { + int i = 0; + String[] keys = new String[headers.size()]; + String[] values = new String[headers.size()]; + for (Map.Entry<String, String> entry: headers.entrySet()) { + keys[i] = entry.getKey(); + values[i] = entry.getValue(); + ++i; + } + _setDataSource(uri, keys, values); + } + + private native void _setDataSource( + String uri, String[] keys, String[] values) + throws IllegalArgumentException; + /** * Sets the data source (FileDescriptor) to use. It is the caller's * responsibility to close the file descriptor. It is safe to do so as soon @@ -398,5 +426,32 @@ public class MediaMetadataRetriever * The metadata key to retrieve the music album compilation status. */ public static final int METADATA_KEY_COMPILATION = 15; + /** + * If this key exists the media contains audio content. + */ + public static final int METADATA_KEY_HAS_AUDIO = 16; + /** + * If this key exists the media contains video content. + */ + public static final int METADATA_KEY_HAS_VIDEO = 17; + /** + * If the media contains video, this key retrieves its width. + */ + public static final int METADATA_KEY_VIDEO_WIDTH = 18; + /** + * If the media contains video, this key retrieves its height. + */ + public static final int METADATA_KEY_VIDEO_HEIGHT = 19; + /** + * This key retrieves the average bitrate (in bits/sec), if available. + */ + public static final int METADATA_KEY_BITRATE = 20; + /** + * This key retrieves the language code of text tracks, if available. + * If multiple text tracks present, the return value will look like: + * "eng:chi" + * @hide + */ + public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES = 21; // Add more here... } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 8b29ea8..84f588e 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -623,6 +623,11 @@ public class MediaPlayer * being played. Note that if a SurfaceTexture is used, the value * set via setScreenOnWhilePlaying has no effect. * + * The timestamps provided by {@link SurfaceTexture#getTimestamp()} for a + * SurfaceTexture set as the video sink have an unspecified zero point, + * and cannot be directly compared between different media sources or different + * instances of the same media source, or across multiple runs of the same + * program. * @hide */ public void setTexture(SurfaceTexture st) { @@ -740,7 +745,6 @@ public class MediaPlayer * @param uri the Content URI of the data you want to play * @param headers the headers to be sent together with the request for the data * @throws IllegalStateException if it is called in an invalid state - * @hide pending API council */ public void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { @@ -795,8 +799,29 @@ public class MediaPlayer * @throws IllegalStateException if it is called in an invalid state * @hide pending API council */ - public native void setDataSource(String path, Map<String, String> headers) - throws IOException, IllegalArgumentException, IllegalStateException; + public void setDataSource(String path, Map<String, String> headers) + throws IOException, IllegalArgumentException, IllegalStateException + { + String[] keys = null; + String[] values = null; + + if (headers != null) { + keys = new String[headers.size()]; + values = new String[headers.size()]; + + int i = 0; + for (Map.Entry<String, String> entry: headers.entrySet()) { + keys[i] = entry.getKey(); + values[i] = entry.getValue(); + ++i; + } + } + _setDataSource(path, keys, values); + } + + private native void _setDataSource( + String path, String[] keys, String[] values) + throws IOException, IllegalArgumentException, IllegalStateException; /** * Sets the data source (FileDescriptor) to use. It is the caller's responsibility @@ -1115,6 +1140,7 @@ public class MediaPlayer mOnErrorListener = null; mOnInfoListener = null; mOnVideoSizeChangedListener = null; + mOnTimedTextListener = null; _release(); } @@ -1222,6 +1248,93 @@ public class MediaPlayer */ public native void attachAuxEffect(int effectId); + /* Do not change these values without updating their counterparts + * in include/media/mediaplayer.h! + */ + /** + * Key used in setParameter method. + * Indicates the index of the timed text track to be enabled/disabled + */ + private static final int KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000; + + /** + * Sets the parameter indicated by key. + * @param key key indicates the parameter to be set. + * @param value value of the parameter to be set. + * @return true if the parameter is set successfully, false otherwise + * {@hide} + */ + public native boolean setParameter(int key, Parcel value); + + /** + * Sets the parameter indicated by key. + * @param key key indicates the parameter to be set. + * @param value value of the parameter to be set. + * @return true if the parameter is set successfully, false otherwise + * {@hide} + */ + public boolean setParameter(int key, String value) { + Parcel p = Parcel.obtain(); + p.writeString(value); + return setParameter(key, p); + } + + /** + * Sets the parameter indicated by key. + * @param key key indicates the parameter to be set. + * @param value value of the parameter to be set. + * @return true if the parameter is set successfully, false otherwise + * {@hide} + */ + public boolean setParameter(int key, int value) { + Parcel p = Parcel.obtain(); + p.writeInt(value); + return setParameter(key, p); + } + + /** + * Gets the value of the parameter indicated by key. + * @param key key indicates the parameter to get. + * @param reply value of the parameter to get. + */ + private native void getParameter(int key, Parcel reply); + + /** + * Gets the value of the parameter indicated by key. + * @param key key indicates the parameter to get. + * @return value of the parameter. + * {@hide} + */ + public Parcel getParcelParameter(int key) { + Parcel p = Parcel.obtain(); + getParameter(key, p); + return p; + } + + /** + * Gets the value of the parameter indicated by key. + * @param key key indicates the parameter to get. + * @return value of the parameter. + * {@hide} + */ + public String getStringParameter(int key) { + Parcel p = Parcel.obtain(); + getParameter(key, p); + return p.readString(); + } + + /** + * Gets the value of the parameter indicated by key. + * @param key key indicates the parameter to get. + * @return value of the parameter. + * {@hide} + */ + public int getIntParameter(int key) { + Parcel p = Parcel.obtain(); + getParameter(key, p); + return p.readInt(); + } + /** * Sets the send level of the player to the attached auxiliary effect * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0. @@ -1277,6 +1390,36 @@ public class MediaPlayer private native final void native_finalize(); /** + * @param index The index of the text track to be turned on. + * @return true if the text track is enabled successfully. + * {@hide} + */ + public boolean enableTimedTextTrackIndex(int index) { + if (index < 0) { + return false; + } + return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, index); + } + + /** + * Enables the first timed text track if any. + * @return true if the text track is enabled successfully + * {@hide} + */ + public boolean enableTimedText() { + return enableTimedTextTrackIndex(0); + } + + /** + * Disables timed text display. + * @return true if the text track is disabled successfully. + * {@hide} + */ + public boolean disableTimedText() { + return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, -1); + } + + /** * @param reply Parcel with audio/video duration info for battery tracking usage * @return The status code. @@ -1296,6 +1439,7 @@ public class MediaPlayer private static final int MEDIA_BUFFERING_UPDATE = 3; private static final int MEDIA_SEEK_COMPLETE = 4; private static final int MEDIA_SET_VIDEO_SIZE = 5; + private static final int MEDIA_TIMED_TEXT = 99; private static final int MEDIA_ERROR = 100; private static final int MEDIA_INFO = 200; @@ -1364,6 +1508,11 @@ public class MediaPlayer } // No real default action so far. return; + case MEDIA_TIMED_TEXT: + if (mOnTimedTextListener != null) { + mOnTimedTextListener.onTimedText(mMediaPlayer, (String)msg.obj); + } + return; case MEDIA_NOP: // interface test message - ignore break; @@ -1457,11 +1606,16 @@ public class MediaPlayer public interface OnBufferingUpdateListener { /** - * Called to update status in buffering a media stream. + * Called to update status in buffering a media stream received through + * progressive HTTP download. The received buffering percentage + * indicates how much of the content has been buffered or played. + * For example a buffering update of 80 percent when half the content + * has already been played indicates that the next 30 percent of the + * content to play has been buffered. * * @param mp the MediaPlayer the update pertains to - * @param percent the percentage (0-100) of the buffer - * that has been filled thus far + * @param percent the percentage (0-100) of the content + * that has been buffered or played thus far */ void onBufferingUpdate(MediaPlayer mp, int percent); } @@ -1535,6 +1689,39 @@ public class MediaPlayer private OnVideoSizeChangedListener mOnVideoSizeChangedListener; + /** + * Interface definition of a callback to be invoked when a + * timed text is available for display. + * {@hide} + */ + public interface OnTimedTextListener + { + /** + * Called to indicate the video size + * + * @param mp the MediaPlayer associated with this callback + * @param text the timed text sample which contains the + * text needed to be displayed. + * {@hide} + */ + public void onTimedText(MediaPlayer mp, String text); + } + + /** + * Register a callback to be invoked when a timed text is available + * for display. + * + * @param listener the callback that will be run + * {@hide} + */ + public void setOnTimedTextListener(OnTimedTextListener listener) + { + mOnTimedTextListener = listener; + } + + private OnTimedTextListener mOnTimedTextListener; + + /* Do not change these values without updating their counterparts * in include/media/mediaplayer.h! */ @@ -1611,10 +1798,12 @@ public class MediaPlayer /** MediaPlayer is temporarily pausing playback internally in order to * buffer more data. + * @see android.media.MediaPlayer.OnInfoListener */ public static final int MEDIA_INFO_BUFFERING_START = 701; /** MediaPlayer is resuming playback after filling buffers. + * @see android.media.MediaPlayer.OnInfoListener */ public static final int MEDIA_INFO_BUFFERING_END = 702; @@ -1649,6 +1838,8 @@ public class MediaPlayer * <ul> * <li>{@link #MEDIA_INFO_UNKNOWN} * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING} + * <li>{@link #MEDIA_INFO_BUFFERING_START} + * <li>{@link #MEDIA_INFO_BUFFERING_END} * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING} * <li>{@link #MEDIA_INFO_NOT_SEEKABLE} * <li>{@link #MEDIA_INFO_METADATA_UPDATE} diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index edefb22..38202f2 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -347,6 +347,39 @@ public class MediaRecorder } /** + * Set and store the geodata (latitude and longitude) in the output file. + * This method should be called before prepare(). The geodata is + * stored in udta box if the output format is OutputFormat.THREE_GPP + * or OutputFormat.MPEG_4, and is ignored for other output formats. + * The geodata is stored according to ISO-6709 standard. + * + * @param latitude latitude in degrees. Its value must be in the + * range [-90, 90]. + * @param longitude longitude in degrees. Its value must be in the + * range [-180, 180]. + * + * @throws IllegalArgumentException if the given latitude or + * longitude is out of range. + * + */ + public void setLocation(float latitude, float longitude) { + int latitudex10000 = (int) (latitude * 10000 + 0.5); + int longitudex10000 = (int) (longitude * 10000 + 0.5); + + if (latitudex10000 > 900000 || latitudex10000 < -900000) { + String msg = "Latitude: " + latitude + " out of range."; + throw new IllegalArgumentException(msg); + } + if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { + String msg = "Longitude: " + longitude + " out of range"; + throw new IllegalArgumentException(msg); + } + + setParameter("param-geotag-latitude=" + latitudex10000); + setParameter("param-geotag-longitude=" + longitudex10000); + } + + /** * Sets the format of the output file produced during recording. Call this * after setAudioSource()/setVideoSource() but before prepare(). * @@ -767,6 +800,75 @@ public class MediaRecorder */ public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; + /** informational events for individual tracks, for testing purpose. + * The track informational event usually contains two parts in the ext1 + * arg of the onInfo() callback: bit 31-28 contains the track id; and + * the rest of the 28 bits contains the informational event defined here. + * For example, ext1 = (1 << 28 | MEDIA_RECORDER_TRACK_INFO_TYPE) if the + * track id is 1 for informational event MEDIA_RECORDER_TRACK_INFO_TYPE; + * while ext1 = (0 << 28 | MEDIA_RECORDER_TRACK_INFO_TYPE) if the track + * id is 0 for informational event MEDIA_RECORDER_TRACK_INFO_TYPE. The + * application should extract the track id and the type of informational + * event from ext1, accordingly. + * + * FIXME: + * Please update the comment for onInfo also when these + * events are unhidden so that application knows how to extract the track + * id and the informational event type from onInfo callback. + * + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_LIST_START = 1000; + /** Signal the completion of the track for the recording session. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS = 1000; + /** Indicate the recording progress in time (ms) during recording. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME = 1001; + /** Indicate the track type: 0 for Audio and 1 for Video. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_TYPE = 1002; + /** Provide the track duration information. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_DURATION_MS = 1003; + /** Provide the max chunk duration in time (ms) for the given track. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS = 1004; + /** Provide the total number of recordd frames. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES = 1005; + /** Provide the max spacing between neighboring chunks for the given track. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS = 1006; + /** Provide the elapsed time measuring from the start of the recording + * till the first output frame of the given track is received, excluding + * any intentional start time offset of a recording session for the + * purpose of eliminating the recording sound in the recorded file. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS = 1007; + /** Provide the start time difference (delay) betweeen this track and + * the start of the movie. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS = 1008; + /** Provide the total number of data (in kilo-bytes) encoded. + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES = 1009; + /** + * {@hide} + */ + public static final int MEDIA_RECORDER_TRACK_INFO_LIST_END = 2000; + + /** * Interface definition for a callback to be invoked when an error * occurs while recording. @@ -811,8 +913,17 @@ public class MediaRecorder /* Do not change these values without updating their counterparts * in include/media/mediarecorder.h! */ - private static final int MEDIA_RECORDER_EVENT_ERROR = 1; - private static final int MEDIA_RECORDER_EVENT_INFO = 2; + private static final int MEDIA_RECORDER_EVENT_LIST_START = 1; + private static final int MEDIA_RECORDER_EVENT_ERROR = 1; + private static final int MEDIA_RECORDER_EVENT_INFO = 2; + private static final int MEDIA_RECORDER_EVENT_LIST_END = 99; + + /* Events related to individual tracks */ + private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_START = 100; + private static final int MEDIA_RECORDER_TRACK_EVENT_ERROR = 100; + private static final int MEDIA_RECORDER_TRACK_EVENT_INFO = 101; + private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_END = 1000; + @Override public void handleMessage(Message msg) { @@ -822,12 +933,14 @@ public class MediaRecorder } switch(msg.what) { case MEDIA_RECORDER_EVENT_ERROR: + case MEDIA_RECORDER_TRACK_EVENT_ERROR: if (mOnErrorListener != null) mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2); return; case MEDIA_RECORDER_EVENT_INFO: + case MEDIA_RECORDER_TRACK_EVENT_INFO: if (mOnInfoListener != null) mOnInfoListener.onInfo(mMediaRecorder, msg.arg1, msg.arg2); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 55b0045..790eaa3 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -46,7 +46,6 @@ import android.sax.Element; import android.sax.ElementListener; import android.sax.RootElement; import android.text.TextUtils; -import android.util.Config; import android.util.Log; import android.util.Xml; @@ -1052,7 +1051,7 @@ public class MediaScanner } for (String fileToDelete : existingFiles) { - if (Config.LOGV) + if (false) Log.v(TAG, "fileToDelete is " + fileToDelete); try { (new File(fileToDelete)).delete(); @@ -1169,7 +1168,7 @@ public class MediaScanner postscan(directories); long end = System.currentTimeMillis(); - if (Config.LOGD) { + if (false) { Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n"); Log.d(TAG, " scan time: " + (scan - prescan) + "ms\n"); Log.d(TAG, "postscan time: " + (end - scan) + "ms\n"); @@ -1604,6 +1603,17 @@ public class MediaScanner private static native final void native_init(); private native final void native_setup(); private native final void native_finalize(); + + /** + * Releases resouces associated with this MediaScanner object. + * It is considered good practice to call this method when + * one is done using the MediaScanner object. After this method + * is called, the MediaScanner object can no longer be used. + */ + public void release() { + native_finalize(); + } + @Override protected void finalize() { mContext.getContentResolver().releaseProvider(mMediaProvider); diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java index 503b5f4..969da39 100644 --- a/media/java/android/media/MediaScannerConnection.java +++ b/media/java/android/media/MediaScannerConnection.java @@ -25,7 +25,6 @@ import android.media.IMediaScannerService; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; -import android.util.Config; import android.util.Log; @@ -126,13 +125,13 @@ public class MediaScannerConnection implements ServiceConnection { public void disconnect() { synchronized (this) { if (mConnected) { - if (Config.LOGV) { + if (false) { Log.v(TAG, "Disconnecting from Media Scanner"); } try { mContext.unbindService(this); } catch (IllegalArgumentException ex) { - if (Config.LOGV) { + if (false) { Log.v(TAG, "disconnect failed: " + ex); } } @@ -164,12 +163,12 @@ public class MediaScannerConnection implements ServiceConnection { throw new IllegalStateException("not connected to MediaScannerService"); } try { - if (Config.LOGV) { + if (false) { Log.v(TAG, "Scanning file " + path); } mService.requestScanFile(path, mimeType, mListener); } catch (RemoteException e) { - if (Config.LOGD) { + if (false) { Log.d(TAG, "Failed to scan file " + path); } } @@ -240,7 +239,7 @@ public class MediaScannerConnection implements ServiceConnection { * Part of the ServiceConnection interface. Do not call. */ public void onServiceConnected(ComponentName className, IBinder service) { - if (Config.LOGV) { + if (false) { Log.v(TAG, "Connected to Media Scanner"); } synchronized (this) { @@ -255,7 +254,7 @@ public class MediaScannerConnection implements ServiceConnection { * Part of the ServiceConnection interface. Do not call. */ public void onServiceDisconnected(ComponentName className) { - if (Config.LOGV) { + if (false) { Log.v(TAG, "Disconnected from Media Scanner"); } synchronized (this) { diff --git a/media/java/android/media/ResampleInputStream.java b/media/java/android/media/ResampleInputStream.java index e26eae5..b025e25 100644 --- a/media/java/android/media/ResampleInputStream.java +++ b/media/java/android/media/ResampleInputStream.java @@ -16,7 +16,6 @@ package android.media; -import android.util.Config; import android.util.Log; import java.io.InputStream; diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java index d3e9a49..39c6d3e 100644 --- a/media/java/android/media/audiofx/AudioEffect.java +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -573,27 +573,16 @@ public class AudioEffect { * * @param param the identifier of the parameter to set * @param value the new value for the specified parameter - * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, - * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or - * {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length - * indicates the maximum size of the returned parameters value. When - * returning, value.length is updated with the actual size of the - * returned value. + * @return the number of meaningful bytes in value array in case of success or + * {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} + * or {@link #ERROR_DEAD_OBJECT} in case of failure. * @throws IllegalStateException * @hide */ public int getParameter(byte[] param, byte[] value) throws IllegalStateException { checkState("getParameter()"); - int[] vSize = new int[1]; - vSize[0] = value.length; - int status = native_getParameter(param.length, param, vSize, value); - if (value.length > vSize[0]) { - byte[] resizedValue = new byte[vSize[0]]; - System.arraycopy(value, 0, resizedValue, 0, vSize[0]); - value = resizedValue; - } - return status; + return native_getParameter(param.length, param, value.length, value); } /** @@ -615,6 +604,7 @@ public class AudioEffect { * array of 1 or 2 integers * * @see #getParameter(byte[], byte[]) + * In case of success, returns the number of meaningful integers in value array. * @hide */ public int getParameter(int param, int[] value) @@ -628,9 +618,14 @@ public class AudioEffect { int status = getParameter(p, v); - value[0] = byteArrayToInt(v); - if (v.length > 4) { - value[1] = byteArrayToInt(v, 4); + if (status == 4 || status == 8) { + value[0] = byteArrayToInt(v); + if (status == 8) { + value[1] = byteArrayToInt(v, 4); + } + status /= 4; + } else { + status = ERROR; } return status; } @@ -640,6 +635,7 @@ public class AudioEffect { * array of 1 or 2 short integers * * @see #getParameter(byte[], byte[]) + * In case of success, returns the number of meaningful short integers in value array. * @hide */ public int getParameter(int param, short[] value) @@ -653,9 +649,14 @@ public class AudioEffect { int status = getParameter(p, v); - value[0] = byteArrayToShort(v); - if (v.length > 2) { - value[1] = byteArrayToShort(v, 2); + if (status == 2 || status == 4) { + value[0] = byteArrayToShort(v); + if (status == 4) { + value[1] = byteArrayToShort(v, 2); + } + status /= 2; + } else { + status = ERROR; } return status; } @@ -665,6 +666,7 @@ public class AudioEffect { * the value is also an array of 1 or 2 integers * * @see #getParameter(byte[], byte[]) + * In case of success, the returns the number of meaningful integers in value array. * @hide */ public int getParameter(int[] param, int[] value) @@ -681,9 +683,14 @@ public class AudioEffect { int status = getParameter(p, v); - value[0] = byteArrayToInt(v); - if (v.length > 4) { - value[1] = byteArrayToInt(v, 4); + if (status == 4 || status == 8) { + value[0] = byteArrayToInt(v); + if (status == 8) { + value[1] = byteArrayToInt(v, 4); + } + status /= 4; + } else { + status = ERROR; } return status; } @@ -693,6 +700,7 @@ public class AudioEffect { * the value is an array of 1 or 2 short integers * * @see #getParameter(byte[], byte[]) + * In case of success, returns the number of meaningful short integers in value array. * @hide */ public int getParameter(int[] param, short[] value) @@ -709,9 +717,14 @@ public class AudioEffect { int status = getParameter(p, v); - value[0] = byteArrayToShort(v); - if (v.length > 2) { - value[1] = byteArrayToShort(v, 2); + if (status == 2 || status == 4) { + value[0] = byteArrayToShort(v); + if (status == 4) { + value[1] = byteArrayToShort(v, 2); + } + status /= 2; + } else { + status = ERROR; } return status; } @@ -740,24 +753,14 @@ public class AudioEffect { /** * Send a command to the effect engine. This method is intended to send * proprietary commands to a particular effect implementation. - * + * In case of success, returns the number of meaningful bytes in reply array. + * In case of failure, the returned value is negative and implementation specific. * @hide */ public int command(int cmdCode, byte[] command, byte[] reply) throws IllegalStateException { checkState("command()"); - int[] replySize = new int[1]; - replySize[0] = reply.length; - - int status = native_command(cmdCode, command.length, command, - replySize, reply); - - if (reply.length > replySize[0]) { - byte[] resizedReply = new byte[replySize[0]]; - System.arraycopy(reply, 0, resizedReply, 0, replySize[0]); - reply = resizedReply; - } - return status; + return native_command(cmdCode, command.length, command, reply.length, reply); } // -------------------------------------------------------------------------- @@ -1145,10 +1148,10 @@ public class AudioEffect { int vsize, byte[] value); private native final int native_getParameter(int psize, byte[] param, - int[] vsize, byte[] value); + int vsize, byte[] value); private native final int native_command(int cmdCode, int cmdSize, - byte[] cmdData, int[] repSize, byte[] repData); + byte[] cmdData, int repSize, byte[] repData); private static native Object[] native_query_effects(); @@ -1172,23 +1175,30 @@ public class AudioEffect { * @hide */ public void checkStatus(int status) { - switch (status) { - case AudioEffect.SUCCESS: - break; - case AudioEffect.ERROR_BAD_VALUE: - throw (new IllegalArgumentException( - "AudioEffect: bad parameter value")); - case AudioEffect.ERROR_INVALID_OPERATION: - throw (new UnsupportedOperationException( - "AudioEffect: invalid parameter operation")); - default: - throw (new RuntimeException("AudioEffect: set/get parameter error")); + if (isError(status)) { + switch (status) { + case AudioEffect.ERROR_BAD_VALUE: + throw (new IllegalArgumentException( + "AudioEffect: bad parameter value")); + case AudioEffect.ERROR_INVALID_OPERATION: + throw (new UnsupportedOperationException( + "AudioEffect: invalid parameter operation")); + default: + throw (new RuntimeException("AudioEffect: set/get parameter error")); + } } } /** * @hide */ + public static boolean isError(int status) { + return (status < 0); + } + + /** + * @hide + */ public int byteArrayToInt(byte[] valueBuf) { return byteArrayToInt(valueBuf, 0); diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 2ddb287..d4b326c 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \ android_media_ResampleInputStream.cpp \ android_media_MediaProfiles.cpp \ android_media_AmrInputStream.cpp \ + android_media_Utils.cpp \ android_mtp_MtpDatabase.cpp \ android_mtp_MtpDevice.cpp \ android_mtp_MtpServer.cpp \ @@ -22,7 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libui \ libcutils \ - libsurfaceflinger_client \ + libgui \ libstagefright \ libcamera_client \ libsqlite \ diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp index f8aecdd..b5220fe 100644 --- a/media/jni/android_media_AmrInputStream.cpp +++ b/media/jni/android_media_AmrInputStream.cpp @@ -49,24 +49,11 @@ struct GsmAmrEncoderState { int32_t mLastModeUsed; }; -// -// helper function to throw an exception -// -static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { - if (jclass cls = env->FindClass(ex)) { - char msg[128]; - sprintf(msg, fmt, data); - env->ThrowNew(cls, msg); - env->DeleteLocalRef(cls); - } -} - static jint android_media_AmrInputStream_GsmAmrEncoderNew (JNIEnv *env, jclass clazz) { GsmAmrEncoderState* gae = new GsmAmrEncoderState(); if (gae == NULL) { - throwException(env, "java/lang/RuntimeException", - "Out of memory", 0); + jniThrowRuntimeException(env, "Out of memory"); } return (jint)gae; } @@ -76,7 +63,7 @@ static void android_media_AmrInputStream_GsmAmrEncoderInitialize GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false); if (nResult != OK) { - throwException(env, "java/lang/IllegalArgumentException", + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "GsmAmrEncoder initialization failed %d", nResult); } } @@ -97,7 +84,7 @@ static jint android_media_AmrInputStream_GsmAmrEncoderEncode (Frame_Type_3GPP*) &state->mLastModeUsed, AMR_TX_WMF); if (length < 0) { - throwException(env, "java/io/IOException", + jniThrowExceptionFmt(env, "java/io/IOException", "Failed to encode a frame with error code: %d", length); return -1; } @@ -148,5 +135,3 @@ int register_android_media_AmrInputStream(JNIEnv *env) return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } - - diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 3d7dbf9..73aea2a 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -28,16 +28,18 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "android_media_Utils.h" using namespace android; struct fields_t { jfieldID context; - jclass bitmapClazz; + jclass bitmapClazz; // Must be a global ref jfieldID nativeBitmap; jmethodID createBitmapMethod; - jclass configClazz; + jmethodID createScaledBitmapMethod; + jclass configClazz; // Must be a global ref jmethodID createConfigMethod; }; @@ -76,32 +78,64 @@ static void setRetriever(JNIEnv* env, jobject thiz, int retriever) env->SetIntField(thiz, fields.context, retriever); } -static void android_media_MediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jstring path) -{ +static void +android_media_MediaMetadataRetriever_setDataSourceAndHeaders( + JNIEnv *env, jobject thiz, jstring path, + jobjectArray keys, jobjectArray values) { + LOGV("setDataSource"); MediaMetadataRetriever* retriever = getRetriever(env, thiz); if (retriever == 0) { - jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); + jniThrowException( + env, + "java/lang/IllegalStateException", "No retriever available"); + return; } + if (!path) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Null pointer"); + jniThrowException( + env, "java/lang/IllegalArgumentException", "Null pointer"); + return; } - const char *pathStr = env->GetStringUTFChars(path, NULL); - if (!pathStr) { // OutOfMemoryError exception already thrown + const char *tmp = env->GetStringUTFChars(path, NULL); + if (!tmp) { // OutOfMemoryError exception already thrown return; } + String8 pathStr(tmp); + env->ReleaseStringUTFChars(path, tmp); + tmp = NULL; + // Don't let somebody trick us in to reading some random block of memory - if (strncmp("mem://", pathStr, 6) == 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid pathname"); + if (strncmp("mem://", pathStr.string(), 6) == 0) { + jniThrowException( + env, "java/lang/IllegalArgumentException", "Invalid pathname"); return; } - process_media_retriever_call(env, retriever->setDataSource(pathStr), "java/lang/RuntimeException", "setDataSource failed"); - env->ReleaseStringUTFChars(path, pathStr); + // We build a similar KeyedVector out of it. + KeyedVector<String8, String8> headersVector; + if (!ConvertKeyValueArraysToKeyedVector( + env, keys, values, &headersVector)) { + return; + } + process_media_retriever_call( + env, + retriever->setDataSource( + pathStr.string(), headersVector.size() > 0 ? &headersVector : NULL), + + "java/lang/RuntimeException", + "setDataSource failed"); +} + + +static void android_media_MediaMetadataRetriever_setDataSource( + JNIEnv *env, jobject thiz, jstring path) { + android_media_MediaMetadataRetriever_setDataSourceAndHeaders( + env, thiz, path, NULL, NULL); } static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) @@ -116,7 +150,7 @@ static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jo jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - int fd = getParcelFileDescriptorFD(env, fileDescriptor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (offset < 0 || length < 0 || fd < 0) { if (offset < 0) { LOGE("negative offset (%lld)", offset); @@ -219,12 +253,14 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, SkBitmap::kRGB_565_Config); size_t width, height; + bool swapWidthAndHeight = false; if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) { - width = videoFrame->mDisplayHeight; - height = videoFrame->mDisplayWidth; + width = videoFrame->mHeight; + height = videoFrame->mWidth; + swapWidthAndHeight = true; } else { - width = videoFrame->mDisplayWidth; - height = videoFrame->mDisplayHeight; + width = videoFrame->mWidth; + height = videoFrame->mHeight; } jobject jBitmap = env->CallStaticObjectMethod( @@ -240,11 +276,30 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, bitmap->lockPixels(); rotate((uint16_t*)bitmap->getPixels(), (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)), - videoFrame->mDisplayWidth, - videoFrame->mDisplayHeight, + videoFrame->mWidth, + videoFrame->mHeight, videoFrame->mRotationAngle); bitmap->unlockPixels(); + if (videoFrame->mDisplayWidth != videoFrame->mWidth || + videoFrame->mDisplayHeight != videoFrame->mHeight) { + size_t displayWidth = videoFrame->mDisplayWidth; + size_t displayHeight = videoFrame->mDisplayHeight; + if (swapWidthAndHeight) { + displayWidth = videoFrame->mDisplayHeight; + displayHeight = videoFrame->mDisplayWidth; + } + LOGV("Bitmap dimension is scaled from %dx%d to %dx%d", + width, height, displayWidth, displayHeight); + jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz, + fields.createScaledBitmapMethod, + jBitmap, + displayWidth, + displayHeight, + true); + return scaledBitmap; + } + return jBitmap; } @@ -328,19 +383,20 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env) { jclass clazz = env->FindClass(kClassPathName); if (clazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaMetadataRetriever"); return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaMetadataRetriever.mNativeContext"); return; } - fields.bitmapClazz = env->FindClass("android/graphics/Bitmap"); + jclass bitmapClazz = env->FindClass("android/graphics/Bitmap"); + if (bitmapClazz == NULL) { + return; + } + fields.bitmapClazz = (jclass) env->NewGlobalRef(bitmapClazz); if (fields.bitmapClazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap"); return; } fields.createBitmapMethod = @@ -348,28 +404,32 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env) "(IILandroid/graphics/Bitmap$Config;)" "Landroid/graphics/Bitmap;"); if (fields.createBitmapMethod == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find Bitmap.createBitmap(int, int, Config) method"); + return; + } + fields.createScaledBitmapMethod = + env->GetStaticMethodID(fields.bitmapClazz, "createScaledBitmap", + "(Landroid/graphics/Bitmap;IIZ)" + "Landroid/graphics/Bitmap;"); + if (fields.createScaledBitmapMethod == NULL) { return; } fields.nativeBitmap = env->GetFieldID(fields.bitmapClazz, "mNativeBitmap", "I"); if (fields.nativeBitmap == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find Bitmap.mNativeBitmap field"); + return; } - fields.configClazz = env->FindClass("android/graphics/Bitmap$Config"); + jclass configClazz = env->FindClass("android/graphics/Bitmap$Config"); + if (configClazz == NULL) { + return; + } + fields.configClazz = (jclass) env->NewGlobalRef(configClazz); if (fields.configClazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find Bitmap$Config class"); return; } fields.createConfigMethod = env->GetStaticMethodID(fields.configClazz, "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;"); if (fields.createConfigMethod == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find Bitmap$Config.nativeToConfig(int) method"); return; } } @@ -388,6 +448,13 @@ static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobje // JNI mapping between Java methods and native methods static JNINativeMethod nativeMethods[] = { {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource}, + + { + "_setDataSource", + "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders + }, + {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime}, {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata}, diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 6d074e9..b03aa38 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1,4 +1,4 @@ -/* //device/libs/android_runtime/android_media_MediaPlayer.cpp +/* ** ** Copyright 2007, The Android Open Source Project ** @@ -33,6 +33,8 @@ #include "utils/Errors.h" // for status_t #include "utils/KeyedVector.h" #include "utils/String8.h" +#include "android_media_Utils.h" + #include "android_util_Binder.h" #include <binder/Parcel.h> #include <gui/SurfaceTexture.h> @@ -69,7 +71,7 @@ class JNIMediaPlayerListener: public MediaPlayerListener public: JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz); ~JNIMediaPlayerListener(); - void notify(int msg, int ext1, int ext2); + virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL); private: JNIMediaPlayerListener(); jclass mClass; // Reference to MediaPlayer class @@ -102,10 +104,23 @@ JNIMediaPlayerListener::~JNIMediaPlayerListener() env->DeleteGlobalRef(mClass); } -void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2) +void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj) { JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0); + if (obj && obj->dataSize() > 0) { + jbyteArray jArray = env->NewByteArray(obj->dataSize()); + if (jArray != NULL) { + jbyte *nArray = env->GetByteArrayElements(jArray, NULL); + memcpy(nArray, obj->data(), obj->dataSize()); + env->ReleaseByteArrayElements(jArray, nArray, 0); + env->CallStaticVoidMethod(mClass, fields.post_event, mObject, + msg, ext1, ext2, jArray); + env->DeleteLocalRef(jArray); + } + } else { + env->CallStaticVoidMethod(mClass, fields.post_event, mObject, + msg, ext1, ext2, NULL); + } } // ---------------------------------------------------------------------------- @@ -173,7 +188,9 @@ 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, jobject headers) { + JNIEnv *env, jobject thiz, jstring path, + jobjectArray keys, jobjectArray values) { + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -185,91 +202,27 @@ android_media_MediaPlayer_setDataSourceAndHeaders( return; } - const char *pathStr = env->GetStringUTFChars(path, NULL); - if (pathStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + const char *tmp = env->GetStringUTFChars(path, NULL); + if (tmp == NULL) { // Out of memory return; } - // headers is a Map<String, String>. - // We build a similar KeyedVector out of it. - KeyedVector<String8, String8> headersVector; - if (headers) { - // Get the Map's entry Set. - jclass mapClass = env->FindClass("java/util/Map"); - - jmethodID entrySet = - env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); - - jobject set = env->CallObjectMethod(headers, entrySet); - // Obtain an iterator over the Set - jclass setClass = env->FindClass("java/util/Set"); - - jmethodID iterator = - env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); - - jobject iter = env->CallObjectMethod(set, iterator); - // Get the Iterator method IDs - jclass iteratorClass = env->FindClass("java/util/Iterator"); - jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); - - jmethodID next = - env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); - - // Get the Entry class method IDs - jclass entryClass = env->FindClass("java/util/Map$Entry"); + String8 pathStr(tmp); + env->ReleaseStringUTFChars(path, tmp); + tmp = NULL; - jmethodID getKey = - env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); - - jmethodID getValue = - env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); - - // Iterate over the entry Set - while (env->CallBooleanMethod(iter, hasNext)) { - jobject entry = env->CallObjectMethod(iter, next); - jstring key = (jstring) env->CallObjectMethod(entry, getKey); - jstring value = (jstring) env->CallObjectMethod(entry, getValue); - - const char* keyStr = env->GetStringUTFChars(key, NULL); - if (!keyStr) { // Out of memory - jniThrowException( - env, "java/lang/RuntimeException", "Out of memory"); - return; - } - - const char* valueStr = env->GetStringUTFChars(value, NULL); - if (!valueStr) { // Out of memory - jniThrowException( - env, "java/lang/RuntimeException", "Out of memory"); - return; - } - - headersVector.add(String8(keyStr), String8(valueStr)); - - env->DeleteLocalRef(entry); - env->ReleaseStringUTFChars(key, keyStr); - env->DeleteLocalRef(key); - env->ReleaseStringUTFChars(value, valueStr); - env->DeleteLocalRef(value); - } - - env->DeleteLocalRef(entryClass); - env->DeleteLocalRef(iteratorClass); - env->DeleteLocalRef(iter); - env->DeleteLocalRef(setClass); - env->DeleteLocalRef(set); - env->DeleteLocalRef(mapClass); + // We build a KeyedVector out of the key and val arrays + KeyedVector<String8, String8> headersVector; + if (!ConvertKeyValueArraysToKeyedVector( + env, keys, values, &headersVector)) { + return; } LOGV("setDataSource: path %s", pathStr); status_t opStatus = mp->setDataSource( - String8(pathStr), - headers ? &headersVector : NULL); - - // Make sure that local ref is released before a potential exception - env->ReleaseStringUTFChars(path, pathStr); + pathStr, + headersVector.size() > 0? &headersVector : NULL); process_media_player_call( env, thiz, opStatus, "java/io/IOException", @@ -279,7 +232,7 @@ android_media_MediaPlayer_setDataSourceAndHeaders( static void android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path) { - android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, 0); + android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, NULL, NULL); } static void @@ -295,7 +248,7 @@ android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fil jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - int fd = getParcelFileDescriptorFD(env, fileDescriptor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); LOGV("setDataSourceFD: fd %d", fd); process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); } @@ -317,8 +270,7 @@ static void setVideoSurfaceOrSurfaceTexture( if (surfaceTexture != NULL) { sp<ISurfaceTexture> native_surfaceTexture( getSurfaceTexture(env, surfaceTexture)); - LOGV("%s: texture=%p (id=%d)", prefix, - native_surfaceTexture.get(), native_surfaceTexture->getIdentity()); + LOGV("%s: texture=%p", prefix, native_surfaceTexture.get()); mp->setVideoSurfaceTexture(native_surfaceTexture); } } @@ -629,62 +581,49 @@ android_media_MediaPlayer_native_init(JNIEnv *env) clazz = env->FindClass("android/media/MediaPlayer"); if (clazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaPlayer"); return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mNativeContext"); return; } fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.post_event == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.postEventFromNative"); return; } fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); if (fields.surface == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mSurface"); return; } jclass surface = env->FindClass("android/view/Surface"); if (surface == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface"); return; } fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I"); if (fields.surface_native == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find Surface." ANDROID_VIEW_SURFACE_JNI_ID); return; } fields.surfaceTexture = env->GetFieldID(clazz, "mSurfaceTexture", "Landroid/graphics/SurfaceTexture;"); if (fields.surfaceTexture == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find MediaPlayer.mSurfaceTexture"); return; } jclass surfaceTexture = env->FindClass("android/graphics/SurfaceTexture"); if (surfaceTexture == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find android/graphics/SurfaceTexture"); return; } fields.surfaceTexture_native = env->GetFieldID(surfaceTexture, ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "I"); if (fields.surfaceTexture_native == NULL) { - jniThrowException(env, "java/lang/RuntimeException", - "Can't find SurfaceTexture." ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID); return; } @@ -785,11 +724,50 @@ android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject jav return service->pullBatteryData(reply); } +static jboolean +android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request) +{ + LOGV("setParameter: key %d", key); + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return false; + } + + Parcel *request = parcelForJavaObject(env, java_request); + status_t err = mp->setParameter(key, *request); + if (err == OK) { + return true; + } else { + return false; + } +} + +static void +android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply) +{ + LOGV("getParameter: key %d", key); + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + Parcel *reply = parcelForJavaObject(env, java_reply); + process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL ); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, - {"setDataSource", "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders}, + + { + "_setDataSource", + "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + (void *)android_media_MediaPlayer_setDataSourceAndHeaders + }, + {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, {"_setVideoSurfaceOrSurfaceTexture", "()V", (void *)android_media_MediaPlayer_setVideoSurfaceOrSurfaceTexture}, {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, @@ -821,6 +799,8 @@ static JNINativeMethod gMethods[] = { {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, + {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter}, + {"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter}, }; static const char* const kClassPathName = "android/media/MediaPlayer"; diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 82b4ac1..2f7d7ee 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -33,6 +33,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include <system/audio.h> // ---------------------------------------------------------------------------- @@ -152,7 +153,7 @@ static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, job { // we should not pass a null camera to get_native_camera() call. if (camera == NULL) { - jniThrowException(env, "java/lang/NullPointerException", "camera object is a NULL pointer"); + jniThrowNullPointerException(env, "camera object is a NULL pointer"); return; } sp<Camera> c = get_native_camera(env, camera, NULL); @@ -177,7 +178,7 @@ static void android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as) { LOGV("setAudioSource(%d)", as); - if (as < AUDIO_SOURCE_DEFAULT || as >= AUDIO_SOURCE_LIST_END) { + if (as < AUDIO_SOURCE_DEFAULT || as >= AUDIO_SOURCE_CNT) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source"); return; } @@ -253,7 +254,7 @@ android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject f jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - int fd = getParcelFileDescriptorFD(env, fileDescriptor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); status_t opStatus = mr->setOutputFile(fd, offset, length); process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); @@ -267,7 +268,7 @@ android_media_MediaRecorder_setOutputFileAuxFD(JNIEnv *env, jobject thiz, jobjec jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - int fd = getParcelFileDescriptorFD(env, fileDescriptor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); status_t opStatus = mr->setOutputFileAuxiliary(fd); process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); @@ -404,38 +405,32 @@ android_media_MediaRecorder_native_init(JNIEnv *env) clazz = env->FindClass("android/media/MediaRecorder"); if (clazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaRecorder"); return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mNativeContext"); return; } fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); if (fields.surface == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mSurface"); return; } jclass surface = env->FindClass("android/view/Surface"); if (surface == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface"); return; } fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I"); if (fields.surface_native == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface"); return; } fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.post_event == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "MediaRecorder.postEventFromNative"); return; } } @@ -505,5 +500,3 @@ int register_android_media_MediaRecorder(JNIEnv *env) return AndroidRuntime::registerNativeMethods(env, "android/media/MediaRecorder", gMethods, NELEM(gMethods)); } - - diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index 19f3ca3..9151799 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -1,4 +1,4 @@ -/* //device/libs/media_jni/MediaScanner.cpp +/* ** ** Copyright 2007, The Android Open Source Project ** @@ -15,36 +15,37 @@ ** limitations under the License. */ -#define LOG_TAG "MediaScanner" -#include "utils/Log.h" - -#include <media/mediascanner.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <unistd.h> -#include <fcntl.h> -#include <cutils/properties.h> +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaScannerJNI" +#include <utils/Log.h> #include <utils/threads.h> +#include <media/mediascanner.h> +#include <media/stagefright/StagefrightMediaScanner.h> #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" -#include <media/stagefright/StagefrightMediaScanner.h> +using namespace android; -// ---------------------------------------------------------------------------- -using namespace android; +static const char* const kClassMediaScannerClient = + "android/media/MediaScannerClient"; + +static const char* const kClassMediaScanner = + "android/media/MediaScanner"; + +static const char* const kRunTimeException = + "java/lang/RuntimeException"; -// ---------------------------------------------------------------------------- +static const char* const kIllegalArgumentException = + "java/lang/IllegalArgumentException"; struct fields_t { jfieldID context; }; static fields_t fields; - -// ---------------------------------------------------------------------------- +static Mutex sLock; class MyMediaScannerClient : public MediaScannerClient { @@ -56,31 +57,48 @@ public: mHandleStringTagMethodID(0), mSetMimeTypeMethodID(0) { - jclass mediaScannerClientInterface = env->FindClass("android/media/MediaScannerClient"); + LOGV("MyMediaScannerClient constructor"); + jclass mediaScannerClientInterface = + env->FindClass(kClassMediaScannerClient); + if (mediaScannerClientInterface == NULL) { - fprintf(stderr, "android/media/MediaScannerClient not found\n"); - } - else { - mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile", - "(Ljava/lang/String;JJZZ)V"); - mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag", - "(Ljava/lang/String;Ljava/lang/String;)V"); - mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType", - "(Ljava/lang/String;)V"); + LOGE("Class %s not found", kClassMediaScannerClient); + } else { + mScanFileMethodID = env->GetMethodID( + mediaScannerClientInterface, + "scanFile", + "(Ljava/lang/String;JJZZ)V"); + + mHandleStringTagMethodID = env->GetMethodID( + mediaScannerClientInterface, + "handleStringTag", + "(Ljava/lang/String;Ljava/lang/String;)V"); + + mSetMimeTypeMethodID = env->GetMethodID( + mediaScannerClientInterface, + "setMimeType", + "(Ljava/lang/String;)V"); } } - + virtual ~MyMediaScannerClient() { + LOGV("MyMediaScannerClient destructor"); mEnv->DeleteGlobalRef(mClient); } - - // returns true if it succeeded, false if an exception occured in the Java code + + // Returns true if it succeeded, false if an exception occured + // in the Java code virtual bool scanFile(const char* path, long long lastModified, long long fileSize, bool isDirectory, bool noMedia) { + LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)", + path, lastModified, fileSize, isDirectory); + jstring pathStr; - if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false; + if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { + return false; + } mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize, isDirectory, noMedia); @@ -89,25 +107,36 @@ public: return (!mEnv->ExceptionCheck()); } - // returns true if it succeeded, false if an exception occured in the Java code + // Returns true if it succeeded, false if an exception occured + // in the Java code virtual bool handleStringTag(const char* name, const char* value) { + LOGV("handleStringTag: name(%s) and value(%s)", name, value); jstring nameStr, valueStr; - if ((nameStr = mEnv->NewStringUTF(name)) == NULL) return false; - if ((valueStr = mEnv->NewStringUTF(value)) == NULL) return false; + if ((nameStr = mEnv->NewStringUTF(name)) == NULL) { + return false; + } + if ((valueStr = mEnv->NewStringUTF(value)) == NULL) { + return false; + } - mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr); + mEnv->CallVoidMethod( + mClient, mHandleStringTagMethodID, nameStr, valueStr); mEnv->DeleteLocalRef(nameStr); mEnv->DeleteLocalRef(valueStr); return (!mEnv->ExceptionCheck()); } - // returns true if it succeeded, false if an exception occured in the Java code + // Returns true if it succeeded, false if an exception occured + // in the Java code virtual bool setMimeType(const char* mimeType) { + LOGV("setMimeType: %s", mimeType); jstring mimeTypeStr; - if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) return false; + if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) { + return false; + } mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr); @@ -118,32 +147,49 @@ public: private: JNIEnv *mEnv; jobject mClient; - jmethodID mScanFileMethodID; - jmethodID mHandleStringTagMethodID; + jmethodID mScanFileMethodID; + jmethodID mHandleStringTagMethodID; jmethodID mSetMimeTypeMethodID; }; -// ---------------------------------------------------------------------------- - static bool ExceptionCheck(void* env) { + LOGV("ExceptionCheck"); return ((JNIEnv *)env)->ExceptionCheck(); } +// Call this method with sLock hold +static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz) +{ + return (MediaScanner *) env->GetIntField(thiz, fields.context); +} + +// Call this method with sLock hold +static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s) +{ + env->SetIntField(thiz, fields.context, (int)s); +} + static void -android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jobject client) +android_media_MediaScanner_processDirectory( + JNIEnv *env, jobject thiz, jstring path, jobject client) { - MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); + LOGV("processDirectory"); + Mutex::Autolock l(sLock); + MediaScanner *mp = getNativeScanner_l(env, thiz); + if (mp == NULL) { + jniThrowException(env, kRunTimeException, "No scanner available"); + return; + } if (path == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, kIllegalArgumentException, NULL); return; } const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } @@ -153,24 +199,34 @@ android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring p } static void -android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client) +android_media_MediaScanner_processFile( + JNIEnv *env, jobject thiz, jstring path, + jstring mimeType, jobject client) { - MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); + LOGV("processFile"); + + // Lock already hold by processDirectory + MediaScanner *mp = getNativeScanner_l(env, thiz); + if (mp == NULL) { + jniThrowException(env, kRunTimeException, "No scanner available"); + return; + } if (path == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, kIllegalArgumentException, NULL); return; } - + const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } - const char *mimeTypeStr = (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); + + const char *mimeTypeStr = + (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); if (mimeType && mimeTypeStr == NULL) { // Out of memory + // ReleaseStringUTFChars can be called with an exception pending. env->ReleaseStringUTFChars(path, pathStr); - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } @@ -183,17 +239,23 @@ android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, } static void -android_media_MediaScanner_setLocale(JNIEnv *env, jobject thiz, jstring locale) +android_media_MediaScanner_setLocale( + JNIEnv *env, jobject thiz, jstring locale) { - MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); + LOGV("setLocale"); + Mutex::Autolock l(sLock); + MediaScanner *mp = getNativeScanner_l(env, thiz); + if (mp == NULL) { + jniThrowException(env, kRunTimeException, "No scanner available"); + return; + } if (locale == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, kIllegalArgumentException, NULL); return; } const char *localeStr = env->GetStringUTFChars(locale, NULL); if (localeStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } mp->setLocale(localeStr); @@ -202,29 +264,36 @@ android_media_MediaScanner_setLocale(JNIEnv *env, jobject thiz, jstring locale) } static jbyteArray -android_media_MediaScanner_extractAlbumArt(JNIEnv *env, jobject thiz, jobject fileDescriptor) +android_media_MediaScanner_extractAlbumArt( + JNIEnv *env, jobject thiz, jobject fileDescriptor) { - MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); + LOGV("extractAlbumArt"); + Mutex::Autolock l(sLock); + MediaScanner *mp = getNativeScanner_l(env, thiz); + if (mp == NULL) { + jniThrowException(env, kRunTimeException, "No scanner available"); + return NULL; + } if (fileDescriptor == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, kIllegalArgumentException, NULL); return NULL; } - int fd = getParcelFileDescriptorFD(env, fileDescriptor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); char* data = mp->extractAlbumArt(fd); if (!data) { return NULL; } long len = *((long*)data); - + jbyteArray array = env->NewByteArray(len); if (array != NULL) { jbyte* bytes = env->GetByteArrayElements(array, NULL); memcpy(bytes, data + 4, len); env->ReleaseByteArrayElements(array, bytes, 0); } - + done: free(data); // if NewByteArray() returned NULL, an out-of-memory @@ -240,17 +309,14 @@ done: static void android_media_MediaScanner_native_init(JNIEnv *env) { - jclass clazz; - - clazz = env->FindClass("android/media/MediaScanner"); + LOGV("native_init"); + jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaScanner"); return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaScanner.mNativeContext"); return; } } @@ -258,10 +324,11 @@ android_media_MediaScanner_native_init(JNIEnv *env) static void android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) { + LOGV("native_setup"); MediaScanner *mp = new StagefrightMediaScanner; if (mp == NULL) { - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + jniThrowException(env, kRunTimeException, "Out of memory"); return; } @@ -271,38 +338,64 @@ android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) static void android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) { - MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); - - //printf("##### android_media_MediaScanner_native_finalize: ctx=0x%p\n", ctx); - - if (mp == 0) + LOGV("native_finalize"); + Mutex::Autolock l(sLock); + MediaScanner *mp = getNativeScanner_l(env, thiz); + if (mp == 0) { return; - + } delete mp; + setNativeScanner_l(env, thiz, 0); } -// ---------------------------------------------------------------------------- - static JNINativeMethod gMethods[] = { - {"processDirectory", "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", - (void *)android_media_MediaScanner_processDirectory}, - {"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", - (void *)android_media_MediaScanner_processFile}, - {"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale}, - {"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt}, - {"native_init", "()V", (void *)android_media_MediaScanner_native_init}, - {"native_setup", "()V", (void *)android_media_MediaScanner_native_setup}, - {"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize}, -}; + { + "processDirectory", + "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", + (void *)android_media_MediaScanner_processDirectory + }, + + { + "processFile", + "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", + (void *)android_media_MediaScanner_processFile + }, + + { + "setLocale", + "(Ljava/lang/String;)V", + (void *)android_media_MediaScanner_setLocale + }, + + { + "extractAlbumArt", + "(Ljava/io/FileDescriptor;)[B", + (void *)android_media_MediaScanner_extractAlbumArt + }, -static const char* const kClassPathName = "android/media/MediaScanner"; + { + "native_init", + "()V", + (void *)android_media_MediaScanner_native_init + }, + + { + "native_setup", + "()V", + (void *)android_media_MediaScanner_native_setup + }, + + { + "native_finalize", + "()V", + (void *)android_media_MediaScanner_native_finalize + }, +}; // This function only registers the native methods, and is called from // JNI_OnLoad in android_media_MediaPlayer.cpp int register_android_media_MediaScanner(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, - "android/media/MediaScanner", gMethods, NELEM(gMethods)); + kClassMediaScanner, gMethods, NELEM(gMethods)); } - - diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp index d965d9a..d5a4a17 100644 --- a/media/jni/android_media_ResampleInputStream.cpp +++ b/media/jni/android_media_ResampleInputStream.cpp @@ -36,19 +36,6 @@ using namespace android; -// -// helper function to throw an exception -// -static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { - if (jclass cls = env->FindClass(ex)) { - char msg[1000]; - sprintf(msg, fmt, data); - env->ThrowNew(cls, msg); - env->DeleteLocalRef(cls); - } -} - - #define FIR_COEF(coef) (short)(0x10000 * coef) static const short fir21[] = { FIR_COEF(-0.006965742326), @@ -90,18 +77,18 @@ static void android_media_ResampleInputStream_fir21(JNIEnv *env, jclass clazz, jbyteArray jIn, jint jInOffset, jbyteArray jOut, jint jOutOffset, jint jNpoints) { - + // safety first! if (nFir21 + jNpoints * 2 > BUF_SIZE) { - throwException(env, "java/lang/IllegalArgumentException", + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "FIR+data too long %d", nFir21 + jNpoints); return; } - + // get input data short in[BUF_SIZE]; env->GetByteArrayRegion(jIn, jInOffset, (jNpoints * 2 + nFir21 - 1) * 2, (jbyte*)in); - + // compute filter short out[BUF_SIZE]; for (int i = 0; i < jNpoints; i++) { @@ -132,5 +119,3 @@ int register_android_media_ResampleInputStream(JNIEnv *env) return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } - - diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp new file mode 100644 index 0000000..27e46a4 --- /dev/null +++ b/media/jni/android_media_Utils.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "AndroidMediaUtils" + +#include <utils/Log.h> +#include "android_media_Utils.h" + +namespace android { + +bool ConvertKeyValueArraysToKeyedVector( + JNIEnv *env, jobjectArray keys, jobjectArray values, + KeyedVector<String8, String8>* keyedVector) { + + int nKeyValuePairs = 0; + bool failed = false; + if (keys != NULL && values != NULL) { + nKeyValuePairs = env->GetArrayLength(keys); + failed = (nKeyValuePairs != env->GetArrayLength(values)); + } + + if (!failed) { + failed = ((keys != NULL && values == NULL) || + (keys == NULL && values != NULL)); + } + + if (failed) { + LOGE("keys and values arrays have different length"); + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + for (int i = 0; i < nKeyValuePairs; ++i) { + // No need to check on the ArrayIndexOutOfBoundsException, since + // it won't happen here. + jstring key = (jstring) env->GetObjectArrayElement(keys, i); + jstring value = (jstring) env->GetObjectArrayElement(values, i); + + const char* keyStr = env->GetStringUTFChars(key, NULL); + if (!keyStr) { // OutOfMemoryError + return false; + } + + const char* valueStr = env->GetStringUTFChars(value, NULL); + if (!valueStr) { // OutOfMemoryError + env->ReleaseStringUTFChars(key, keyStr); + return false; + } + + keyedVector->add(String8(keyStr), String8(valueStr)); + + env->ReleaseStringUTFChars(key, keyStr); + env->ReleaseStringUTFChars(value, valueStr); + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } + return true; +} + +} // namespace android + diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h new file mode 100644 index 0000000..a2c155a --- /dev/null +++ b/media/jni/android_media_Utils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_MEDIA_UTILS_H_ +#define _ANDROID_MEDIA_UTILS_H_ + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <utils/KeyedVector.h> +#include <utils/String8.h> + +namespace android { + +/** + * Returns true if the conversion is successful; otherwise, false. + */ +bool ConvertKeyValueArraysToKeyedVector( + JNIEnv *env, jobjectArray keys, jobjectArray values, + KeyedVector<String8, String8>* vector); + +}; // namespace android + +#endif // _ANDROID_MEDIA_UTILS_H_ diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index b78af44..0f3c063 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -189,28 +189,23 @@ MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client) mLongBuffer(NULL), mStringBuffer(NULL) { - jintArray intArray; - jlongArray longArray; - jcharArray charArray; - // create buffers for out arguments // we don't need to be thread-safe so this is OK - intArray = env->NewIntArray(3); - if (!intArray) - goto out_of_memory; + jintArray intArray = env->NewIntArray(3); + if (!intArray) { + return; // Already threw. + } mIntBuffer = (jintArray)env->NewGlobalRef(intArray); - longArray = env->NewLongArray(2); - if (!longArray) - goto out_of_memory; + jlongArray longArray = env->NewLongArray(2); + if (!longArray) { + return; // Already threw. + } mLongBuffer = (jlongArray)env->NewGlobalRef(longArray); - charArray = env->NewCharArray(256); - if (!charArray) - goto out_of_memory; + jcharArray charArray = env->NewCharArray(256); + if (!charArray) { + return; // Already threw. + } mStringBuffer = (jcharArray)env->NewGlobalRef(charArray); - return; - -out_of_memory: - env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL); } void MyMtpDatabase::cleanup(JNIEnv *env) { @@ -434,6 +429,9 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); if (stringValue) { const char* str = env->GetStringUTFChars(stringValue, NULL); + if (str == NULL) { + return MTP_RESPONSE_GENERAL_ERROR; + } packet.putString(str); env->ReleaseStringUTFChars(stringValue, str); } else { @@ -865,7 +863,7 @@ MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, outFileLength = longValues[0]; outFormat = longValues[1]; env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); - + checkAndClearExceptionFromCallback(env, __FUNCTION__); return result; } diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index f5fcb4e..40bbaa3 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -110,6 +110,10 @@ android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint f #ifdef HAVE_ANDROID_OS LOGD("open\n"); const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + if (deviceNameStr == NULL) { + return false; + } + MtpDevice* device = MtpDevice::open(deviceNameStr, fd); env->ReleaseStringUTFChars(deviceName, deviceNameStr); @@ -426,12 +430,16 @@ android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jst MtpDevice* device = get_device_from_object(env, thiz); if (device) { const char *destPathStr = env->GetStringUTFChars(dest_path, NULL); + if (destPathStr == NULL) { + return false; + } + bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664); env->ReleaseStringUTFChars(dest_path, destPathStr); return result; } #endif - return NULL; + return false; } // ---------------------------------------------------------------------------- diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 4f6bb15..4d84cb7 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -98,7 +98,7 @@ public: void removeStorage(MtpStorageID id) { MtpStorage* storage = mServer->getStorage(id); if (storage) { - for (int i = 0; i < mStorageList.size(); i++) { + for (size_t i = 0; i < mStorageList.size(); i++) { if (mStorageList[i] == storage) { mStorageList.removeAt(i); break; @@ -123,7 +123,7 @@ public: (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP)); mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775); - for (int i = 0; i < mStorageList.size(); i++) { + for (size_t i = 0; i < mStorageList.size(); i++) { mServer->addStorage(mStorageList[i]); } } else { @@ -249,14 +249,17 @@ android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable); const char *pathStr = env->GetStringUTFChars(path, NULL); - const char *descriptionStr = env->GetStringUTFChars(description, NULL); - - MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, - reserveSpace, removable); - thread->addStorage(storage); - - env->ReleaseStringUTFChars(path, pathStr); - env->ReleaseStringUTFChars(description, descriptionStr); + if (pathStr != NULL) { + const char *descriptionStr = env->GetStringUTFChars(description, NULL); + if (descriptionStr != NULL) { + MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace, removable); + thread->addStorage(storage); + env->ReleaseStringUTFChars(path, pathStr); + env->ReleaseStringUTFChars(description, descriptionStr); + } else { + env->ReleaseStringUTFChars(path, pathStr); + } + } } else { LOGE("MtpThread is null in add_storage"); } diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index cb2f0f9..e71e727 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -570,12 +570,11 @@ setParameter_Exit: static jint android_media_AudioEffect_native_getParameter(JNIEnv *env, - jobject thiz, int psize, jbyteArray pJavaParam, - jintArray pJavaValueSize, jbyteArray pJavaValue) { + jobject thiz, jint psize, jbyteArray pJavaParam, + jint vsize, jbyteArray pJavaValue) { // retrieve the AudioEffect object jbyte* lpParam = NULL; jbyte* lpValue = NULL; - jbyte* lpValueSize = NULL; jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; effect_param_t *p; int voffset; @@ -589,7 +588,7 @@ android_media_AudioEffect_native_getParameter(JNIEnv *env, return AUDIOEFFECT_ERROR_NO_INIT; } - if (psize == 0 || pJavaValueSize == NULL || pJavaParam == NULL || pJavaValue == NULL) { + if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) { return AUDIOEFFECT_ERROR_BAD_VALUE; } @@ -607,26 +606,18 @@ android_media_AudioEffect_native_getParameter(JNIEnv *env, goto getParameter_Exit; } - // get the pointer for the value size from the java array - lpValueSize = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValueSize, NULL); - if (lpValueSize == NULL) { - LOGE("getParameter: Error retrieving value size pointer"); - goto getParameter_Exit; - } - voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int); - p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset - + lpValueSize[0]); + p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize); memcpy(p->data, lpParam, psize); p->psize = psize; - p->vsize = lpValueSize[0]; + p->vsize = vsize; lStatus = lpAudioEffect->getParameter(p); if (lStatus == NO_ERROR) { lStatus = p->status; if (lStatus == NO_ERROR) { memcpy(lpValue, p->data + voffset, p->vsize); - lpValueSize[0] = p->vsize; + vsize = p->vsize; } } @@ -640,19 +631,18 @@ getParameter_Exit: if (lpValue != NULL) { env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0); } - if (lpValueSize != NULL) { - env->ReleasePrimitiveArrayCritical(pJavaValueSize, lpValueSize, 0); - } + if (lStatus == NO_ERROR) { + return vsize; + } return translateError(lStatus); } static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz, - jint cmdCode, jint cmdSize, jbyteArray jCmdData, jintArray jReplySize, + jint cmdCode, jint cmdSize, jbyteArray jCmdData, jint replySize, jbyteArray jReplyData) { jbyte* pCmdData = NULL; jbyte* pReplyData = NULL; - jint* pReplySize = NULL; jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; // retrieve the AudioEffect object @@ -665,7 +655,7 @@ static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz, return AUDIOEFFECT_ERROR_NO_INIT; } - if ((cmdSize != 0 && jCmdData == NULL) || (jReplySize != NULL && jReplyData == NULL)) { + if ((cmdSize != 0 && jCmdData == NULL) || (replySize != 0 && jReplyData == NULL)) { return AUDIOEFFECT_ERROR_BAD_VALUE; } @@ -678,17 +668,8 @@ static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz, } } - // get the pointer for the reply size from the java array - if (jReplySize != NULL) { - pReplySize = (jint *) env->GetPrimitiveArrayCritical(jReplySize, NULL); - if (pReplySize == NULL) { - LOGE("setParameter: Error retrieving reply pointer"); - goto command_Exit; - } - } - // get the pointer for the reply from the java array - if (pReplySize != NULL && pReplySize[0] != 0 && jReplyData != NULL) { + if (replySize != 0 && jReplyData != NULL) { pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL); if (pReplyData == NULL) { LOGE("setParameter: Error retrieving reply pointer"); @@ -699,7 +680,7 @@ static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz, lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode, (uint32_t)cmdSize, pCmdData, - (uint32_t *)pReplySize, + (uint32_t *)&replySize, pReplyData)); command_Exit: @@ -710,10 +691,10 @@ command_Exit: if (pReplyData != NULL) { env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0); } - if (pReplySize != NULL) { - env->ReleasePrimitiveArrayCritical(jReplySize, pReplySize, 0); - } + if (lStatus == NO_ERROR) { + return replySize; + } return lStatus; } @@ -803,8 +784,8 @@ static JNINativeMethod gMethods[] = { {"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled}, {"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl}, {"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter}, - {"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter}, - {"native_command", "(II[B[I[B)I", (void *)android_media_AudioEffect_native_command}, + {"native_getParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_getParameter}, + {"native_command", "(II[BI[B)I", (void *)android_media_AudioEffect_native_command}, {"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects}, }; diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk index 6a7116c..69cfe8c 100755 --- a/media/jni/mediaeditor/Android.mk +++ b/media/jni/mediaeditor/Android.mk @@ -55,7 +55,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libstagefright \ libstagefright_omx \ - libsurfaceflinger_client \ + libgui \ libvideoeditorplayer @@ -68,9 +68,6 @@ LOCAL_CFLAGS += \ -DUSE_STAGEFRIGHT_READERS \ -DUSE_STAGEFRIGHT_3GPP_READER - -LOCAL_LDFLAGS += -fuse-ld=bfd - LOCAL_STATIC_LIBRARIES := \ libvideoeditor_core \ libstagefright_color_conversion \ @@ -82,10 +79,6 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_MODULE:= libvideoeditor_jni -# Don't prelink this library. For more efficient code, you may want -# to add this library to the prelink map and set this to true. -LOCAL_PRELINK_MODULE := false - LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) diff --git a/media/jni/mediaeditor/VideoBrowserInternal.h b/media/jni/mediaeditor/VideoBrowserInternal.h index ed63129..3cfb6b9 100755 --- a/media/jni/mediaeditor/VideoBrowserInternal.h +++ b/media/jni/mediaeditor/VideoBrowserInternal.h @@ -67,7 +67,7 @@ { \ if (M4OSA_NULL != p) \ { \ - M4OSA_free((M4OSA_MemAddr32)p) ; \ + free(p) ; \ p = M4OSA_NULL ; \ } \ } diff --git a/media/jni/mediaeditor/VideoBrowserMain.c b/media/jni/mediaeditor/VideoBrowserMain.c index caf4497..6ef688d 100755 --- a/media/jni/mediaeditor/VideoBrowserMain.c +++ b/media/jni/mediaeditor/VideoBrowserMain.c @@ -73,7 +73,7 @@ M4OSA_ERR videoBrowserSetWindow( if (pC->m_frameColorType == VideoBrowser_kGB565) { pC->m_outputPlane[0].u_stride = pC->m_outputPlane[0].u_width << 1; - pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)M4OSA_malloc( + pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc( pC->m_outputPlane[0].u_stride * pC->m_outputPlane[0].u_height, VIDEOBROWSER, (M4OSA_Char *)"output plane"); @@ -154,20 +154,20 @@ M4OSA_ERR videoBrowserCreate( CHECK_PTR(videoBrowserCreate, pURL, err, M4ERR_PARAMETER); /*--- Create context ---*/ - pContext = (VideoBrowserContext*)M4OSA_malloc( + pContext = (VideoBrowserContext*)M4OSA_32bitAlignedMalloc( sizeof(VideoBrowserContext), VIDEOBROWSER, (M4OSA_Char*)"Video browser context"); CHECK_PTR(videoBrowserCreate, pContext,err, M4ERR_ALLOC); - M4OSA_memset((M4OSA_MemAddr8)pContext, sizeof(VideoBrowserContext), 0); + memset((void *)pContext, 0,sizeof(VideoBrowserContext)); /*--- Initialize the context parameters ---*/ pContext->m_state = VideoBrowser_kVBCreating ; pContext->m_frameColorType = clrType; /*--- Copy the file reader functions ---*/ - M4OSA_memcpy((M4OSA_MemAddr8)&pContext->m_fileReadPtr, - (M4OSA_MemAddr8)ptrF, + memcpy((void *)&pContext->m_fileReadPtr, + (void *)ptrF, sizeof(M4OSA_FileReadPointer)) ; /* PR#SP00013 DGR bug 13 : first frame is not visible */ diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp index ea73e11..5696433 100755 --- a/media/jni/mediaeditor/VideoEditorClasses.cpp +++ b/media/jni/mediaeditor/VideoEditorClasses.cpp @@ -28,7 +28,6 @@ extern "C" { #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> #include <M4OSA_Debug.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4VSS3GPP_API.h> #include <M4xVSS_API.h> @@ -2465,7 +2464,7 @@ videoEditClasses_getEffectSettings( if (pSettings->xVSS.pFramingFilePath != M4OSA_NULL) { pSettings->xVSS.pFramingBuffer = - (M4VIFI_ImagePlane *)M4OSA_malloc(sizeof(M4VIFI_ImagePlane), + (M4VIFI_ImagePlane *)M4OSA_32bitAlignedMalloc(sizeof(M4VIFI_ImagePlane), 0x00,(M4OSA_Char *)"framing buffer"); } diff --git a/media/jni/mediaeditor/VideoEditorJava.cpp b/media/jni/mediaeditor/VideoEditorJava.cpp index 884256a..13f6350 100755 --- a/media/jni/mediaeditor/VideoEditorJava.cpp +++ b/media/jni/mediaeditor/VideoEditorJava.cpp @@ -339,7 +339,7 @@ videoEditJava_getString( { // Determine the length of the path // (add one extra character for the zero terminator). - length = M4OSA_chrLength(pLocal) + 1; + length = strlen((const char *)pLocal) + 1; // Allocate memory for the string. pString = videoEditOsal_alloc(pResult, pEnv, length, "String"); diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index 11e2a5e..c95a0c2 100755 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -41,15 +41,12 @@ extern "C" { #include <M4OSA_FileCommon.h> #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> -#include <M4OSA_FileExtra.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4xVSS_API.h> #include <M4VSS3GPP_ErrorCodes.h> #include <M4MCS_API.h> #include <M4MCS_ErrorCodes.h> -#include <M4MDP_API.h> #include <M4READER_Common.h> #include <M4WRITER_common.h> }; @@ -416,7 +413,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, LOGV("MSG_TYPE_OVERLAY_UPDATE"); if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -424,7 +421,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, strlen((const char*)pContext->pEditSettings->Effects[overlayEffectIndex].xVSS.pFramingFilePath); pContext->mOverlayFileName = - (char*)M4OSA_malloc(overlayFileNameLen+1, + (char*)M4OSA_32bitAlignedMalloc(overlayFileNameLen+1, M4VS, (M4OSA_Char*)"videoEdito JNI overlayFile"); if (pContext->mOverlayFileName != NULL) { strncpy (pContext->mOverlayFileName, @@ -454,7 +451,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, case MSG_TYPE_OVERLAY_CLEAR: isSendProgress = false; if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -504,7 +501,7 @@ static int videoEditor_stopPreview(JNIEnv* pEnv, lastProgressTimeMs = pContext->mPreviewController->stopPreview(); if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -750,7 +747,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, framesizeYuv = width * height * 1.5; - pixelArray = (M4VIFI_UInt8 *)M4OSA_malloc(framesizeYuv, M4VS, + pixelArray = (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(framesizeYuv, M4VS, (M4OSA_Char*)"videoEditor pixelArray"); if (pixelArray == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", @@ -768,7 +765,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, ClipProperties.uiVideoHeight, &tnTimeMs); if (result != M4NO_ERROR) { - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); ThumbnailClose(tnContext); return -1; } @@ -780,7 +777,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, { M4OSA_Context fileContext; M4OSA_Char* fileName = (M4OSA_Char*)"/mnt/sdcard/FirstRGB565.raw"; - M4OSA_fileExtraDelete((const M4OSA_Char *)fileName); + remove((const char *)fileName); M4OSA_fileWriteOpen(&fileContext, (M4OSA_Void*) fileName,\ M4OSA_kFileWrite|M4OSA_kFileCreate); M4OSA_fileWriteData(fileContext, (M4OSA_MemAddr8) pixelArray, @@ -792,12 +789,12 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, /** * Allocate output YUV planes */ - yuvPlane = (M4VIFI_ImagePlane*)M4OSA_malloc(3*sizeof(M4VIFI_ImagePlane), M4VS, + yuvPlane = (M4VIFI_ImagePlane*)M4OSA_32bitAlignedMalloc(3*sizeof(M4VIFI_ImagePlane), M4VS, (M4OSA_Char*)"videoEditor_renderPreviewFrame Output plane YUV"); if (yuvPlane == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_renderPreviewFrame() malloc error for yuv plane"); - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); pMessage = videoEditJava_getErrorName(M4ERR_ALLOC); jniThrowException(pEnv, "java/lang/RuntimeException", pMessage); return -1; @@ -826,7 +823,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, { M4OSA_Context fileContext; M4OSA_Char* fileName = (M4OSA_Char*)"/mnt/sdcard/ConvertedYuv.yuv"; - M4OSA_fileExtraDelete((const M4OSA_Char *)fileName); + remove((const char *)fileName); M4OSA_fileWriteOpen(&fileContext, (M4OSA_Void*) fileName,\ M4OSA_kFileWrite|M4OSA_kFileCreate); M4OSA_fileWriteData(fileContext, @@ -902,10 +899,10 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType ==\ /*M4VIDEOEDITING_kFileType_JPG */ M4VIDEOEDITING_kFileType_ARGB8888) { - M4OSA_free((M4OSA_MemAddr32)frameStr.pBuffer); + free(frameStr.pBuffer); } else { - M4OSA_free((M4OSA_MemAddr32)yuvPlane[0].pac_data); - M4OSA_free((M4OSA_MemAddr32)yuvPlane); + free(yuvPlane[0].pac_data); + free(yuvPlane); } return tnTimeMs; } @@ -981,7 +978,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, framesizeYuv = ((frameWidth)*(frameHeight)*1.5); - pixelArray = (M4VIFI_UInt8 *)M4OSA_malloc(framesizeYuv, M4VS,\ + pixelArray = (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(framesizeYuv, M4VS,\ (M4OSA_Char*)"videoEditor pixelArray"); if (pixelArray == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", @@ -996,7 +993,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, frameWidth, frameHeight, &timeMs); if (result != M4NO_ERROR) { - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); ThumbnailClose(tnContext); return fromMs; } @@ -1066,7 +1063,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, (M4NO_ERROR != result), result); /* free the pixelArray and yuvPlane[0].pac_data */ - M4OSA_free((M4OSA_MemAddr32)yuvPlane[0].pac_data); + free(yuvPlane[0].pac_data); ThumbnailClose(tnContext); @@ -1148,7 +1145,7 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "M4MCS_init()"); - pOutputParams = (M4MCS_OutputParams *)M4OSA_malloc( + pOutputParams = (M4MCS_OutputParams *)M4OSA_32bitAlignedMalloc( sizeof(M4MCS_OutputParams),0x00, (M4OSA_Char *)"M4MCS_OutputParams"); videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv, @@ -1158,14 +1155,14 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, return M4ERR_ALLOC; } - pEncodingParams = (M4MCS_EncodingParams *)M4OSA_malloc( + pEncodingParams = (M4MCS_EncodingParams *)M4OSA_32bitAlignedMalloc( sizeof(M4MCS_EncodingParams),0x00, (M4OSA_Char *)"M4MCS_EncodingParams"); videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv, (M4OSA_NULL == pEncodingParams), "not initialized"); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return M4ERR_ALLOC; } @@ -1179,34 +1176,34 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, (M4OSA_NULL == mcsContext), "not initialized"); if(needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } // generate the path for temp 3gp output file - pTemp3gpFilePath = (M4OSA_Char*) M4OSA_malloc ( - (M4OSA_chrLength((M4OSA_Char*)pContext->initParams.pTempPath) - + M4OSA_chrLength ((M4OSA_Char*)TEMP_MCS_OUT_FILE_PATH)) + 1 /* for null termination */ , 0x0, - (M4OSA_Char*) "Malloc for temp 3gp file"); - if ( pTemp3gpFilePath != M4OSA_NULL ) + pTemp3gpFilePath = (M4OSA_Char*) M4OSA_32bitAlignedMalloc ( + (strlen((const char*)pContext->initParams.pTempPath) + + strlen((const char*)TEMP_MCS_OUT_FILE_PATH)) + 1 /* for null termination */ , 0x0, + (M4OSA_Char*)"Malloc for temp 3gp file"); + if (pTemp3gpFilePath != M4OSA_NULL) { - M4OSA_memset(pTemp3gpFilePath , - M4OSA_chrLength((M4OSA_Char*)pContext->initParams.pTempPath) - + M4OSA_chrLength((M4OSA_Char*)TEMP_MCS_OUT_FILE_PATH) + 1, 0); - M4OSA_chrNCat ( (M4OSA_Char*)pTemp3gpFilePath, - (M4OSA_Char*)pContext->initParams.pTempPath , - M4OSA_chrLength ((M4OSA_Char*)pContext->initParams.pTempPath)); - M4OSA_chrNCat ( pTemp3gpFilePath , (M4OSA_Char*)TEMP_MCS_OUT_FILE_PATH, - M4OSA_chrLength ((M4OSA_Char*)TEMP_MCS_OUT_FILE_PATH)); + memset((void *)pTemp3gpFilePath ,0, + strlen((const char*)pContext->initParams.pTempPath) + + strlen((const char*)TEMP_MCS_OUT_FILE_PATH) + 1); + strncat((char *)pTemp3gpFilePath, + (const char *)pContext->initParams.pTempPath , + (size_t) ((M4OSA_Char*)pContext->initParams.pTempPath)); + strncat((char *)pTemp3gpFilePath , (const char *)TEMP_MCS_OUT_FILE_PATH, + (size_t)strlen ((const char*)TEMP_MCS_OUT_FILE_PATH)); } else { M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return M4ERR_ALLOC; } @@ -1219,7 +1216,7 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, pInputFileType = (M4VIDEOEDITING_FileType)pContext->mAudioSettings->fileType; VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR", "TEMP_MCS_OUT_FILE_PATH len %d", - M4OSA_chrLength ((M4OSA_Char*)TEMP_MCS_OUT_FILE_PATH)); + strlen ((const char*)TEMP_MCS_OUT_FILE_PATH)); VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR", "pTemp3gpFilePath %s", pOutputFile); @@ -1231,12 +1228,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if(needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1281,12 +1278,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1311,12 +1308,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1327,12 +1324,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1379,12 +1376,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4MCS_WAR_TRANSCODING_DONE != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1395,17 +1392,17 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, (M4NO_ERROR != result), result); //pContext->mAudioSettings->pFile = pOutputParams->pOutputPCMfile; - M4OSA_fileExtraDelete((const M4OSA_Char *) pTemp3gpFilePath); + remove((const char *) pTemp3gpFilePath); VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_generateAudio() EXIT "); if (pTemp3gpFilePath != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); } if (pOutputParams != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); } if(pEncodingParams != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); } return result; } @@ -1420,7 +1417,7 @@ static int removeAlphafromRGB8888 ( LOGV("removeAlphafromRGB8888: width %d", pFramingCtx->width); - M4OSA_UInt8 *pTmpData = (M4OSA_UInt8*) M4OSA_malloc(frameSize_argb, M4VS, (M4OSA_Char*)"Image argb data"); + M4OSA_UInt8 *pTmpData = (M4OSA_UInt8*) M4OSA_32bitAlignedMalloc(frameSize_argb, M4VS, (M4OSA_Char*)"Image argb data"); if (pTmpData == M4OSA_NULL) { LOGE("Failed to allocate memory for Image clip"); return M4ERR_ALLOC; @@ -1433,7 +1430,7 @@ static int removeAlphafromRGB8888 ( if ((lerr != M4NO_ERROR) || (lImageFileFp == M4OSA_NULL)) { LOGE("removeAlphafromRGB8888: Can not open the file "); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4ERR_FILE_NOT_FOUND; } @@ -1443,22 +1440,22 @@ static int removeAlphafromRGB8888 ( { LOGE("removeAlphafromRGB8888: can not read the data "); M4OSA_fileReadClose(lImageFileFp); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return lerr; } M4OSA_fileReadClose(lImageFileFp); M4OSA_UInt32 frameSize = (pFramingCtx->width * pFramingCtx->height * 3); //Size of RGB 888 data. - pFramingCtx->FramingRgb = (M4VIFI_ImagePlane*)M4OSA_malloc( + pFramingCtx->FramingRgb = (M4VIFI_ImagePlane*)M4OSA_32bitAlignedMalloc( sizeof(M4VIFI_ImagePlane), M4VS, (M4OSA_Char*)"Image clip RGB888 data"); - pFramingCtx->FramingRgb->pac_data = (M4VIFI_UInt8*)M4OSA_malloc( + pFramingCtx->FramingRgb->pac_data = (M4VIFI_UInt8*)M4OSA_32bitAlignedMalloc( frameSize, M4VS, (M4OSA_Char*)"Image clip RGB888 data"); if (pFramingCtx->FramingRgb == M4OSA_NULL) { LOGE("Failed to allocate memory for Image clip"); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4ERR_ALLOC; } @@ -1468,7 +1465,7 @@ static int removeAlphafromRGB8888 ( pFramingCtx->FramingRgb->pac_data[j] = pTmpData[i]; j++; } - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4NO_ERROR; } @@ -1566,7 +1563,7 @@ videoEditor_populateSettings( { if (pContext->pEditSettings->Effects[j].xVSS.pFramingFilePath != M4OSA_NULL) { if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[j].xVSS.pFramingBuffer); pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer = M4OSA_NULL; } @@ -1613,7 +1610,7 @@ videoEditor_populateSettings( if (pContext->pEditSettings->nbEffects > 0) { pOverlayIndex - = (int*) M4OSA_malloc(pContext->pEditSettings->nbEffects * sizeof(int), 0, + = (int*) M4OSA_32bitAlignedMalloc(pContext->pEditSettings->nbEffects * sizeof(int), 0, (M4OSA_Char*)"pOverlayIndex"); if (pOverlayIndex == M4OSA_NULL) { videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, @@ -1633,7 +1630,7 @@ videoEditor_populateSettings( M4xVSS_FramingStruct *aFramingCtx = M4OSA_NULL; aFramingCtx - = (M4xVSS_FramingStruct*)M4OSA_malloc(sizeof(M4xVSS_FramingStruct), M4VS, + = (M4xVSS_FramingStruct*)M4OSA_32bitAlignedMalloc(sizeof(M4xVSS_FramingStruct), M4VS, (M4OSA_Char*)"M4xVSS_internalDecodeGIF: Context of the framing effect"); if (aFramingCtx == M4OSA_NULL) { @@ -1671,7 +1668,7 @@ videoEditor_populateSettings( if (needToBeLoaded == false) { M4OSA_TRACE1_1("M4xVSS_internalConvertARGB888toYUV420_FrammingEffect returned 0x%x", result); if (aFramingCtx != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx); + free(aFramingCtx); aFramingCtx = M4OSA_NULL; } goto videoEditor_populateSettings_cleanup; @@ -1699,7 +1696,7 @@ videoEditor_populateSettings( //for RGB565 pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->u_topleft = 0; pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->pac_data = - (M4VIFI_UInt8 *)M4OSA_malloc(width*height*2, + (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(width*height*2, 0x00,(M4OSA_Char *)"pac_data buffer"); if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->pac_data == M4OSA_NULL) { @@ -1709,9 +1706,9 @@ videoEditor_populateSettings( goto videoEditor_populateSettings_cleanup; } - M4OSA_memcpy((M4OSA_Int8 *)&pContext->pEditSettings->\ + memcpy((void *)&pContext->pEditSettings->\ Effects[j].xVSS.pFramingBuffer->\ - pac_data[0],(M4OSA_Int8 *)&aFramingCtx->FramingRgb->pac_data[0],(width*height*2)); + pac_data[0],(void *)&aFramingCtx->FramingRgb->pac_data[0],(width*height*2)); //As of now rgb type is 565 pContext->pEditSettings->Effects[j].xVSS.rgbType = @@ -1720,31 +1717,31 @@ videoEditor_populateSettings( if (aFramingCtx->FramingYuv != M4OSA_NULL ) { if (aFramingCtx->FramingYuv[0].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[0].pac_data); + free(aFramingCtx->FramingYuv[0].pac_data); aFramingCtx->FramingYuv[0].pac_data = M4OSA_NULL; } if (aFramingCtx->FramingYuv[1].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[1].pac_data); + free(aFramingCtx->FramingYuv[1].pac_data); aFramingCtx->FramingYuv[1].pac_data = M4OSA_NULL; } if (aFramingCtx->FramingYuv[2].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[2].pac_data); + free(aFramingCtx->FramingYuv[2].pac_data); aFramingCtx->FramingYuv[2].pac_data = M4OSA_NULL; } - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv); + free(aFramingCtx->FramingYuv); aFramingCtx->FramingYuv = M4OSA_NULL; } if (aFramingCtx->FramingRgb->pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingRgb->pac_data); + free(aFramingCtx->FramingRgb->pac_data); aFramingCtx->FramingRgb->pac_data = M4OSA_NULL; } if (aFramingCtx->FramingRgb != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingRgb); + free(aFramingCtx->FramingRgb); aFramingCtx->FramingRgb = M4OSA_NULL; } if (aFramingCtx != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx); + free(aFramingCtx); aFramingCtx = M4OSA_NULL; } nbOverlays++; @@ -1775,11 +1772,11 @@ videoEditor_populateSettings( /* free previous allocations , if any */ if (pContext->mAudioSettings != M4OSA_NULL) { if (pContext->mAudioSettings->pFile != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + free(pContext->mAudioSettings->pFile); pContext->mAudioSettings->pFile = M4OSA_NULL; } if (pContext->mAudioSettings->pPCMFilePath != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + free(pContext->mAudioSettings->pPCMFilePath); pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; } } @@ -1850,12 +1847,12 @@ videoEditor_populateSettings( strPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPath, M4OSA_NULL); if (pTempChar != NULL) { - pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_malloc( + pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_32bitAlignedMalloc( (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, (M4OSA_Char*)"strPath allocation " ); if (pContext->mAudioSettings->pFile != M4OSA_NULL) { - M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pFile , - (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + memcpy((void *)pContext->mAudioSettings->pFile , + (void *)pTempChar , strlen((const char*)pTempChar)); ((M4OSA_Int8 *)(pContext->mAudioSettings->pFile))[strlen((const char*)pTempChar)] = '\0'; pEnv->ReleaseStringUTFChars(strPath,(const char *)pTempChar); } else { @@ -1875,12 +1872,12 @@ videoEditor_populateSettings( strPCMPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPCMPath, M4OSA_NULL); if (pTempChar != NULL) { - pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_malloc( + pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_32bitAlignedMalloc( (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, (M4OSA_Char*)"strPCMPath allocation " ); if (pContext->mAudioSettings->pPCMFilePath != M4OSA_NULL) { - M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pPCMFilePath , - (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + memcpy((void *)pContext->mAudioSettings->pPCMFilePath , + (void *)pTempChar , strlen((const char*)pTempChar)); ((M4OSA_Int8 *)(pContext->mAudioSettings->pPCMFilePath))[strlen((const char*)pTempChar)] = '\0'; pEnv->ReleaseStringUTFChars(strPCMPath,(const char *)pTempChar); } else { @@ -1986,7 +1983,7 @@ videoEditor_populateSettings_cleanup: { if (pContext->pEditSettings->Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data != \ M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data); pContext->pEditSettings->\ Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data = M4OSA_NULL; @@ -1999,7 +1996,7 @@ videoEditor_populateSettings_cleanup: { if (pContext->pEditSettings->Effects[j].xVSS.pFramingFilePath != M4OSA_NULL) { if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[j].xVSS.pFramingBuffer); pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer = M4OSA_NULL; } @@ -2009,7 +2006,7 @@ videoEditor_populateSettings_cleanup: if (pOverlayIndex != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pOverlayIndex); + free(pOverlayIndex); pOverlayIndex = M4OSA_NULL; } return; @@ -2237,7 +2234,7 @@ videoEditor_toUTF8Fct( // Determine the length of the input buffer. if (M4OSA_NULL != pBufferIn) { - length = M4OSA_chrLength((M4OSA_Char *)pBufferIn); + length = strlen((const char *)pBufferIn); } // Check if the output buffer is large enough to hold the input buffer. @@ -2282,7 +2279,7 @@ videoEditor_fromUTF8Fct( // Determine the length of the input buffer. if (M4OSA_NULL != pBufferIn) { - length = M4OSA_chrLength((M4OSA_Char *)pBufferIn); + length = strlen((const char *)pBufferIn); } // Check if the output buffer is large enough to hold the input buffer. @@ -2498,14 +2495,15 @@ videoEditor_init( (M4OSA_Char *)videoEditJava_getString(&initialized, pEnv, tempPath, NULL, M4OSA_NULL); pContext->initParams.pTempPath = (M4OSA_Char *) - M4OSA_malloc(M4OSA_chrLength(tmpString) + 1, 0x0, + M4OSA_32bitAlignedMalloc(strlen((const char *)tmpString) + 1, 0x0, (M4OSA_Char *)"tempPath"); //initialize the first char. so that strcat works. M4OSA_Char *ptmpChar = (M4OSA_Char*)pContext->initParams.pTempPath; ptmpChar[0] = 0x00; - M4OSA_chrNCat((M4OSA_Char*)pContext->initParams.pTempPath, tmpString, M4OSA_chrLength(tmpString)); - M4OSA_chrNCat((M4OSA_Char*)pContext->initParams.pTempPath, (M4OSA_Char*)"/", 1); - M4OSA_free((M4OSA_MemAddr32)tmpString); + strncat((char *)pContext->initParams.pTempPath, (const char *)tmpString, + (size_t)strlen((const char *)tmpString)); + strncat((char *)pContext->initParams.pTempPath, (const char *)"/", (size_t)1); + free(tmpString); pContext->mIsUpdateOverlay = false; pContext->mOverlayFileName = NULL; } @@ -2563,7 +2561,7 @@ videoEditor_init( "not initialized"); pContext->mAudioSettings = (M4xVSS_AudioMixingSettings *) - M4OSA_malloc(sizeof(M4xVSS_AudioMixingSettings),0x0, + M4OSA_32bitAlignedMalloc(sizeof(M4xVSS_AudioMixingSettings),0x0, (M4OSA_Char *)"mAudioSettings"); videoEditJava_checkAndThrowIllegalStateException(&initialized, pEnv, (M4OSA_NULL == pContext->mAudioSettings), @@ -3065,15 +3063,15 @@ videoEditor_release( if(pContext->mAudioSettings != M4OSA_NULL) { if (pContext->mAudioSettings->pFile != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + free(pContext->mAudioSettings->pFile); pContext->mAudioSettings->pFile = M4OSA_NULL; } if (pContext->mAudioSettings->pPCMFilePath != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + free(pContext->mAudioSettings->pPCMFilePath); pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; } - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings); + free(pContext->mAudioSettings); pContext->mAudioSettings = M4OSA_NULL; } videoEditor_freeContext(pEnv, &pContext); @@ -3251,7 +3249,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, *******************************************************************************/ samplesCountInBytes = (samplesPerValue * sizeof(M4OSA_UInt16) * channels); - bufferIn.m_dataAddress = (M4OSA_UInt8*)M4OSA_malloc(samplesCountInBytes*sizeof(M4OSA_UInt16), 0, + bufferIn.m_dataAddress = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(samplesCountInBytes*sizeof(M4OSA_UInt16), 0, (M4OSA_Char*)"AudioGraph" ); if ( bufferIn.m_dataAddress != M4OSA_NULL) { bufferIn.m_bufferSize = samplesCountInBytes*sizeof(M4OSA_UInt16); @@ -3286,7 +3284,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, /* loop until EOF */ do { - M4OSA_memset((M4OSA_MemAddr8)bufferIn.m_dataAddress,bufferIn.m_bufferSize, 0); + memset((void *)bufferIn.m_dataAddress,0,bufferIn.m_bufferSize); numBytesToRead = samplesCountInBytes; @@ -3379,7 +3377,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, /****************************************************************************** CLOSE AND FREE ALLOCATIONS *******************************************************************************/ - M4OSA_free((M4OSA_MemAddr32)bufferIn.m_dataAddress); + free(bufferIn.m_dataAddress); M4OSA_fileReadClose(inputFileHandle); M4OSA_fileWriteClose(outFileHandle); /* final finish callback */ diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp index 035f59a..53e7de1 100755 --- a/media/jni/mediaeditor/VideoEditorOsal.cpp +++ b/media/jni/mediaeditor/VideoEditorOsal.cpp @@ -25,7 +25,6 @@ extern "C" { #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4xVSS_API.h> #include <M4VSS3GPP_ErrorCodes.h> @@ -82,14 +81,6 @@ static const VideoEdit_Osal_Result gkRESULTS[] = VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_BAD_MODE_ACCESS ), VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_INVALID_POSITION ), - // M4OSA_String.h - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_STRING ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_CONV_FAILED ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_OVERFLOW ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_ARGS ), - VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_OVERFLOW ), - VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_NOT_FOUND ), - // M4OSA_Thread.h VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_THREAD_NOT_STARTED ), @@ -276,11 +267,11 @@ videoEditOsal_alloc( if (*pResult) { // Allocate memory for the settings. - pData = (M4VSS3GPP_EditSettings*)M4OSA_malloc(size, 0, (M4OSA_Char*)pDescription); + pData = (M4VSS3GPP_EditSettings*)M4OSA_32bitAlignedMalloc(size, 0, (M4OSA_Char*)pDescription); if (M4OSA_NULL != pData) { // Reset the allocated memory. - M4OSA_memset((M4OSA_MemAddr8)pData, size, 0); + memset((void *)pData, 0,size); #ifdef OSAL_MEM_LEAK_DEBUG // Update the allocated block count. gAllocatedBlockCount++; @@ -314,10 +305,10 @@ videoEditOsal_free( VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "videoEditOsal_free()"); // Log the API call. - VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "M4OSA_free()"); + VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "free"); // Free the memory. - M4OSA_free((M4OSA_MemAddr32)pData); + free(pData); #ifdef OSAL_MEM_LEAK_DEBUG // Update the allocated block count. gAllocatedBlockCount--; diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp index 3b795ce..9de7207 100755 --- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp +++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp @@ -34,13 +34,11 @@ extern "C" { #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4VSS3GPP_API.h> #include <M4VSS3GPP_ErrorCodes.h> #include <M4MCS_API.h> #include <M4MCS_ErrorCodes.h> -#include <M4MDP_API.h> #include <M4READER_Common.h> #include <M4WRITER_common.h> #include <M4DECODER_Common.h> @@ -147,8 +145,8 @@ jobject videoEditProp_getProperties( if (gotten) { // Retrieve the extension. - result = M4OSA_chrReverseFindChar(pFile, '.', &pExtension); - if ((M4NO_ERROR == result) && (M4OSA_NULL != pExtension)) + pExtension = (M4OSA_Char *)strrchr((const char *)pFile, (int)'.'); + if (M4OSA_NULL != pExtension) { // Skip the dot. pExtension++; @@ -341,7 +339,7 @@ static void getFileAndMediaTypeFromExtension ( M4OSA_UInt32 index = 0; M4OSA_ERR result = M4NO_ERROR; M4OSA_Int32 cmpResult = 0; - M4OSA_UInt32 extLength = M4OSA_chrLength(pExtension); + M4OSA_UInt32 extLength = strlen((const char *)pExtension); // Assign default *pFileType = VideoEditClasses_kFileType_Unsupported; @@ -353,7 +351,7 @@ static void getFileAndMediaTypeFromExtension ( // Convert the extension to lowercase. for (index = 0; index < extLength ; index++) { - extension[index] = M4OSA_chrToLower(pExtension[index]); + extension[index] = tolower((int)pExtension[index]); } // Check if the extension is ".mp3". @@ -539,7 +537,7 @@ VideoEdit_chrCompare(M4OSA_Char* pStrIn1, M4OSA_Char* pStrIn2, M4OSA_Int32* pCmpResult) { - M4OSA_chrCompare(pStrIn1, pStrIn2, pCmpResult); + *pCmpResult = strcmp((const char *)pStrIn1, (const char *)pStrIn2); return *pCmpResult; } diff --git a/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp b/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp index b1f9fe4..fe3734f 100755 --- a/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp +++ b/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp @@ -134,7 +134,7 @@ M4OSA_Void VBcallback( M4OSA_Context pInstance, for (j = 0; j < pPlane->u_height; j++) { - M4OSA_memcpy((M4OSA_MemAddr8 )dst, (M4OSA_MemAddr8 )src, pPlane->u_stride); + memcpy((void * )dst, (void * )src, pPlane->u_stride); for (i = pPlane->u_width; i < pC->m_width; i++) { dst[i] = 0; @@ -165,12 +165,12 @@ M4OSA_ERR ThumbnailOpen(M4OSA_Context *pPContext, CHECK_PTR(ThumbnailOpen, pString, err, M4ERR_BAD_CONTEXT); /*--- Create context ---*/ - pContext = (ThumbnailContext*)M4OSA_malloc(sizeof(ThumbnailContext), VIDEOBROWSER, + pContext = (ThumbnailContext*)M4OSA_32bitAlignedMalloc(sizeof(ThumbnailContext), VIDEOBROWSER, (M4OSA_Char*)"Thumbnail context") ; M4OSA_TRACE3_1("context value is = %d",pContext); CHECK_PTR(ThumbnailOpen, pContext, err, M4ERR_ALLOC); - M4OSA_memset((M4OSA_MemAddr8)pContext, sizeof(ThumbnailContext), 0); + memset((void *)pContext, 0,sizeof(ThumbnailContext)); M4OSA_FileReadPointer optFP; M4OSA_FileReadPointer llFP; @@ -211,7 +211,7 @@ ThumbnailOpen_cleanUp: { videoBrowserCleanUp(pContext->m_pVideoBrowser) ; } - M4OSA_free((M4OSA_MemAddr32)pContext) ; + free(pContext) ; } return err; } @@ -320,7 +320,7 @@ void ThumbnailClose(const M4OSA_Context pContext) { videoBrowserCleanUp(pC->m_pVideoBrowser); } - M4OSA_free((M4OSA_MemAddr32)pC); + free(pC); } ThumbnailClose_cleanUp: diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 1e2d6ce..3ea13a6 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -27,6 +27,8 @@ #include <media/AudioTrack.h> #include <media/mediaplayer.h> +#include <system/audio.h> + #include "SoundPool.h" #include "SoundPoolThread.h" @@ -584,7 +586,7 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV if (loop) { frameCount = sample->size()/numChannels/ - ((sample->format() == AudioSystem::PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); + ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); } #ifndef USE_SHARED_MEM_BUFFER @@ -602,7 +604,7 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV unsigned long toggle = mToggle ^ 1; void *userData = (void *)((unsigned long)this | toggle); uint32_t channels = (numChannels == 2) ? - AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO; + AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO; // do not create a new audio track if current track is compatible with sample parameters #ifdef USE_SHARED_MEM_BUFFER @@ -865,7 +867,7 @@ void SoundChannel::setLoop(int loop) Mutex::Autolock lock(&mLock); if (mAudioTrack != 0 && mSample.get() != 0) { uint32_t loopEnd = mSample->size()/mNumChannels/ - ((mSample->format() == AudioSystem::PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); + ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); mAudioTrack->setLoop(0, loopEnd, loop); mLoop = loop; } diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp index 447f931..03d3388 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool.cpp @@ -60,7 +60,7 @@ android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescripto LOGV("android_media_SoundPool_load_FD"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return 0; - return ap->load(getParcelFileDescriptorFD(env, fileDescriptor), + return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor), int64_t(offset), int64_t(length), int(priority)); } diff --git a/media/libdrm/mobile1/Android.mk b/media/libdrm/mobile1/Android.mk index f105799..b07d91c 100644 --- a/media/libdrm/mobile1/Android.mk +++ b/media/libdrm/mobile1/Android.mk @@ -72,6 +72,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := libdrm1 \ + libnativehelper \ libutils \ libcutils diff --git a/media/libdrm/mobile1/src/jni/drm1_jni.c b/media/libdrm/mobile1/src/jni/drm1_jni.c index 79ce931..11353a7 100644 --- a/media/libdrm/mobile1/src/jni/drm1_jni.c +++ b/media/libdrm/mobile1/src/jni/drm1_jni.c @@ -24,6 +24,7 @@ #include <jni/drm1_jni.h> #include <objmng/svc_drm.h> #include "log.h" +#include "JNIHelp.h" #define MS_PER_SECOND 1000 /* Milliseconds per second */ @@ -659,23 +660,13 @@ Java_android_drm_mobile1_DrmRawContent_nativeReadContent jfieldID field; if (NULL == buf) { - jclass newExcCls = (*env)->FindClass(env, "java/lang/NullPointerException"); - - if (newExcCls == NULL) - /* Unable to find the exception class, give up. */ - return JNI_DRM_FAILURE; - - (*env)->ThrowNew(env, newExcCls, "b is null"); + jniThrowNullPointerException(env, "b == null"); + return JNI_DRM_FAILURE; } if (len < 0 || bufOff < 0 || len + bufOff > (*env)->GetArrayLength(env, buf)) { - jclass newExcCls = (*env)->FindClass(env, "java/lang/IndexOutOfBoundsException"); - - if (newExcCls == NULL) - /* Unable to find the exception class, give up. */ - return JNI_DRM_FAILURE; - - (*env)->ThrowNew(env, newExcCls, NULL); + jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); + return JNI_DRM_FAILURE; } if (mediaOff < 0 || len == 0) diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk index ff34707..f49267e 100644 --- a/media/libeffects/lvm/lib/Android.mk +++ b/media/libeffects/lvm/lib/Android.mk @@ -105,7 +105,7 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libmusicbundle -LOCAL_PRELINK_MODULE := false + LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Eq/lib \ @@ -168,7 +168,7 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libreverb -LOCAL_PRELINK_MODULE := false + LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Reverb/lib \ diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk index 2e9b9b4..99cfdfa 100644 --- a/media/libeffects/lvm/wrapper/Android.mk +++ b/media/libeffects/lvm/wrapper/Android.mk @@ -13,7 +13,7 @@ LOCAL_MODULE:= libbundlewrapper LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx -LOCAL_PRELINK_MODULE := false + LOCAL_STATIC_LIBRARIES += libmusicbundle @@ -47,7 +47,7 @@ LOCAL_MODULE:= libreverbwrapper LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx -LOCAL_PRELINK_MODULE := false + LOCAL_STATIC_LIBRARIES += libreverb diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index e6ff654..3a0f438 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -25,6 +25,6 @@ endif LOCAL_C_INCLUDES := \ $(call include-path-for, graphics corecg) -LOCAL_PRELINK_MODULE := false + include $(BUILD_SHARED_LIBRARY) diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index fd4c6c6..121e38a4 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -1,4 +1,14 @@ LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioParameter.cpp +LOCAL_MODULE:= libmedia_helper +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -33,13 +43,16 @@ LOCAL_SRC_FILES:= \ IEffectClient.cpp \ AudioEffect.cpp \ Visualizer.cpp \ + MemoryLeakTrackUtil.cpp \ fixedfft.cpp.arm LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ - libsurfaceflinger_client libcamera_client libstagefright_foundation \ + libcamera_client libstagefright_foundation \ libgui +LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper + LOCAL_MODULE:= libmedia ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index aadeba5..a043329 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -170,7 +170,6 @@ AudioEffect::~AudioEffect() LOGV("Destructor %p", this); if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) { - setEnabled(false); if (mIEffect != NULL) { mIEffect->disconnect(); mIEffect->asBinder()->unlinkToDeath(mIEffectClient); diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp new file mode 100644 index 0000000..59ccfd0 --- /dev/null +++ b/media/libmedia/AudioParameter.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2006-2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioParameter" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> + +#include <media/AudioParameter.h> + +namespace android { + +const char *AudioParameter::keyRouting = "routing"; +const char *AudioParameter::keySamplingRate = "sampling_rate"; +const char *AudioParameter::keyFormat = "format"; +const char *AudioParameter::keyChannels = "channels"; +const char *AudioParameter::keyFrameCount = "frame_count"; +const char *AudioParameter::keyInputSource = "input_source"; + +AudioParameter::AudioParameter(const String8& keyValuePairs) +{ + char *str = new char[keyValuePairs.length()+1]; + mKeyValuePairs = keyValuePairs; + + strcpy(str, keyValuePairs.string()); + char *pair = strtok(str, ";"); + while (pair != NULL) { + if (strlen(pair) != 0) { + size_t eqIdx = strcspn(pair, "="); + String8 key = String8(pair, eqIdx); + String8 value; + if (eqIdx == strlen(pair)) { + value = String8(""); + } else { + value = String8(pair + eqIdx + 1); + } + if (mParameters.indexOfKey(key) < 0) { + mParameters.add(key, value); + } else { + mParameters.replaceValueFor(key, value); + } + } else { + LOGV("AudioParameter() cstor empty key value pair"); + } + pair = strtok(NULL, ";"); + } + + delete[] str; +} + +AudioParameter::~AudioParameter() +{ + mParameters.clear(); +} + +String8 AudioParameter::toString() +{ + String8 str = String8(""); + + size_t size = mParameters.size(); + for (size_t i = 0; i < size; i++) { + str += mParameters.keyAt(i); + str += "="; + str += mParameters.valueAt(i); + if (i < (size - 1)) str += ";"; + } + return str; +} + +status_t AudioParameter::add(const String8& key, const String8& value) +{ + if (mParameters.indexOfKey(key) < 0) { + mParameters.add(key, value); + return NO_ERROR; + } else { + mParameters.replaceValueFor(key, value); + return ALREADY_EXISTS; + } +} + +status_t AudioParameter::addInt(const String8& key, const int value) +{ + char str[12]; + if (snprintf(str, 12, "%d", value) > 0) { + String8 str8 = String8(str); + return add(key, str8); + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::addFloat(const String8& key, const float value) +{ + char str[23]; + if (snprintf(str, 23, "%.10f", value) > 0) { + String8 str8 = String8(str); + return add(key, str8); + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::remove(const String8& key) +{ + if (mParameters.indexOfKey(key) >= 0) { + mParameters.removeItem(key); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::get(const String8& key, String8& value) +{ + if (mParameters.indexOfKey(key) >= 0) { + value = mParameters.valueFor(key); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::getInt(const String8& key, int& value) +{ + String8 str8; + status_t result = get(key, str8); + value = 0; + if (result == NO_ERROR) { + int val; + if (sscanf(str8.string(), "%d", &val) == 1) { + value = val; + } else { + result = INVALID_OPERATION; + } + } + return result; +} + +status_t AudioParameter::getFloat(const String8& key, float& value) +{ + String8 str8; + status_t result = get(key, str8); + value = 0; + if (result == NO_ERROR) { + float val; + if (sscanf(str8.string(), "%f", &val) == 1) { + value = val; + } else { + result = INVALID_OPERATION; + } + } + return result; +} + +status_t AudioParameter::getAt(size_t index, String8& key, String8& value) +{ + if (mParameters.size() > index) { + key = mParameters.keyAt(index); + value = mParameters.valueAt(index); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +}; // namespace android diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index a18bedb..446e3df 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -35,6 +35,10 @@ #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <utils/Timers.h> +#include <utils/Atomic.h> + +#include <system/audio.h> +#include <cutils/bitops.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -65,8 +69,8 @@ status_t AudioRecord::getMinFrameCount( // We double the size of input buffer for ping pong use of record buffer. size <<= 1; - if (AudioSystem::isLinearPCM(format)) { - size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1); + if (audio_is_linear_pcm(format)) { + size /= channelCount * (format == AUDIO_FORMAT_PCM_16_BIT ? 2 : 1); } *frameCount = size; @@ -144,22 +148,22 @@ status_t AudioRecord::set( } // these below should probably come from the audioFlinger too... if (format == 0) { - format = AudioSystem::PCM_16_BIT; + format = AUDIO_FORMAT_PCM_16_BIT; } // validate parameters - if (!AudioSystem::isValidFormat(format)) { + if (!audio_is_valid_format(format)) { LOGE("Invalid format"); return BAD_VALUE; } - if (!AudioSystem::isInputChannel(channels)) { + if (!audio_is_input_channel(channels)) { return BAD_VALUE; } - int channelCount = AudioSystem::popCount(channels); + int channelCount = popcount(channels); audio_io_handle_t input = AudioSystem::getInput(inputSource, - sampleRate, format, channels, (AudioSystem::audio_in_acoustics)flags); + sampleRate, format, channels, (audio_in_acoustics_t)flags); if (input == 0) { LOGE("Could not get audio input for record source %d", inputSource); return BAD_VALUE; @@ -253,8 +257,8 @@ uint32_t AudioRecord::frameCount() const int AudioRecord::frameSize() const { - if (AudioSystem::isLinearPCM(mFormat)) { - return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); + if (audio_is_linear_pcm(mFormat)) { + return channelCount()*((format() == AUDIO_FORMAT_PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); } else { return sizeof(uint8_t); } @@ -299,7 +303,7 @@ status_t AudioRecord::start() ret = mAudioRecord->start(); cblk->lock.lock(); if (ret == DEAD_OBJECT) { - cblk->flags |= CBLK_INVALID_MSK; + android_atomic_or(CBLK_INVALID_ON, &cblk->flags); } } if (cblk->flags & CBLK_INVALID_MSK) { @@ -467,7 +471,7 @@ status_t AudioRecord::openRecord_l( mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - mCblk->flags &= ~CBLK_DIRECTION_MSK; + android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags); mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; mCblk->waitTimeMs = 0; return NO_ERROR; @@ -522,7 +526,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = mAudioRecord->start(); cblk->lock.lock(); if (result == DEAD_OBJECT) { - cblk->flags |= CBLK_INVALID_MSK; + android_atomic_or(CBLK_INVALID_ON, &cblk->flags); create_new_record: result = AudioRecord::restoreRecord_l(cblk); } @@ -586,7 +590,7 @@ audio_io_handle_t AudioRecord::getInput_l() mInput = AudioSystem::getInput(mInputSource, mCblk->sampleRate, mFormat, mChannels, - (AudioSystem::audio_in_acoustics)mFlags); + (audio_in_acoustics_t)mFlags); return mInput; } @@ -722,9 +726,8 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) // Manage overrun callback if (mActive && (cblk->framesAvailable() == 0)) { LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { + if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { mCbf(EVENT_OVERRUN, mUserData, 0); - cblk->flags |= CBLK_UNDERRUN_ON; } } @@ -743,10 +746,8 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) { status_t result; - if (!(cblk->flags & CBLK_RESTORING_MSK)) { + if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) { LOGW("dead IAudioRecord, creating a new one"); - - cblk->flags |= CBLK_RESTORING_ON; // signal old cblk condition so that other threads waiting for available buffers stop // waiting now cblk->cv.broadcast(); @@ -765,10 +766,8 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) } // signal old cblk condition for other threads waiting for restore completion - cblk->lock.lock(); - cblk->flags |= CBLK_RESTORED_MSK; + android_atomic_or(CBLK_RESTORED_ON, &cblk->flags); cblk->cv.broadcast(); - cblk->lock.unlock(); } else { if (!(cblk->flags & CBLK_RESTORED_MSK)) { LOGW("dead IAudioRecord, waiting for a new one to be created"); diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 2f694ba..8a180d8 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -23,6 +23,8 @@ #include <media/IAudioPolicyService.h> #include <math.h> +#include <system/audio.h> + // ---------------------------------------------------------------------------- // the sim build doesn't have gettid @@ -45,7 +47,7 @@ DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSyst // Cached values for recording queries uint32_t AudioSystem::gPrevInSamplingRate = 16000; -int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT; +int AudioSystem::gPrevInFormat = AUDIO_FORMAT_PCM_16_BIT; int AudioSystem::gPrevInChannelCount = 1; size_t AudioSystem::gInBuffSize = 0; @@ -127,7 +129,7 @@ status_t AudioSystem::getMasterMute(bool* mute) status_t AudioSystem::setStreamVolume(int stream, float value, int output) { - if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; af->setStreamVolume(stream, value, output); @@ -136,7 +138,7 @@ status_t AudioSystem::setStreamVolume(int stream, float value, int output) status_t AudioSystem::setStreamMute(int stream, bool mute) { - if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; af->setStreamMute(stream, mute); @@ -145,7 +147,7 @@ status_t AudioSystem::setStreamMute(int stream, bool mute) status_t AudioSystem::getStreamVolume(int stream, float* volume, int output) { - if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; *volume = af->streamVolume(stream, output); @@ -154,7 +156,7 @@ status_t AudioSystem::getStreamVolume(int stream, float* volume, int output) status_t AudioSystem::getStreamMute(int stream, bool* mute) { - if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; *mute = af->streamMute(stream); @@ -163,7 +165,7 @@ status_t AudioSystem::getStreamMute(int stream, bool* mute) status_t AudioSystem::setMode(int mode) { - if (mode >= NUM_MODES) return BAD_VALUE; + if (mode >= AUDIO_MODE_CNT) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; return af->setMode(mode); @@ -213,11 +215,11 @@ status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) OutputDescriptor *outputDesc; audio_io_handle_t output; - if (streamType == DEFAULT) { - streamType = MUSIC; + if (streamType == AUDIO_STREAM_DEFAULT) { + streamType = AUDIO_STREAM_MUSIC; } - output = getOutput((stream_type)streamType); + output = getOutput((audio_stream_type_t)streamType); if (output == 0) { return PERMISSION_DENIED; } @@ -246,11 +248,11 @@ status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) OutputDescriptor *outputDesc; audio_io_handle_t output; - if (streamType == DEFAULT) { - streamType = MUSIC; + if (streamType == AUDIO_STREAM_DEFAULT) { + streamType = AUDIO_STREAM_MUSIC; } - output = getOutput((stream_type)streamType); + output = getOutput((audio_stream_type_t)streamType); if (output == 0) { return PERMISSION_DENIED; } @@ -277,11 +279,11 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType) OutputDescriptor *outputDesc; audio_io_handle_t output; - if (streamType == DEFAULT) { - streamType = MUSIC; + if (streamType == AUDIO_STREAM_DEFAULT) { + streamType = AUDIO_STREAM_MUSIC; } - output = getOutput((stream_type)streamType); + output = getOutput((audio_stream_type_t)streamType); if (output == 0) { return PERMISSION_DENIED; } @@ -338,11 +340,11 @@ status_t AudioSystem::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - if (stream == DEFAULT) { - stream = MUSIC; + if (stream == AUDIO_STREAM_DEFAULT) { + stream = AUDIO_STREAM_MUSIC; } - return af->getRenderPosition(halFrames, dspFrames, getOutput((stream_type)stream)); + return af->getRenderPosition(halFrames, dspFrames, getOutput((audio_stream_type_t)stream)); } unsigned int AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) { @@ -455,10 +457,10 @@ void AudioSystem::setErrorCallback(audio_error_callback cb) { bool AudioSystem::routedToA2dpOutput(int streamType) { switch(streamType) { - case MUSIC: - case VOICE_CALL: - case BLUETOOTH_SCO: - case SYSTEM: + case AUDIO_STREAM_MUSIC: + case AUDIO_STREAM_VOICE_CALL: + case AUDIO_STREAM_BLUETOOTH_SCO: + case AUDIO_STREAM_SYSTEM: return true; default: return false; @@ -497,9 +499,9 @@ const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service() return gAudioPolicyService; } -status_t AudioSystem::setDeviceConnectionState(audio_devices device, - device_connection_state state, - const char *device_address) +status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; @@ -507,11 +509,11 @@ status_t AudioSystem::setDeviceConnectionState(audio_devices device, return aps->setDeviceConnectionState(device, state, device_address); } -AudioSystem::device_connection_state AudioSystem::getDeviceConnectionState(audio_devices device, +audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device, const char *device_address) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return DEVICE_STATE_UNAVAILABLE; + if (aps == 0) return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; return aps->getDeviceConnectionState(device, device_address); } @@ -531,26 +533,26 @@ status_t AudioSystem::setRingerMode(uint32_t mode, uint32_t mask) return aps->setRingerMode(mode, mask); } -status_t AudioSystem::setForceUse(force_use usage, forced_config config) +status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->setForceUse(usage, config); } -AudioSystem::forced_config AudioSystem::getForceUse(force_use usage) +audio_policy_forced_cfg_t AudioSystem::getForceUse(audio_policy_force_use_t usage) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return FORCE_NONE; + if (aps == 0) return AUDIO_POLICY_FORCE_NONE; return aps->getForceUse(usage); } -audio_io_handle_t AudioSystem::getOutput(stream_type stream, +audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream, uint32_t samplingRate, uint32_t format, uint32_t channels, - output_flags flags) + audio_policy_output_flags_t flags) { audio_io_handle_t output = 0; // Do not use stream to output map cache if the direct output @@ -561,9 +563,9 @@ audio_io_handle_t AudioSystem::getOutput(stream_type stream, // be reworked for proper operation with direct outputs. This code is too specific // to the first use case we want to cover (Voice Recognition and Voice Dialer over // Bluetooth SCO - if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0 && - ((stream != AudioSystem::VOICE_CALL && stream != AudioSystem::BLUETOOTH_SCO) || - channels != AudioSystem::CHANNEL_OUT_MONO || + if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) == 0 && + ((stream != AUDIO_STREAM_VOICE_CALL && stream != AUDIO_STREAM_BLUETOOTH_SCO) || + channels != AUDIO_CHANNEL_OUT_MONO || (samplingRate != 8000 && samplingRate != 16000))) { Mutex::Autolock _l(gLock); output = AudioSystem::gStreamOutputMap.valueFor(stream); @@ -573,7 +575,7 @@ audio_io_handle_t AudioSystem::getOutput(stream_type stream, const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; output = aps->getOutput(stream, samplingRate, format, channels, flags); - if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) { + if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) == 0) { Mutex::Autolock _l(gLock); AudioSystem::gStreamOutputMap.add(stream, output); } @@ -582,7 +584,7 @@ audio_io_handle_t AudioSystem::getOutput(stream_type stream, } status_t AudioSystem::startOutput(audio_io_handle_t output, - AudioSystem::stream_type stream, + audio_stream_type_t stream, int session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); @@ -591,7 +593,7 @@ status_t AudioSystem::startOutput(audio_io_handle_t output, } status_t AudioSystem::stopOutput(audio_io_handle_t output, - AudioSystem::stream_type stream, + audio_stream_type_t stream, int session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); @@ -610,7 +612,7 @@ audio_io_handle_t AudioSystem::getInput(int inputSource, uint32_t samplingRate, uint32_t format, uint32_t channels, - audio_in_acoustics acoustics) + audio_in_acoustics_t acoustics) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; @@ -638,7 +640,7 @@ void AudioSystem::releaseInput(audio_io_handle_t input) aps->releaseInput(input); } -status_t AudioSystem::initStreamVolume(stream_type stream, +status_t AudioSystem::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) { @@ -647,28 +649,28 @@ status_t AudioSystem::initStreamVolume(stream_type stream, return aps->initStreamVolume(stream, indexMin, indexMax); } -status_t AudioSystem::setStreamVolumeIndex(stream_type stream, int index) +status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->setStreamVolumeIndex(stream, index); } -status_t AudioSystem::getStreamVolumeIndex(stream_type stream, int *index) +status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream, int *index) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->getStreamVolumeIndex(stream, index); } -uint32_t AudioSystem::getStrategyForStream(AudioSystem::stream_type stream) +uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; return aps->getStrategyForStream(stream); } -uint32_t AudioSystem::getDevicesForStream(AudioSystem::stream_type stream) +uint32_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; @@ -717,276 +719,5 @@ void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) { LOGW("AudioPolicyService server died!"); } -// --------------------------------------------------------------------------- - - -// use emulated popcount optimization -// http://www.df.lth.se/~john_e/gems/gem002d.html -uint32_t AudioSystem::popCount(uint32_t u) -{ - u = ((u&0x55555555) + ((u>>1)&0x55555555)); - u = ((u&0x33333333) + ((u>>2)&0x33333333)); - u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); - u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); - u = ( u&0x0000ffff) + (u>>16); - return u; -} - -bool AudioSystem::isOutputDevice(audio_devices device) -{ - if ((popCount(device) == 1 ) && - ((device & ~AudioSystem::DEVICE_OUT_ALL) == 0)) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isInputDevice(audio_devices device) -{ - if ((popCount(device) == 1 ) && - ((device & ~AudioSystem::DEVICE_IN_ALL) == 0)) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isA2dpDevice(audio_devices device) -{ - if ((popCount(device) == 1 ) && - (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | - AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER))) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isBluetoothScoDevice(audio_devices device) -{ - if ((popCount(device) == 1 ) && - (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_SCO | - AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET | - AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT | - AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET))) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isLowVisibility(stream_type stream) -{ - if (stream == AudioSystem::SYSTEM || - stream == AudioSystem::NOTIFICATION || - stream == AudioSystem::RING) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isInputChannel(uint32_t channel) -{ - if ((channel & ~AudioSystem::CHANNEL_IN_ALL) == 0) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isOutputChannel(uint32_t channel) -{ - if ((channel & ~AudioSystem::CHANNEL_OUT_ALL) == 0) { - return true; - } else { - return false; - } -} - -bool AudioSystem::isValidFormat(uint32_t format) -{ - switch (format & MAIN_FORMAT_MASK) { - case PCM: - case MP3: - case AMR_NB: - case AMR_WB: - case AAC: - case HE_AAC_V1: - case HE_AAC_V2: - case VORBIS: - return true; - default: - return false; - } -} - -bool AudioSystem::isLinearPCM(uint32_t format) -{ - switch (format) { - case PCM_16_BIT: - case PCM_8_BIT: - return true; - default: - return false; - } -} - -//------------------------- AudioParameter class implementation --------------- - -const char *AudioParameter::keyRouting = "routing"; -const char *AudioParameter::keySamplingRate = "sampling_rate"; -const char *AudioParameter::keyFormat = "format"; -const char *AudioParameter::keyChannels = "channels"; -const char *AudioParameter::keyFrameCount = "frame_count"; -const char *AudioParameter::keyInputSource = "input_source"; - -AudioParameter::AudioParameter(const String8& keyValuePairs) -{ - char *str = new char[keyValuePairs.length()+1]; - mKeyValuePairs = keyValuePairs; - - strcpy(str, keyValuePairs.string()); - char *pair = strtok(str, ";"); - while (pair != NULL) { - if (strlen(pair) != 0) { - size_t eqIdx = strcspn(pair, "="); - String8 key = String8(pair, eqIdx); - String8 value; - if (eqIdx == strlen(pair)) { - value = String8(""); - } else { - value = String8(pair + eqIdx + 1); - } - if (mParameters.indexOfKey(key) < 0) { - mParameters.add(key, value); - } else { - mParameters.replaceValueFor(key, value); - } - } else { - LOGV("AudioParameter() cstor empty key value pair"); - } - pair = strtok(NULL, ";"); - } - - delete[] str; -} - -AudioParameter::~AudioParameter() -{ - mParameters.clear(); -} - -String8 AudioParameter::toString() -{ - String8 str = String8(""); - - size_t size = mParameters.size(); - for (size_t i = 0; i < size; i++) { - str += mParameters.keyAt(i); - str += "="; - str += mParameters.valueAt(i); - if (i < (size - 1)) str += ";"; - } - return str; -} - -status_t AudioParameter::add(const String8& key, const String8& value) -{ - if (mParameters.indexOfKey(key) < 0) { - mParameters.add(key, value); - return NO_ERROR; - } else { - mParameters.replaceValueFor(key, value); - return ALREADY_EXISTS; - } -} - -status_t AudioParameter::addInt(const String8& key, const int value) -{ - char str[12]; - if (snprintf(str, 12, "%d", value) > 0) { - String8 str8 = String8(str); - return add(key, str8); - } else { - return BAD_VALUE; - } -} - -status_t AudioParameter::addFloat(const String8& key, const float value) -{ - char str[23]; - if (snprintf(str, 23, "%.10f", value) > 0) { - String8 str8 = String8(str); - return add(key, str8); - } else { - return BAD_VALUE; - } -} - -status_t AudioParameter::remove(const String8& key) -{ - if (mParameters.indexOfKey(key) >= 0) { - mParameters.removeItem(key); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioParameter::get(const String8& key, String8& value) -{ - if (mParameters.indexOfKey(key) >= 0) { - value = mParameters.valueFor(key); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioParameter::getInt(const String8& key, int& value) -{ - String8 str8; - status_t result = get(key, str8); - value = 0; - if (result == NO_ERROR) { - int val; - if (sscanf(str8.string(), "%d", &val) == 1) { - value = val; - } else { - result = INVALID_OPERATION; - } - } - return result; -} - -status_t AudioParameter::getFloat(const String8& key, float& value) -{ - String8 str8; - status_t result = get(key, str8); - value = 0; - if (result == NO_ERROR) { - float val; - if (sscanf(str8.string(), "%f", &val) == 1) { - value = val; - } else { - result = INVALID_OPERATION; - } - } - return result; -} - -status_t AudioParameter::getAt(size_t index, String8& key, String8& value) -{ - if (mParameters.size() > index) { - key = mParameters.keyAt(index); - value = mParameters.valueAt(index); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} }; // namespace android diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 8d8f67b..7520ed9 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -35,6 +35,12 @@ #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <utils/Timers.h> +#include <utils/Atomic.h> + +#include <cutils/bitops.h> + +#include <system/audio.h> +#include <hardware/audio_policy.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -164,39 +170,41 @@ status_t AudioTrack::set( } // handle default values first. - if (streamType == AudioSystem::DEFAULT) { - streamType = AudioSystem::MUSIC; + if (streamType == AUDIO_STREAM_DEFAULT) { + streamType = AUDIO_STREAM_MUSIC; } if (sampleRate == 0) { sampleRate = afSampleRate; } // these below should probably come from the audioFlinger too... if (format == 0) { - format = AudioSystem::PCM_16_BIT; + format = AUDIO_FORMAT_PCM_16_BIT; } if (channels == 0) { - channels = AudioSystem::CHANNEL_OUT_STEREO; + channels = AUDIO_CHANNEL_OUT_STEREO; } // validate parameters - if (!AudioSystem::isValidFormat(format)) { + if (!audio_is_valid_format(format)) { LOGE("Invalid format"); return BAD_VALUE; } // force direct flag if format is not linear PCM - if (!AudioSystem::isLinearPCM(format)) { - flags |= AudioSystem::OUTPUT_FLAG_DIRECT; + if (!audio_is_linear_pcm(format)) { + flags |= AUDIO_POLICY_OUTPUT_FLAG_DIRECT; } - if (!AudioSystem::isOutputChannel(channels)) { + if (!audio_is_output_channel(channels)) { LOGE("Invalid channel mask"); return BAD_VALUE; } - uint32_t channelCount = AudioSystem::popCount(channels); + uint32_t channelCount = popcount(channels); - audio_io_handle_t output = AudioSystem::getOutput((AudioSystem::stream_type)streamType, - sampleRate, format, channels, (AudioSystem::output_flags)flags); + audio_io_handle_t output = AudioSystem::getOutput( + (audio_stream_type_t)streamType, + sampleRate,format, channels, + (audio_policy_output_flags_t)flags); if (output == 0) { LOGE("Could not get audio output for stream type %d", streamType); @@ -289,8 +297,8 @@ uint32_t AudioTrack::frameCount() const int AudioTrack::frameSize() const { - if (AudioSystem::isLinearPCM(mFormat)) { - return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); + if (audio_is_linear_pcm(mFormat)) { + return channelCount()*((format() == AUDIO_FORMAT_PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); } else { return sizeof(uint8_t); } @@ -329,9 +337,10 @@ void AudioTrack::start() if (mActive == 0) { mActive = 1; mNewPosition = cblk->server + mUpdatePeriod; + cblk->lock.lock(); cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; cblk->waitTimeMs = 0; - cblk->flags &= ~CBLK_DISABLED_ON; + android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags); if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -339,13 +348,12 @@ void AudioTrack::start() } LOGV("start %p before lock cblk %p", this, mCblk); - cblk->lock.lock(); if (!(cblk->flags & CBLK_INVALID_MSK)) { cblk->lock.unlock(); status = mAudioTrack->start(); cblk->lock.lock(); if (status == DEAD_OBJECT) { - cblk->flags |= CBLK_INVALID_MSK; + android_atomic_or(CBLK_INVALID_ON, &cblk->flags); } } if (cblk->flags & CBLK_INVALID_MSK) { @@ -546,12 +554,13 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou } if (loopStart >= loopEnd || - loopEnd - loopStart > cblk->frameCount) { + loopEnd - loopStart > cblk->frameCount || + cblk->server > loopStart) { LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user); return BAD_VALUE; } - if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { + if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d", loopStart, loopEnd, cblk->frameCount); return BAD_VALUE; @@ -635,7 +644,7 @@ status_t AudioTrack::setPosition(uint32_t position) if (position > mCblk->user) return BAD_VALUE; mCblk->server = position; - mCblk->flags |= CBLK_FORCEREADY_ON; + android_atomic_or(CBLK_FORCEREADY_ON, &mCblk->flags); return NO_ERROR; } @@ -671,8 +680,8 @@ audio_io_handle_t AudioTrack::getOutput() // must be called with mLock held audio_io_handle_t AudioTrack::getOutput_l() { - return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType, - mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags); + return AudioSystem::getOutput((audio_stream_type_t)mStreamType, + mCblk->sampleRate, mFormat, mChannels, (audio_policy_output_flags_t)mFlags); } int AudioTrack::getSessionId() @@ -725,7 +734,7 @@ status_t AudioTrack::createTrack_l( } mNotificationFramesAct = mNotificationFramesReq; - if (!AudioSystem::isLinearPCM(format)) { + if (!audio_is_linear_pcm(format)) { if (sharedBuffer != 0) { frameCount = sharedBuffer->size(); } @@ -792,7 +801,7 @@ status_t AudioTrack::createTrack_l( mCblkMemory.clear(); mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); - mCblk->flags |= CBLK_DIRECTION_OUT; + android_atomic_or(CBLK_DIRECTION_OUT, &mCblk->flags); if (sharedBuffer == 0) { mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); } else { @@ -825,6 +834,12 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t framesAvail = cblk->framesAvailable(); + cblk->lock.lock(); + if (cblk->flags & CBLK_INVALID_MSK) { + goto create_new_track; + } + cblk->lock.unlock(); + if (framesAvail == 0) { cblk->lock.lock(); goto start_loop_here; @@ -866,7 +881,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = mAudioTrack->start(); cblk->lock.lock(); if (result == DEAD_OBJECT) { - cblk->flags |= CBLK_INVALID_MSK; + android_atomic_or(CBLK_INVALID_ON, &cblk->flags); create_new_track: result = restoreTrack_l(cblk, false); } @@ -893,7 +908,7 @@ create_new_track: // restart track if it was disabled by audioflinger due to previous underrun if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) { - cblk->flags &= ~CBLK_DISABLED_ON; + android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags); LOGW("obtainBuffer() track %p disabled, restarting", this); mAudioTrack->start(); } @@ -915,8 +930,8 @@ create_new_track: audioBuffer->channelCount = mChannelCount; audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq * cblk->frameSize; - if (AudioSystem::isLinearPCM(mFormat)) { - audioBuffer->format = AudioSystem::PCM_16_BIT; + if (audio_is_linear_pcm(mFormat)) { + audioBuffer->format = AUDIO_FORMAT_PCM_16_BIT; } else { audioBuffer->format = mFormat; } @@ -957,9 +972,10 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) ssize_t written = 0; const int8_t *src = (const int8_t *)buffer; Buffer audioBuffer; + size_t frameSz = (size_t)frameSize(); do { - audioBuffer.frameCount = userSize/frameSize(); + audioBuffer.frameCount = userSize/frameSz; // Calling obtainBuffer() with a negative wait count causes // an (almost) infinite wait time. @@ -973,7 +989,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) size_t toWrite; - if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) { + if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) { // Divide capacity by 2 to take expansion into account toWrite = audioBuffer.size>>1; // 8 to 16 bit conversion @@ -991,7 +1007,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) written += toWrite; releaseBuffer(&audioBuffer); - } while (userSize); + } while (userSize >= frameSz); return written; } @@ -1013,14 +1029,13 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) mLock.unlock(); // Manage underrun callback - if (mActive && (cblk->framesReady() == 0)) { + if (mActive && (cblk->framesAvailable() == cblk->frameCount)) { LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { + if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { mCbf(EVENT_UNDERRUN, mUserData, 0); if (cblk->server == cblk->frameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); } - cblk->flags |= CBLK_UNDERRUN_ON; if (mSharedBuffer != 0) return false; } } @@ -1077,7 +1092,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // Divide buffer size by 2 to take into account the expansion // due to 8 to 16 bit conversion: the callback must fill only half // of the destination buffer - if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) { + if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) { audioBuffer.size >>= 1; } @@ -1096,7 +1111,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) } if (writtenSize > reqSize) writtenSize = reqSize; - if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) { + if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) { // 8 to 16 bit conversion const int8_t *src = audioBuffer.i8 + writtenSize-1; int count = writtenSize; @@ -1134,11 +1149,10 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) { status_t result; - if (!(cblk->flags & CBLK_RESTORING_MSK)) { + if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) { LOGW("dead IAudioTrack, creating a new one from %s", fromStart ? "start()" : "obtainBuffer()"); - cblk->flags |= CBLK_RESTORING_ON; // signal old cblk condition so that other threads waiting for available buffers stop // waiting now cblk->cv.broadcast(); @@ -1158,10 +1172,20 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) false); if (result == NO_ERROR) { + // restore write index and set other indexes to reflect empty buffer status + mCblk->user = cblk->user; + mCblk->server = cblk->user; + mCblk->userBase = cblk->user; + mCblk->serverBase = cblk->user; + // restore loop: this is not guaranteed to succeed if new frame count is not + // compatible with loop length + setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); if (!fromStart) { mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; } - result = mAudioTrack->start(); + if (mActive) { + result = mAudioTrack->start(); + } if (fromStart && result == NO_ERROR) { mNewPosition = mCblk->server + mUpdatePeriod; } @@ -1171,10 +1195,8 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) } // signal old cblk condition for other threads waiting for restore completion - cblk->lock.lock(); - cblk->flags |= CBLK_RESTORED_MSK; + android_atomic_or(CBLK_RESTORED_ON, &cblk->flags); cblk->cv.broadcast(); - cblk->lock.unlock(); } else { if (!(cblk->flags & CBLK_RESTORED_MSK)) { LOGW("dead IAudioTrack, waiting for a new one"); @@ -1248,11 +1270,12 @@ void AudioTrack::AudioTrackThread::onFirstRef() // ========================================================================= + audio_track_cblk_t::audio_track_cblk_t() : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), - flags(0), sendLevel(0) + sendLevel(0), flags(0) { } @@ -1279,25 +1302,17 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) this->user = u; // Clear flow control error condition as new data has been written/read to/from buffer. - flags &= ~CBLK_UNDERRUN_MSK; + if (flags & CBLK_UNDERRUN_MSK) { + android_atomic_and(~CBLK_UNDERRUN_MSK, &flags); + } return u; } bool audio_track_cblk_t::stepServer(uint32_t frameCount) { - // the code below simulates lock-with-timeout - // we MUST do this to protect the AudioFlinger server - // as this lock is shared with the client. - status_t err; - - err = lock.tryLock(); - if (err == -EBUSY) { // just wait a bit - usleep(1000); - err = lock.tryLock(); - } - if (err != NO_ERROR) { - // probably, the client just died. + if (!tryLock()) { + LOGW("stepServer() could not lock cblk"); return false; } @@ -1374,18 +1389,42 @@ uint32_t audio_track_cblk_t::framesReady() if (u < loopEnd) { return u - s; } else { - Mutex::Autolock _l(lock); + // do not block on mutex shared with client on AudioFlinger side + if (!tryLock()) { + LOGW("framesReady() could not lock cblk"); + return 0; + } + uint32_t frames = UINT_MAX; if (loopCount >= 0) { - return (loopEnd - loopStart)*loopCount + u - s; - } else { - return UINT_MAX; + frames = (loopEnd - loopStart)*loopCount + u - s; } + lock.unlock(); + return frames; } } else { return s - u; } } +bool audio_track_cblk_t::tryLock() +{ + // the code below simulates lock-with-timeout + // we MUST do this to protect the AudioFlinger server + // as this lock is shared with the client. + status_t err; + + err = lock.tryLock(); + if (err == -EBUSY) { // just wait a bit + usleep(1000); + err = lock.tryLock(); + } + if (err != NO_ERROR) { + // probably, the client just died. + return false; + } + return true; +} + // ------------------------------------------------------------------------- }; // namespace android diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index b89a278..9fbcee0 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -25,6 +25,8 @@ #include <media/IAudioPolicyService.h> +#include <system/audio.h> + namespace android { enum { @@ -62,8 +64,8 @@ public: } virtual status_t setDeviceConnectionState( - AudioSystem::audio_devices device, - AudioSystem::device_connection_state state, + audio_devices_t device, + audio_policy_dev_state_t state, const char *device_address) { Parcel data, reply; @@ -75,8 +77,8 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual AudioSystem::device_connection_state getDeviceConnectionState( - AudioSystem::audio_devices device, + virtual audio_policy_dev_state_t getDeviceConnectionState( + audio_devices_t device, const char *device_address) { Parcel data, reply; @@ -84,7 +86,7 @@ public: data.writeInt32(static_cast <uint32_t>(device)); data.writeCString(device_address); remote()->transact(GET_DEVICE_CONNECTION_STATE, data, &reply); - return static_cast <AudioSystem::device_connection_state>(reply.readInt32()); + return static_cast <audio_policy_dev_state_t>(reply.readInt32()); } virtual status_t setPhoneState(int state) @@ -106,7 +108,7 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) + virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -116,21 +118,21 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage) + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast <uint32_t>(usage)); remote()->transact(GET_FORCE_USE, data, &reply); - return static_cast <AudioSystem::forced_config> (reply.readInt32()); + return static_cast <audio_policy_forced_cfg_t> (reply.readInt32()); } virtual audio_io_handle_t getOutput( - AudioSystem::stream_type stream, + audio_stream_type_t stream, uint32_t samplingRate, uint32_t format, uint32_t channels, - AudioSystem::output_flags flags) + audio_policy_output_flags_t flags) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -144,7 +146,7 @@ public: } virtual status_t startOutput(audio_io_handle_t output, - AudioSystem::stream_type stream, + audio_stream_type_t stream, int session) { Parcel data, reply; @@ -157,7 +159,7 @@ public: } virtual status_t stopOutput(audio_io_handle_t output, - AudioSystem::stream_type stream, + audio_stream_type_t stream, int session) { Parcel data, reply; @@ -182,7 +184,7 @@ public: uint32_t samplingRate, uint32_t format, uint32_t channels, - AudioSystem::audio_in_acoustics acoustics) + audio_in_acoustics_t acoustics) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -221,7 +223,7 @@ public: remote()->transact(RELEASE_INPUT, data, &reply); } - virtual status_t initStreamVolume(AudioSystem::stream_type stream, + virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) { @@ -234,7 +236,7 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index) + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -244,7 +246,7 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -255,7 +257,7 @@ public: return static_cast <status_t> (reply.readInt32()); } - virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream) + virtual uint32_t getStrategyForStream(audio_stream_type_t stream) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -264,7 +266,7 @@ public: return reply.readInt32(); } - virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream) + virtual uint32_t getDevicesForStream(audio_stream_type_t stream) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -330,10 +332,10 @@ status_t BnAudioPolicyService::onTransact( switch(code) { case SET_DEVICE_CONNECTION_STATE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::audio_devices device = - static_cast <AudioSystem::audio_devices>(data.readInt32()); - AudioSystem::device_connection_state state = - static_cast <AudioSystem::device_connection_state>(data.readInt32()); + audio_devices_t device = + static_cast <audio_devices_t>(data.readInt32()); + audio_policy_dev_state_t state = + static_cast <audio_policy_dev_state_t>(data.readInt32()); const char *device_address = data.readCString(); reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device, state, @@ -343,8 +345,8 @@ status_t BnAudioPolicyService::onTransact( case GET_DEVICE_CONNECTION_STATE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::audio_devices device = - static_cast<AudioSystem::audio_devices> (data.readInt32()); + audio_devices_t device = + static_cast<audio_devices_t> (data.readInt32()); const char *device_address = data.readCString(); reply->writeInt32(static_cast<uint32_t> (getDeviceConnectionState(device, device_address))); @@ -367,29 +369,29 @@ status_t BnAudioPolicyService::onTransact( case SET_FORCE_USE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32()); - AudioSystem::forced_config config = - static_cast <AudioSystem::forced_config>(data.readInt32()); + audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(data.readInt32()); + audio_policy_forced_cfg_t config = + static_cast <audio_policy_forced_cfg_t>(data.readInt32()); reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config))); return NO_ERROR; } break; case GET_FORCE_USE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32()); + audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(data.readInt32()); reply->writeInt32(static_cast <uint32_t>(getForceUse(usage))); return NO_ERROR; } break; case GET_OUTPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); uint32_t samplingRate = data.readInt32(); uint32_t format = data.readInt32(); uint32_t channels = data.readInt32(); - AudioSystem::output_flags flags = - static_cast <AudioSystem::output_flags>(data.readInt32()); + audio_policy_output_flags_t flags = + static_cast <audio_policy_output_flags_t>(data.readInt32()); audio_io_handle_t output = getOutput(stream, samplingRate, @@ -406,7 +408,7 @@ status_t BnAudioPolicyService::onTransact( uint32_t stream = data.readInt32(); int session = data.readInt32(); reply->writeInt32(static_cast <uint32_t>(startOutput(output, - (AudioSystem::stream_type)stream, + (audio_stream_type_t)stream, session))); return NO_ERROR; } break; @@ -417,7 +419,7 @@ status_t BnAudioPolicyService::onTransact( uint32_t stream = data.readInt32(); int session = data.readInt32(); reply->writeInt32(static_cast <uint32_t>(stopOutput(output, - (AudioSystem::stream_type)stream, + (audio_stream_type_t)stream, session))); return NO_ERROR; } break; @@ -435,8 +437,8 @@ status_t BnAudioPolicyService::onTransact( uint32_t samplingRate = data.readInt32(); uint32_t format = data.readInt32(); uint32_t channels = data.readInt32(); - AudioSystem::audio_in_acoustics acoustics = - static_cast <AudioSystem::audio_in_acoustics>(data.readInt32()); + audio_in_acoustics_t acoustics = + static_cast <audio_in_acoustics_t>(data.readInt32()); audio_io_handle_t input = getInput(inputSource, samplingRate, format, @@ -469,8 +471,8 @@ status_t BnAudioPolicyService::onTransact( case INIT_STREAM_VOLUME: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); int indexMin = data.readInt32(); int indexMax = data.readInt32(); reply->writeInt32(static_cast <uint32_t>(initStreamVolume(stream, indexMin,indexMax))); @@ -479,8 +481,8 @@ status_t BnAudioPolicyService::onTransact( case SET_STREAM_VOLUME: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); int index = data.readInt32(); reply->writeInt32(static_cast <uint32_t>(setStreamVolumeIndex(stream, index))); return NO_ERROR; @@ -488,8 +490,8 @@ status_t BnAudioPolicyService::onTransact( case GET_STREAM_VOLUME: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); int index; status_t status = getStreamVolumeIndex(stream, &index); reply->writeInt32(index); @@ -499,16 +501,16 @@ status_t BnAudioPolicyService::onTransact( case GET_STRATEGY_FOR_STREAM: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); reply->writeInt32(getStrategyForStream(stream)); return NO_ERROR; } break; case GET_DEVICES_FOR_STREAM: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - AudioSystem::stream_type stream = - static_cast <AudioSystem::stream_type>(data.readInt32()); + audio_stream_type_t stream = + static_cast <audio_stream_type_t>(data.readInt32()); reply->writeInt32(static_cast <int>(getDevicesForStream(stream))); return NO_ERROR; } break; diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index d5298c9..ebe821f 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -20,6 +20,7 @@ #include <binder/Parcel.h> #include <SkBitmap.h> #include <media/IMediaMetadataRetriever.h> +#include <utils/String8.h> // The binder is supposed to propagate the scheduler group across // the binder interface so that remote calls are executed with @@ -102,11 +103,24 @@ public: remote()->transact(DISCONNECT, data, &reply); } - status_t setDataSource(const char* srcUrl) + status_t setDataSource( + const char *srcUrl, const KeyedVector<String8, String8> *headers) { Parcel data, reply; data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); data.writeCString(srcUrl); + + if (headers == NULL) { + data.writeInt32(0); + } else { + // serialize the headers + data.writeInt32(headers->size()); + for (size_t i = 0; i < headers->size(); ++i) { + data.writeString8(headers->keyAt(i)); + data.writeString8(headers->valueAt(i)); + } + } + remote()->transact(SET_DATA_SOURCE_URL, data, &reply); return reply.readInt32(); } @@ -188,7 +202,18 @@ status_t BnMediaMetadataRetriever::onTransact( case SET_DATA_SOURCE_URL: { CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); const char* srcUrl = data.readCString(); - reply->writeInt32(setDataSource(srcUrl)); + + KeyedVector<String8, String8> headers; + int32_t numHeaders = data.readInt32(); + for (int i = 0; i < numHeaders; ++i) { + String8 key = data.readString8(); + String8 value = data.readString8(); + headers.add(key, value); + } + + reply->writeInt32( + setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL)); + return NO_ERROR; } break; case SET_DATA_SOURCE_FD: { diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 2399216..76a8a91 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -48,6 +48,8 @@ enum { SET_AUX_EFFECT_SEND_LEVEL, ATTACH_AUX_EFFECT, SET_VIDEO_SURFACETEXTURE, + SET_PARAMETER, + GET_PARAMETER, }; class BpMediaPlayer: public BpInterface<IMediaPlayer> @@ -192,8 +194,9 @@ public: } status_t invoke(const Parcel& request, Parcel *reply) - { // Avoid doing any extra copy. The interface descriptor should - // have been set by MediaPlayer.java. + { + // Avoid doing any extra copy. The interface descriptor should + // have been set by MediaPlayer.java. return remote()->transact(INVOKE, request, reply); } @@ -235,6 +238,26 @@ public: return reply.readInt32(); } + status_t setParameter(int key, const Parcel& request) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(key); + if (request.dataSize() > 0) { + data.appendFrom(const_cast<Parcel *>(&request), 0, request.dataSize()); + } + remote()->transact(SET_PARAMETER, data, &reply); + return reply.readInt32(); + } + + status_t getParameter(int key, Parcel *reply) + { + Parcel data; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(key); + return remote()->transact(GET_PARAMETER, data, reply); + } + }; IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer"); @@ -334,8 +357,8 @@ status_t BnMediaPlayer::onTransact( } break; case INVOKE: { CHECK_INTERFACE(IMediaPlayer, data, reply); - invoke(data, reply); - return NO_ERROR; + status_t result = invoke(data, reply); + return result; } break; case SET_METADATA_FILTER: { CHECK_INTERFACE(IMediaPlayer, data, reply); @@ -360,6 +383,23 @@ status_t BnMediaPlayer::onTransact( reply->writeInt32(attachAuxEffect(data.readInt32())); return NO_ERROR; } break; + case SET_PARAMETER: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int key = data.readInt32(); + + Parcel request; + if (data.dataAvail() > 0) { + request.appendFrom( + const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail()); + } + request.setDataPosition(0); + reply->writeInt32(setParameter(key, request)); + return NO_ERROR; + } break; + case GET_PARAMETER: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + return getParameter(data.readInt32(), reply); + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp index bf51829..1f135c4 100644 --- a/media/libmedia/IMediaPlayerClient.cpp +++ b/media/libmedia/IMediaPlayerClient.cpp @@ -35,13 +35,16 @@ public: { } - virtual void notify(int msg, int ext1, int ext2) + virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor()); data.writeInt32(msg); data.writeInt32(ext1); data.writeInt32(ext2); + if (obj && obj->dataSize() > 0) { + data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize()); + } remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); } }; @@ -59,7 +62,12 @@ status_t BnMediaPlayerClient::onTransact( int msg = data.readInt32(); int ext1 = data.readInt32(); int ext2 = data.readInt32(); - notify(msg, ext1, ext2); + Parcel obj; + if (data.dataAvail() > 0) { + obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail()); + } + + notify(msg, ext1, ext2, &obj); return NO_ERROR; } break; default: diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index ee9e1d8..88157d2 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -96,10 +96,10 @@ int JetPlayer::init() // create the output AudioTrack mAudioTrack = new AudioTrack(); - mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this + mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parametrize this pLibConfig->sampleRate, 1, // format = PCM 16bits per sample, - (pLibConfig->numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, + (pLibConfig->numChannels == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO, mTrackBufferSize, 0); diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index e6f3a33..069bbb7 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -356,6 +356,18 @@ MediaProfiles::getCameraId(const char** atts) return atoi(atts[1]); } +void MediaProfiles::addStartTimeOffset(int cameraId, const char** atts) +{ + int offsetTimeMs = 700; + if (atts[2]) { + CHECK(!strcmp("startOffsetMs", atts[2])); + offsetTimeMs = atoi(atts[3]); + } + + LOGV("%s: cameraId=%d, offset=%d ms", __func__, cameraId, offsetTimeMs); + mStartTimeOffsets.replaceValueFor(cameraId, offsetTimeMs); +} + /*static*/ void MediaProfiles::startElementHandler(void *userData, const char *name, const char **atts) { @@ -380,6 +392,7 @@ MediaProfiles::startElementHandler(void *userData, const char *name, const char profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts)); } else if (strcmp("CamcorderProfiles", name) == 0) { profiles->mCurrentCameraId = getCameraId(atts); + profiles->addStartTimeOffset(profiles->mCurrentCameraId, atts); } else if (strcmp("EncoderProfile", name) == 0) { profiles->mCamcorderProfiles.add( createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds)); @@ -997,6 +1010,16 @@ Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const return result; } +int MediaProfiles::getStartTimeOffsetMs(int cameraId) const { + int offsetTimeMs = -1; + ssize_t index = mStartTimeOffsets.indexOfKey(cameraId); + if (index >= 0) { + offsetTimeMs = mStartTimeOffsets.valueFor(cameraId); + } + LOGV("%s: offsetTime=%d ms and cameraId=%d", offsetTimeMs, cameraId); + return offsetTimeMs; +} + MediaProfiles::~MediaProfiles() { CHECK("destructor should never be called" == 0); diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp new file mode 100644 index 0000000..6a108ae --- /dev/null +++ b/media/libmedia/MemoryLeakTrackUtil.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <media/MemoryLeakTrackUtil.h> + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +/* + * The code here originally resided in MediaPlayerService.cpp and was + * shamelessly copied over to support memory leak tracking from + * multiple places. + */ +namespace android { + +#if defined(__arm__) + +extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, + size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); + +extern "C" void free_malloc_leak_info(uint8_t* info); + +// Use the String-class below instead of String8 to allocate all memory +// beforehand and not reenter the heap while we are examining it... +struct MyString8 { + static const size_t MAX_SIZE = 256 * 1024; + + MyString8() + : mPtr((char *)malloc(MAX_SIZE)) { + *mPtr = '\0'; + } + + ~MyString8() { + free(mPtr); + } + + void append(const char *s) { + strcat(mPtr, s); + } + + const char *string() const { + return mPtr; + } + + size_t size() const { + return strlen(mPtr); + } + +private: + char *mPtr; + + MyString8(const MyString8 &); + MyString8 &operator=(const MyString8 &); +}; + +void dumpMemoryAddresses(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + MyString8 result; + + typedef struct { + size_t size; + size_t dups; + intptr_t * backtrace; + } AllocEntry; + + uint8_t *info = NULL; + size_t overallSize = 0; + size_t infoSize = 0; + size_t totalMemory = 0; + size_t backtraceSize = 0; + + get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize); + if (info) { + uint8_t *ptr = info; + size_t count = overallSize / infoSize; + + snprintf(buffer, SIZE, " Allocation count %i\n", count); + result.append(buffer); + snprintf(buffer, SIZE, " Total memory %i\n", totalMemory); + result.append(buffer); + + AllocEntry * entries = new AllocEntry[count]; + + for (size_t i = 0; i < count; i++) { + // Each entry should be size_t, size_t, intptr_t[backtraceSize] + AllocEntry *e = &entries[i]; + + e->size = *reinterpret_cast<size_t *>(ptr); + ptr += sizeof(size_t); + + e->dups = *reinterpret_cast<size_t *>(ptr); + ptr += sizeof(size_t); + + e->backtrace = reinterpret_cast<intptr_t *>(ptr); + ptr += sizeof(intptr_t) * backtraceSize; + } + + // Now we need to sort the entries. They come sorted by size but + // not by stack trace which causes problems using diff. + bool moved; + do { + moved = false; + for (size_t i = 0; i < (count - 1); i++) { + AllocEntry *e1 = &entries[i]; + AllocEntry *e2 = &entries[i+1]; + + bool swap = e1->size < e2->size; + if (e1->size == e2->size) { + for(size_t j = 0; j < backtraceSize; j++) { + if (e1->backtrace[j] == e2->backtrace[j]) { + continue; + } + swap = e1->backtrace[j] < e2->backtrace[j]; + break; + } + } + if (swap) { + AllocEntry t = entries[i]; + entries[i] = entries[i+1]; + entries[i+1] = t; + moved = true; + } + } + } while (moved); + + for (size_t i = 0; i < count; i++) { + AllocEntry *e = &entries[i]; + + snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups); + result.append(buffer); + for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) { + if (ct) { + result.append(", "); + } + snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]); + result.append(buffer); + } + result.append("\n"); + } + + delete[] entries; + free_malloc_leak_info(info); + } + + write(fd, result.string(), result.size()); +} + +#else +// Does nothing +void dumpMemoryAddresses(int fd) {} + +#endif +} // namespace android diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 82fe2d4..9f1b3d6 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -1026,8 +1026,8 @@ bool ToneGenerator::initAudioTrack() { mpAudioTrack->set(mStreamType, 0, - AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_OUT_MONO, + AUDIO_FORMAT_PCM_16_BIT, + AUDIO_CHANNEL_OUT_MONO, 0, 0, audioCallback, diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index 43571cf..366707c 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -24,6 +24,8 @@ #include <sys/types.h> #include <limits.h> +#include <cutils/bitops.h> + #include <media/Visualizer.h> extern void fixed_fft_real(int n, int32_t *v); @@ -127,7 +129,7 @@ status_t Visualizer::setCaptureSize(uint32_t size) { if (size > VISUALIZER_CAPTURE_SIZE_MAX || size < VISUALIZER_CAPTURE_SIZE_MIN || - AudioSystem::popCount(size) != 1) { + popcount(size) != 1) { return BAD_VALUE; } diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index 8dfcb3b..cee06ab 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -92,7 +92,8 @@ void MediaMetadataRetriever::disconnect() } } -status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +status_t MediaMetadataRetriever::setDataSource( + const char *srcUrl, const KeyedVector<String8, String8> *headers) { LOGV("setDataSource"); Mutex::Autolock _l(mLock); @@ -105,7 +106,7 @@ status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) return UNKNOWN_ERROR; } LOGV("data source (%s)", srcUrl); - return mRetriever->setDataSource(srcUrl); + return mRetriever->setDataSource(srcUrl, headers); } status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 0ee0249..7b7ba74 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -37,6 +37,8 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> +#include <system/audio.h> + namespace android { MediaPlayer::MediaPlayer() @@ -45,7 +47,7 @@ MediaPlayer::MediaPlayer() mListener = NULL; mCookie = NULL; mDuration = -1; - mStreamType = AudioSystem::MUSIC; + mStreamType = AUDIO_STREAM_MUSIC; mCurrentPosition = -1; mSeekPosition = -1; mCurrentState = MEDIA_PLAYER_IDLE; @@ -551,7 +553,29 @@ status_t MediaPlayer::attachAuxEffect(int effectId) return mPlayer->attachAuxEffect(effectId); } -void MediaPlayer::notify(int msg, int ext1, int ext2) +status_t MediaPlayer::setParameter(int key, const Parcel& request) +{ + LOGV("MediaPlayer::setParameter(%d)", key); + Mutex::Autolock _l(mLock); + if (mPlayer != NULL) { + return mPlayer->setParameter(key, request); + } + LOGV("setParameter: no active player"); + return INVALID_OPERATION; +} + +status_t MediaPlayer::getParameter(int key, Parcel *reply) +{ + LOGV("MediaPlayer::getParameter(%d)", key); + Mutex::Autolock _l(mLock); + if (mPlayer != NULL) { + return mPlayer->getParameter(key, reply); + } + LOGV("getParameter: no active player"); + return INVALID_OPERATION; +} + +void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) { LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); bool send = true; @@ -641,6 +665,9 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) mVideoWidth = ext1; mVideoHeight = ext2; break; + case MEDIA_TIMED_TEXT: + LOGV("Received timed text message"); + break; default: LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); break; @@ -653,7 +680,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) if ((listener != 0) && send) { Mutex::Autolock _l(mNotifyLock); LOGV("callback application"); - listener->notify(msg, ext1, ext2); + listener->notify(msg, ext1, ext2, obj); LOGV("back from callback"); } } diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index e65f6d8..fadad28 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -31,8 +31,7 @@ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libstagefright \ libstagefright_omx \ - libstagefright_foundation \ - libsurfaceflinger_client \ + libstagefright_foundation \ libgui LOCAL_STATIC_LIBRARIES := \ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 0156634..d51c946 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -51,6 +51,9 @@ #include <media/MediaMetadataRetrieverInterface.h> #include <media/Metadata.h> #include <media/AudioTrack.h> +#include <media/MemoryLeakTrackUtil.h> + +#include <system/audio.h> #include <private/android_filesystem_config.h> @@ -392,139 +395,6 @@ static int myTid() { #endif } -#if defined(__arm__) -extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, - size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); -extern "C" void free_malloc_leak_info(uint8_t* info); - -// Use the String-class below instead of String8 to allocate all memory -// beforehand and not reenter the heap while we are examining it... -struct MyString8 { - static const size_t MAX_SIZE = 256 * 1024; - - MyString8() - : mPtr((char *)malloc(MAX_SIZE)) { - *mPtr = '\0'; - } - - ~MyString8() { - free(mPtr); - } - - void append(const char *s) { - strcat(mPtr, s); - } - - const char *string() const { - return mPtr; - } - - size_t size() const { - return strlen(mPtr); - } - -private: - char *mPtr; - - MyString8(const MyString8 &); - MyString8 &operator=(const MyString8 &); -}; - -void memStatus(int fd, const Vector<String16>& args) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - MyString8 result; - - typedef struct { - size_t size; - size_t dups; - intptr_t * backtrace; - } AllocEntry; - - uint8_t *info = NULL; - size_t overallSize = 0; - size_t infoSize = 0; - size_t totalMemory = 0; - size_t backtraceSize = 0; - - get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize); - if (info) { - uint8_t *ptr = info; - size_t count = overallSize / infoSize; - - snprintf(buffer, SIZE, " Allocation count %i\n", count); - result.append(buffer); - snprintf(buffer, SIZE, " Total memory %i\n", totalMemory); - result.append(buffer); - - AllocEntry * entries = new AllocEntry[count]; - - for (size_t i = 0; i < count; i++) { - // Each entry should be size_t, size_t, intptr_t[backtraceSize] - AllocEntry *e = &entries[i]; - - e->size = *reinterpret_cast<size_t *>(ptr); - ptr += sizeof(size_t); - - e->dups = *reinterpret_cast<size_t *>(ptr); - ptr += sizeof(size_t); - - e->backtrace = reinterpret_cast<intptr_t *>(ptr); - ptr += sizeof(intptr_t) * backtraceSize; - } - - // Now we need to sort the entries. They come sorted by size but - // not by stack trace which causes problems using diff. - bool moved; - do { - moved = false; - for (size_t i = 0; i < (count - 1); i++) { - AllocEntry *e1 = &entries[i]; - AllocEntry *e2 = &entries[i+1]; - - bool swap = e1->size < e2->size; - if (e1->size == e2->size) { - for(size_t j = 0; j < backtraceSize; j++) { - if (e1->backtrace[j] == e2->backtrace[j]) { - continue; - } - swap = e1->backtrace[j] < e2->backtrace[j]; - break; - } - } - if (swap) { - AllocEntry t = entries[i]; - entries[i] = entries[i+1]; - entries[i+1] = t; - moved = true; - } - } - } while (moved); - - for (size_t i = 0; i < count; i++) { - AllocEntry *e = &entries[i]; - - snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups); - result.append(buffer); - for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) { - if (ct) { - result.append(", "); - } - snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]); - result.append(buffer); - } - result.append("\n"); - } - - delete[] entries; - free_malloc_leak_info(info); - } - - write(fd, result.string(), result.size()); -} -#endif - status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -623,7 +493,6 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) result.append("\n"); } -#if defined(__arm__) bool dumpMem = false; for (size_t i = 0; i < args.size(); i++) { if (args[i] == String16("-m")) { @@ -631,9 +500,8 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) } } if (dumpMem) { - memStatus(fd, args); + dumpMemoryAddresses(fd); } -#endif } write(fd, result.string(), result.size()); return NO_ERROR; @@ -1156,7 +1024,22 @@ status_t MediaPlayerService::Client::attachAuxEffect(int effectId) return NO_ERROR; } -void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2) +status_t MediaPlayerService::Client::setParameter(int key, const Parcel &request) { + LOGV("[%d] setParameter(%d)", mConnId, key); + sp<MediaPlayerBase> p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->setParameter(key, request); +} + +status_t MediaPlayerService::Client::getParameter(int key, Parcel *reply) { + LOGV("[%d] getParameter(%d)", mConnId, key); + sp<MediaPlayerBase> p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->getParameter(key, reply); +} + +void MediaPlayerService::Client::notify( + void* cookie, int msg, int ext1, int ext2, const Parcel *obj) { Client* client = static_cast<Client*>(cookie); @@ -1173,7 +1056,7 @@ void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext client->addNewMetadataUpdate(metadata_type); } LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2); - client->mClient->notify(msg, ext1, ext2); + client->mClient->notify(msg, ext1, ext2, obj); } @@ -1342,7 +1225,7 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) mSessionId(sessionId) { LOGV("AudioOutput(%d)", sessionId); mTrack = 0; - mStreamType = AudioSystem::MUSIC; + mStreamType = AUDIO_STREAM_MUSIC; mLeftVolume = 1.0; mRightVolume = 1.0; mLatency = 0; @@ -1452,7 +1335,7 @@ status_t MediaPlayerService::AudioOutput::open( mStreamType, sampleRate, format, - (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, + (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO, frameCount, 0 /* flags */, CallbackWrapper, @@ -1464,7 +1347,7 @@ status_t MediaPlayerService::AudioOutput::open( mStreamType, sampleRate, format, - (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, + (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO, frameCount, 0, NULL, @@ -1583,8 +1466,15 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( size_t actualSize = (*me->mCallback)( me, buffer->raw, buffer->size, me->mCallbackCookie); - buffer->size = actualSize; + if (actualSize == 0 && buffer->size > 0) { + // We've reached EOS but the audio track is not stopped yet, + // keep playing silence. + memset(buffer->raw, 0, buffer->size); + actualSize = buffer->size; + } + + buffer->size = actualSize; } int MediaPlayerService::AudioOutput::getSessionId() @@ -1750,7 +1640,8 @@ status_t MediaPlayerService::AudioCache::wait() return mError; } -void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int ext2) +void MediaPlayerService::AudioCache::notify( + void* cookie, int msg, int ext1, int ext2, const Parcel *obj) { LOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2); AudioCache* p = static_cast<AudioCache*>(cookie); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index ff6ccf5..8bab471 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -30,6 +30,8 @@ #include <media/MediaPlayerInterface.h> #include <media/Metadata.h> +#include <system/audio.h> + namespace android { class IMediaRecorder; @@ -130,7 +132,7 @@ class MediaPlayerService : public BnMediaPlayerService virtual ssize_t bufferSize() const { return frameSize() * mFrameCount; } virtual ssize_t frameCount() const { return mFrameCount; } virtual ssize_t channelCount() const { return (ssize_t)mChannelCount; } - virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); } + virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AUDIO_FORMAT_PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); } virtual uint32_t latency() const; virtual float msecsPerFrame() const; virtual status_t getPosition(uint32_t *position); @@ -156,7 +158,8 @@ class MediaPlayerService : public BnMediaPlayerService sp<IMemoryHeap> getHeap() const { return mHeap; } - static void notify(void* cookie, int msg, int ext1, int ext2); + static void notify(void* cookie, int msg, + int ext1, int ext2, const Parcel *obj); virtual status_t dump(int fd, const Vector<String16>& args) const; private: @@ -276,6 +279,8 @@ private: Parcel *reply); virtual status_t setAuxEffectSendLevel(float level); virtual status_t attachAuxEffect(int effectId); + virtual status_t setParameter(int key, const Parcel &request); + virtual status_t getParameter(int key, Parcel *reply); sp<MediaPlayerBase> createPlayer(player_type playerType); @@ -287,7 +292,8 @@ private: status_t setDataSource(const sp<IStreamSource> &source); - static void notify(void* cookie, int msg, int ext1, int ext2); + static void notify(void* cookie, int msg, + int ext1, int ext2, const Parcel *obj); pid_t pid() const { return mPid; } virtual status_t dump(int fd, const Vector<String16>& args) const; diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 1a1780c..29cc019 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -35,6 +35,8 @@ #include <media/AudioTrack.h> +#include <system/audio.h> + #include "MediaRecorderClient.h" #include "MediaPlayerService.h" @@ -102,7 +104,7 @@ status_t MediaRecorderClient::setAudioSource(int as) LOGE("recorder is not initialized"); return NO_INIT; } - return mRecorder->setAudioSource((audio_source)as); + return mRecorder->setAudioSource((audio_source_t)as); } status_t MediaRecorderClient::setOutputFormat(int of) diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 5fcf2a7..8f776b4 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -120,7 +120,8 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) return p; } -status_t MetadataRetrieverClient::setDataSource(const char *url) +status_t MetadataRetrieverClient::setDataSource( + const char *url, const KeyedVector<String8, String8> *headers) { LOGV("setDataSource(%s)", url); Mutex::Autolock lock(mLock); @@ -131,7 +132,7 @@ status_t MetadataRetrieverClient::setDataSource(const char *url) LOGV("player type = %d", playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL) return NO_INIT; - status_t ret = p->setDataSource(url); + status_t ret = p->setDataSource(url, headers); if (ret == NO_ERROR) mRetriever = p; return ret; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h index b834715..f08f933 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.h +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -41,7 +41,10 @@ public: // Implements IMediaMetadataRetriever interface // These methods are called in IMediaMetadataRetriever.cpp? virtual void disconnect(); - virtual status_t setDataSource(const char *url); + + virtual status_t setDataSource( + const char *url, const KeyedVector<String8, String8> *headers); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option); virtual sp<IMemory> extractAlbumArt(); diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 1b0b05f..589c625 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -30,6 +30,8 @@ #include <sys/types.h> #include <sys/stat.h> +#include <system/audio.h> + #include "MidiFile.h" #ifdef HAVE_GETTID @@ -58,7 +60,7 @@ static const S_EAS_LIB_CONFIG* pLibConfig = NULL; MidiFile::MidiFile() : mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL), mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR), - mStreamType(AudioSystem::MUSIC), mLoop(false), mExit(false), + mStreamType(AUDIO_STREAM_MUSIC), mLoop(false), mExit(false), mPaused(false), mRender(false), mTid(-1) { LOGV("constructor"); @@ -423,7 +425,7 @@ status_t MidiFile::setLooping(int loop) } status_t MidiFile::createOutputTrack() { - if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AudioSystem::PCM_16_BIT, 2) != NO_ERROR) { + if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) { LOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index a98231c..b35696f 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -55,6 +55,13 @@ public: virtual status_t invoke(const Parcel& request, Parcel *reply) { return INVALID_OPERATION; } + virtual status_t setParameter(int key, const Parcel &request) { + return INVALID_OPERATION; + } + virtual status_t getParameter(int key, Parcel *reply) { + return INVALID_OPERATION; + } + private: status_t createOutputTrack(); diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp index ad95fac..aaf2d18 100644 --- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp +++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp @@ -35,7 +35,8 @@ void MidiMetadataRetriever::clearMetadataValues() mMetadataValues[0][0] = '\0'; } -status_t MidiMetadataRetriever::setDataSource(const char *url) +status_t MidiMetadataRetriever::setDataSource( + const char *url, const KeyedVector<String8, String8> *headers) { LOGV("setDataSource: %s", url? url: "NULL pointer"); Mutex::Autolock lock(mLock); @@ -43,8 +44,7 @@ status_t MidiMetadataRetriever::setDataSource(const char *url) if (mMidiPlayer == 0) { mMidiPlayer = new MidiFile(); } - // TODO: support headers in MetadataRetriever interface! - return mMidiPlayer->setDataSource(url, NULL /* headers */); + return mMidiPlayer->setDataSource(url, headers); } status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h index 73ff347..4cee42d 100644 --- a/media/libmediaplayerservice/MidiMetadataRetriever.h +++ b/media/libmediaplayerservice/MidiMetadataRetriever.h @@ -31,7 +31,9 @@ public: MidiMetadataRetriever() {} ~MidiMetadataRetriever() {} - virtual status_t setDataSource(const char *url); + virtual status_t setDataSource( + const char *url, const KeyedVector<String8, String8> *headers); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual const char* extractMetadata(int keyCode); diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index c5cbd23..02ec911 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -110,7 +110,7 @@ bool StagefrightPlayer::isPlaying() { } status_t StagefrightPlayer::seekTo(int msec) { - LOGV("seekTo"); + LOGV("seekTo %.2f secs", msec / 1E3); status_t err = mPlayer->seekTo((int64_t)msec * 1000); @@ -177,6 +177,16 @@ void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) { mPlayer->setAudioSink(audioSink); } +status_t StagefrightPlayer::setParameter(int key, const Parcel &request) { + LOGV("setParameter"); + return mPlayer->setParameter(key, request); +} + +status_t StagefrightPlayer::getParameter(int key, Parcel *reply) { + LOGV("getParameter"); + return mPlayer->getParameter(key, reply); +} + status_t StagefrightPlayer::getMetadata( const media::Metadata::Filter& ids, Parcel *records) { using media::Metadata; diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index e2796d2..ddd37e4 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -55,6 +55,8 @@ public: virtual player_type playerType(); virtual status_t invoke(const Parcel &request, Parcel *reply); virtual void setAudioSink(const sp<AudioSink> &audioSink); + virtual status_t setParameter(int key, const Parcel &request); + virtual status_t getParameter(int key, Parcel *reply); virtual status_t getMetadata( const media::Metadata::Filter& ids, Parcel *records); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index e3dfabb..978571c 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -26,6 +26,7 @@ #include <media/IMediaPlayerService.h> #include <media/stagefright/AudioSource.h> #include <media/stagefright/AMRWriter.h> +#include <media/stagefright/AACWriter.h> #include <media/stagefright/CameraSource.h> #include <media/stagefright/VideoSourceDownSampler.h> #include <media/stagefright/CameraSourceTimeLapse.h> @@ -46,6 +47,8 @@ #include <ctype.h> #include <unistd.h> +#include <system/audio.h> + #include "ARTPWriter.h" namespace android { @@ -64,7 +67,7 @@ static void addBatteryData(uint32_t params) { StagefrightRecorder::StagefrightRecorder() : mWriter(NULL), mWriterAux(NULL), mOutputFd(-1), mOutputFdAux(-1), - mAudioSource(AUDIO_SOURCE_LIST_END), + mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), mStarted(false) { @@ -82,10 +85,10 @@ status_t StagefrightRecorder::init() { return OK; } -status_t StagefrightRecorder::setAudioSource(audio_source as) { +status_t StagefrightRecorder::setAudioSource(audio_source_t as) { LOGV("setAudioSource: %d", as); if (as < AUDIO_SOURCE_DEFAULT || - as >= AUDIO_SOURCE_LIST_END) { + as >= AUDIO_SOURCE_CNT) { LOGE("Invalid audio source: %d", as); return BAD_VALUE; } @@ -590,6 +593,26 @@ status_t StagefrightRecorder::setParamAuxVideoEncodingBitRate(int32_t bitRate) { return OK; } +status_t StagefrightRecorder::setParamGeoDataLongitude( + int32_t longitudex10000) { + + if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { + return BAD_VALUE; + } + mLongitudex10000 = longitudex10000; + return OK; +} + +status_t StagefrightRecorder::setParamGeoDataLatitude( + int32_t latitudex10000) { + + if (latitudex10000 > 900000 || latitudex10000 < -900000) { + return BAD_VALUE; + } + mLatitudex10000 = latitudex10000; + return OK; +} + status_t StagefrightRecorder::setParameter( const String8 &key, const String8 &value) { LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); @@ -618,6 +641,16 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &use64BitOffset)) { return setParam64BitFileOffset(use64BitOffset != 0); } + } else if (key == "param-geotag-longitude") { + int32_t longitudex10000; + if (safe_strtoi32(value.string(), &longitudex10000)) { + return setParamGeoDataLongitude(longitudex10000); + } + } else if (key == "param-geotag-latitude") { + int32_t latitudex10000; + if (safe_strtoi32(value.string(), &latitudex10000)) { + return setParamGeoDataLatitude(latitudex10000); + } } else if (key == "param-track-time-status") { int64_t timeDurationUs; if (safe_strtoi64(value.string(), &timeDurationUs)) { @@ -800,7 +833,7 @@ status_t StagefrightRecorder::start() { mStarted = true; uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted; - if (mAudioSource != AUDIO_SOURCE_LIST_END) { + if (mAudioSource != AUDIO_SOURCE_CNT) { params |= IMediaPlayerService::kBatteryDataTrackAudio; } if (mVideoSource != VIDEO_SOURCE_LIST_END) { @@ -870,15 +903,21 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { } status_t StagefrightRecorder::startAACRecording() { - CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF || - mOutputFormat == OUTPUT_FORMAT_AAC_ADTS); + // FIXME: + // Add support for OUTPUT_FORMAT_AAC_ADIF + CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADTS); CHECK(mAudioEncoder == AUDIO_ENCODER_AAC); - CHECK(mAudioSource != AUDIO_SOURCE_LIST_END); + CHECK(mAudioSource != AUDIO_SOURCE_CNT); - CHECK(0 == "AACWriter is not implemented yet"); + mWriter = new AACWriter(mOutputFd); + status_t status = startRawAudioRecording(); + if (status != OK) { + mWriter.clear(); + mWriter = NULL; + } - return OK; + return status; } status_t StagefrightRecorder::startAMRRecording() { @@ -900,7 +939,17 @@ status_t StagefrightRecorder::startAMRRecording() { } } - if (mAudioSource >= AUDIO_SOURCE_LIST_END) { + mWriter = new AMRWriter(mOutputFd); + status_t status = startRawAudioRecording(); + if (status != OK) { + mWriter.clear(); + mWriter = NULL; + } + return status; +} + +status_t StagefrightRecorder::startRawAudioRecording() { + if (mAudioSource >= AUDIO_SOURCE_CNT) { LOGE("Invalid audio source: %d", mAudioSource); return BAD_VALUE; } @@ -915,7 +964,7 @@ status_t StagefrightRecorder::startAMRRecording() { return UNKNOWN_ERROR; } - mWriter = new AMRWriter(mOutputFd); + CHECK(mWriter != 0); mWriter->addSource(audioEncoder); if (mMaxFileDurationUs != 0) { @@ -933,9 +982,9 @@ status_t StagefrightRecorder::startAMRRecording() { status_t StagefrightRecorder::startRTPRecording() { CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP); - if ((mAudioSource != AUDIO_SOURCE_LIST_END + if ((mAudioSource != AUDIO_SOURCE_CNT && mVideoSource != VIDEO_SOURCE_LIST_END) - || (mAudioSource == AUDIO_SOURCE_LIST_END + || (mAudioSource == AUDIO_SOURCE_CNT && mVideoSource == VIDEO_SOURCE_LIST_END)) { // Must have exactly one source. return BAD_VALUE; @@ -947,7 +996,7 @@ status_t StagefrightRecorder::startRTPRecording() { sp<MediaSource> source; - if (mAudioSource != AUDIO_SOURCE_LIST_END) { + if (mAudioSource != AUDIO_SOURCE_CNT) { source = createAudioSource(); } else { @@ -975,7 +1024,7 @@ status_t StagefrightRecorder::startMPEG2TSRecording() { sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd); - if (mAudioSource != AUDIO_SOURCE_LIST_END) { + if (mAudioSource != AUDIO_SOURCE_CNT) { if (mAudioEncoder != AUDIO_ENCODER_AAC) { return ERROR_UNSUPPORTED; } @@ -1383,7 +1432,7 @@ status_t StagefrightRecorder::setupMPEG4Recording( // Audio source is added at the end if it exists. // This help make sure that the "recoding" sound is suppressed for // camcorder applications in the recorded files. - if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) { + if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) { err = setupAudioEncoder(writer); if (err != OK) return err; *totalBitRate += mAudioBitRate; @@ -1393,6 +1442,10 @@ status_t StagefrightRecorder::setupMPEG4Recording( reinterpret_cast<MPEG4Writer *>(writer.get())-> setInterleaveDuration(mInterleaveDurationUs); } + if (mLongitudex10000 > -3600000 && mLatitudex10000 > -3600000) { + reinterpret_cast<MPEG4Writer *>(writer.get())-> + setGeoData(mLatitudex10000, mLongitudex10000); + } if (mMaxFileDurationUs != 0) { writer->setMaxFileDuration(mMaxFileDurationUs); } @@ -1400,6 +1453,12 @@ status_t StagefrightRecorder::setupMPEG4Recording( writer->setMaxFileSize(mMaxFileSizeBytes); } + mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId); + if (mStartTimeOffsetMs > 0) { + reinterpret_cast<MPEG4Writer *>(writer.get())-> + setStartTimeOffsetMs(mStartTimeOffsetMs); + } + writer->setListener(mListener); *mediaWriter = writer; return OK; @@ -1504,7 +1563,7 @@ status_t StagefrightRecorder::pause() { mStarted = false; uint32_t params = 0; - if (mAudioSource != AUDIO_SOURCE_LIST_END) { + if (mAudioSource != AUDIO_SOURCE_CNT) { params |= IMediaPlayerService::kBatteryDataTrackAudio; } if (mVideoSource != VIDEO_SOURCE_LIST_END) { @@ -1555,7 +1614,7 @@ status_t StagefrightRecorder::stop() { mStarted = false; uint32_t params = 0; - if (mAudioSource != AUDIO_SOURCE_LIST_END) { + if (mAudioSource != AUDIO_SOURCE_CNT) { params |= IMediaPlayerService::kBatteryDataTrackAudio; } if (mVideoSource != VIDEO_SOURCE_LIST_END) { @@ -1581,7 +1640,7 @@ status_t StagefrightRecorder::reset() { stop(); // No audio or video source by default - mAudioSource = AUDIO_SOURCE_LIST_END; + mAudioSource = AUDIO_SOURCE_CNT; mVideoSource = VIDEO_SOURCE_LIST_END; // Default parameters @@ -1606,6 +1665,7 @@ status_t StagefrightRecorder::reset() { mAudioTimeScale = -1; mVideoTimeScale = -1; mCameraId = 0; + mStartTimeOffsetMs = -1; mVideoEncoderProfile = -1; mVideoEncoderLevel = -1; mMaxFileDurationUs = 0; @@ -1619,6 +1679,8 @@ status_t StagefrightRecorder::reset() { mIsMetaDataStoredInVideoBuffers = false; mEncoderProfiles = MediaProfiles::getInstance(); mRotationDegrees = 0; + mLatitudex10000 = -3600000; + mLongitudex10000 = -3600000; mOutputFd = -1; mOutputFdAux = -1; @@ -1692,6 +1754,8 @@ status_t StagefrightRecorder::dump( result.append(buffer); snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); result.append(buffer); + snprintf(buffer, SIZE, " Start time offset (ms): %d\n", mStartTimeOffsetMs); + result.append(buffer); snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder); result.append(buffer); snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 2c440c1..aa67aa7 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -22,6 +22,8 @@ #include <camera/CameraParameters.h> #include <utils/String8.h> +#include <system/audio.h> + namespace android { class Camera; @@ -39,7 +41,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual ~StagefrightRecorder(); virtual status_t init(); - virtual status_t setAudioSource(audio_source as); + virtual status_t setAudioSource(audio_source_t as); virtual status_t setVideoSource(video_source vs); virtual status_t setOutputFormat(output_format of); virtual status_t setAudioEncoder(audio_encoder ae); @@ -67,9 +69,10 @@ private: sp<Surface> mPreviewSurface; sp<IMediaRecorderClient> mListener; sp<MediaWriter> mWriter, mWriterAux; + int mOutputFd, mOutputFdAux; sp<AudioSource> mAudioSourceNode; - audio_source mAudioSource; + audio_source_t mAudioSource; video_source mVideoSource; output_format mOutputFormat; audio_encoder mAudioEncoder; @@ -94,6 +97,9 @@ private: int64_t mMaxFileDurationUs; int64_t mTrackEveryTimeDurationUs; int32_t mRotationDegrees; // Clockwise + int32_t mLatitudex10000; + int32_t mLongitudex10000; + int32_t mStartTimeOffsetMs; bool mCaptureTimeLapse; int64_t mTimeBetweenTimeLapseFrameCaptureUs; @@ -102,7 +108,6 @@ private: sp<CameraSourceTimeLapse> mCameraSourceTimeLapse; String8 mParams; - int mOutputFd, mOutputFdAux; bool mIsMetaDataStoredInVideoBuffers; MediaProfiles *mEncoderProfiles; @@ -121,6 +126,7 @@ private: status_t startMPEG4Recording(); status_t startAMRRecording(); status_t startAACRecording(); + status_t startRawAudioRecording(); status_t startRTPRecording(); status_t startMPEG2TSRecording(); sp<MediaSource> createAudioSource(); @@ -157,6 +163,8 @@ private: status_t setParamMaxFileDurationUs(int64_t timeUs); status_t setParamMaxFileSizeBytes(int64_t bytes); status_t setParamMovieTimeScale(int32_t timeScale); + status_t setParamGeoDataLongitude(int32_t longitudex10000); + status_t setParamGeoDataLatitude(int32_t latitudex10000); void clipVideoBitRate(); void clipVideoFrameRate(); void clipVideoFrameWidth(); diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h index d9c3db3..802a11b 100644 --- a/media/libmediaplayerservice/TestPlayerStub.h +++ b/media/libmediaplayerservice/TestPlayerStub.h @@ -99,6 +99,12 @@ class TestPlayerStub : public MediaPlayerInterface { virtual status_t invoke(const android::Parcel& in, android::Parcel *out) { return mPlayer->invoke(in, out); } + virtual status_t setParameter(int key, const Parcel &request) { + return mPlayer->setParameter(key, request); + } + virtual status_t getParameter(int key, Parcel *reply) { + return mPlayer->getParameter(key, reply); + } // @return true if the current build is 'eng' or 'test' and the diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk index c20e279..e761509 100644 --- a/media/libmediaplayerservice/nuplayer/Android.mk +++ b/media/libmediaplayerservice/nuplayer/Android.mk @@ -8,7 +8,6 @@ LOCAL_SRC_FILES:= \ NuPlayerDriver.cpp \ NuPlayerRenderer.cpp \ NuPlayerStreamListener.cpp \ - DecoderWrapper.cpp \ StreamingSource.cpp \ LOCAL_C_INCLUDES := \ diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp deleted file mode 100644 index 802d1fb..0000000 --- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "DecoderWrapper" -#include <utils/Log.h> - -#include "DecoderWrapper.h" - -#include "AACDecoder.h" - -#include <media/stagefright/foundation/hexdump.h> -#include <media/stagefright/foundation/ABuffer.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/ACodec.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaSource.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -struct DecoderWrapper::WrapperSource : public MediaSource { - WrapperSource( - const sp<MetaData> &meta, - const sp<AMessage> ¬ify); - - virtual status_t start(MetaData *params); - virtual status_t stop(); - virtual sp<MetaData> getFormat(); - - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options); - - void queueBuffer(const sp<ABuffer> &buffer); - void queueEOS(status_t finalResult); - void clear(); - -protected: - virtual ~WrapperSource(); - -private: - Mutex mLock; - Condition mCondition; - - sp<MetaData> mMeta; - sp<AMessage> mNotify; - - List<sp<ABuffer> > mQueue; - status_t mFinalResult; - - DISALLOW_EVIL_CONSTRUCTORS(WrapperSource); -}; - -DecoderWrapper::WrapperSource::WrapperSource( - const sp<MetaData> &meta, const sp<AMessage> ¬ify) - : mMeta(meta), - mNotify(notify), - mFinalResult(OK) { -} - -DecoderWrapper::WrapperSource::~WrapperSource() { -} - -status_t DecoderWrapper::WrapperSource::start(MetaData *params) { - return OK; -} - -status_t DecoderWrapper::WrapperSource::stop() { - return OK; -} - -sp<MetaData> DecoderWrapper::WrapperSource::getFormat() { - return mMeta; -} - -status_t DecoderWrapper::WrapperSource::read( - MediaBuffer **out, const ReadOptions *options) { - Mutex::Autolock autoLock(mLock); - - bool requestedBuffer = false; - - while (mQueue.empty() && mFinalResult == OK) { - if (!requestedBuffer) { - mNotify->dup()->post(); - requestedBuffer = true; - } - - mCondition.wait(mLock); - } - - if (mQueue.empty()) { - return mFinalResult; - } - - sp<ABuffer> src = *mQueue.begin(); - mQueue.erase(mQueue.begin()); - - MediaBuffer *dst = new MediaBuffer(src->size()); - memcpy(dst->data(), src->data(), src->size()); - - int64_t timeUs; - CHECK(src->meta()->findInt64("timeUs", &timeUs)); - - dst->meta_data()->setInt64(kKeyTime, timeUs); - - *out = dst; - - return OK; -} - -void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) { - Mutex::Autolock autoLock(mLock); - mQueue.push_back(buffer); - mCondition.broadcast(); -} - -void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) { - CHECK_NE(finalResult, (status_t)OK); - - Mutex::Autolock autoLock(mLock); - mFinalResult = finalResult; - mCondition.broadcast(); -} - -void DecoderWrapper::WrapperSource::clear() { - Mutex::Autolock autoLock(mLock); - mQueue.clear(); - mFinalResult = OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -struct DecoderWrapper::WrapperReader : public AHandler { - WrapperReader( - const sp<MediaSource> &decoder, - const sp<AMessage> ¬ify); - - void start(); - void stop(); - void readMore(bool flush = false); - -protected: - virtual ~WrapperReader(); - - virtual void onMessageReceived(const sp<AMessage> &msg); - -private: - enum { - kWhatRead - }; - - sp<MediaSource> mDecoder; - sp<AMessage> mNotify; - bool mEOS; - bool mSentFormat; - - void sendFormatChange(); - - DISALLOW_EVIL_CONSTRUCTORS(WrapperReader); -}; - -DecoderWrapper::WrapperReader::WrapperReader( - const sp<MediaSource> &decoder, const sp<AMessage> ¬ify) - : mDecoder(decoder), - mNotify(notify), - mEOS(false), - mSentFormat(false) { -} - -DecoderWrapper::WrapperReader::~WrapperReader() { -} - -void DecoderWrapper::WrapperReader::start() { - CHECK_EQ(mDecoder->start(), (status_t)OK); - readMore(); -} - -void DecoderWrapper::WrapperReader::stop() { - CHECK_EQ(mDecoder->stop(), (status_t)OK); -} - -void DecoderWrapper::WrapperReader::readMore(bool flush) { - if (!flush && mEOS) { - return; - } - - sp<AMessage> msg = new AMessage(kWhatRead, id()); - msg->setInt32("flush", static_cast<int32_t>(flush)); - msg->post(); -} - -void DecoderWrapper::WrapperReader::onMessageReceived( - const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatRead: - { - int32_t flush; - CHECK(msg->findInt32("flush", &flush)); - - MediaSource::ReadOptions options; - if (flush) { - // Dummy seek - options.setSeekTo(0); - mEOS = false; - } - - CHECK(!mEOS); - - MediaBuffer *src; - status_t err = mDecoder->read(&src, &options); - - if (err == OK) { - if (!mSentFormat) { - sendFormatChange(); - mSentFormat = true; - } - - sp<AMessage> notify = mNotify->dup(); - - sp<AMessage> realNotify; - CHECK(notify->findMessage("real-notify", &realNotify)); - - realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer); - - sp<ABuffer> dst = new ABuffer(src->range_length()); - memcpy(dst->data(), - (const uint8_t *)src->data() + src->range_offset(), - src->range_length()); - - int64_t timeUs; - CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs)); - src->release(); - src = NULL; - - dst->meta()->setInt64("timeUs", timeUs); - - realNotify->setObject("buffer", dst); - - notify->post(); - } else if (err == INFO_FORMAT_CHANGED) { - sendFormatChange(); - - readMore(false /* flush */); - } else { - sp<AMessage> notify = mNotify->dup(); - - sp<AMessage> realNotify; - CHECK(notify->findMessage("real-notify", &realNotify)); - - realNotify->setInt32("what", ACodec::kWhatEOS); - mEOS = true; - - notify->post(); - } - break; - } - - default: - TRESPASS(); - break; - } -} - -void DecoderWrapper::WrapperReader::sendFormatChange() { - sp<AMessage> notify = mNotify->dup(); - - sp<AMessage> realNotify; - CHECK(notify->findMessage("real-notify", &realNotify)); - - realNotify->setInt32("what", ACodec::kWhatOutputFormatChanged); - - sp<MetaData> meta = mDecoder->getFormat(); - - const char *mime; - CHECK(meta->findCString(kKeyMIMEType, &mime)); - - realNotify->setString("mime", mime); - - if (!strncasecmp("audio/", mime, 6)) { - int32_t numChannels; - CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); - - int32_t sampleRate; - CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); - - realNotify->setInt32("channel-count", numChannels); - realNotify->setInt32("sample-rate", sampleRate); - } else { - CHECK(!strncasecmp("video/", mime, 6)); - - int32_t width, height; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); - - realNotify->setInt32("width", width); - realNotify->setInt32("height", height); - - int32_t cropLeft, cropTop, cropRight, cropBottom; - if (!meta->findRect( - kKeyCropRect, - &cropLeft, &cropTop, &cropRight, &cropBottom)) { - cropLeft = 0; - cropTop = 0; - cropRight = width - 1; - cropBottom = height - 1; - } - - realNotify->setRect("crop", cropLeft, cropTop, cropRight, cropBottom); - } - - notify->post(); - - mSentFormat = true; -} - -//////////////////////////////////////////////////////////////////////////////// - -DecoderWrapper::DecoderWrapper() - : mNumOutstandingInputBuffers(0), - mNumOutstandingOutputBuffers(0), - mNumPendingDecodes(0), - mFlushing(false) { -} - -DecoderWrapper::~DecoderWrapper() { -} - -void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) { - mNotify = msg; -} - -void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) { - msg->setWhat(kWhatSetup); - msg->setTarget(id()); - msg->post(); -} - -void DecoderWrapper::initiateShutdown() { - (new AMessage(kWhatShutdown, id()))->post(); -} - -void DecoderWrapper::signalFlush() { - (new AMessage(kWhatFlush, id()))->post(); -} - -void DecoderWrapper::signalResume() { - (new AMessage(kWhatResume, id()))->post(); -} - -void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatSetup: - onSetup(msg); - break; - - case kWhatShutdown: - onShutdown(); - break; - - case kWhatInputDataRequested: - { - postFillBuffer(); - ++mNumOutstandingInputBuffers; - break; - } - - case kWhatInputBufferFilled: - { - CHECK_GT(mNumOutstandingInputBuffers, 0); - --mNumOutstandingInputBuffers; - - if (mFlushing) { - mSource->queueEOS(INFO_DISCONTINUITY); - - completeFlushIfPossible(); - break; - } - - sp<RefBase> obj; - if (!msg->findObject("buffer", &obj)) { - int32_t err = OK; - CHECK(msg->findInt32("err", &err)); - - mSource->queueEOS(err); - break; - } - - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); - - mSource->queueBuffer(buffer); - break; - } - - case kWhatFillBufferDone: - { - sp<AMessage> notify; - CHECK(msg->findMessage("real-notify", ¬ify)); - - int32_t what; - CHECK(notify->findInt32("what", &what)); - - if (what == ACodec::kWhatDrainThisBuffer) { - CHECK_GT(mNumPendingDecodes, 0); - --mNumPendingDecodes; - - sp<AMessage> reply = - new AMessage(kWhatOutputBufferDrained, id()); - - notify->setMessage("reply", reply); - - ++mNumOutstandingOutputBuffers; - } else if (what == ACodec::kWhatEOS) { - CHECK_GT(mNumPendingDecodes, 0); - --mNumPendingDecodes; - - if (mFlushing) { - completeFlushIfPossible(); - break; - } - } - - notify->post(); - break; - } - - case kWhatOutputBufferDrained: - { - CHECK_GT(mNumOutstandingOutputBuffers, 0); - --mNumOutstandingOutputBuffers; - - if (mFlushing) { - completeFlushIfPossible(); - break; - } - - ++mNumPendingDecodes; - mReader->readMore(); - break; - } - - case kWhatFlush: - { - onFlush(); - break; - } - - case kWhatResume: - { - onResume(); - break; - } - - default: - TRESPASS(); - break; - } -} - -void DecoderWrapper::onSetup(const sp<AMessage> &msg) { - AString mime; - CHECK(msg->findString("mime", &mime)); - - CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)); - - int32_t numChannels, sampleRate; - CHECK(msg->findInt32("channel-count", &numChannels)); - CHECK(msg->findInt32("sample-rate", &sampleRate)); - - sp<RefBase> obj; - CHECK(msg->findObject("esds", &obj)); - sp<ABuffer> esds = static_cast<ABuffer *>(obj.get()); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, mime.c_str()); - meta->setInt32(kKeySampleRate, sampleRate); - meta->setInt32(kKeyChannelCount, numChannels); - meta->setData(kKeyESDS, 0, esds->data(), esds->size()); - - mSource = new WrapperSource( - meta, new AMessage(kWhatInputDataRequested, id())); - - sp<MediaSource> decoder = new AACDecoder(mSource); - - mReaderLooper = new ALooper; - mReaderLooper->setName("DecoderWrapper looper"); - - mReaderLooper->start( - false, /* runOnCallingThread */ - false, /* canCallJava */ - PRIORITY_AUDIO); - - sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id()); - notify->setMessage("real-notify", mNotify); - - mReader = new WrapperReader(decoder, notify); - mReaderLooper->registerHandler(mReader); - - mReader->start(); - ++mNumPendingDecodes; -} - -void DecoderWrapper::onShutdown() { - mReaderLooper->stop(); - mReaderLooper.clear(); - - mReader->stop(); - mReader.clear(); - - mSource.clear(); - - mNumOutstandingInputBuffers = 0; - mNumOutstandingOutputBuffers = 0; - mNumPendingDecodes = 0; - mFlushing = false; - - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", ACodec::kWhatShutdownCompleted); - notify->post(); -} - -void DecoderWrapper::postFillBuffer() { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", ACodec::kWhatFillThisBuffer); - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); - notify->setMessage("reply", reply); - notify->post(); -} - -void DecoderWrapper::onFlush() { - CHECK(!mFlushing); - mFlushing = true; - - completeFlushIfPossible(); -} - -void DecoderWrapper::completeFlushIfPossible() { - CHECK(mFlushing); - - if (mNumOutstandingInputBuffers > 0 - || mNumOutstandingOutputBuffers > 0 - || mNumPendingDecodes > 0) { - return; - } - - mFlushing = false; - - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", ACodec::kWhatFlushCompleted); - notify->post(); -} - -void DecoderWrapper::onResume() { - CHECK(!mFlushing); - - ++mNumPendingDecodes; - - mSource->clear(); - mReader->readMore(true /* flush */); -} - -} // namespace android diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h deleted file mode 100644 index b9be12c..0000000 --- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DECODER_WRAPPER_H_ - -#define DECODER_WRAPPER_H_ - -#include <media/stagefright/foundation/AHandler.h> - -namespace android { - -struct MediaSource; - -struct DecoderWrapper : public AHandler { - DecoderWrapper(); - - void setNotificationMessage(const sp<AMessage> &msg); - void initiateSetup(const sp<AMessage> &msg); - void initiateShutdown(); - void signalFlush(); - void signalResume(); - -protected: - virtual ~DecoderWrapper(); - - virtual void onMessageReceived(const sp<AMessage> &msg); - -private: - struct WrapperSource; - struct WrapperReader; - - enum { - kWhatSetup, - kWhatInputBufferFilled, - kWhatOutputBufferDrained, - kWhatShutdown, - kWhatFillBufferDone, - kWhatInputDataRequested, - kWhatFlush, - kWhatResume, - }; - - sp<AMessage> mNotify; - - sp<WrapperSource> mSource; - - sp<ALooper> mReaderLooper; - sp<WrapperReader> mReader; - - int32_t mNumOutstandingInputBuffers; - int32_t mNumOutstandingOutputBuffers; - int32_t mNumPendingDecodes; - bool mFlushing; - - void onSetup(const sp<AMessage> &msg); - void onShutdown(); - void onFlush(); - void onResume(); - - void postFillBuffer(); - void completeFlushIfPossible(); - - DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper); -}; - -} // namespace android - -#endif // DECODER_WRAPPER_H_ - diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index d07ea1b..576a850 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -33,11 +33,25 @@ namespace android { -NuPlayer::HTTPLiveSource::HTTPLiveSource(const char *url, uint32_t flags) +NuPlayer::HTTPLiveSource::HTTPLiveSource( + const char *url, + const KeyedVector<String8, String8> *headers) : mURL(url), - mFlags(flags), + mFlags(0), mEOS(false), mOffset(0) { + if (headers) { + mExtraHeaders = *headers; + + ssize_t index = + mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); + + if (index >= 0) { + mFlags |= kFlagIncognito; + + mExtraHeaders.removeItemsAt(index); + } + } } NuPlayer::HTTPLiveSource::~HTTPLiveSource() { @@ -55,7 +69,8 @@ void NuPlayer::HTTPLiveSource::start() { mLiveLooper->registerHandler(mLiveSession); - mLiveSession->connect(mURL.c_str()); + mLiveSession->connect( + mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); mTSParser = new ATSParser; } diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index a8ce7f4..7a337e9 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -27,11 +27,9 @@ struct ATSParser; struct LiveSession; struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { - enum Flags { - // Don't log any URLs. - kFlagIncognito = 1, - }; - HTTPLiveSource(const char *url, uint32_t flags = 0); + HTTPLiveSource( + const char *url, + const KeyedVector<String8, String8> *headers); virtual void start(); @@ -49,7 +47,13 @@ protected: virtual ~HTTPLiveSource(); private: + enum Flags { + // Don't log any URLs. + kFlagIncognito = 1, + }; + AString mURL; + KeyedVector<String8, String8> mExtraHeaders; uint32_t mFlags; bool mEOS; off64_t mOffset; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index d439f6e..effa703 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -72,17 +72,7 @@ void NuPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); - uint32_t flags = 0; - - if (headers) { - ssize_t index = headers->indexOfKey(String8("x-hide-urls-from-log")); - - if (index >= 0) { - flags |= HTTPLiveSource::kFlagIncognito; - } - } - - msg->setObject("source", new HTTPLiveSource(url, flags)); + msg->setObject("source", new HTTPLiveSource(url, headers)); msg->post(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 517acc9..81b41ef 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -20,7 +20,6 @@ #include "NuPlayerDecoder.h" -#include "DecoderWrapper.h" #include "ESDS.h" #include <media/stagefright/foundation/ABuffer.h> @@ -47,7 +46,6 @@ NuPlayer::Decoder::~Decoder() { void NuPlayer::Decoder::configure(const sp<MetaData> &meta) { CHECK(mCodec == NULL); - CHECK(mWrapper == NULL); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -61,19 +59,11 @@ void NuPlayer::Decoder::configure(const sp<MetaData> &meta) { format->setObject("native-window", mNativeWindow); } - if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { - mWrapper = new DecoderWrapper; - looper()->registerHandler(mWrapper); + mCodec = new ACodec; + looper()->registerHandler(mCodec); - mWrapper->setNotificationMessage(notifyMsg); - mWrapper->initiateSetup(format); - } else { - mCodec = new ACodec; - looper()->registerHandler(mCodec); - - mCodec->setNotificationMessage(notifyMsg); - mCodec->initiateSetup(format); - } + mCodec->setNotificationMessage(notifyMsg); + mCodec->initiateSetup(format); } void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { @@ -214,7 +204,6 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) { msg->setObject("csd", buffer); } else if (meta->findData(kKeyESDS, &type, &data, &size)) { -#if 0 ESDS esds((const char *)data, size); CHECK_EQ(esds.InitCheck(), (status_t)OK); @@ -230,12 +219,6 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) { buffer->meta()->setInt32("csd", true); mCSD.push(buffer); -#else - sp<ABuffer> buffer = new ABuffer(size); - memcpy(buffer->data(), data, size); - - msg->setObject("esds", buffer); -#endif } return msg; @@ -270,27 +253,18 @@ void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) { void NuPlayer::Decoder::signalFlush() { if (mCodec != NULL) { mCodec->signalFlush(); - } else { - CHECK(mWrapper != NULL); - mWrapper->signalFlush(); } } void NuPlayer::Decoder::signalResume() { if (mCodec != NULL) { mCodec->signalResume(); - } else { - CHECK(mWrapper != NULL); - mWrapper->signalResume(); } } void NuPlayer::Decoder::initiateShutdown() { if (mCodec != NULL) { mCodec->initiateShutdown(); - } else { - CHECK(mWrapper != NULL); - mWrapper->initiateShutdown(); } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 732f090..fabc606 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -25,7 +25,6 @@ namespace android { struct ABuffer; -struct DecoderWrapper; struct NuPlayer::Decoder : public AHandler { Decoder(const sp<AMessage> ¬ify, @@ -51,7 +50,6 @@ private: sp<NativeWindowWrapper> mNativeWindow; sp<ACodec> mCodec; - sp<DecoderWrapper> mWrapper; Vector<sp<ABuffer> > mCSD; size_t mCSDIndex; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 0eca958..e1213f4 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -246,6 +246,14 @@ void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) { mPlayer->setAudioSink(audioSink); } +status_t NuPlayerDriver::setParameter(int key, const Parcel &request) { + return INVALID_OPERATION; +} + +status_t NuPlayerDriver::getParameter(int key, Parcel *reply) { + return INVALID_OPERATION; +} + status_t NuPlayerDriver::getMetadata( const media::Metadata::Filter& ids, Parcel *records) { return INVALID_OPERATION; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 67d0f3e..145fd80 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -52,6 +52,8 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual player_type playerType(); virtual status_t invoke(const Parcel &request, Parcel *reply); virtual void setAudioSink(const sp<AudioSink> &audioSink); + virtual status_t setParameter(int key, const Parcel &request); + virtual status_t getParameter(int key, Parcel *reply); virtual status_t getMetadata( const media::Metadata::Filter& ids, Parcel *records); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 369a3a8..828e008 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -198,18 +198,21 @@ void NuPlayer::Renderer::signalAudioSinkChanged() { } void NuPlayer::Renderer::onDrainAudioQueue() { - uint32_t numFramesPlayed; - CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); - ssize_t numFramesAvailableToWrite = - mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); + for (;;) { + uint32_t numFramesPlayed; + CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); - CHECK_GE(numFramesAvailableToWrite, 0); + ssize_t numFramesAvailableToWrite = + mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); - size_t numBytesAvailableToWrite = - numFramesAvailableToWrite * mAudioSink->frameSize(); + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * mAudioSink->frameSize(); + + if (numBytesAvailableToWrite == 0) { + break; + } - while (numBytesAvailableToWrite > 0) { if (mAudioQueue.empty()) { break; } @@ -264,10 +267,10 @@ void NuPlayer::Renderer::onDrainAudioQueue() { if (entry->mOffset == entry->mBuffer->size()) { entry->mNotifyConsumed->post(); mAudioQueue.erase(mAudioQueue.begin()); + entry = NULL; } - numBytesAvailableToWrite -= copy; mNumFramesWritten += copy / mAudioSink->frameSize(); } diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp new file mode 100644 index 0000000..8413208 --- /dev/null +++ b/media/libstagefright/AACWriter.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AACWriter" +#include <utils/Log.h> + +#include <media/stagefright/AACWriter.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/mediarecorder.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <fcntl.h> + +namespace android { + +AACWriter::AACWriter(const char *filename) + : mFd(-1), + mInitCheck(NO_INIT), + mStarted(false), + mPaused(false), + mResumed(false), + mChannelCount(-1), + mSampleRate(-1) { + + LOGV("AACWriter Constructor"); + + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); + if (mFd >= 0) { + mInitCheck = OK; + } +} + +AACWriter::AACWriter(int fd) + : mFd(dup(fd)), + mInitCheck(mFd < 0? NO_INIT: OK), + mStarted(false), + mPaused(false), + mResumed(false), + mChannelCount(-1), + mSampleRate(-1) { +} + +AACWriter::~AACWriter() { + if (mStarted) { + stop(); + } + + if (mFd != -1) { + close(mFd); + mFd = -1; + } +} + +status_t AACWriter::initCheck() const { + return mInitCheck; +} + +static int writeInt8(int fd, uint8_t x) { + return ::write(fd, &x, 1); +} + + +status_t AACWriter::addSource(const sp<MediaSource> &source) { + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mSource != NULL) { + LOGE("AAC files only support a single track of audio."); + return UNKNOWN_ERROR; + } + + sp<MetaData> meta = source->getFormat(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)); + CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount)); + CHECK(meta->findInt32(kKeySampleRate, &mSampleRate)); + CHECK(mChannelCount >= 1 && mChannelCount <= 2); + + mSource = source; + return OK; +} + +status_t AACWriter::start(MetaData *params) { + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mSource == NULL) { + return UNKNOWN_ERROR; + } + + if (mStarted && mPaused) { + mPaused = false; + mResumed = true; + return OK; + } else if (mStarted) { + // Already started, does nothing + return OK; + } + + mFrameDurationUs = (kSamplesPerFrame * 1000000LL + (mSampleRate >> 1)) + / mSampleRate; + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + mReachedEOS = false; + mDone = false; + + pthread_create(&mThread, &attr, ThreadWrapper, this); + pthread_attr_destroy(&attr); + + mStarted = true; + + return OK; +} + +status_t AACWriter::pause() { + if (!mStarted) { + return OK; + } + mPaused = true; + return OK; +} + +status_t AACWriter::stop() { + if (!mStarted) { + return OK; + } + + mDone = true; + + void *dummy; + pthread_join(mThread, &dummy); + + status_t err = (status_t) dummy; + { + status_t status = mSource->stop(); + if (err == OK && + (status != OK && status != ERROR_END_OF_STREAM)) { + err = status; + } + } + + mStarted = false; + return err; +} + +bool AACWriter::exceedsFileSizeLimit() { + if (mMaxFileSizeLimitBytes == 0) { + return false; + } + return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes; +} + +bool AACWriter::exceedsFileDurationLimit() { + if (mMaxFileDurationLimitUs == 0) { + return false; + } + return mEstimatedDurationUs >= mMaxFileDurationLimitUs; +} + +// static +void *AACWriter::ThreadWrapper(void *me) { + return (void *) static_cast<AACWriter *>(me)->threadFunc(); +} + +/* +* Returns an index into the sample rate table if the +* given sample rate is found; otherwise, returns -1. +*/ +static bool getSampleRateTableIndex(int sampleRate, uint8_t* tableIndex) { + static const int kSampleRateTable[] = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000 + }; + const int tableSize = + sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]); + + *tableIndex = 0; + for (int index = 0; index < tableSize; ++index) { + if (sampleRate == kSampleRateTable[index]) { + LOGV("Sample rate: %d and index: %d", + sampleRate, index); + *tableIndex = index; + return true; + } + } + + LOGE("Sampling rate %d bps is not supported", sampleRate); + return false; +} + +/* + * ADTS (Audio data transport stream) header structure. + * It consists of 7 or 9 bytes (with or without CRC): + * 12 bits of syncword 0xFFF, all bits must be 1 + * 1 bit of field ID. 0 for MPEG-4, and 1 for MPEG-2 + * 2 bits of MPEG layer. If in MPEG-TS, set to 0 + * 1 bit of protection absense. Set to 1 if no CRC. + * 2 bits of profile code. Set to 1 (The MPEG-4 Audio + * object type minus 1. We are using AAC-LC = 2) + * 4 bits of sampling frequency index code (15 is not allowed) + * 1 bit of private stream. Set to 0. + * 3 bits of channel configuration code. 0 resevered for inband PCM + * 1 bit of originality. Set to 0. + * 1 bit of home. Set to 0. + * 1 bit of copyrighted steam. Set to 0. + * 1 bit of copyright start. Set to 0. + * 13 bits of frame length. It included 7 ot 9 bytes header length. + * it is set to (protection absense? 7: 9) + size(AAC frame) + * 11 bits of buffer fullness. 0x7FF for VBR. + * 2 bits of frames count in one packet. Set to 0. + */ +status_t AACWriter::writeAdtsHeader(uint32_t frameLength) { + uint8_t data = 0xFF; + write(mFd, &data, 1); + + const uint8_t kFieldId = 0; + const uint8_t kMpegLayer = 0; + const uint8_t kProtectionAbsense = 1; // 1: kAdtsHeaderLength = 7 + data = 0xF0; + data |= (kFieldId << 3); + data |= (kMpegLayer << 1); + data |= kProtectionAbsense; + write(mFd, &data, 1); + + const uint8_t kProfileCode = 1; // AAC-LC + uint8_t kSampleFreqIndex; + CHECK(getSampleRateTableIndex(mSampleRate, &kSampleFreqIndex)); + const uint8_t kPrivateStream = 0; + const uint8_t kChannelConfigCode = mChannelCount; + data = (kProfileCode << 6); + data |= (kSampleFreqIndex << 2); + data |= (kPrivateStream << 1); + data |= (kChannelConfigCode >> 2); + write(mFd, &data, 1); + + // 4 bits from originality to copyright start + const uint8_t kCopyright = 0; + const uint32_t kFrameLength = frameLength; + data = ((kChannelConfigCode & 3) << 6); + data |= (kCopyright << 2); + data |= ((kFrameLength & 0x1800) >> 11); + write(mFd, &data, 1); + + data = ((kFrameLength & 0x07F8) >> 3); + write(mFd, &data, 1); + + const uint32_t kBufferFullness = 0x7FF; // VBR + data = ((kFrameLength & 0x07) << 5); + data |= ((kBufferFullness & 0x07C0) >> 6); + write(mFd, &data, 1); + + const uint8_t kFrameCount = 0; + data = ((kBufferFullness & 0x03F) << 2); + data |= kFrameCount; + write(mFd, &data, 1); + + return OK; +} + +status_t AACWriter::threadFunc() { + mEstimatedDurationUs = 0; + mEstimatedSizeBytes = 0; + int64_t previousPausedDurationUs = 0; + int64_t maxTimestampUs = 0; + status_t err = OK; + + prctl(PR_SET_NAME, (unsigned long)"AACWriterThread", 0, 0, 0); + + while (!mDone && err == OK) { + MediaBuffer *buffer; + err = mSource->read(&buffer); + + if (err != OK) { + break; + } + + if (mPaused) { + buffer->release(); + buffer = NULL; + continue; + } + + mEstimatedSizeBytes += kAdtsHeaderLength + buffer->range_length(); + if (exceedsFileSizeLimit()) { + buffer->release(); + buffer = NULL; + notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); + break; + } + + int32_t isCodecSpecific = 0; + if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecSpecific) && isCodecSpecific) { + LOGV("Drop codec specific info buffer"); + buffer->release(); + buffer = NULL; + continue; + } + + int64_t timestampUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); + if (timestampUs > mEstimatedDurationUs) { + mEstimatedDurationUs = timestampUs; + } + if (mResumed) { + previousPausedDurationUs += (timestampUs - maxTimestampUs - mFrameDurationUs); + mResumed = false; + } + timestampUs -= previousPausedDurationUs; + LOGV("time stamp: %lld, previous paused duration: %lld", + timestampUs, previousPausedDurationUs); + if (timestampUs > maxTimestampUs) { + maxTimestampUs = timestampUs; + } + + if (exceedsFileDurationLimit()) { + buffer->release(); + buffer = NULL; + notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); + break; + } + + // Each output AAC audio frame to the file contains + // 1. an ADTS header, followed by + // 2. the compressed audio data. + ssize_t dataLength = buffer->range_length(); + uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset(); + if (writeAdtsHeader(kAdtsHeaderLength + dataLength) != OK || + dataLength != write(mFd, data, dataLength)) { + err = ERROR_IO; + } + + buffer->release(); + buffer = NULL; + } + + close(mFd); + mFd = -1; + mReachedEOS = true; + if (err == ERROR_END_OF_STREAM) { + return OK; + } + return err; +} + +bool AACWriter::reachedEOS() { + return mReachedEOS; +} + +} // namespace android diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 9928f44..4189354 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -505,7 +505,7 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { - android_native_buffer_t *buf; + ANativeWindowBuffer *buf; err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); if (err != 0) { LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); @@ -574,7 +574,7 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { } ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { - android_native_buffer_t *buf; + ANativeWindowBuffer *buf; CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0); for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { @@ -1644,7 +1644,7 @@ void ACodec::UninitializedState::onSetup( if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) { componentName = "OMX.Nvidia.h264.decode"; } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { - componentName = "OMX.Nvidia.aac.decoder"; + componentName = "OMX.google.aac.decoder"; } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) { componentName = "OMX.Nvidia.mp3.decoder"; } else { diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index 0db3d1d..b10d52c 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -37,7 +37,7 @@ AMRWriter::AMRWriter(const char *filename) mPaused(false), mResumed(false) { - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); if (mFd >= 0) { mInitCheck = OK; } @@ -269,7 +269,7 @@ status_t AMRWriter::threadFunc() { } if (stoppedPrematurely) { - notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); + notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); } close(mFd); diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp new file mode 100644 index 0000000..6313ca3 --- /dev/null +++ b/media/libstagefright/AVIExtractor.cpp @@ -0,0 +1,922 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AVIExtractor" +#include <utils/Log.h> + +#include "include/AVIExtractor.h" + +#include <binder/ProcessState.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +namespace android { + +struct AVIExtractor::AVISource : public MediaSource { + AVISource(const sp<AVIExtractor> &extractor, size_t trackIndex); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options); + +protected: + virtual ~AVISource(); + +private: + sp<AVIExtractor> mExtractor; + size_t mTrackIndex; + const AVIExtractor::Track &mTrack; + MediaBufferGroup *mBufferGroup; + size_t mSampleIndex; + + DISALLOW_EVIL_CONSTRUCTORS(AVISource); +}; + +//////////////////////////////////////////////////////////////////////////////// + +AVIExtractor::AVISource::AVISource( + const sp<AVIExtractor> &extractor, size_t trackIndex) + : mExtractor(extractor), + mTrackIndex(trackIndex), + mTrack(mExtractor->mTracks.itemAt(trackIndex)), + mBufferGroup(NULL) { +} + +AVIExtractor::AVISource::~AVISource() { + if (mBufferGroup) { + stop(); + } +} + +status_t AVIExtractor::AVISource::start(MetaData *params) { + CHECK(!mBufferGroup); + + mBufferGroup = new MediaBufferGroup; + + mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize)); + mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize)); + mSampleIndex = 0; + + return OK; +} + +status_t AVIExtractor::AVISource::stop() { + CHECK(mBufferGroup); + + delete mBufferGroup; + mBufferGroup = NULL; + + return OK; +} + +sp<MetaData> AVIExtractor::AVISource::getFormat() { + return mTrack.mMeta; +} + +status_t AVIExtractor::AVISource::read( + MediaBuffer **buffer, const ReadOptions *options) { + CHECK(mBufferGroup); + + *buffer = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode seekMode; + if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { + status_t err = + mExtractor->getSampleIndexAtTime( + mTrackIndex, seekTimeUs, seekMode, &mSampleIndex); + + if (err != OK) { + return ERROR_END_OF_STREAM; + } + } + + int64_t timeUs = + (mSampleIndex * 1000000ll * mTrack.mRate) / mTrack.mScale; + + off64_t offset; + size_t size; + bool isKey; + status_t err = mExtractor->getSampleInfo( + mTrackIndex, mSampleIndex, &offset, &size, &isKey); + + ++mSampleIndex; + + if (err != OK) { + return ERROR_END_OF_STREAM; + } + + MediaBuffer *out; + CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK); + + ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size); + + if (n < (ssize_t)size) { + return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED; + } + + out->set_range(0, size); + + out->meta_data()->setInt64(kKeyTime, timeUs); + + if (isKey) { + out->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } + + *buffer = out; + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +AVIExtractor::AVIExtractor(const sp<DataSource> &dataSource) + : mDataSource(dataSource) { + mInitCheck = parseHeaders(); + + if (mInitCheck != OK) { + mTracks.clear(); + } +} + +AVIExtractor::~AVIExtractor() { +} + +size_t AVIExtractor::countTracks() { + return mTracks.size(); +} + +sp<MediaSource> AVIExtractor::getTrack(size_t index) { + return index < mTracks.size() ? new AVISource(this, index) : NULL; +} + +sp<MetaData> AVIExtractor::getTrackMetaData( + size_t index, uint32_t flags) { + return index < mTracks.size() ? mTracks.editItemAt(index).mMeta : NULL; +} + +sp<MetaData> AVIExtractor::getMetaData() { + sp<MetaData> meta = new MetaData; + + if (mInitCheck == OK) { + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_AVI); + } + + return meta; +} + +status_t AVIExtractor::parseHeaders() { + mTracks.clear(); + mMovieOffset = 0; + mFoundIndex = false; + mOffsetsAreAbsolute = false; + + ssize_t res = parseChunk(0ll, -1ll); + + if (res < 0) { + return (status_t)res; + } + + if (mMovieOffset == 0ll || !mFoundIndex) { + return ERROR_MALFORMED; + } + + return OK; +} + +ssize_t AVIExtractor::parseChunk(off64_t offset, off64_t size, int depth) { + if (size >= 0 && size < 8) { + return ERROR_MALFORMED; + } + + uint8_t tmp[12]; + ssize_t n = mDataSource->readAt(offset, tmp, 8); + + if (n < 8) { + return (n < 0) ? n : (ssize_t)ERROR_MALFORMED; + } + + uint32_t fourcc = U32_AT(tmp); + uint32_t chunkSize = U32LE_AT(&tmp[4]); + + if (size >= 0 && chunkSize + 8 > size) { + return ERROR_MALFORMED; + } + + static const char kPrefix[] = " "; + const char *prefix = &kPrefix[strlen(kPrefix) - 2 * depth]; + + if (fourcc == FOURCC('L', 'I', 'S', 'T') + || fourcc == FOURCC('R', 'I', 'F', 'F')) { + // It's a list of chunks + + if (size >= 0 && size < 12) { + return ERROR_MALFORMED; + } + + n = mDataSource->readAt(offset + 8, &tmp[8], 4); + + if (n < 4) { + return (n < 0) ? n : (ssize_t)ERROR_MALFORMED; + } + + uint32_t subFourcc = U32_AT(&tmp[8]); + + LOGV("%s offset 0x%08llx LIST of '%c%c%c%c', size %d", + prefix, + offset, + (char)(subFourcc >> 24), + (char)((subFourcc >> 16) & 0xff), + (char)((subFourcc >> 8) & 0xff), + (char)(subFourcc & 0xff), + chunkSize - 4); + + if (subFourcc == FOURCC('m', 'o', 'v', 'i')) { + // We're not going to parse this, but will take note of the + // offset. + + mMovieOffset = offset; + } else { + off64_t subOffset = offset + 12; + off64_t subOffsetLimit = subOffset + chunkSize - 4; + while (subOffset < subOffsetLimit) { + ssize_t res = + parseChunk(subOffset, subOffsetLimit - subOffset, depth + 1); + + if (res < 0) { + return res; + } + + subOffset += res; + } + } + } else { + LOGV("%s offset 0x%08llx CHUNK '%c%c%c%c'", + prefix, + offset, + (char)(fourcc >> 24), + (char)((fourcc >> 16) & 0xff), + (char)((fourcc >> 8) & 0xff), + (char)(fourcc & 0xff)); + + status_t err = OK; + + switch (fourcc) { + case FOURCC('s', 't', 'r', 'h'): + { + err = parseStreamHeader(offset + 8, chunkSize); + break; + } + + case FOURCC('s', 't', 'r', 'f'): + { + err = parseStreamFormat(offset + 8, chunkSize); + break; + } + + case FOURCC('i', 'd', 'x', '1'): + { + err = parseIndex(offset + 8, chunkSize); + break; + } + + default: + break; + } + + if (err != OK) { + return err; + } + } + + if (chunkSize & 1) { + ++chunkSize; + } + + return chunkSize + 8; +} + +static const char *GetMIMETypeForHandler(uint32_t handler) { + switch (handler) { + // Wow... shamelessly copied from + // http://wiki.multimedia.cx/index.php?title=ISO_MPEG-4 + + case FOURCC('3', 'I', 'V', '2'): + case FOURCC('3', 'i', 'v', '2'): + case FOURCC('B', 'L', 'Z', '0'): + case FOURCC('D', 'I', 'G', 'I'): + case FOURCC('D', 'I', 'V', '1'): + case FOURCC('d', 'i', 'v', '1'): + case FOURCC('D', 'I', 'V', 'X'): + case FOURCC('d', 'i', 'v', 'x'): + case FOURCC('D', 'X', '5', '0'): + case FOURCC('d', 'x', '5', '0'): + case FOURCC('D', 'X', 'G', 'M'): + case FOURCC('E', 'M', '4', 'A'): + case FOURCC('E', 'P', 'H', 'V'): + case FOURCC('F', 'M', 'P', '4'): + case FOURCC('f', 'm', 'p', '4'): + case FOURCC('F', 'V', 'F', 'W'): + case FOURCC('H', 'D', 'X', '4'): + case FOURCC('h', 'd', 'x', '4'): + case FOURCC('M', '4', 'C', 'C'): + case FOURCC('M', '4', 'S', '2'): + case FOURCC('m', '4', 's', '2'): + case FOURCC('M', 'P', '4', 'S'): + case FOURCC('m', 'p', '4', 's'): + case FOURCC('M', 'P', '4', 'V'): + case FOURCC('m', 'p', '4', 'v'): + case FOURCC('M', 'V', 'X', 'M'): + case FOURCC('R', 'M', 'P', '4'): + case FOURCC('S', 'E', 'D', 'G'): + case FOURCC('S', 'M', 'P', '4'): + case FOURCC('U', 'M', 'P', '4'): + case FOURCC('W', 'V', '1', 'F'): + case FOURCC('X', 'V', 'I', 'D'): + case FOURCC('X', 'v', 'i', 'D'): + case FOURCC('x', 'v', 'i', 'd'): + case FOURCC('X', 'V', 'I', 'X'): + return MEDIA_MIMETYPE_VIDEO_MPEG4; + + default: + return NULL; + } +} + +status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) { + if (size != 56) { + return ERROR_MALFORMED; + } + + if (mTracks.size() > 99) { + return -ERANGE; + } + + sp<ABuffer> buffer = new ABuffer(size); + ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); + + if (n < (ssize_t)size) { + return n < 0 ? (status_t)n : ERROR_MALFORMED; + } + + const uint8_t *data = buffer->data(); + + uint32_t type = U32_AT(data); + uint32_t handler = U32_AT(&data[4]); + uint32_t flags = U32LE_AT(&data[8]); + + sp<MetaData> meta = new MetaData; + + uint32_t rate = U32LE_AT(&data[20]); + uint32_t scale = U32LE_AT(&data[24]); + + const char *mime = NULL; + Track::Kind kind = Track::OTHER; + + if (type == FOURCC('v', 'i', 'd', 's')) { + mime = GetMIMETypeForHandler(handler); + + if (mime && strncasecmp(mime, "video/", 6)) { + return ERROR_MALFORMED; + } + + kind = Track::VIDEO; + } else if (type == FOURCC('a', 'u', 'd', 's')) { + if (mime && strncasecmp(mime, "audio/", 6)) { + return ERROR_MALFORMED; + } + + kind = Track::AUDIO; + } + + if (!mime) { + mime = "application/octet-stream"; + } + + meta->setCString(kKeyMIMEType, mime); + + mTracks.push(); + Track *track = &mTracks.editItemAt(mTracks.size() - 1); + + track->mMeta = meta; + track->mRate = rate; + track->mScale = scale; + track->mKind = kind; + track->mNumSyncSamples = 0; + track->mThumbnailSampleSize = 0; + track->mThumbnailSampleIndex = -1; + track->mMaxSampleSize = 0; + + return OK; +} + +status_t AVIExtractor::parseStreamFormat(off64_t offset, size_t size) { + if (mTracks.isEmpty()) { + return ERROR_MALFORMED; + } + + Track *track = &mTracks.editItemAt(mTracks.size() - 1); + + if (track->mKind == Track::OTHER) { + // We don't support this content, but that's not a parsing error. + return OK; + } + + bool isVideo = (track->mKind == Track::VIDEO); + + if ((isVideo && size < 40) || (!isVideo && size < 18)) { + // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively. + return ERROR_MALFORMED; + } + + sp<ABuffer> buffer = new ABuffer(size); + ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); + + if (n < (ssize_t)size) { + return n < 0 ? (status_t)n : ERROR_MALFORMED; + } + + const uint8_t *data = buffer->data(); + + if (isVideo) { + uint32_t width = U32LE_AT(&data[4]); + uint32_t height = U32LE_AT(&data[8]); + + track->mMeta->setInt32(kKeyWidth, width); + track->mMeta->setInt32(kKeyHeight, height); + } else { + uint32_t format = U16LE_AT(data); + if (format == 0x55) { + track->mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + } + + uint32_t numChannels = U16LE_AT(&data[2]); + uint32_t sampleRate = U32LE_AT(&data[4]); + + track->mMeta->setInt32(kKeyChannelCount, numChannels); + track->mMeta->setInt32(kKeySampleRate, sampleRate); + } + + return OK; +} + +// static +bool AVIExtractor::IsCorrectChunkType( + ssize_t trackIndex, Track::Kind kind, uint32_t chunkType) { + uint32_t chunkBase = chunkType & 0xffff; + + switch (kind) { + case Track::VIDEO: + { + if (chunkBase != FOURCC(0, 0, 'd', 'c') + && chunkBase != FOURCC(0, 0, 'd', 'b')) { + return false; + } + break; + } + + case Track::AUDIO: + { + if (chunkBase != FOURCC(0, 0, 'w', 'b')) { + return false; + } + break; + } + + default: + break; + } + + if (trackIndex < 0) { + return true; + } + + uint8_t hi = chunkType >> 24; + uint8_t lo = (chunkType >> 16) & 0xff; + + if (hi < '0' || hi > '9' || lo < '0' || lo > '9') { + return false; + } + + if (trackIndex != (10 * (hi - '0') + (lo - '0'))) { + return false; + } + + return true; +} + +status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { + if ((size % 16) != 0) { + return ERROR_MALFORMED; + } + + sp<ABuffer> buffer = new ABuffer(size); + ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); + + if (n < (ssize_t)size) { + return n < 0 ? (status_t)n : ERROR_MALFORMED; + } + + const uint8_t *data = buffer->data(); + + while (size > 0) { + uint32_t chunkType = U32_AT(data); + + uint8_t hi = chunkType >> 24; + uint8_t lo = (chunkType >> 16) & 0xff; + + if (hi < '0' || hi > '9' || lo < '0' || lo > '9') { + return ERROR_MALFORMED; + } + + size_t trackIndex = 10 * (hi - '0') + (lo - '0'); + + if (trackIndex >= mTracks.size()) { + return ERROR_MALFORMED; + } + + Track *track = &mTracks.editItemAt(trackIndex); + + if (!IsCorrectChunkType(-1, track->mKind, chunkType)) { + return ERROR_MALFORMED; + } + + if (track->mKind == Track::OTHER) { + data += 16; + size -= 16; + continue; + } + + uint32_t flags = U32LE_AT(&data[4]); + uint32_t offset = U32LE_AT(&data[8]); + uint32_t chunkSize = U32LE_AT(&data[12]); + + if (chunkSize > track->mMaxSampleSize) { + track->mMaxSampleSize = chunkSize; + } + + track->mSamples.push(); + + SampleInfo *info = + &track->mSamples.editItemAt(track->mSamples.size() - 1); + + info->mOffset = offset; + info->mIsKey = (flags & 0x10) != 0; + + if (info->mIsKey) { + static const size_t kMaxNumSyncSamplesToScan = 20; + + if (track->mNumSyncSamples < kMaxNumSyncSamplesToScan) { + if (chunkSize > track->mThumbnailSampleSize) { + track->mThumbnailSampleSize = chunkSize; + + track->mThumbnailSampleIndex = + track->mSamples.size() - 1; + } + } + + ++track->mNumSyncSamples; + } + + data += 16; + size -= 16; + } + + if (!mTracks.isEmpty()) { + off64_t offset; + size_t size; + bool isKey; + status_t err = getSampleInfo(0, 0, &offset, &size, &isKey); + + if (err != OK) { + mOffsetsAreAbsolute = !mOffsetsAreAbsolute; + err = getSampleInfo(0, 0, &offset, &size, &isKey); + + if (err != OK) { + return err; + } + } + + LOGV("Chunk offsets are %s", + mOffsetsAreAbsolute ? "absolute" : "movie-chunk relative"); + } + + 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; + + LOGV("track %d duration = %.2f secs", i, durationUs / 1E6); + + track->mMeta->setInt64(kKeyDuration, durationUs); + track->mMeta->setInt32(kKeyMaxInputSize, track->mMaxSampleSize); + + const char *tmp; + CHECK(track->mMeta->findCString(kKeyMIMEType, &tmp)); + + AString mime = tmp; + + if (!strncasecmp("video/", mime.c_str(), 6) + && track->mThumbnailSampleIndex >= 0) { + int64_t thumbnailTimeUs = + (track->mThumbnailSampleIndex * 1000000ll * track->mRate) + / track->mScale; + + track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); + + if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) { + status_t err = addMPEG4CodecSpecificData(i); + + if (err != OK) { + return err; + } + } + } + } + + mFoundIndex = true; + + return OK; +} + +static size_t GetSizeWidth(size_t x) { + size_t n = 1; + while (x > 127) { + ++n; + x >>= 7; + } + return n; +} + +static uint8_t *EncodeSize(uint8_t *dst, size_t x) { + while (x > 127) { + *dst++ = (x & 0x7f) | 0x80; + x >>= 7; + } + *dst++ = x; + return dst; +} + +sp<ABuffer> MakeMPEG4VideoCodecSpecificData(const sp<ABuffer> &config) { + size_t len1 = config->size() + GetSizeWidth(config->size()) + 1; + size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13; + size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3; + + sp<ABuffer> csd = new ABuffer(len3); + uint8_t *dst = csd->data(); + *dst++ = 0x03; + dst = EncodeSize(dst, len2 + 3); + *dst++ = 0x00; // ES_ID + *dst++ = 0x00; + *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag + + *dst++ = 0x04; + dst = EncodeSize(dst, len1 + 13); + *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile + for (size_t i = 0; i < 12; ++i) { + *dst++ = 0x00; + } + + *dst++ = 0x05; + dst = EncodeSize(dst, config->size()); + memcpy(dst, config->data(), config->size()); + dst += config->size(); + + // hexdump(csd->data(), csd->size()); + + return csd; +} + +status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) { + Track *track = &mTracks.editItemAt(trackIndex); + + off64_t offset; + size_t size; + bool isKey; + status_t err = getSampleInfo(trackIndex, 0, &offset, &size, &isKey); + + if (err != OK) { + return err; + } + + sp<ABuffer> buffer = new ABuffer(size); + ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); + + if (n < (ssize_t)size) { + return n < 0 ? (status_t)n : ERROR_MALFORMED; + } + + // Extract everything up to the first VOP start code from the first + // frame's encoded data and use it to construct an ESDS with the + // codec specific data. + + size_t i = 0; + bool found = false; + while (i + 3 < buffer->size()) { + if (!memcmp("\x00\x00\x01\xb6", &buffer->data()[i], 4)) { + found = true; + break; + } + + ++i; + } + + if (!found) { + return ERROR_MALFORMED; + } + + buffer->setRange(0, i); + + sp<ABuffer> csd = MakeMPEG4VideoCodecSpecificData(buffer); + track->mMeta->setData(kKeyESDS, kTypeESDS, csd->data(), csd->size()); + + return OK; +} + +status_t AVIExtractor::getSampleInfo( + size_t trackIndex, size_t sampleIndex, + off64_t *offset, size_t *size, bool *isKey) { + if (trackIndex >= mTracks.size()) { + return -ERANGE; + } + + const Track &track = mTracks.itemAt(trackIndex); + + if (sampleIndex >= track.mSamples.size()) { + return -ERANGE; + } + + const SampleInfo &info = track.mSamples.itemAt(sampleIndex); + + if (!mOffsetsAreAbsolute) { + *offset = info.mOffset + mMovieOffset + 8; + } else { + *offset = info.mOffset; + } + + *size = 0; + + uint8_t tmp[8]; + ssize_t n = mDataSource->readAt(*offset, tmp, 8); + + if (n < 8) { + return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED; + } + + uint32_t chunkType = U32_AT(tmp); + + if (!IsCorrectChunkType(trackIndex, track.mKind, chunkType)) { + return ERROR_MALFORMED; + } + + *offset += 8; + *size = U32LE_AT(&tmp[4]); + + *isKey = info.mIsKey; + + return OK; +} + +status_t AVIExtractor::getSampleIndexAtTime( + size_t trackIndex, + int64_t timeUs, MediaSource::ReadOptions::SeekMode mode, + size_t *sampleIndex) const { + if (trackIndex >= mTracks.size()) { + return -ERANGE; + } + + const Track &track = mTracks.itemAt(trackIndex); + + ssize_t closestSampleIndex = + timeUs / track.mRate * track.mScale / 1000000ll; + + ssize_t numSamples = track.mSamples.size(); + + if (closestSampleIndex < 0) { + closestSampleIndex = 0; + } else if (closestSampleIndex >= numSamples) { + closestSampleIndex = numSamples - 1; + } + + if (mode == MediaSource::ReadOptions::SEEK_CLOSEST) { + *sampleIndex = closestSampleIndex; + + return OK; + } + + ssize_t prevSyncSampleIndex = closestSampleIndex; + while (prevSyncSampleIndex >= 0) { + const SampleInfo &info = + track.mSamples.itemAt(prevSyncSampleIndex); + + if (info.mIsKey) { + break; + } + + --prevSyncSampleIndex; + } + + ssize_t nextSyncSampleIndex = closestSampleIndex; + while (nextSyncSampleIndex < numSamples) { + const SampleInfo &info = + track.mSamples.itemAt(nextSyncSampleIndex); + + if (info.mIsKey) { + break; + } + + ++nextSyncSampleIndex; + } + + switch (mode) { + case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC: + { + *sampleIndex = prevSyncSampleIndex; + + return prevSyncSampleIndex >= 0 ? OK : UNKNOWN_ERROR; + } + + case MediaSource::ReadOptions::SEEK_NEXT_SYNC: + { + *sampleIndex = nextSyncSampleIndex; + + return nextSyncSampleIndex < numSamples ? OK : UNKNOWN_ERROR; + } + + case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC: + { + if (prevSyncSampleIndex < 0 && nextSyncSampleIndex >= numSamples) { + return UNKNOWN_ERROR; + } + + if (prevSyncSampleIndex < 0) { + *sampleIndex = nextSyncSampleIndex; + return OK; + } + + if (nextSyncSampleIndex >= numSamples) { + *sampleIndex = prevSyncSampleIndex; + return OK; + } + + size_t dist1 = closestSampleIndex - prevSyncSampleIndex; + size_t dist2 = nextSyncSampleIndex - closestSampleIndex; + + *sampleIndex = + (dist1 < dist2) ? prevSyncSampleIndex : nextSyncSampleIndex; + + return OK; + } + + default: + TRESPASS(); + break; + } +} + +bool SniffAVI( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { + char tmp[12]; + if (source->readAt(0, tmp, 12) < 12) { + return false; + } + + if (!memcmp(tmp, "RIFF", 4) && !memcmp(&tmp[8], "AVI ", 4)) { + mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_AVI); + *confidence = 0.2; + + return true; + } + + return false; +} + +} // namespace android diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 88069e9..f731dfb 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -3,11 +3,15 @@ include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk +BUILD_WITH_SOFTWARE_DECODERS := false + LOCAL_SRC_FILES:= \ ACodec.cpp \ AACExtractor.cpp \ + AACWriter.cpp \ AMRExtractor.cpp \ AMRWriter.cpp \ + AVIExtractor.cpp \ AudioPlayer.cpp \ AudioSource.cpp \ AwesomePlayer.cpp \ @@ -19,6 +23,7 @@ LOCAL_SRC_FILES:= \ ESDS.cpp \ FileSource.cpp \ FLACExtractor.cpp \ + HTTPBase.cpp \ HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ @@ -42,10 +47,10 @@ LOCAL_SRC_FILES:= \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ - ThreadedSource.cpp \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ + TimedTextPlayer.cpp \ Utils.cpp \ VBRISeeker.cpp \ WAVExtractor.cpp \ @@ -69,38 +74,102 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libsonivox \ libvorbisidec \ - libsurfaceflinger_client \ libstagefright_yuv \ libcamera_client \ libdrmframework \ libcrypto \ libssl \ - libgui + libgui \ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ - libstagefright_aacdec \ libstagefright_aacenc \ - libstagefright_amrnbdec \ libstagefright_amrnbenc \ - libstagefright_amrwbdec \ libstagefright_amrwbenc \ - libstagefright_avcdec \ libstagefright_avcenc \ - libstagefright_m4vh263dec \ libstagefright_m4vh263enc \ - libstagefright_mp3dec \ - libstagefright_vorbisdec \ libstagefright_matroska \ - libstagefright_vpxdec \ libvpx \ libstagefright_mpeg2ts \ libstagefright_httplive \ libstagefright_rtsp \ libstagefright_id3 \ - libstagefright_g711dec \ libFLAC \ +ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) + +LOCAL_SRC_FILES += \ + ThreadedSource.cpp \ + +LOCAL_STATIC_LIBRARIES += \ + libstagefright_aacdec \ + libstagefright_amrnbdec \ + libstagefright_amrwbdec \ + libstagefright_avcdec \ + libstagefright_g711dec \ + libstagefright_mp3dec \ + libstagefright_m4vh263dec \ + libstagefright_vorbisdec \ + libstagefright_vpxdec \ + libvpx \ + +endif + + +################################################################################ + +# The following was shamelessly copied from external/webkit/Android.mk and +# currently must follow the same logic to determine how webkit was built and +# if it's safe to link against libchromium.net + +# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot +# use the Chrome http stack either. +ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true) + USE_ALT_HTTP := true +endif + +# See if the user has specified a stack they want to use +HTTP_STACK = $(HTTP) +# We default to the Chrome HTTP stack. +DEFAULT_HTTP = chrome +ALT_HTTP = android + +ifneq ($(HTTP_STACK),chrome) + ifneq ($(HTTP_STACK),android) + # No HTTP stack is specified, pickup the one we want as default. + ifeq ($(USE_ALT_HTTP),true) + HTTP_STACK = $(ALT_HTTP) + else + HTTP_STACK = $(DEFAULT_HTTP) + endif + endif +endif + +ifeq ($(HTTP_STACK),chrome) + +LOCAL_SHARED_LIBRARIES += \ + liblog \ + libicuuc \ + libicui18n \ + libz \ + libdl \ + +LOCAL_STATIC_LIBRARIES += \ + libstagefright_chromium_http \ + libchromium_net \ + libwebcore \ + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libstlport +include external/stlport/libstlport.mk +endif + +LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 + +endif # ifeq ($(HTTP_STACK),chrome) + +################################################################################ + LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ libstagefright_enc_common \ @@ -123,6 +192,10 @@ endif LOCAL_CFLAGS += -Wno-multichar +ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) + LOCAL_CFLAGS += -DHAVE_SOFTWARE_DECODERS +endif + LOCAL_MODULE:= libstagefright include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index bd04a26..dd69e6b 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -84,7 +84,13 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { CHECK(mFirstBuffer == NULL); - mFirstBufferResult = mSource->read(&mFirstBuffer); + MediaSource::ReadOptions options; + if (mSeeking) { + options.setSeekTo(mSeekTimeUs); + mSeeking = false; + } + + mFirstBufferResult = mSource->read(&mFirstBuffer, &options); if (mFirstBufferResult == INFO_FORMAT_CHANGED) { LOGV("INFO_FORMAT_CHANGED!!!"); @@ -110,7 +116,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { if (mAudioSink.get() != NULL) { status_t err = mAudioSink->open( - mSampleRate, numChannels, AudioSystem::PCM_16_BIT, + mSampleRate, numChannels, AUDIO_FORMAT_PCM_16_BIT, DEFAULT_AUDIOSINK_BUFFERCOUNT, &AudioPlayer::AudioSinkCallback, this); if (err != OK) { @@ -132,10 +138,10 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { mAudioSink->start(); } else { mAudioTrack = new AudioTrack( - AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT, + AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, (numChannels == 2) - ? AudioSystem::CHANNEL_OUT_STEREO - : AudioSystem::CHANNEL_OUT_MONO, + ? AUDIO_CHANNEL_OUT_STEREO + : AUDIO_CHANNEL_OUT_MONO, 0, 0, &AudioCallback, this, 0); if ((err = mAudioTrack->initCheck()) != OK) { @@ -280,17 +286,39 @@ void AudioPlayer::AudioCallback(int event, void *info) { buffer->size = numBytesWritten; } +uint32_t AudioPlayer::getNumFramesPendingPlayout() const { + uint32_t numFramesPlayedOut; + status_t err; + + if (mAudioSink != NULL) { + err = mAudioSink->getPosition(&numFramesPlayedOut); + } else { + err = mAudioTrack->getPosition(&numFramesPlayedOut); + } + + if (err != OK || mNumFramesPlayed < numFramesPlayedOut) { + return 0; + } + + // mNumFramesPlayed is the number of frames submitted + // to the audio sink for playback, but not all of them + // may have played out by now. + return mNumFramesPlayed - numFramesPlayedOut; +} + size_t AudioPlayer::fillBuffer(void *data, size_t size) { if (mNumFramesPlayed == 0) { LOGV("AudioCallback"); } if (mReachedEOS) { - memset(data, 0, size); - - return size; + return 0; } + bool postSeekComplete = false; + bool postEOS = false; + int64_t postEOSDelayUs = 0; + size_t size_done = 0; size_t size_remaining = size; while (size_remaining > 0) { @@ -317,7 +345,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { mSeeking = false; if (mObserver) { - mObserver->postAudioSeekComplete(); + postSeekComplete = true; } } } @@ -342,7 +370,35 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { if (err != OK) { if (mObserver && !mReachedEOS) { - mObserver->postAudioEOS(); + // We don't want to post EOS right away but only + // after all frames have actually been played out. + + // These are the number of frames submitted to the + // AudioTrack that you haven't heard yet. + uint32_t numFramesPendingPlayout = + getNumFramesPendingPlayout(); + + // These are the number of frames we're going to + // submit to the AudioTrack by returning from this + // callback. + uint32_t numAdditionalFrames = size_done / mFrameSize; + + numFramesPendingPlayout += numAdditionalFrames; + + int64_t timeToCompletionUs = + (1000000ll * numFramesPendingPlayout) / mSampleRate; + + LOGV("total number of frames played: %lld (%lld us)", + (mNumFramesPlayed + numAdditionalFrames), + 1000000ll * (mNumFramesPlayed + numAdditionalFrames) + / mSampleRate); + + LOGV("%d frames left to play, %lld us (%.2f secs)", + numFramesPendingPlayout, + timeToCompletionUs, timeToCompletionUs / 1E6); + + postEOS = true; + postEOSDelayUs = timeToCompletionUs + mLatencyUs; } mReachedEOS = true; @@ -386,8 +442,18 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { size_remaining -= copy; } - Mutex::Autolock autoLock(mLock); - mNumFramesPlayed += size_done / mFrameSize; + { + Mutex::Autolock autoLock(mLock); + mNumFramesPlayed += size_done / mFrameSize; + } + + if (postEOS) { + mObserver->postAudioEOS(postEOSDelayUs); + } + + if (postSeekComplete) { + mObserver->postAudioSeekComplete(); + } return size_done; } diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index bbdec02..99c3682 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -60,8 +60,8 @@ AudioSource::AudioSource( AudioRecord::RECORD_NS_ENABLE | AudioRecord::RECORD_IIR_ENABLE; mRecord = new AudioRecord( - inputSource, sampleRate, AudioSystem::PCM_16_BIT, - channels > 1? AudioSystem::CHANNEL_IN_STEREO: AudioSystem::CHANNEL_IN_MONO, + inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT, + channels > 1? AUDIO_CHANNEL_IN_STEREO: AUDIO_CHANNEL_IN_MONO, 4 * kMaxBufferSize / sizeof(int16_t), /* Enable ping-pong buffers */ flags, AudioRecordCallbackFunction, diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index a8e0a4d..fb7a871 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -29,6 +29,7 @@ #include "include/NuCachedSource2.h" #include "include/ThrottledSource.h" #include "include/MPEG2TSExtractor.h" +#include "include/TimedTextPlayer.h" #include "include/WVMExtractor.h" #include <binder/IPCThreadState.h> @@ -57,7 +58,6 @@ #include <cutils/properties.h> #define USE_SURFACE_ALLOC 1 -#define FRAME_DROP_FREQ 0 namespace android { @@ -187,7 +187,9 @@ AwesomePlayer::AwesomePlayer() mFlags(0), mExtractorFlags(0), mVideoBuffer(NULL), - mDecryptHandle(NULL) { + mDecryptHandle(NULL), + mLastVideoTimeUs(-1), + mTextPlayer(NULL) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -310,7 +312,7 @@ status_t AwesomePlayer::setDataSource_l( return UNKNOWN_ERROR; } - dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); + dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient); if (mDecryptHandle != NULL) { CHECK(mDrmManagerClient); if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { @@ -383,10 +385,8 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { mFlags |= AUTO_LOOPING; } } - } - - if (haveAudio && haveVideo) { - break; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { + addTextSource(extractor->getTrack(i)); } } @@ -472,30 +472,20 @@ void AwesomePlayer::reset_l() { delete mAudioPlayer; mAudioPlayer = NULL; - mVideoRenderer.clear(); - - if (mVideoBuffer) { - mVideoBuffer->release(); - mVideoBuffer = NULL; + if (mTextPlayer != NULL) { + delete mTextPlayer; + mTextPlayer = NULL; } + mVideoRenderer.clear(); + if (mRTSPController != NULL) { mRTSPController->disconnect(); mRTSPController.clear(); } if (mVideoSource != NULL) { - mVideoSource->stop(); - - // The following hack is necessary to ensure that the OMX - // component is completely released by the time we may try - // to instantiate it again. - wp<MediaSource> tmp = mVideoSource; - mVideoSource.clear(); - while (tmp.promote() != NULL) { - usleep(1000); - } - IPCThreadState::self()->flushCommands(); + shutdownVideoDecoder_l(); } mDurationUs = -1; @@ -514,6 +504,7 @@ void AwesomePlayer::reset_l() { mFileSource.clear(); mBitrate = -1; + mLastVideoTimeUs = -1; } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { @@ -872,6 +863,8 @@ status_t AwesomePlayer::startAudioPlayer_l() { if (!(mFlags & AUDIOPLAYER_STARTED)) { mFlags |= AUDIOPLAYER_STARTED; + bool wasSeeking = mAudioPlayer->isSeeking(); + // We've already started the MediaSource in order to enable // the prefetcher to read its data. status_t err = mAudioPlayer->start( @@ -881,6 +874,13 @@ status_t AwesomePlayer::startAudioPlayer_l() { notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); return err; } + + if (wasSeeking) { + CHECK(!mAudioPlayer->isSeeking()); + + // We will have finished the seek while starting the audio player. + postAudioSeekComplete_l(); + } } else { mAudioPlayer->resume(); } @@ -912,6 +912,17 @@ void AwesomePlayer::notifyVideoSize_l() { cropLeft, cropTop, cropRight, cropBottom); } + int32_t displayWidth; + if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) { + LOGV("Display width changed (%d=>%d)", mDisplayWidth, displayWidth); + mDisplayWidth = displayWidth; + } + int32_t displayHeight; + if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) { + LOGV("Display height changed (%d=>%d)", mDisplayHeight, displayHeight); + mDisplayHeight = displayHeight; + } + int32_t usableWidth = cropRight - cropLeft + 1; int32_t usableHeight = cropBottom - cropTop + 1; if (mDisplayWidth != 0) { @@ -963,7 +974,9 @@ void AwesomePlayer::initRenderer_l() { // before creating a new one. IPCThreadState::self()->flushCommands(); - if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) { + if (USE_SURFACE_ALLOC + && !strncmp(component, "OMX.", 4) + && strncmp(component, "OMX.google.", 11)) { // Hardware decoders avoid the CPU color conversion by decoding // directly to ANativeBuffers, so we must use a renderer that // just pushes those buffers to the ANativeWindow. @@ -1006,6 +1019,11 @@ status_t AwesomePlayer::pause_l(bool at_eos) { mFlags &= ~AUDIO_RUNNING; } + if (mFlags & TEXTPLAYER_STARTED) { + mTextPlayer->pause(); + mFlags &= ~TEXT_RUNNING; + } + mFlags &= ~PLAYING; if (mDecryptHandle != NULL) { @@ -1034,7 +1052,7 @@ void AwesomePlayer::setSurface(const sp<Surface> &surface) { Mutex::Autolock autoLock(mLock); mSurface = surface; - mNativeWindow = surface; + setNativeWindow_l(surface); } void AwesomePlayer::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) { @@ -1042,9 +1060,57 @@ void AwesomePlayer::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) mSurface.clear(); if (surfaceTexture != NULL) { - mNativeWindow = new SurfaceTextureClient(surfaceTexture); + setNativeWindow_l(new SurfaceTextureClient(surfaceTexture)); + } +} + +void AwesomePlayer::shutdownVideoDecoder_l() { + if (mVideoBuffer) { + mVideoBuffer->release(); + mVideoBuffer = NULL; } + mVideoSource->stop(); + + // The following hack is necessary to ensure that the OMX + // component is completely released by the time we may try + // to instantiate it again. + wp<MediaSource> tmp = mVideoSource; + mVideoSource.clear(); + while (tmp.promote() != NULL) { + usleep(1000); + } + IPCThreadState::self()->flushCommands(); +} + +void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) { + mNativeWindow = native; + + if (mVideoSource == NULL) { + return; + } + + LOGI("attempting to reconfigure to use new surface"); + + bool wasPlaying = (mFlags & PLAYING) != 0; + + pause_l(); + mVideoRenderer.clear(); + + shutdownVideoDecoder_l(); + + CHECK_EQ(initVideoDecoder(), (status_t)OK); + + if (mLastVideoTimeUs >= 0) { + mSeeking = SEEK; + mSeekNotificationSent = true; + mSeekTimeUs = mLastVideoTimeUs; + mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS); + } + + if (wasPlaying) { + play_l(); + } } void AwesomePlayer::setAudioSink( @@ -1106,6 +1172,32 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { return OK; } +status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { + if (mTextPlayer != NULL) { + if (index >= 0) { // to turn on a text track + status_t err = mTextPlayer->setTimedTextTrackIndex(index); + if (err != OK) { + return err; + } + + mFlags |= TEXT_RUNNING; + mFlags |= TEXTPLAYER_STARTED; + return OK; + } else { // to turn off the text track display + if (mFlags & TEXT_RUNNING) { + mFlags &= ~TEXT_RUNNING; + } + if (mFlags & TEXTPLAYER_STARTED) { + mFlags &= ~TEXTPLAYER_STARTED; + } + + return mTextPlayer->setTimedTextTrackIndex(index); + } + } else { + return INVALID_OPERATION; + } +} + // static void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) { static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone(); @@ -1142,6 +1234,10 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { seekAudioIfNecessary_l(); + if (mFlags & TEXTPLAYER_STARTED) { + mTextPlayer->seekTo(mSeekTimeUs); + } + if (!(mFlags & PLAYING)) { LOGV("seeking while paused, sending SEEK_COMPLETE notification" " immediately."); @@ -1164,7 +1260,6 @@ void AwesomePlayer::seekAudioIfNecessary_l() { mWatchForAudioSeekComplete = true; mWatchForAudioEOS = true; - mSeekNotificationSent = false; if (mDecryptHandle != NULL) { mDrmManagerClient->setPlaybackStatus(mDecryptHandle, @@ -1181,6 +1276,16 @@ void AwesomePlayer::setAudioSource(sp<MediaSource> source) { mAudioTrack = source; } +void AwesomePlayer::addTextSource(sp<MediaSource> source) { + CHECK(source != NULL); + + if (mTextPlayer == NULL) { + mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + } + + mTextPlayer->addTextSource(source); +} + status_t AwesomePlayer::initAudioDecoder() { sp<MetaData> meta = mAudioTrack->getFormat(); @@ -1323,11 +1428,11 @@ void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) { // If we're playing video only, report seek complete now, // otherwise audio player will notify us later. notifyListener_l(MEDIA_SEEK_COMPLETE); + mSeekNotificationSent = true; } mFlags |= FIRST_FRAME; mSeeking = NO_SEEK; - mSeekNotificationSent = false; if (mDecryptHandle != NULL) { mDrmManagerClient->setPlaybackStatus(mDecryptHandle, @@ -1435,6 +1540,8 @@ void AwesomePlayer::onVideoEvent() { int64_t timeUs; CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); + mLastVideoTimeUs = timeUs; + if (mSeeking == SEEK_VIDEO_ONLY) { if (mSeekTimeUs > timeUs) { LOGI("XXX mSeekTimeUs = %lld us, timeUs = %lld us", @@ -1458,11 +1565,15 @@ void AwesomePlayer::onVideoEvent() { } } + if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) { + mTextPlayer->resume(); + mFlags |= TEXT_RUNNING; + } + TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource; if (mFlags & FIRST_FRAME) { mFlags &= ~FIRST_FRAME; - mSinceLastDropped = 0; mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs; } @@ -1509,17 +1620,13 @@ void AwesomePlayer::onVideoEvent() { if (latenessUs > 40000) { // We're more than 40ms late. - LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); - if ( mSinceLastDropped > FRAME_DROP_FREQ) - { - LOGV("we're late by %lld us (%.2f secs) dropping one after %d frames", latenessUs, latenessUs / 1E6, mSinceLastDropped); - mSinceLastDropped = 0; - mVideoBuffer->release(); - mVideoBuffer = NULL; + LOGV("we're late by %lld us (%.2f secs), dropping frame", + latenessUs, latenessUs / 1E6); + mVideoBuffer->release(); + mVideoBuffer = NULL; - postVideoEvent_l(); - return; - } + postVideoEvent_l(); + return; } if (latenessUs < -10000) { @@ -1537,7 +1644,6 @@ void AwesomePlayer::onVideoEvent() { } if (mVideoRenderer != NULL) { - mSinceLastDropped++; mVideoRenderer->render(mVideoBuffer); } @@ -1587,12 +1693,12 @@ void AwesomePlayer::postVideoLagEvent_l() { mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll); } -void AwesomePlayer::postCheckAudioStatusEvent_l() { +void AwesomePlayer::postCheckAudioStatusEvent_l(int64_t delayUs) { if (mAudioStatusEventPending) { return; } mAudioStatusEventPending = true; - mQueue.postEvent(mCheckAudioStatusEvent); + mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs); } void AwesomePlayer::onCheckAudioStatus() { @@ -1699,8 +1805,10 @@ status_t AwesomePlayer::finishSetDataSource_l() { if (!strncasecmp("http://", mUri.string(), 7) || !strncasecmp("https://", mUri.string(), 8) || isWidevineStreaming) { - mConnectingDataSource = new NuHTTPDataSource( - (mFlags & INCOGNITO) ? NuHTTPDataSource::kFlagIncognito : 0); + mConnectingDataSource = HTTPBase::Create( + (mFlags & INCOGNITO) + ? HTTPBase::kFlagIncognito + : 0); mLock.unlock(); status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders); @@ -1731,34 +1839,48 @@ status_t AwesomePlayer::finishSetDataSource_l() { mConnectingDataSource.clear(); - if (mCachedSource != NULL) { + + String8 contentType = dataSource->getMIMEType(); + + if (strncasecmp(contentType.string(), "audio/", 6)) { + // We're not doing this for streams that appear to be audio-only + // streams to ensure that even low bandwidth streams start + // playing back fairly instantly. + // We're going to prefill the cache before trying to instantiate // the extractor below, as the latter is an operation that otherwise // could block on the datasource for a significant amount of time. // During that time we'd be unable to abort the preparation phase // without this prefill. + if (mCachedSource != NULL) { + // We're going to prefill the cache before trying to instantiate + // the extractor below, as the latter is an operation that otherwise + // could block on the datasource for a significant amount of time. + // During that time we'd be unable to abort the preparation phase + // without this prefill. + + mLock.unlock(); + + for (;;) { + status_t finalStatus; + size_t cachedDataRemaining = + mCachedSource->approxDataRemaining(&finalStatus); + + if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes + || (mFlags & PREPARE_CANCELLED)) { + break; + } - mLock.unlock(); - - for (;;) { - status_t finalStatus; - size_t cachedDataRemaining = - mCachedSource->approxDataRemaining(&finalStatus); - - if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes - || (mFlags & PREPARE_CANCELLED)) { - break; + usleep(200000); } - usleep(200000); + mLock.lock(); } - mLock.lock(); - } - - if (mFlags & PREPARE_CANCELLED) { - LOGI("Prepare cancelled while waiting for initial cache fill."); - return UNKNOWN_ERROR; + if (mFlags & PREPARE_CANCELLED) { + LOGI("Prepare cancelled while waiting for initial cache fill."); + return UNKNOWN_ERROR; + } } } else if (!strncasecmp("rtsp://", mUri.string(), 7)) { if (mLooper == NULL) { @@ -1817,7 +1939,8 @@ status_t AwesomePlayer::finishSetDataSource_l() { } } - dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); + dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient); + if (mDecryptHandle != NULL) { CHECK(mDrmManagerClient); if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { @@ -1923,12 +2046,29 @@ uint32_t AwesomePlayer::flags() const { return mExtractorFlags; } -void AwesomePlayer::postAudioEOS() { - postCheckAudioStatusEvent_l(); +void AwesomePlayer::postAudioEOS(int64_t delayUs) { + Mutex::Autolock autoLock(mLock); + postCheckAudioStatusEvent_l(delayUs); } void AwesomePlayer::postAudioSeekComplete() { - postCheckAudioStatusEvent_l(); + Mutex::Autolock autoLock(mLock); + postAudioSeekComplete_l(); +} + +void AwesomePlayer::postAudioSeekComplete_l() { + postCheckAudioStatusEvent_l(0 /* delayUs */); +} + +status_t AwesomePlayer::setParameter(int key, const Parcel &request) { + if (key == KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX) { + return setTimedTextTrackIndex(request.readInt32()); + } + return ERROR_UNSUPPORTED; +} + +status_t AwesomePlayer::getParameter(int key, Parcel *reply) { + return OK; } bool AwesomePlayer::isStreamingHTTP() const { diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 8a24bc4..a1f04d3 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -740,28 +740,6 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, mFrameAvailableCondition.signal(); } -size_t CameraSource::getNumberOfVideoBuffers() const { - LOGV("getNumberOfVideoBuffers"); - size_t nBuffers = 0; - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - if (mInitCheck == OK && mCamera != 0) { - nBuffers = mCamera->getNumberOfVideoBuffers(); - } - IPCThreadState::self()->restoreCallingIdentity(token); - return nBuffers; -} - -sp<IMemory> CameraSource::getVideoBuffer(size_t index) const { - LOGV("getVideoBuffer: %d", index); - sp<IMemory> buffer = 0; - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - if (mInitCheck == OK && mCamera != 0) { - buffer = mCamera->getVideoBuffer(index); - } - IPCThreadState::self()->restoreCallingIdentity(token); - return buffer; -} - bool CameraSource::isMetaDataStoredInVideoBuffers() const { LOGV("isMetaDataStoredInVideoBuffers"); return mIsMetaDataStoredInVideoBuffers; diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp index 6db922e..1f3d581 100644 --- a/media/libstagefright/DRMExtractor.cpp +++ b/media/libstagefright/DRMExtractor.cpp @@ -41,7 +41,7 @@ namespace android { class DRMSource : public MediaSource { public: DRMSource(const sp<MediaSource> &mediaSource, - DecryptHandle *decryptHandle, + const sp<DecryptHandle> &decryptHandle, DrmManagerClient *managerClient, int32_t trackId, DrmBuffer *ipmpBox); @@ -56,7 +56,7 @@ protected: private: sp<MediaSource> mOriginalMediaSource; - DecryptHandle* mDecryptHandle; + sp<DecryptHandle> mDecryptHandle; DrmManagerClient* mDrmManagerClient; size_t mTrackId; mutable Mutex mDRMLock; @@ -70,7 +70,7 @@ private: //////////////////////////////////////////////////////////////////////////////// DRMSource::DRMSource(const sp<MediaSource> &mediaSource, - DecryptHandle *decryptHandle, + const sp<DecryptHandle> &decryptHandle, DrmManagerClient *managerClient, int32_t trackId, DrmBuffer *ipmpBox) : mOriginalMediaSource(mediaSource), @@ -241,7 +241,7 @@ DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime) mOriginalExtractor->setDrmFlag(true); mOriginalExtractor->getMetaData()->setInt32(kKeyIsDRM, 1); - source->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); + source->getDrmInfo(mDecryptHandle, &mDrmManagerClient); } DRMExtractor::~DRMExtractor() { @@ -277,7 +277,7 @@ sp<MetaData> DRMExtractor::getMetaData() { bool SniffDRM( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { - DecryptHandle *decryptHandle = source->DrmInitialization(); + sp<DecryptHandle> decryptHandle = source->DrmInitialization(); if (decryptHandle != NULL) { if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 3b38208..c16b3b5 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -15,13 +15,14 @@ */ #include "include/AMRExtractor.h" +#include "include/AVIExtractor.h" #include "include/MP3Extractor.h" #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" #include "include/MPEG2TSExtractor.h" #include "include/NuCachedSource2.h" -#include "include/NuHTTPDataSource.h" +#include "include/HTTPBase.h" #include "include/DRMExtractor.h" #include "include/FLACExtractor.h" #include "include/AACExtractor.h" @@ -111,6 +112,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMPEG2TS); RegisterSniffer(SniffMP3); RegisterSniffer(SniffAAC); + RegisterSniffer(SniffAVI); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) @@ -127,7 +129,7 @@ sp<DataSource> DataSource::CreateFromURI( source = new FileSource(uri + 7); } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) { - sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource; + sp<HTTPBase> httpSource = HTTPBase::Create(); if (httpSource->connect(uri, headers) != OK) { return NULL; } @@ -144,4 +146,8 @@ sp<DataSource> DataSource::CreateFromURI( return source; } +String8 DataSource::getMIMEType() const { + return String8("application/octet-stream"); +} + } // namespace android diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index 02a78c9..f2f3500 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -125,7 +125,7 @@ status_t FileSource::getSize(off64_t *size) { return OK; } -DecryptHandle* FileSource::DrmInitialization() { +sp<DecryptHandle> FileSource::DrmInitialization() { if (mDrmManagerClient == NULL) { mDrmManagerClient = new DrmManagerClient(); } @@ -147,8 +147,8 @@ DecryptHandle* FileSource::DrmInitialization() { return mDecryptHandle; } -void FileSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) { - *handle = mDecryptHandle; +void FileSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) { + handle = mDecryptHandle; *client = mDrmManagerClient; } diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp new file mode 100644 index 0000000..58b17a7 --- /dev/null +++ b/media/libstagefright/HTTPBase.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/HTTPBase.h" + +#if CHROMIUM_AVAILABLE +#include "include/ChromiumHTTPDataSource.h" +#endif + +#include "include/NuHTTPDataSource.h" + +#include <cutils/properties.h> + +namespace android { + +HTTPBase::HTTPBase() {} + +// static +sp<HTTPBase> HTTPBase::Create(uint32_t flags) { +#if CHROMIUM_AVAILABLE + char value[PROPERTY_VALUE_MAX]; + if (!property_get("media.stagefright.use-chromium", value, NULL) + || (strcasecmp("false", value) && strcmp("0", value))) { + return new ChromiumHTTPDataSource(flags); + } else +#endif + { + return new NuHTTPDataSource(flags); + } +} + +} // namespace android diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 03ce202..4bdfc6f 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -48,7 +48,7 @@ static const uint32_t kMask = 0xfffe0c00; bool MP3Extractor::get_mp3_frame_size( uint32_t header, size_t *frame_size, int *out_sampling_rate, int *out_channels, - int *out_bitrate) { + int *out_bitrate, int *out_num_samples) { *frame_size = 0; if (out_sampling_rate) { @@ -63,6 +63,10 @@ bool MP3Extractor::get_mp3_frame_size( *out_bitrate = 0; } + if (out_num_samples) { + *out_num_samples = 1152; + } + if ((header & 0xffe00000) != 0xffe00000) { return false; } @@ -127,6 +131,10 @@ bool MP3Extractor::get_mp3_frame_size( } *frame_size = (12000 * bitrate / sampling_rate + padding) * 4; + + if (out_num_samples) { + *out_num_samples = 384; + } } else { // layer II or III @@ -150,10 +158,17 @@ bool MP3Extractor::get_mp3_frame_size( bitrate = (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] : kBitrateV1L3[bitrate_index - 1]; + + if (out_num_samples) { + *out_num_samples = 1152; + } } else { // V2 (or 2.5) bitrate = kBitrateV2[bitrate_index - 1]; + if (out_num_samples) { + *out_num_samples = 576; + } } if (out_bitrate) { @@ -374,6 +389,9 @@ private: sp<MP3Seeker> mSeeker; MediaBufferGroup *mGroup; + int64_t mBasisTimeUs; + int64_t mSamplesRead; + MP3Source(const MP3Source &); MP3Source &operator=(const MP3Source &); }; @@ -489,7 +507,9 @@ MP3Source::MP3Source( mCurrentTimeUs(0), mStarted(false), mSeeker(seeker), - mGroup(NULL) { + mGroup(NULL), + mBasisTimeUs(0), + mSamplesRead(0) { } MP3Source::~MP3Source() { @@ -509,6 +529,9 @@ status_t MP3Source::start(MetaData *) { mCurrentPos = mFirstFramePos; mCurrentTimeUs = 0; + mBasisTimeUs = mCurrentTimeUs; + mSamplesRead = 0; + mStarted = true; return OK; @@ -552,6 +575,9 @@ status_t MP3Source::read( } else { mCurrentTimeUs = actualSeekTimeUs; } + + mBasisTimeUs = mCurrentTimeUs; + mSamplesRead = 0; } MediaBuffer *buffer; @@ -562,6 +588,8 @@ status_t MP3Source::read( size_t frame_size; int bitrate; + int num_samples; + int sample_rate; for (;;) { ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4); if (n < 4) { @@ -575,7 +603,7 @@ status_t MP3Source::read( if ((header & kMask) == (mFixedHeader & kMask) && MP3Extractor::get_mp3_frame_size( - header, &frame_size, NULL, NULL, &bitrate)) { + header, &frame_size, &sample_rate, NULL, &bitrate, &num_samples)) { break; } @@ -613,7 +641,9 @@ status_t MP3Source::read( buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); mCurrentPos += frame_size; - mCurrentTimeUs += frame_size * 8000ll / bitrate; + + mSamplesRead += num_samples; + mCurrentTimeUs = mBasisTimeUs + ((mSamplesRead * 1000000) / sample_rate); *out = buffer; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 7b96d01..6692809 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -19,6 +19,8 @@ #include "include/MPEG4Extractor.h" #include "include/SampleTable.h" +#include "include/ESDS.h" +#include "include/TimedTextPlayer.h" #include <arpa/inet.h> @@ -29,7 +31,6 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/DataSource.h> -#include "include/ESDS.h" #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> @@ -262,7 +263,7 @@ static const char *FourCC2MIME(uint32_t fourcc) { MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source) : mDataSource(source), - mHaveMetadata(false), + mInitCheck(NO_INIT), mHasVideo(false), mFirstTrack(NULL), mLastTrack(NULL), @@ -361,8 +362,8 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData( } status_t MPEG4Extractor::readMetaData() { - if (mHaveMetadata) { - return OK; + if (mInitCheck != NO_INIT) { + return mInitCheck; } off64_t offset = 0; @@ -370,17 +371,20 @@ status_t MPEG4Extractor::readMetaData() { while ((err = parseChunk(&offset, 0)) == OK) { } - if (mHaveMetadata) { + if (mInitCheck == OK) { if (mHasVideo) { mFileMetaData->setCString(kKeyMIMEType, "video/mp4"); } else { mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); } - return OK; + mInitCheck = OK; + } else { + mInitCheck = err; } - return err; + CHECK_NE(err, (status_t)NO_INIT); + return mInitCheck; } void MPEG4Extractor::setDrmFlag(bool flag) { @@ -755,7 +759,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return err; } } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) { - mHaveMetadata = true; + mInitCheck = OK; if (!mIsDrm) { return UNKNOWN_ERROR; // Return a dummy error. @@ -829,6 +833,33 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setInt64( kKeyDuration, (duration * 1000000) / mLastTrack->timescale); + uint8_t lang[2]; + off64_t lang_offset; + if (version == 1) { + lang_offset = timescale_offset + 4 + 8; + } else if (version == 0) { + lang_offset = timescale_offset + 4 + 4; + } else { + return ERROR_IO; + } + + if (mDataSource->readAt(lang_offset, &lang, sizeof(lang)) + < (ssize_t)sizeof(lang)) { + return ERROR_IO; + } + + // To get the ISO-639-2/T three character language code + // 1 bit pad followed by 3 5-bits characters. Each character + // is packed as the difference between its ASCII value and 0x60. + char lang_code[4]; + lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60; + lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60; + lang_code[2] = (lang[1] & 0x1f) + 0x60; + lang_code[3] = '\0'; + + mLastTrack->meta->setCString( + kKeyMediaLanguage, lang_code); + *offset += chunk_size; break; } @@ -1292,6 +1323,14 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return parseDrmSINF(offset, data_offset); } + case FOURCC('t', 'x', '3', 'g'): + { + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP); + + *offset += chunk_size; + break; + } + default: { *offset += chunk_size; @@ -1901,7 +1940,7 @@ status_t MPEG4Source::read( off64_t offset; size_t size; - uint32_t dts; + uint32_t cts; bool isSyncSample; bool newBuffer = false; if (mBuffer == NULL) { @@ -1909,7 +1948,7 @@ status_t MPEG4Source::read( status_t err = mSampleTable->getMetaDataForSample( - mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample); + mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample); if (err != OK) { return err; @@ -1939,7 +1978,7 @@ status_t MPEG4Source::read( mBuffer->set_range(0, size); mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { mBuffer->meta_data()->setInt64( @@ -2025,14 +2064,18 @@ status_t MPEG4Source::read( size_t dstOffset = 0; while (srcOffset < size) { - CHECK(srcOffset + mNALLengthSize <= size); - size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]); - srcOffset += mNALLengthSize; + bool isMalFormed = (srcOffset + mNALLengthSize > size); + size_t nalLength = 0; + if (!isMalFormed) { + nalLength = parseNALSize(&mSrcBuffer[srcOffset]); + srcOffset += mNALLengthSize; + isMalFormed = srcOffset + nalLength > size; + } - if (srcOffset + nalLength > size) { + if (isMalFormed) { + LOGE("Video is malformed"); mBuffer->release(); mBuffer = NULL; - return ERROR_MALFORMED; } @@ -2057,7 +2100,7 @@ status_t MPEG4Source::read( mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { mBuffer->meta_data()->setInt64( @@ -2077,6 +2120,20 @@ status_t MPEG4Source::read( } } +MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( + const char *mimePrefix) { + for (Track *track = mFirstTrack; track != NULL; track = track->next) { + const char *mime; + if (track->meta != NULL + && track->meta->findCString(kKeyMIMEType, &mime) + && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) { + return track; + } + } + + return NULL; +} + static bool LegacySniffMPEG4( const sp<DataSource> &source, String8 *mimeType, float *confidence) { uint8_t header[8]; @@ -2109,6 +2166,14 @@ static bool isCompatibleBrand(uint32_t fourcc) { FOURCC('3', 'g', 'p', '4'), FOURCC('m', 'p', '4', '1'), FOURCC('m', 'p', '4', '2'), + + // Won't promise that the following file types can be played. + // Just give these file types a chance. + FOURCC('q', 't', ' ', ' '), // Apple's QuickTime + FOURCC('M', 'S', 'N', 'V'), // Sony's PSP + + FOURCC('3', 'g', '2', 'a'), // 3GPP2 + FOURCC('3', 'g', '2', 'b'), }; for (size_t i = 0; diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 5d6ea7c..f6a8b17 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -33,6 +33,7 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> #include <media/mediarecorder.h> +#include <cutils/properties.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -45,6 +46,7 @@ namespace android { static const int64_t kMax32BitFileSize = 0x007fffffffLL; static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; +static const int64_t kInitialDelayTimeUs = 700000LL; // Using longer adjustment period to suppress fluctuations in // the audio encoding paths @@ -52,7 +54,7 @@ static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 600000000LL; // 10 min class MPEG4Writer::Track { public: - Track(MPEG4Writer *owner, const sp<MediaSource> &source); + Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId); ~Track(); @@ -63,12 +65,13 @@ public: int64_t getDurationUs() const; int64_t getEstimatedTrackSizeBytes() const; - void writeTrackHeader(int32_t trackID, bool use32BitOffset = true); + void writeTrackHeader(bool use32BitOffset = true); void bufferChunk(int64_t timestampUs); bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } void addChunkOffset(off64_t offset); + int32_t getTrackId() const { return mTrackId; } status_t dump(int fd, const Vector<String16>& args) const; private: @@ -82,7 +85,9 @@ private: bool mIsAvc; bool mIsAudio; bool mIsMPEG4; + int32_t mTrackId; int64_t mTrackDurationUs; + int64_t mMaxChunkDurationUs; // For realtime applications, we need to adjust the media clock // for video track based on the audio media clock @@ -155,6 +160,8 @@ private: bool mReachedEOS; int64_t mStartTimestampUs; + int64_t mStartTimeRealUs; + int64_t mFirstSampleTimeRealUs; int64_t mPreviousTrackTimeUs; int64_t mTrackEveryTimeDurationUs; @@ -186,12 +193,9 @@ private: const uint8_t *parseParamSet( const uint8_t *data, size_t length, int type, size_t *paramSetLen); - status_t makeAVCCodecSpecificData( - const uint8_t *data, size_t size); - status_t copyAVCCodecSpecificData( - const uint8_t *data, size_t size); - status_t parseAVCCodecSpecificData( - const uint8_t *data, size_t size); + status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size); + status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size); + status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size); // Track authoring progress status void trackProgressStatus(int64_t timeUs, status_t err = OK); @@ -213,6 +217,31 @@ private: void addOneStscTableEntry(size_t chunkId, size_t sampleId); void addOneStssTableEntry(size_t sampleId); void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs); + void sendTrackSummary(bool hasMultipleTracks); + + // Write the boxes + void writeStcoBox(bool use32BitOffset); + void writeStscBox(); + void writeStszBox(); + void writeStssBox(); + void writeSttsBox(); + void writeD263Box(); + void writePaspBox(); + void writeAvccBox(); + void writeUrlBox(); + void writeDrefBox(); + void writeDinfBox(); + void writeDamrBox(); + void writeMdhdBox(time_t now); + void writeSmhdBox(); + void writeVmhdBox(); + void writeHdlrBox(); + void writeTkhdBox(time_t now); + void writeMp4aEsdsBox(); + void writeMp4vEsdsBox(); + void writeAudioFourCCBox(); + void writeVideoFourCCBox(); + void writeStblBox(bool use32BitOffset); Track(const Track &); Track &operator=(const Track &); @@ -229,9 +258,13 @@ MPEG4Writer::MPEG4Writer(const char *filename) mOffset(0), mMdatOffset(0), mEstimatedMoovBoxSize(0), - mInterleaveDurationUs(1000000) { + mInterleaveDurationUs(1000000), + mLatitudex10000(0), + mLongitudex10000(0), + mAreGeoTagsAvailable(false), + mStartTimeOffsetMs(-1) { - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); if (mFd >= 0) { mInitCheck = OK; } @@ -248,7 +281,11 @@ MPEG4Writer::MPEG4Writer(int fd) mOffset(0), mMdatOffset(0), mEstimatedMoovBoxSize(0), - mInterleaveDurationUs(1000000) { + mInterleaveDurationUs(1000000), + mLatitudex10000(0), + mLongitudex10000(0), + mAreGeoTagsAvailable(false), + mStartTimeOffsetMs(-1) { } MPEG4Writer::~MPEG4Writer() { @@ -295,7 +332,12 @@ status_t MPEG4Writer::Track::dump( } status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { - Track *track = new Track(this, source); + Mutex::Autolock l(mLock); + if (mStarted) { + LOGE("Attempt to add source AFTER recording is started"); + return UNKNOWN_ERROR; + } + Track *track = new Track(this, source, mTracks.size()); mTracks.push_back(track); return OK; @@ -444,20 +486,7 @@ status_t MPEG4Writer::start(MetaData *param) { mMoovBoxBuffer = NULL; mMoovBoxBufferOffset = 0; - beginBox("ftyp"); - { - int32_t fileType; - if (param && param->findInt32(kKeyFileType, &fileType) && - fileType != OUTPUT_FORMAT_MPEG_4) { - writeFourcc("3gp4"); - } else { - writeFourcc("isom"); - } - } - writeInt32(0); - writeFourcc("isom"); - writeFourcc("3gp4"); - endBox(); + writeFtypBox(param); mFreeBoxOffset = mOffset; @@ -637,43 +666,12 @@ status_t MPEG4Writer::stop() { } lseek64(mFd, mOffset, SEEK_SET); - time_t now = time(NULL); const off64_t moovOffset = mOffset; mWriteMoovBoxToMemory = true; mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); mMoovBoxBufferOffset = 0; CHECK(mMoovBoxBuffer != NULL); - int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6; - - beginBox("moov"); - - beginBox("mvhd"); - writeInt32(0); // version=0, flags=0 - writeInt32(now); // creation time - writeInt32(now); // modification time - writeInt32(mTimeScale); // mvhd timescale - writeInt32(duration); - writeInt32(0x10000); // rate: 1.0 - writeInt16(0x100); // volume - writeInt16(0); // reserved - writeInt32(0); // reserved - writeInt32(0); // reserved - writeCompositionMatrix(0); // matrix - writeInt32(0); // predefined - writeInt32(0); // predefined - writeInt32(0); // predefined - writeInt32(0); // predefined - writeInt32(0); // predefined - writeInt32(0); // predefined - writeInt32(mTracks.size() + 1); // nextTrackID - endBox(); // mvhd - - int32_t id = 1; - for (List<Track *>::iterator it = mTracks.begin(); - it != mTracks.end(); ++it, ++id) { - (*it)->writeTrackHeader(id, mUse32BitOffset); - } - endBox(); // moov + writeMoovBox(maxDurationUs); mWriteMoovBoxToMemory = false; if (mStreamableFile) { @@ -703,9 +701,96 @@ status_t MPEG4Writer::stop() { mFd = -1; mInitCheck = NO_INIT; mStarted = false; + return err; } +void MPEG4Writer::writeMvhdBox(int64_t durationUs) { + time_t now = time(NULL); + beginBox("mvhd"); + writeInt32(0); // version=0, flags=0 + writeInt32(now); // creation time + writeInt32(now); // modification time + writeInt32(mTimeScale); // mvhd timescale + int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6; + writeInt32(duration); + writeInt32(0x10000); // rate: 1.0 + writeInt16(0x100); // volume + writeInt16(0); // reserved + writeInt32(0); // reserved + writeInt32(0); // reserved + writeCompositionMatrix(0); // matrix + writeInt32(0); // predefined + writeInt32(0); // predefined + writeInt32(0); // predefined + writeInt32(0); // predefined + writeInt32(0); // predefined + writeInt32(0); // predefined + writeInt32(mTracks.size() + 1); // nextTrackID + endBox(); // mvhd +} + +void MPEG4Writer::writeMoovBox(int64_t durationUs) { + beginBox("moov"); + writeMvhdBox(durationUs); + if (mAreGeoTagsAvailable) { + writeUdtaBox(); + } + int32_t id = 1; + for (List<Track *>::iterator it = mTracks.begin(); + it != mTracks.end(); ++it, ++id) { + (*it)->writeTrackHeader(mUse32BitOffset); + } + endBox(); // moov +} + +void MPEG4Writer::writeFtypBox(MetaData *param) { + beginBox("ftyp"); + + int32_t fileType; + if (param && param->findInt32(kKeyFileType, &fileType) && + fileType != OUTPUT_FORMAT_MPEG_4) { + writeFourcc("3gp4"); + } else { + writeFourcc("isom"); + } + + writeInt32(0); + writeFourcc("isom"); + writeFourcc("3gp4"); + endBox(); +} + +static bool isTestModeEnabled() { +#if (PROPERTY_VALUE_MAX < 5) +#error "PROPERTY_VALUE_MAX must be at least 5" +#endif + + // Test mode is enabled only if rw.media.record.test system + // property is enabled. + char value[PROPERTY_VALUE_MAX]; + if (property_get("rw.media.record.test", value, NULL) && + (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) { + return true; + } + return false; +} + +void MPEG4Writer::sendSessionSummary() { + // Send session summary only if test mode is enabled + if (!isTestModeEnabled()) { + return; + } + + for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); + it != mChunkInfos.end(); ++it) { + int trackNum = it->mTrack->getTrackId() << 28; + notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS, + it->mMaxInterChunkDurUs); + } +} + status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) { mInterleaveDurationUs = durationUs; return OK; @@ -868,6 +953,77 @@ void MPEG4Writer::writeFourcc(const char *s) { write(s, 1, 4); } + +// Written in +/-DD.DDDD format +void MPEG4Writer::writeLatitude(int degreex10000) { + bool isNegative = (degreex10000 < 0); + char sign = isNegative? '-': '+'; + + // Handle the whole part + char str[9]; + int wholePart = degreex10000 / 10000; + if (wholePart == 0) { + snprintf(str, 5, "%c%.2d.", sign, wholePart); + } else { + snprintf(str, 5, "%+.2d.", wholePart); + } + + // Handle the fractional part + int fractionalPart = degreex10000 - (wholePart * 10000); + if (fractionalPart < 0) { + fractionalPart = -fractionalPart; + } + snprintf(&str[4], 5, "%.4d", fractionalPart); + + // Do not write the null terminator + write(str, 1, 8); +} + +// Written in +/- DDD.DDDD format +void MPEG4Writer::writeLongitude(int degreex10000) { + bool isNegative = (degreex10000 < 0); + char sign = isNegative? '-': '+'; + + // Handle the whole part + char str[10]; + int wholePart = degreex10000 / 10000; + if (wholePart == 0) { + snprintf(str, 6, "%c%.3d.", sign, wholePart); + } else { + snprintf(str, 6, "%+.3d.", wholePart); + } + + // Handle the fractional part + int fractionalPart = degreex10000 - (wholePart * 10000); + if (fractionalPart < 0) { + fractionalPart = -fractionalPart; + } + snprintf(&str[5], 5, "%.4d", fractionalPart); + + // Do not write the null terminator + write(str, 1, 9); +} + +/* + * Geodata is stored according to ISO-6709 standard. + * latitudex10000 is latitude in degrees times 10000, and + * longitudex10000 is longitude in degrees times 10000. + * The range for the latitude is in [-90, +90], and + * The range for the longitude is in [-180, +180] + */ +status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) { + // Is latitude or longitude out of range? + if (latitudex10000 < -900000 || latitudex10000 > 900000 || + longitudex10000 < -1800000 || longitudex10000 > 1800000) { + return BAD_VALUE; + } + + mLatitudex10000 = latitudex10000; + mLongitudex10000 = longitudex10000; + mAreGeoTagsAvailable = true; + return OK; +} + void MPEG4Writer::write(const void *data, size_t size) { write(data, 1, size); } @@ -945,7 +1101,7 @@ size_t MPEG4Writer::numTracks() { //////////////////////////////////////////////////////////////////////////////// MPEG4Writer::Track::Track( - MPEG4Writer *owner, const sp<MediaSource> &source) + MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId) : mOwner(owner), mMeta(source->getFormat()), mSource(source), @@ -953,6 +1109,7 @@ MPEG4Writer::Track::Track( mPaused(false), mResumed(false), mStarted(false), + mTrackId(trackId), mTrackDurationUs(0), mEstimatedTrackSizeBytes(0), mSamplesHaveSameSize(true), @@ -1149,18 +1306,13 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { void MPEG4Writer::writeAllChunks() { LOGV("writeAllChunks"); size_t outstandingChunks = 0; - while (!mChunkInfos.empty()) { - List<ChunkInfo>::iterator it = mChunkInfos.begin(); - while (!it->mChunks.empty()) { - Chunk chunk; - if (findChunkToWrite(&chunk)) { - writeChunkToFile(&chunk); - ++outstandingChunks; - } - } - it->mTrack = NULL; - mChunkInfos.erase(it); + Chunk chunk; + while (findChunkToWrite(&chunk)) { + ++outstandingChunks; } + + sendSessionSummary(); + mChunkInfos.clear(); LOGD("%d chunks are written in the last batch", outstandingChunks); } @@ -1168,8 +1320,6 @@ void MPEG4Writer::writeAllChunks() { bool MPEG4Writer::findChunkToWrite(Chunk *chunk) { LOGV("findChunkToWrite"); - // Find the smallest timestamp, and write that chunk out - // XXX: What if some track is just too slow? int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL; Track *track = NULL; for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); @@ -1198,6 +1348,13 @@ bool MPEG4Writer::findChunkToWrite(Chunk *chunk) { *chunk = *(it->mChunks.begin()); it->mChunks.erase(it->mChunks.begin()); CHECK_EQ(chunk->mTrack, track); + + int64_t interChunkTimeUs = + chunk->mTimeStampUs - it->mPrevChunkTimestampUs; + if (interChunkTimeUs > it->mPrevChunkTimestampUs) { + it->mMaxInterChunkDurUs = interChunkTimeUs; + } + return true; } } @@ -1241,6 +1398,8 @@ status_t MPEG4Writer::startWriterThread() { it != mTracks.end(); ++it) { ChunkInfo info; info.mTrack = *it; + info.mPrevChunkTimestampUs = 0; + info.mMaxInterChunkDurUs = 0; mChunkInfos.push_back(info); } @@ -1264,6 +1423,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) { if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) { startTimeUs = 0; } + mStartTimeRealUs = startTimeUs; int32_t rotationDegrees; if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) { @@ -1288,10 +1448,15 @@ status_t MPEG4Writer::Track::start(MetaData *params) { * session, and it also helps eliminate the "recording" sound for * camcorder applications. * - * Ideally, this platform-specific value should be defined - * in media_profiles.xml file + * If client does not set the start time offset, we fall back to + * use the default initial delay value. */ - startTimeUs += 700000; + int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL; + if (startTimeOffsetUs < 0) { // Start time offset was not set + startTimeOffsetUs = kInitialDelayTimeUs; + } + startTimeUs += startTimeOffsetUs; + LOGI("Start time offset: %lld us", startTimeOffsetUs); } meta->setInt64(kKeyTime, startTimeUs); @@ -1322,6 +1487,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) { mPrevMediaTimeAdjustSample = 0; mTotalDriftTimeToAdjustUs = 0; mPrevTotalAccumDriftTimeUs = 0; + mMaxChunkDurationUs = 0; pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); @@ -1759,6 +1925,7 @@ void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) { status_t MPEG4Writer::Track::threadEntry() { int32_t count = 0; const int64_t interleaveDurationUs = mOwner->interleaveDuration(); + const bool hasMultipleTracks = (mOwner->numTracks() > 1); int64_t chunkTimestampUs = 0; int32_t nChunks = 0; int32_t nZeroLengthFrames = 0; @@ -1896,7 +2063,8 @@ status_t MPEG4Writer::Track::threadEntry() { LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs); //////////////////////////////////////////////////////////////////////////////// - if (mSampleSizes.empty()) { + if (mNumSamples == 0) { + mFirstSampleTimeRealUs = systemTime() / 1000; mStartTimestampUs = timestampUs; mOwner->setStartTimestampUs(mStartTimestampUs); previousPausedDurationUs = mStartTimestampUs; @@ -1991,7 +2159,7 @@ status_t MPEG4Writer::Track::threadEntry() { } trackProgressStatus(timestampUs); } - if (mOwner->numTracks() == 1) { + if (!hasMultipleTracks) { off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); if (mChunkOffsets.empty()) { @@ -2010,7 +2178,11 @@ status_t MPEG4Writer::Track::threadEntry() { if (chunkTimestampUs == 0) { chunkTimestampUs = timestampUs; } else { - if (timestampUs - chunkTimestampUs > interleaveDurationUs) { + int64_t chunkDurationUs = timestampUs - chunkTimestampUs; + if (chunkDurationUs > interleaveDurationUs) { + if (chunkDurationUs > mMaxChunkDurationUs) { + mMaxChunkDurationUs = chunkDurationUs; + } ++nChunks; if (nChunks == 1 || // First chunk (--(mStscTableEntries.end()))->samplesPerChunk != @@ -2030,10 +2202,10 @@ status_t MPEG4Writer::Track::threadEntry() { (OK != checkCodecSpecificData())) { // no codec specific data err = ERROR_MALFORMED; } - mOwner->trackProgressStatus(this, -1, err); + mOwner->trackProgressStatus(mTrackId, -1, err); // Last chunk - if (mOwner->numTracks() == 1) { + if (!hasMultipleTracks) { addOneStscTableEntry(1, mNumSamples); } else if (!mChunkSamples.empty()) { addOneStscTableEntry(++nChunks, mChunkSamples.size()); @@ -2060,6 +2232,9 @@ status_t MPEG4Writer::Track::threadEntry() { mTrackDurationUs += lastDurationUs; mReachedEOS = true; + + sendTrackSummary(hasMultipleTracks); + LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s", count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video"); if (mIsAudio) { @@ -2072,46 +2247,94 @@ status_t MPEG4Writer::Track::threadEntry() { return err; } +void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { + + // Send track summary only if test mode is enabled. + if (!isTestModeEnabled()) { + return; + } + + int trackNum = (mTrackId << 28); + + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE, + mIsAudio? 0: 1); + + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS, + mTrackDurationUs / 1000); + + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES, + mNumSamples); + + { + // The system delay time excluding the requested initial delay that + // is used to eliminate the recording sound. + int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL; + if (startTimeOffsetUs < 0) { // Start time offset was not set + startTimeOffsetUs = kInitialDelayTimeUs; + } + int64_t initialDelayUs = + mFirstSampleTimeRealUs - mStartTimeRealUs - startTimeOffsetUs; + + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS, + (initialDelayUs) / 1000); + } + + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES, + mMdatSizeBytes / 1024); + + if (hasMultipleTracks) { + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS, + mMaxChunkDurationUs / 1000); + + int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); + if (mStartTimestampUs != moovStartTimeUs) { + int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; + mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS, + startTimeOffsetUs / 1000); + } + } +} + void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { LOGV("trackProgressStatus: %lld us", timeUs); if (mTrackEveryTimeDurationUs > 0 && timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { LOGV("Fire time tracking progress status at %lld us", timeUs); - mOwner->trackProgressStatus(this, timeUs - mPreviousTrackTimeUs, err); + mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err); mPreviousTrackTimeUs = timeUs; } } void MPEG4Writer::trackProgressStatus( - const MPEG4Writer::Track* track, int64_t timeUs, status_t err) { + size_t trackId, int64_t timeUs, status_t err) { Mutex::Autolock lock(mLock); - int32_t nTracks = mTracks.size(); - CHECK(nTracks >= 1); - CHECK(nTracks < 64); // Arbitrary number - - int32_t trackNum = 0; - CHECK(trackNum < nTracks); - trackNum <<= 16; + int32_t trackNum = (trackId << 28); // Error notification // Do not consider ERROR_END_OF_STREAM an error if (err != OK && err != ERROR_END_OF_STREAM) { - notify(MEDIA_RECORDER_EVENT_ERROR, - trackNum | MEDIA_RECORDER_ERROR_UNKNOWN, + notify(MEDIA_RECORDER_TRACK_EVENT_ERROR, + trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err); return; } if (timeUs == -1) { // Send completion notification - notify(MEDIA_RECORDER_EVENT_INFO, - trackNum | MEDIA_RECORDER_INFO_COMPLETION_STATUS, + notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, err); - return; } else { // Send progress status - notify(MEDIA_RECORDER_EVENT_INFO, - trackNum | MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS, + notify(MEDIA_RECORDER_TRACK_EVENT_INFO, + trackNum | MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME, timeUs / 1000); } } @@ -2169,388 +2392,484 @@ status_t MPEG4Writer::Track::checkCodecSpecificData() const { return OK; } -void MPEG4Writer::Track::writeTrackHeader( - int32_t trackID, bool use32BitOffset) { - const char *mime; - bool success = mMeta->findCString(kKeyMIMEType, &mime); - CHECK(success); +void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) { LOGV("%s track time scale: %d", mIsAudio? "Audio": "Video", mTimeScale); time_t now = time(NULL); + mOwner->beginBox("trak"); + writeTkhdBox(now); + mOwner->beginBox("mdia"); + writeMdhdBox(now); + writeHdlrBox(); + mOwner->beginBox("minf"); + if (mIsAudio) { + writeSmhdBox(); + } else { + writeVmhdBox(); + } + writeDinfBox(); + writeStblBox(use32BitOffset); + mOwner->endBox(); // minf + mOwner->endBox(); // mdia + mOwner->endBox(); // trak +} + +void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) { + mOwner->beginBox("stbl"); + mOwner->beginBox("stsd"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(1); // entry count + if (mIsAudio) { + writeAudioFourCCBox(); + } else { + writeVideoFourCCBox(); + } + mOwner->endBox(); // stsd + writeSttsBox(); + if (!mIsAudio) { + writeStssBox(); + } + writeStszBox(); + writeStscBox(); + writeStcoBox(use32BitOffset); + mOwner->endBox(); // stbl +} + +void MPEG4Writer::Track::writeVideoFourCCBox() { + const char *mime; + bool success = mMeta->findCString(kKeyMIMEType, &mime); + CHECK(success); + if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { + mOwner->beginBox("mp4v"); + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { + mOwner->beginBox("s263"); + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + mOwner->beginBox("avc1"); + } else { + LOGE("Unknown mime type '%s'.", mime); + CHECK(!"should not be here, unknown mime type."); + } + + mOwner->writeInt32(0); // reserved + mOwner->writeInt16(0); // reserved + mOwner->writeInt16(1); // data ref index + mOwner->writeInt16(0); // predefined + mOwner->writeInt16(0); // reserved + mOwner->writeInt32(0); // predefined + mOwner->writeInt32(0); // predefined + mOwner->writeInt32(0); // predefined + + int32_t width, height; + success = mMeta->findInt32(kKeyWidth, &width); + success = success && mMeta->findInt32(kKeyHeight, &height); + CHECK(success); + + mOwner->writeInt16(width); + mOwner->writeInt16(height); + mOwner->writeInt32(0x480000); // horiz resolution + mOwner->writeInt32(0x480000); // vert resolution + mOwner->writeInt32(0); // reserved + mOwner->writeInt16(1); // frame count + mOwner->write(" ", 32); + mOwner->writeInt16(0x18); // depth + mOwner->writeInt16(-1); // predefined + + CHECK(23 + mCodecSpecificDataSize < 128); + + if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { + writeMp4vEsdsBox(); + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { + writeD263Box(); + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + writeAvccBox(); + } + + writePaspBox(); + mOwner->endBox(); // mp4v, s263 or avc1 +} + +void MPEG4Writer::Track::writeAudioFourCCBox() { + const char *mime; + bool success = mMeta->findCString(kKeyMIMEType, &mime); + CHECK(success); + const char *fourcc = NULL; + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { + fourcc = "samr"; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { + fourcc = "sawb"; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + fourcc = "mp4a"; + } else { + LOGE("Unknown mime type '%s'.", mime); + CHECK(!"should not be here, unknown mime type."); + } + + mOwner->beginBox(fourcc); // audio format + mOwner->writeInt32(0); // reserved + mOwner->writeInt16(0); // reserved + mOwner->writeInt16(0x1); // data ref index + mOwner->writeInt32(0); // reserved + mOwner->writeInt32(0); // reserved + int32_t nChannels; + CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); + mOwner->writeInt16(nChannels); // channel count + mOwner->writeInt16(16); // sample size + mOwner->writeInt16(0); // predefined + mOwner->writeInt16(0); // reserved + + int32_t samplerate; + success = mMeta->findInt32(kKeySampleRate, &samplerate); + CHECK(success); + mOwner->writeInt32(samplerate << 16); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + writeMp4aEsdsBox(); + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) || + !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { + writeDamrBox(); + } + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeMp4aEsdsBox() { + mOwner->beginBox("esds"); + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize > 0); + + // Make sure all sizes encode to a single byte. + CHECK(mCodecSpecificDataSize + 23 < 128); + + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt8(0x03); // ES_DescrTag + mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->writeInt16(0x0000);// ES_ID + mOwner->writeInt8(0x00); + + mOwner->writeInt8(0x04); // DecoderConfigDescrTag + mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 + mOwner->writeInt8(0x15); // streamType AudioStream + + mOwner->writeInt16(0x03); // XXX + mOwner->writeInt8(0x00); // buffer size 24-bit + mOwner->writeInt32(96000); // max bit rate + mOwner->writeInt32(96000); // avg bit rate + + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag + mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); + + static const uint8_t kData2[] = { + 0x06, // SLConfigDescriptorTag + 0x01, + 0x02 + }; + mOwner->write(kData2, sizeof(kData2)); + + mOwner->endBox(); // esds +} + +void MPEG4Writer::Track::writeMp4vEsdsBox() { + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize > 0); + mOwner->beginBox("esds"); + + mOwner->writeInt32(0); // version=0, flags=0 + + mOwner->writeInt8(0x03); // ES_DescrTag + mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->writeInt16(0x0000); // ES_ID + mOwner->writeInt8(0x1f); + + mOwner->writeInt8(0x04); // DecoderConfigDescrTag + mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 + mOwner->writeInt8(0x11); // streamType VisualStream + + static const uint8_t kData[] = { + 0x01, 0x77, 0x00, + 0x00, 0x03, 0xe8, 0x00, + 0x00, 0x03, 0xe8, 0x00 + }; + mOwner->write(kData, sizeof(kData)); + + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag + + mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); + + static const uint8_t kData2[] = { + 0x06, // SLConfigDescriptorTag + 0x01, + 0x02 + }; + mOwner->write(kData2, sizeof(kData2)); + + mOwner->endBox(); // esds +} + +void MPEG4Writer::Track::writeTkhdBox(time_t now) { + mOwner->beginBox("tkhd"); + // Flags = 7 to indicate that the track is enabled, and + // part of the presentation + mOwner->writeInt32(0x07); // version=0, flags=7 + mOwner->writeInt32(now); // creation time + mOwner->writeInt32(now); // modification time + mOwner->writeInt32(mTrackId + 1); // track id starts with 1 + mOwner->writeInt32(0); // reserved + int64_t trakDurationUs = getDurationUs(); int32_t mvhdTimeScale = mOwner->getTimeScale(); + int32_t tkhdDuration = + (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; + mOwner->writeInt32(tkhdDuration); // in mvhd timescale + mOwner->writeInt32(0); // reserved + mOwner->writeInt32(0); // reserved + mOwner->writeInt16(0); // layer + mOwner->writeInt16(0); // alternate group + mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume + mOwner->writeInt16(0); // reserved + + mOwner->writeCompositionMatrix(mRotation); // matrix + + if (mIsAudio) { + mOwner->writeInt32(0); + mOwner->writeInt32(0); + } else { + int32_t width, height; + bool success = mMeta->findInt32(kKeyWidth, &width); + success = success && mMeta->findInt32(kKeyHeight, &height); + CHECK(success); + + mOwner->writeInt32(width << 16); // 32-bit fixed-point value + mOwner->writeInt32(height << 16); // 32-bit fixed-point value + } + mOwner->endBox(); // tkhd +} + +void MPEG4Writer::Track::writeVmhdBox() { + mOwner->beginBox("vmhd"); + mOwner->writeInt32(0x01); // version=0, flags=1 + mOwner->writeInt16(0); // graphics mode + mOwner->writeInt16(0); // opcolor + mOwner->writeInt16(0); + mOwner->writeInt16(0); + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeSmhdBox() { + mOwner->beginBox("smhd"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt16(0); // balance + mOwner->writeInt16(0); // reserved + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeHdlrBox() { + mOwner->beginBox("hdlr"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(0); // component type: should be mhlr + mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype + mOwner->writeInt32(0); // reserved + mOwner->writeInt32(0); // reserved + mOwner->writeInt32(0); // reserved + // Removing "r" for the name string just makes the string 4 byte aligned + mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeMdhdBox(time_t now) { int64_t trakDurationUs = getDurationUs(); + mOwner->beginBox("mdhd"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(now); // creation time + mOwner->writeInt32(now); // modification time + mOwner->writeInt32(mTimeScale); // media timescale + int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6; + mOwner->writeInt32(mdhdDuration); // use media timescale + // Language follows the three letter standard ISO-639-2/T + // 'e', 'n', 'g' for "English", for instance. + // Each character is packed as the difference between its ASCII value and 0x60. + // For "English", these are 00101, 01110, 00111. + // XXX: Where is the padding bit located: 0x15C7? + mOwner->writeInt16(0); // language code + mOwner->writeInt16(0); // predefined + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeDamrBox() { + // 3gpp2 Spec AMRSampleEntry fields + mOwner->beginBox("damr"); + mOwner->writeCString(" "); // vendor: 4 bytes + mOwner->writeInt8(0); // decoder version + mOwner->writeInt16(0x83FF); // mode set: all enabled + mOwner->writeInt8(0); // mode change period + mOwner->writeInt8(1); // frames per sample + mOwner->endBox(); +} + +void MPEG4Writer::Track::writeUrlBox() { + // The table index here refers to the sample description index + // in the sample table entries. + mOwner->beginBox("url "); + mOwner->writeInt32(1); // version=0, flags=1 (self-contained) + mOwner->endBox(); // url +} + +void MPEG4Writer::Track::writeDrefBox() { + mOwner->beginBox("dref"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(1); // entry count (either url or urn) + writeUrlBox(); + mOwner->endBox(); // dref +} + +void MPEG4Writer::Track::writeDinfBox() { + mOwner->beginBox("dinf"); + writeDrefBox(); + mOwner->endBox(); // dinf +} + +void MPEG4Writer::Track::writeAvccBox() { + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize >= 5); + + // Patch avcc's lengthSize field to match the number + // of bytes we use to indicate the size of a nal unit. + uint8_t *ptr = (uint8_t *)mCodecSpecificData; + ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1); + mOwner->beginBox("avcC"); + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); + mOwner->endBox(); // avcC +} + +void MPEG4Writer::Track::writeD263Box() { + mOwner->beginBox("d263"); + mOwner->writeInt32(0); // vendor + mOwner->writeInt8(0); // decoder version + mOwner->writeInt8(10); // level: 10 + mOwner->writeInt8(0); // profile: 0 + mOwner->endBox(); // d263 +} + +// This is useful if the pixel is not square +void MPEG4Writer::Track::writePaspBox() { + mOwner->beginBox("pasp"); + mOwner->writeInt32(1 << 16); // hspacing + mOwner->writeInt32(1 << 16); // vspacing + mOwner->endBox(); // pasp +} + +void MPEG4Writer::Track::writeSttsBox() { + mOwner->beginBox("stts"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mNumSttsTableEntries); // Compensate for small start time difference from different media tracks int64_t trackStartTimeOffsetUs = 0; + int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); + if (mStartTimestampUs != moovStartTimeUs) { + CHECK(mStartTimestampUs > moovStartTimeUs); + trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; + } + int64_t prevTimestampUs = trackStartTimeOffsetUs; + for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); + it != mSttsTableEntries.end(); ++it) { + mOwner->writeInt32(it->sampleCount); - mOwner->beginBox("trak"); + // Make sure that we are calculating the sample duration the exactly + // same way as we made decision on how to create stts entries. + int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs; + int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL - + (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL); + prevTimestampUs += (it->sampleCount * it->sampleDurationUs); - mOwner->beginBox("tkhd"); - // Flags = 7 to indicate that the track is enabled, and - // part of the presentation - mOwner->writeInt32(0x07); // version=0, flags=7 - mOwner->writeInt32(now); // creation time - mOwner->writeInt32(now); // modification time - mOwner->writeInt32(trackID); - mOwner->writeInt32(0); // reserved - int32_t tkhdDuration = - (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; - mOwner->writeInt32(tkhdDuration); // in mvhd timescale - mOwner->writeInt32(0); // reserved - mOwner->writeInt32(0); // reserved - mOwner->writeInt16(0); // layer - mOwner->writeInt16(0); // alternate group - mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume - mOwner->writeInt16(0); // reserved - - mOwner->writeCompositionMatrix(mRotation); // matrix - - if (mIsAudio) { - mOwner->writeInt32(0); - mOwner->writeInt32(0); - } else { - int32_t width, height; - bool success = mMeta->findInt32(kKeyWidth, &width); - success = success && mMeta->findInt32(kKeyHeight, &height); - CHECK(success); + mOwner->writeInt32(dur); + } + mOwner->endBox(); // stts +} - mOwner->writeInt32(width << 16); // 32-bit fixed-point value - mOwner->writeInt32(height << 16); // 32-bit fixed-point value +void MPEG4Writer::Track::writeStssBox() { + mOwner->beginBox("stss"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mNumStssTableEntries); // number of sync frames + for (List<int32_t>::iterator it = mStssTableEntries.begin(); + it != mStssTableEntries.end(); ++it) { + mOwner->writeInt32(*it); + } + mOwner->endBox(); // stss +} + +void MPEG4Writer::Track::writeStszBox() { + mOwner->beginBox("stsz"); + mOwner->writeInt32(0); // version=0, flags=0 + if (mSamplesHaveSameSize) { + List<size_t>::iterator it = mSampleSizes.begin(); + mOwner->writeInt32(*it); // default sample size + } else { + mOwner->writeInt32(0); + } + mOwner->writeInt32(mNumSamples); + if (!mSamplesHaveSameSize) { + for (List<size_t>::iterator it = mSampleSizes.begin(); + it != mSampleSizes.end(); ++it) { + mOwner->writeInt32(*it); } - mOwner->endBox(); // tkhd - - int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); - if (mStartTimestampUs != moovStartTimeUs) { - CHECK(mStartTimestampUs > moovStartTimeUs); - trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; - } - - mOwner->beginBox("mdia"); - - mOwner->beginBox("mdhd"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(now); // creation time - mOwner->writeInt32(now); // modification time - mOwner->writeInt32(mTimeScale); // media timescale - int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6; - mOwner->writeInt32(mdhdDuration); // use media timescale - // Language follows the three letter standard ISO-639-2/T - // 'e', 'n', 'g' for "English", for instance. - // Each character is packed as the difference between its ASCII value and 0x60. - // For "English", these are 00101, 01110, 00111. - // XXX: Where is the padding bit located: 0x15C7? - mOwner->writeInt16(0); // language code - mOwner->writeInt16(0); // predefined - mOwner->endBox(); - - mOwner->beginBox("hdlr"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(0); // component type: should be mhlr - mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype - mOwner->writeInt32(0); // reserved - mOwner->writeInt32(0); // reserved - mOwner->writeInt32(0); // reserved - // Removing "r" for the name string just makes the string 4 byte aligned - mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name - mOwner->endBox(); - - mOwner->beginBox("minf"); - if (mIsAudio) { - mOwner->beginBox("smhd"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt16(0); // balance - mOwner->writeInt16(0); // reserved - mOwner->endBox(); - } else { - mOwner->beginBox("vmhd"); - mOwner->writeInt32(0x01); // version=0, flags=1 - mOwner->writeInt16(0); // graphics mode - mOwner->writeInt16(0); // opcolor - mOwner->writeInt16(0); - mOwner->writeInt16(0); - mOwner->endBox(); - } - - mOwner->beginBox("dinf"); - mOwner->beginBox("dref"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(1); // entry count (either url or urn) - // The table index here refers to the sample description index - // in the sample table entries. - mOwner->beginBox("url "); - mOwner->writeInt32(1); // version=0, flags=1 (self-contained) - mOwner->endBox(); // url - mOwner->endBox(); // dref - mOwner->endBox(); // dinf - - mOwner->beginBox("stbl"); - - mOwner->beginBox("stsd"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(1); // entry count - if (mIsAudio) { - const char *fourcc = NULL; - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { - fourcc = "samr"; - } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { - fourcc = "sawb"; - } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { - fourcc = "mp4a"; - } else { - LOGE("Unknown mime type '%s'.", mime); - CHECK(!"should not be here, unknown mime type."); - } + } + mOwner->endBox(); // stsz +} - mOwner->beginBox(fourcc); // audio format - mOwner->writeInt32(0); // reserved - mOwner->writeInt16(0); // reserved - mOwner->writeInt16(0x1); // data ref index - mOwner->writeInt32(0); // reserved - mOwner->writeInt32(0); // reserved - int32_t nChannels; - CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); - mOwner->writeInt16(nChannels); // channel count - mOwner->writeInt16(16); // sample size - mOwner->writeInt16(0); // predefined - mOwner->writeInt16(0); // reserved - - int32_t samplerate; - bool success = mMeta->findInt32(kKeySampleRate, &samplerate); - CHECK(success); - mOwner->writeInt32(samplerate << 16); - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { - mOwner->beginBox("esds"); - CHECK(mCodecSpecificData); - CHECK(mCodecSpecificDataSize > 0); - - // Make sure all sizes encode to a single byte. - CHECK(mCodecSpecificDataSize + 23 < 128); - - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt8(0x03); // ES_DescrTag - mOwner->writeInt8(23 + mCodecSpecificDataSize); - mOwner->writeInt16(0x0000);// ES_ID - mOwner->writeInt8(0x00); - - mOwner->writeInt8(0x04); // DecoderConfigDescrTag - mOwner->writeInt8(15 + mCodecSpecificDataSize); - mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 - mOwner->writeInt8(0x15); // streamType AudioStream - - mOwner->writeInt16(0x03); // XXX - mOwner->writeInt8(0x00); // buffer size 24-bit - mOwner->writeInt32(96000); // max bit rate - mOwner->writeInt32(96000); // avg bit rate - - mOwner->writeInt8(0x05); // DecoderSpecificInfoTag - mOwner->writeInt8(mCodecSpecificDataSize); - mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); - - static const uint8_t kData2[] = { - 0x06, // SLConfigDescriptorTag - 0x01, - 0x02 - }; - mOwner->write(kData2, sizeof(kData2)); - - mOwner->endBox(); // esds - } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) || - !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { - // 3gpp2 Spec AMRSampleEntry fields - mOwner->beginBox("damr"); - mOwner->writeCString(" "); // vendor: 4 bytes - mOwner->writeInt8(0); // decoder version - mOwner->writeInt16(0x83FF); // mode set: all enabled - mOwner->writeInt8(0); // mode change period - mOwner->writeInt8(1); // frames per sample - mOwner->endBox(); - } - mOwner->endBox(); - } else { - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { - mOwner->beginBox("mp4v"); - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { - mOwner->beginBox("s263"); - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { - mOwner->beginBox("avc1"); - } else { - LOGE("Unknown mime type '%s'.", mime); - CHECK(!"should not be here, unknown mime type."); - } +void MPEG4Writer::Track::writeStscBox() { + mOwner->beginBox("stsc"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mNumStscTableEntries); + for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); + it != mStscTableEntries.end(); ++it) { + mOwner->writeInt32(it->firstChunk); + mOwner->writeInt32(it->samplesPerChunk); + mOwner->writeInt32(it->sampleDescriptionId); + } + mOwner->endBox(); // stsc +} - mOwner->writeInt32(0); // reserved - mOwner->writeInt16(0); // reserved - mOwner->writeInt16(1); // data ref index - mOwner->writeInt16(0); // predefined - mOwner->writeInt16(0); // reserved - mOwner->writeInt32(0); // predefined - mOwner->writeInt32(0); // predefined - mOwner->writeInt32(0); // predefined - - int32_t width, height; - bool success = mMeta->findInt32(kKeyWidth, &width); - success = success && mMeta->findInt32(kKeyHeight, &height); - CHECK(success); - - mOwner->writeInt16(width); - mOwner->writeInt16(height); - mOwner->writeInt32(0x480000); // horiz resolution - mOwner->writeInt32(0x480000); // vert resolution - mOwner->writeInt32(0); // reserved - mOwner->writeInt16(1); // frame count - mOwner->write(" ", 32); - mOwner->writeInt16(0x18); // depth - mOwner->writeInt16(-1); // predefined - - CHECK(23 + mCodecSpecificDataSize < 128); - - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { - CHECK(mCodecSpecificData); - CHECK(mCodecSpecificDataSize > 0); - mOwner->beginBox("esds"); - - mOwner->writeInt32(0); // version=0, flags=0 - - mOwner->writeInt8(0x03); // ES_DescrTag - mOwner->writeInt8(23 + mCodecSpecificDataSize); - mOwner->writeInt16(0x0000); // ES_ID - mOwner->writeInt8(0x1f); - - mOwner->writeInt8(0x04); // DecoderConfigDescrTag - mOwner->writeInt8(15 + mCodecSpecificDataSize); - mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 - mOwner->writeInt8(0x11); // streamType VisualStream - - static const uint8_t kData[] = { - 0x01, 0x77, 0x00, - 0x00, 0x03, 0xe8, 0x00, - 0x00, 0x03, 0xe8, 0x00 - }; - mOwner->write(kData, sizeof(kData)); - - mOwner->writeInt8(0x05); // DecoderSpecificInfoTag - - mOwner->writeInt8(mCodecSpecificDataSize); - mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); - - static const uint8_t kData2[] = { - 0x06, // SLConfigDescriptorTag - 0x01, - 0x02 - }; - mOwner->write(kData2, sizeof(kData2)); - - mOwner->endBox(); // esds - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { - mOwner->beginBox("d263"); - - mOwner->writeInt32(0); // vendor - mOwner->writeInt8(0); // decoder version - mOwner->writeInt8(10); // level: 10 - mOwner->writeInt8(0); // profile: 0 - - mOwner->endBox(); // d263 - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { - CHECK(mCodecSpecificData); - CHECK(mCodecSpecificDataSize >= 5); - - // Patch avcc's lengthSize field to match the number - // of bytes we use to indicate the size of a nal unit. - uint8_t *ptr = (uint8_t *)mCodecSpecificData; - ptr[4] = - (ptr[4] & 0xfc) - | (mOwner->useNalLengthFour() ? 3 : 1); - - mOwner->beginBox("avcC"); - mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); - mOwner->endBox(); // avcC - } - - mOwner->beginBox("pasp"); - // This is useful if the pixel is not square - mOwner->writeInt32(1 << 16); // hspacing - mOwner->writeInt32(1 << 16); // vspacing - mOwner->endBox(); // pasp - mOwner->endBox(); // mp4v, s263 or avc1 - } - mOwner->endBox(); // stsd - - mOwner->beginBox("stts"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumSttsTableEntries); - int64_t prevTimestampUs = trackStartTimeOffsetUs; - for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); - it != mSttsTableEntries.end(); ++it) { - mOwner->writeInt32(it->sampleCount); - - // Make sure that we are calculating the sample duration the exactly - // same way as we made decision on how to create stts entries. - int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs; - int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL - - (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL); - prevTimestampUs += (it->sampleCount * it->sampleDurationUs); - - mOwner->writeInt32(dur); - } - mOwner->endBox(); // stts - - if (!mIsAudio) { - mOwner->beginBox("stss"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStssTableEntries); // number of sync frames - for (List<int32_t>::iterator it = mStssTableEntries.begin(); - it != mStssTableEntries.end(); ++it) { - mOwner->writeInt32(*it); - } - mOwner->endBox(); // stss - } - - mOwner->beginBox("stsz"); - mOwner->writeInt32(0); // version=0, flags=0 - if (mSamplesHaveSameSize) { - List<size_t>::iterator it = mSampleSizes.begin(); - mOwner->writeInt32(*it); // default sample size - } else { - mOwner->writeInt32(0); - } - mOwner->writeInt32(mNumSamples); - if (!mSamplesHaveSameSize) { - for (List<size_t>::iterator it = mSampleSizes.begin(); - it != mSampleSizes.end(); ++it) { - mOwner->writeInt32(*it); - } - } - mOwner->endBox(); // stsz - - mOwner->beginBox("stsc"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStscTableEntries); - for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); - it != mStscTableEntries.end(); ++it) { - mOwner->writeInt32(it->firstChunk); - mOwner->writeInt32(it->samplesPerChunk); - mOwner->writeInt32(it->sampleDescriptionId); - } - mOwner->endBox(); // stsc - mOwner->beginBox(use32BitOffset? "stco": "co64"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStcoTableEntries); - for (List<off64_t>::iterator it = mChunkOffsets.begin(); - it != mChunkOffsets.end(); ++it) { - if (use32BitOffset) { - mOwner->writeInt32(static_cast<int32_t>(*it)); - } else { - mOwner->writeInt64((*it)); - } - } - mOwner->endBox(); // stco or co64 +void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) { + mOwner->beginBox(use32BitOffset? "stco": "co64"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mNumStcoTableEntries); + for (List<off64_t>::iterator it = mChunkOffsets.begin(); + it != mChunkOffsets.end(); ++it) { + if (use32BitOffset) { + mOwner->writeInt32(static_cast<int32_t>(*it)); + } else { + mOwner->writeInt64((*it)); + } + } + mOwner->endBox(); // stco or co64 +} - mOwner->endBox(); // stbl - mOwner->endBox(); // minf - mOwner->endBox(); // mdia - mOwner->endBox(); // trak +void MPEG4Writer::writeUdtaBox() { + beginBox("udta"); + writeGeoDataBox(); + endBox(); +} + +/* + * Geodata is stored according to ISO-6709 standard. + */ +void MPEG4Writer::writeGeoDataBox() { + beginBox("\xA9xyz"); + /* + * For historical reasons, any user data start + * with "\0xA9", must be followed by its assoicated + * language code. + * 0x0012: locale en + * 0x15c7: language 5575 + */ + writeInt32(0x001215c7); + writeLatitude(mLatitudex10000); + writeLongitude(mLongitudex10000); + writeInt8(0x2F); + endBox(); } } // namespace android diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 0be7261..8cd08bc 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -43,7 +43,10 @@ const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg"; const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts"; +const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi"; const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm"; +const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt"; + } // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 23bad5b..af0131e 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include "include/AMRExtractor.h" +#include "include/AVIExtractor.h" #include "include/MP3Extractor.h" #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" @@ -108,6 +109,8 @@ sp<MediaExtractor> MediaExtractor::Create( ret = new MatroskaExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { ret = new MPEG2TSExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) { + ret = new AVIExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) { ret = new WVMExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) { diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index 248b678..81f2e47 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -323,25 +323,28 @@ void NuCachedSource2::onRead(const sp<AMessage> &msg) { } void NuCachedSource2::restartPrefetcherIfNecessary_l( - bool ignoreLowWaterThreshold) { + bool ignoreLowWaterThreshold, bool force) { static const size_t kGrayArea = 1024 * 1024; if (mFetching || mFinalStatus != OK) { return; } - if (!ignoreLowWaterThreshold + if (!ignoreLowWaterThreshold && !force && mCacheOffset + mCache->totalSize() - mLastAccessPos >= kLowWaterThreshold) { return; } size_t maxBytes = mLastAccessPos - mCacheOffset; - if (maxBytes < kGrayArea) { - return; - } - maxBytes -= kGrayArea; + if (!force) { + if (maxBytes < kGrayArea) { + return; + } + + maxBytes -= kGrayArea; + } size_t actualBytes = mCache->releaseFromStart(maxBytes); mCacheOffset += actualBytes; @@ -413,10 +416,19 @@ size_t NuCachedSource2::approxDataRemaining_l(status_t *finalStatus) { } ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) { + CHECK_LE(size, (size_t)kHighWaterThreshold); + LOGV("readInternal offset %lld size %d", offset, size); Mutex::Autolock autoLock(mLock); + if (!mFetching) { + mLastAccessPos = offset; + restartPrefetcherIfNecessary_l( + false, // ignoreLowWaterThreshold + true); // force + } + if (offset < mCacheOffset || offset >= (off64_t)(mCacheOffset + mCache->totalSize())) { static const off64_t kPadding = 256 * 1024; @@ -438,6 +450,11 @@ ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) { } size_t avail = mCache->totalSize() - delta; + + if (avail > size) { + avail = size; + } + mCache->copy(delta, data, avail); return avail; @@ -481,11 +498,11 @@ void NuCachedSource2::resumeFetchingIfNecessary() { restartPrefetcherIfNecessary_l(true /* ignore low water threshold */); } -DecryptHandle* NuCachedSource2::DrmInitialization() { +sp<DecryptHandle> NuCachedSource2::DrmInitialization() { return mSource->DrmInitialization(); } -void NuCachedSource2::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) { +void NuCachedSource2::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) { mSource->getDrmInfo(handle, client); } @@ -493,4 +510,8 @@ String8 NuCachedSource2::getUri() { return mSource->getUri(); } +String8 NuCachedSource2::getMIMEType() const { + return mSource->getMIMEType(); +} + } // namespace android diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index dd29c84..ce30fc8 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -100,7 +100,6 @@ NuHTTPDataSource::NuHTTPDataSource(uint32_t flags) mNumBandwidthHistoryItems(0), mTotalTransferTimeUs(0), mTotalTransferBytes(0), - mPrevBandwidthMeasureTimeUs(0), mDecryptHandle(NULL), mDrmManagerClient(NULL) { } @@ -137,6 +136,7 @@ status_t NuHTTPDataSource::connect( unsigned port; mUri = uri; + mContentType = String8("application/octet-stream"); bool https; if (!ParseURL(uri, &host, &port, &path, &https)) { @@ -266,6 +266,15 @@ status_t NuHTTPDataSource::connect( } } + { + AString value; + if (mHTTP.find_header_value("Content-Type", &value)) { + mContentType = String8(value.c_str()); + } else { + mContentType = String8("application/octet-stream"); + } + } + applyTimeoutResponse(); if (offset == 0) { @@ -542,20 +551,10 @@ void NuHTTPDataSource::addBandwidthMeasurement_l( mTotalTransferBytes -= entry->mNumBytes; mBandwidthHistory.erase(mBandwidthHistory.begin()); --mNumBandwidthHistoryItems; - int64_t timeNowUs = ALooper::GetNowUs(); - if (timeNowUs - mPrevBandwidthMeasureTimeUs > 2000000LL) { - if (mPrevBandwidthMeasureTimeUs != 0) { - double estimatedBandwidth = - ((double)mTotalTransferBytes * 8E3 / mTotalTransferTimeUs); - LOGI("estimated avg bandwidth is %8.2f kbps in the past %lld us", - estimatedBandwidth, timeNowUs - mPrevBandwidthMeasureTimeUs); - } - mPrevBandwidthMeasureTimeUs = timeNowUs; - } } } -DecryptHandle* NuHTTPDataSource::DrmInitialization() { +sp<DecryptHandle> NuHTTPDataSource::DrmInitialization() { if (mDrmManagerClient == NULL) { mDrmManagerClient = new DrmManagerClient(); } @@ -579,8 +578,8 @@ DecryptHandle* NuHTTPDataSource::DrmInitialization() { return mDecryptHandle; } -void NuHTTPDataSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) { - *handle = mDecryptHandle; +void NuHTTPDataSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) { + handle = mDecryptHandle; *client = mDrmManagerClient; } @@ -589,4 +588,8 @@ String8 NuHTTPDataSource::getUri() { return mUri; } +String8 NuHTTPDataSource::getMIMEType() const { + return mContentType; +} + } // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 5d26fd5..0f0ffd4 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -53,7 +53,10 @@ #include <OMX_Audio.h> #include <OMX_Component.h> +#if HAVE_SOFTWARE_DECODERS #include "include/ThreadedSource.h" +#endif + #include "include/avc_utils.h" namespace android { @@ -65,11 +68,6 @@ struct CodecInfo { const char *codec; }; -#define FACTORY_CREATE(name) \ -static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \ - return new name(source); \ -} - #define FACTORY_CREATE_ENCODER(name) \ static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \ return new name(source, meta); \ @@ -77,20 +75,29 @@ static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaDa #define FACTORY_REF(name) { #name, Make##name }, -FACTORY_CREATE(MP3Decoder) +FACTORY_CREATE_ENCODER(AMRNBEncoder) +FACTORY_CREATE_ENCODER(AMRWBEncoder) +FACTORY_CREATE_ENCODER(AACEncoder) +FACTORY_CREATE_ENCODER(AVCEncoder) +FACTORY_CREATE_ENCODER(M4vH263Encoder) + +#if HAVE_SOFTWARE_DECODERS + +#define FACTORY_CREATE(name) \ +static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \ + return new name(source); \ +} + FACTORY_CREATE(AMRNBDecoder) FACTORY_CREATE(AMRWBDecoder) FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AVCDecoder) FACTORY_CREATE(G711Decoder) +FACTORY_CREATE(MP3Decoder) FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(VorbisDecoder) FACTORY_CREATE(VPXDecoder) -FACTORY_CREATE_ENCODER(AMRNBEncoder) -FACTORY_CREATE_ENCODER(AMRWBEncoder) -FACTORY_CREATE_ENCODER(AACEncoder) -FACTORY_CREATE_ENCODER(AVCEncoder) -FACTORY_CREATE_ENCODER(M4vH263Encoder) +#endif static sp<MediaSource> InstantiateSoftwareEncoder( const char *name, const sp<MediaSource> &source, @@ -119,18 +126,19 @@ static sp<MediaSource> InstantiateSoftwareEncoder( static sp<MediaSource> InstantiateSoftwareCodec( const char *name, const sp<MediaSource> &source) { +#if HAVE_SOFTWARE_DECODERS struct FactoryInfo { const char *name; sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &); }; static const FactoryInfo kFactoryInfo[] = { - FACTORY_REF(MP3Decoder) FACTORY_REF(AMRNBDecoder) FACTORY_REF(AMRWBDecoder) FACTORY_REF(AACDecoder) FACTORY_REF(AVCDecoder) FACTORY_REF(G711Decoder) + FACTORY_REF(MP3Decoder) FACTORY_REF(M4vH263Decoder) FACTORY_REF(VorbisDecoder) FACTORY_REF(VPXDecoder) @@ -145,6 +153,7 @@ static sp<MediaSource> InstantiateSoftwareCodec( return (*kFactoryInfo[i].CreateFunc)(source); } } +#endif return NULL; } @@ -156,36 +165,47 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" }, + { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" }, { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" }, + { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" }, + { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, + { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" }, { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" }, { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" }, { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" }, + { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" }, { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" }, }; @@ -277,6 +297,10 @@ static void InitOMXParams(T *params) { } static bool IsSoftwareCodec(const char *componentName) { + if (!strncmp("OMX.google.", componentName, 11)) { + return true; + } + if (!strncmp("OMX.", componentName, 4)) { return false; } @@ -284,26 +308,29 @@ static bool IsSoftwareCodec(const char *componentName) { return true; } -// A sort order in which non-OMX components are first, -// followed by software codecs, and followed by all the others. +// A sort order in which OMX software codecs are first, followed +// by other (non-OMX) software codecs, followed by everything else. static int CompareSoftwareCodecsFirst( const String8 *elem1, const String8 *elem2) { - bool isNotOMX1 = strncmp(elem1->string(), "OMX.", 4); - bool isNotOMX2 = strncmp(elem2->string(), "OMX.", 4); - - if (isNotOMX1) { - if (isNotOMX2) { return 0; } - return -1; - } - if (isNotOMX2) { - return 1; - } + bool isOMX1 = !strncmp(elem1->string(), "OMX.", 4); + bool isOMX2 = !strncmp(elem2->string(), "OMX.", 4); bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string()); bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string()); if (isSoftwareCodec1) { - if (isSoftwareCodec2) { return 0; } + if (!isSoftwareCodec2) { return -1; } + + if (isOMX1) { + if (isOMX2) { return 0; } + + return -1; + } else { + if (isOMX2) { return 0; } + + return 1; + } + return -1; } @@ -622,6 +649,11 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) { LOGE("Profile and/or level exceed the decoder's capabilities."); return ERROR_UNSUPPORTED; } + } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + addCodecSpecificData(data, size); + + CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); + addCodecSpecificData(data, size); } } @@ -631,16 +663,23 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) { } if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) { setAMRFormat(false /* isWAMR */, bitRate); - } - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { setAMRFormat(true /* isWAMR */, bitRate); - } - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { int32_t numChannels, sampleRate; CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); setAACFormat(numChannels, sampleRate, bitRate); + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME) + || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) { + // These are PCM-like formats with a fixed sample rate but + // a variable number of channels. + + int32_t numChannels; + CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); + + setG711Format(numChannels); } if (!strncasecmp(mMIME, "video/", 6)) { @@ -1316,6 +1355,8 @@ status_t OMXCodec::setVideoOutputFormat( compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { + compressionFormat = OMX_VIDEO_CodingVPX; } else { LOGE("Not a supported video mime type: %s", mime); CHECK(!"Should not be here. Not a supported video mime type."); @@ -1443,7 +1484,8 @@ OMXCodec::OMXCodec( mOutputPortSettingsChangedPending(false), mLeftOverBuffer(NULL), mPaused(false), - mNativeWindow(nativeWindow) { + mNativeWindow(!strncmp(componentName, "OMX.google.", 11) + ? NULL : nativeWindow) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; @@ -1830,7 +1872,7 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { - android_native_buffer_t* buf; + ANativeWindowBuffer* buf; err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); if (err != 0) { LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); @@ -1900,7 +1942,7 @@ status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) { OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { // Dequeue the next buffer from the native window. - android_native_buffer_t* buf; + ANativeWindowBuffer* buf; int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); if (err != 0) { CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err); @@ -1936,6 +1978,11 @@ OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { } void OMXCodec::on_message(const omx_message &msg) { + if (mState == ERROR) { + LOGW("Dropping OMX message - we're in ERROR state."); + return; + } + switch (msg.type) { case omx_message::EVENT: { @@ -2239,13 +2286,15 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) { onPortSettingsChanged(data1); - } else if (data1 == kPortIndexOutput - && data2 == OMX_IndexConfigCommonOutputCrop) { + } else if (data1 == kPortIndexOutput && + (data2 == OMX_IndexConfigCommonOutputCrop || + data2 == OMX_IndexConfigCommonScale)) { sp<MetaData> oldOutputFormat = mOutputFormat; initOutputFormat(mSource->getFormat()); - if (formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) { + if (data2 == OMX_IndexConfigCommonOutputCrop && + formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) { mOutputPortSettingsHaveChanged = true; if (mNativeWindow != NULL) { @@ -2264,6 +2313,39 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { // already invalid, we'll know soon enough. native_window_set_crop(mNativeWindow.get(), &crop); } + } else if (data2 == OMX_IndexConfigCommonScale) { + OMX_CONFIG_SCALEFACTORTYPE scale; + InitOMXParams(&scale); + scale.nPortIndex = kPortIndexOutput; + + // Change display dimension only when necessary. + if (OK == mOMX->getConfig( + mNode, + OMX_IndexConfigCommonScale, + &scale, sizeof(scale))) { + int32_t left, top, right, bottom; + CHECK(mOutputFormat->findRect(kKeyCropRect, + &left, &top, + &right, &bottom)); + + // The scale is in 16.16 format. + // scale 1.0 = 0x010000. When there is no + // need to change the display, skip it. + LOGV("Get OMX_IndexConfigScale: 0x%lx/0x%lx", + scale.xWidth, scale.xHeight); + + if (scale.xWidth != 0x010000) { + mOutputFormat->setInt32(kKeyDisplayWidth, + ((right - left + 1) * scale.xWidth) >> 16); + mOutputPortSettingsHaveChanged = true; + } + + if (scale.xHeight != 0x010000) { + mOutputFormat->setInt32(kKeyDisplayHeight, + ((bottom - top + 1) * scale.xHeight) >> 16); + mOutputPortSettingsHaveChanged = true; + } + } } } break; @@ -2859,6 +2941,23 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { offset += srcBuffer->range_length(); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) { + CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer)); + CHECK_GE(info->mSize, offset + sizeof(int32_t)); + + int32_t numPageSamples; + if (!srcBuffer->meta_data()->findInt32( + kKeyValidSamples, &numPageSamples)) { + numPageSamples = -1; + } + + memcpy((uint8_t *)info->mData + offset, + &numPageSamples, + sizeof(numPageSamples)); + + offset += sizeof(numPageSamples); + } + if (releaseBuffer) { srcBuffer->release(); srcBuffer = NULL; @@ -3184,6 +3283,11 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit } } +void OMXCodec::setG711Format(int32_t numChannels) { + CHECK(!mIsEncoder); + setRawAudioFormat(kPortIndexInput, 8000, numChannels); +} + void OMXCodec::setImageOutputFormat( OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) { CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height); @@ -3973,6 +4077,13 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { numChannels, params.nChannels); } + if (sampleRate != (int32_t)params.nSamplingRate) { + LOGW("Codec outputs at different sampling rate than " + "what the input stream contains (contains data at " + "%d Hz, codec outputs %lu Hz)", + sampleRate, params.nSamplingRate); + } + mOutputFormat->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); @@ -3985,8 +4096,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { (mQuirks & kDecoderLiesAboutNumberOfChannels) ? numChannels : params.nChannels); - // The codec-reported sampleRate is not reliable... - mOutputFormat->setInt32(kKeySampleRate, sampleRate); + mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate); } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) { OMX_AUDIO_PARAM_AMRTYPE amr; InitOMXParams(&amr); @@ -4093,6 +4203,14 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { break; } } + + // If the input format contains rotation information, flag the output + // format accordingly. + + int32_t rotationDegrees; + if (mSource->getFormat()->findInt32(kKeyRotation, &rotationDegrees)) { + mOutputFormat->setInt32(kKeyRotation, rotationDegrees); + } } status_t OMXCodec::pause() { diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 6538a05..1560b8e 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -378,12 +378,19 @@ status_t MyVorbisExtractor::seekToOffset(off64_t offset) { ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) { uint8_t header[27]; - if (mSource->readAt(offset, header, sizeof(header)) + ssize_t n; + if ((n = mSource->readAt(offset, header, sizeof(header))) < (ssize_t)sizeof(header)) { - LOGV("failed to read %d bytes at offset 0x%016llx", - sizeof(header), offset); + LOGV("failed to read %d bytes at offset 0x%016llx, got %ld bytes", + sizeof(header), offset, n); - return ERROR_IO; + if (n < 0) { + return n; + } else if (n == 0) { + return ERROR_END_OF_STREAM; + } else { + return ERROR_IO; + } } if (memcmp(header, "OggS", 4)) { @@ -498,8 +505,8 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { packetSize); if (n < (ssize_t)packetSize) { - LOGV("failed to read %d bytes at 0x%016llx", - packetSize, dataOffset); + LOGV("failed to read %d bytes at 0x%016llx, got %ld bytes", + packetSize, dataOffset, n); return ERROR_IO; } diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 423df70..eb135ab 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -53,6 +53,7 @@ SampleTable::SampleTable(const sp<DataSource> &source) mNumSampleSizes(0), mTimeToSampleCount(0), mTimeToSample(NULL), + mSampleTimeEntries(NULL), mCompositionTimeDeltaEntries(NULL), mNumCompositionTimeDeltaEntries(0), mSyncSampleOffset(-1), @@ -73,6 +74,9 @@ SampleTable::~SampleTable() { delete[] mCompositionTimeDeltaEntries; mCompositionTimeDeltaEntries = NULL; + delete[] mSampleTimeEntries; + mSampleTimeEntries = NULL; + delete[] mTimeToSample; mTimeToSample = NULL; @@ -216,7 +220,7 @@ status_t SampleTable::setSampleSizeParams( return ERROR_MALFORMED; } - mSampleSizeFieldSize = mDefaultSampleSize & 0xf; + mSampleSizeFieldSize = mDefaultSampleSize & 0xff; mDefaultSampleSize = 0; if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8 @@ -381,67 +385,132 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } -status_t SampleTable::findSampleAtTime( - uint32_t req_time, uint32_t *sample_index, uint32_t flags) { - // XXX this currently uses decoding time, instead of composition time. +// static +int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) { + const SampleTimeEntry *a = (const SampleTimeEntry *)_a; + const SampleTimeEntry *b = (const SampleTimeEntry *)_b; - *sample_index = 0; + if (a->mCompositionTime < b->mCompositionTime) { + return -1; + } else if (a->mCompositionTime > b->mCompositionTime) { + return 1; + } + + return 0; +} +void SampleTable::buildSampleEntriesTable() { Mutex::Autolock autoLock(mLock); - uint32_t cur_sample = 0; - uint32_t time = 0; + if (mSampleTimeEntries != NULL) { + return; + } + + mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes]; + + uint32_t sampleIndex = 0; + uint32_t sampleTime = 0; + for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; - if (req_time < time + n * delta) { - int j = (req_time - time) / delta; - - uint32_t time1 = time + j * delta; - uint32_t time2 = time1 + delta; - - uint32_t sampleTime; - if (i+1 == mTimeToSampleCount - || (abs_difference(req_time, time1) - < abs_difference(req_time, time2))) { - *sample_index = cur_sample + j; - sampleTime = time1; - } else { - *sample_index = cur_sample + j + 1; - sampleTime = time2; - } + for (uint32_t j = 0; j < n; ++j) { + CHECK(sampleIndex < mNumSampleSizes); - switch (flags) { - case kFlagBefore: - { - if (sampleTime > req_time && *sample_index > 0) { - --*sample_index; - } - break; - } + mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex; - case kFlagAfter: - { - if (sampleTime < req_time - && *sample_index + 1 < mNumSampleSizes) { - ++*sample_index; - } - break; - } + mSampleTimeEntries[sampleIndex].mCompositionTime = + sampleTime + getCompositionTimeOffset(sampleIndex); + + ++sampleIndex; + sampleTime += delta; + } + } + + qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry), + CompareIncreasingTime); +} - default: - break; +status_t SampleTable::findSampleAtTime( + uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + buildSampleEntriesTable(); + + uint32_t left = 0; + uint32_t right = mNumSampleSizes; + while (left < right) { + uint32_t center = (left + right) / 2; + uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; + + if (req_time < centerTime) { + right = center; + } else if (req_time > centerTime) { + left = center + 1; + } else { + left = center; + break; + } + } + + if (left == mNumSampleSizes) { + if (flags == kFlagAfter) { + return ERROR_OUT_OF_RANGE; + } + + --left; + } + + uint32_t closestIndex = left; + + switch (flags) { + case kFlagBefore: + { + while (closestIndex > 0 + && mSampleTimeEntries[closestIndex].mCompositionTime + > req_time) { + --closestIndex; } + break; + } - return OK; + case kFlagAfter: + { + while (closestIndex + 1 < mNumSampleSizes + && mSampleTimeEntries[closestIndex].mCompositionTime + < req_time) { + ++closestIndex; + } + break; } - time += delta * n; - cur_sample += n; + default: + { + CHECK(flags == kFlagClosest); + + if (closestIndex > 0) { + // Check left neighbour and pick closest. + uint32_t absdiff1 = + abs_difference( + mSampleTimeEntries[closestIndex].mCompositionTime, + req_time); + + uint32_t absdiff2 = + abs_difference( + mSampleTimeEntries[closestIndex - 1].mCompositionTime, + req_time); + + if (absdiff1 > absdiff2) { + closestIndex = closestIndex - 1; + } + } + + break; + } } - return ERROR_OUT_OF_RANGE; + *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; + + return OK; } status_t SampleTable::findSyncSampleNear( @@ -613,7 +682,7 @@ status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *decodingTime, + uint32_t *compositionTime, bool *isSyncSample) { Mutex::Autolock autoLock(mLock); @@ -630,8 +699,8 @@ status_t SampleTable::getMetaDataForSample( *size = mSampleIterator->getSampleSize(); } - if (decodingTime) { - *decodingTime = mSampleIterator->getSampleTime(); + if (compositionTime) { + *compositionTime = mSampleIterator->getSampleTime(); } if (isSyncSample) { diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 84f65ff..f82ff32 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -37,7 +37,8 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac" + ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", + ".avi", }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); @@ -124,7 +125,8 @@ status_t StagefrightMediaScanner::processFile( || !strcasecmp(extension, ".xmf") || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") - || !strcasecmp(extension, ".ota")) { + || !strcasecmp(extension, ".ota") + || !strcasecmp(extension, ".mxmf")) { status_t status = HandleMIDI(path, &client); if (status != OK) { return status; diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index ea3b801..4c3dc47 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -27,6 +27,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/MediaDefs.h> namespace android { @@ -48,7 +49,8 @@ StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { mClient.disconnect(); } -status_t StagefrightMetadataRetriever::setDataSource(const char *uri) { +status_t StagefrightMetadataRetriever::setDataSource( + const char *uri, const KeyedVector<String8, String8> *headers) { LOGV("setDataSource(%s)", uri); mParsedMetaData = false; @@ -56,7 +58,7 @@ status_t StagefrightMetadataRetriever::setDataSource(const char *uri) { delete mAlbumArt; mAlbumArt = NULL; - mSource = DataSource::CreateFromURI(uri); + mSource = DataSource::CreateFromURI(uri, headers); if (mSource == NULL) { return UNKNOWN_ERROR; @@ -145,7 +147,8 @@ static VideoFrame *extractVideoFrameWithCodecFlags( int64_t thumbNailTime; if (frameTimeUs < 0) { - if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) { + if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime) + || thumbNailTime < 0) { thumbNailTime = 0; } options.setSeekTo(thumbNailTime, mode); @@ -231,6 +234,14 @@ static VideoFrame *extractVideoFrameWithCodecFlags( frame->mData = new uint8_t[frame->mSize]; frame->mRotationAngle = rotationAngle; + int32_t displayWidth, displayHeight; + if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) { + frame->mDisplayWidth = displayWidth; + } + if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) { + frame->mDisplayHeight = displayHeight; + } + int32_t srcFormat; CHECK(meta->findInt32(kKeyColorFormat, &srcFormat)); @@ -411,8 +422,15 @@ void StagefrightMetadataRetriever::parseMetaData() { mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp)); + bool hasAudio = false; + bool hasVideo = false; + int32_t videoWidth = -1; + int32_t videoHeight = -1; + int32_t audioBitrate = -1; + // The overall duration is the duration of the longest track. int64_t maxDurationUs = 0; + String8 timedTextLang; for (size_t i = 0; i < numTracks; ++i) { sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i); @@ -422,12 +440,67 @@ void StagefrightMetadataRetriever::parseMetaData() { maxDurationUs = durationUs; } } + + const char *mime; + if (trackMeta->findCString(kKeyMIMEType, &mime)) { + if (!hasAudio && !strncasecmp("audio/", mime, 6)) { + hasAudio = true; + + if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) { + audioBitrate = -1; + } + } else if (!hasVideo && !strncasecmp("video/", mime, 6)) { + hasVideo = true; + + CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth)); + CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight)); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { + const char *lang; + trackMeta->findCString(kKeyMediaLanguage, &lang); + timedTextLang.append(String8(lang)); + timedTextLang.append(String8(":")); + } + } + } + + // To save the language codes for all timed text tracks + // If multiple text tracks present, the format will look + // like "eng:chi" + if (!timedTextLang.isEmpty()) { + mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang); } // The duration value is a string representing the duration in ms. sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000); mMetaData.add(METADATA_KEY_DURATION, String8(tmp)); + if (hasAudio) { + mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes")); + } + + if (hasVideo) { + mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes")); + + sprintf(tmp, "%d", videoWidth); + mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp)); + + sprintf(tmp, "%d", videoHeight); + mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp)); + } + + if (numTracks == 1 && hasAudio && audioBitrate >= 0) { + sprintf(tmp, "%d", audioBitrate); + mMetaData.add(METADATA_KEY_BITRATE, String8(tmp)); + } else { + off64_t sourceSize; + if (mSource->getSize(&sourceSize) == OK) { + int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs); + + sprintf(tmp, "%lld", avgBitRate); + mMetaData.add(METADATA_KEY_BITRATE, String8(tmp)); + } + } + if (numTracks == 1) { const char *fileMIME; CHECK(meta->findCString(kKeyMIMEType, &fileMIME)); diff --git a/media/libstagefright/TimedTextPlayer.cpp b/media/libstagefright/TimedTextPlayer.cpp new file mode 100644 index 0000000..1ac22cb --- /dev/null +++ b/media/libstagefright/TimedTextPlayer.cpp @@ -0,0 +1,252 @@ + /* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextPlayer" +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/Utils.h> +#include "include/AwesomePlayer.h" +#include "include/TimedTextPlayer.h" + +namespace android { + +struct TimedTextEvent : public TimedEventQueue::Event { + TimedTextEvent( + TimedTextPlayer *player, + void (TimedTextPlayer::*method)()) + : mPlayer(player), + mMethod(method) { + } + +protected: + virtual ~TimedTextEvent() {} + + virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { + (mPlayer->*mMethod)(); + } + +private: + TimedTextPlayer *mPlayer; + void (TimedTextPlayer::*mMethod)(); + + TimedTextEvent(const TimedTextEvent &); + TimedTextEvent &operator=(const TimedTextEvent &); +}; + +TimedTextPlayer::TimedTextPlayer( + AwesomePlayer *observer, + const wp<MediaPlayerBase> &listener, + TimedEventQueue *queue) + : mSource(NULL), + mSeekTimeUs(0), + mStarted(false), + mTextEventPending(false), + mQueue(queue), + mListener(listener), + mObserver(observer), + mTextBuffer(NULL) { + mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent); +} + +TimedTextPlayer::~TimedTextPlayer() { + if (mStarted) { + reset(); + } + + mTextTrackVector.clear(); +} + +status_t TimedTextPlayer::start(uint8_t index) { + CHECK(!mStarted); + + if (index >= mTextTrackVector.size()) { + LOGE("Incorrect text track index"); + return BAD_VALUE; + } + + mSource = mTextTrackVector.itemAt(index); + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + int64_t positionUs; + mObserver->getPosition(&positionUs); + seekTo(positionUs); + + postTextEvent(); + + mStarted = true; + + return OK; +} + +void TimedTextPlayer::pause() { + CHECK(mStarted); + + cancelTextEvent(); +} + +void TimedTextPlayer::resume() { + CHECK(mStarted); + + postTextEvent(); +} + +void TimedTextPlayer::reset() { + CHECK(mStarted); + + // send an empty text to clear the screen + notifyListener(MEDIA_TIMED_TEXT); + + cancelTextEvent(); + + mSeeking = false; + mStarted = false; + + if (mTextBuffer != NULL) { + mTextBuffer->release(); + mTextBuffer = NULL; + } + + if (mSource != NULL) { + mSource->stop(); + mSource.clear(); + mSource = NULL; + } +} + +status_t TimedTextPlayer::seekTo(int64_t time_us) { + Mutex::Autolock autoLock(mLock); + + mSeeking = true; + mSeekTimeUs = time_us; + + return OK; +} + +status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) { + if (index >= (int)(mTextTrackVector.size())) { + return BAD_VALUE; + } + + if (mStarted) { + reset(); + } + + if (index >= 0) { + return start(index); + } + return OK; +} + +void TimedTextPlayer::onTextEvent() { + Mutex::Autolock autoLock(mLock); + + if (!mTextEventPending) { + return; + } + mTextEventPending = false; + + MediaSource::ReadOptions options; + if (mSeeking) { + options.setSeekTo(mSeekTimeUs, + MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + mSeeking = false; + + if (mTextBuffer != NULL) { + mTextBuffer->release(); + mTextBuffer = NULL; + } + + notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen + } + + if (mTextBuffer != NULL) { + uint8_t *tmp = (uint8_t *)(mTextBuffer->data()); + size_t len = (*tmp) << 8 | (*(tmp + 1)); + + notifyListener(MEDIA_TIMED_TEXT, + tmp + 2, + len); + + mTextBuffer->release(); + mTextBuffer = NULL; + + } + + if (mSource->read(&mTextBuffer, &options) != OK) { + return; + } + + int64_t positionUs, timeUs; + mObserver->getPosition(&positionUs); + mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); + + //send the text now + if (timeUs <= positionUs + 100000ll) { + postTextEvent(); + } else { + postTextEvent(timeUs - positionUs - 100000ll); + } +} + +void TimedTextPlayer::postTextEvent(int64_t delayUs) { + if (mTextEventPending) { + return; + } + + mTextEventPending = true; + mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs); +} + +void TimedTextPlayer::cancelTextEvent() { + mQueue->cancelEvent(mTextEvent->eventID()); + mTextEventPending = false; +} + +void TimedTextPlayer::addTextSource(sp<MediaSource> source) { + mTextTrackVector.add(source); +} + +void TimedTextPlayer::notifyListener( + int msg, const void *data, size_t size) { + if (mListener != NULL) { + sp<MediaPlayerBase> listener = mListener.promote(); + + if (listener != NULL) { + if (size > 0) { + mData.freeData(); + mData.write(data, size); + + listener->sendEvent(msg, 0, 0, &mData); + } else { // send an empty timed text to clear the screen + listener->sendEvent(msg); + } + } + } +} +} diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 9332120..bf978d7 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -264,6 +264,8 @@ WAVSource::WAVSource( mGroup(NULL) { CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate)); CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels)); + + mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize); } WAVSource::~WAVSource() { @@ -353,8 +355,6 @@ status_t WAVSource::read( return ERROR_END_OF_STREAM; } - mCurrentPos += n; - buffer->set_range(0, n); if (mWaveFormat == WAVE_FORMAT_PCM) { @@ -406,6 +406,7 @@ status_t WAVSource::read( / (mNumChannels * bytesPerSample) / mSampleRate); buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + mCurrentPos += n; *out = buffer; @@ -426,6 +427,11 @@ bool SniffWAV( return false; } + sp<MediaExtractor> extractor = new WAVExtractor(source); + if (extractor->countTracks() == 0) { + return false; + } + *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV; *confidence = 0.3f; diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp index 616836c..0d0d6c2 100644 --- a/media/libstagefright/XINGSeeker.cpp +++ b/media/libstagefright/XINGSeeker.cpp @@ -41,8 +41,6 @@ sp<XINGSeeker> XINGSeeker::CreateFromSource( return NULL; } - LOGI("Found XING header."); - return seeker; } diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk new file mode 100644 index 0000000..80b2478 --- /dev/null +++ b/media/libstagefright/chromium_http/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + ChromiumHTTPDataSource.cpp \ + support.cpp \ + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax \ + external/chromium \ + external/chromium/android + +LOCAL_CFLAGS += -Wno-multichar + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libstlport +include external/stlport/libstlport.mk +endif + +LOCAL_MODULE:= libstagefright_chromium_http + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp new file mode 100644 index 0000000..1096717 --- /dev/null +++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ChromiumHTTPDataSource" +#include <media/stagefright/foundation/ADebug.h> + +#include "include/ChromiumHTTPDataSource.h" + +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaErrors.h> + +#include "support.h" + +namespace android { + +ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags) + : mFlags(flags), + mState(DISCONNECTED), + mDelegate(new SfDelegate), + mCurrentOffset(0), + mIOResult(OK), + mContentSize(-1), + mNumBandwidthHistoryItems(0), + mTotalTransferTimeUs(0), + mTotalTransferBytes(0), + mDecryptHandle(NULL), + mDrmManagerClient(NULL) { + mDelegate->setOwner(this); +} + +ChromiumHTTPDataSource::~ChromiumHTTPDataSource() { + disconnect(); + + delete mDelegate; + mDelegate = NULL; + + if (mDrmManagerClient != NULL) { + delete mDrmManagerClient; + mDrmManagerClient = NULL; + } +} + +status_t ChromiumHTTPDataSource::connect( + const char *uri, + const KeyedVector<String8, String8> *headers, + off64_t offset) { + Mutex::Autolock autoLock(mLock); + + return connect_l(uri, headers, offset); +} + +status_t ChromiumHTTPDataSource::connect_l( + const char *uri, + const KeyedVector<String8, String8> *headers, + off64_t offset) { + if (mState != DISCONNECTED) { + disconnect_l(); + } + + if (!(mFlags & kFlagIncognito)) { + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "connect to %s @%lld", uri, offset); + } else { + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, + "connect to <URL suppressed> @%lld", offset); + } + + mURI = uri; + mContentType = String8("application/octet-stream"); + + if (headers != NULL) { + mHeaders = *headers; + } else { + mHeaders.clear(); + } + + mState = CONNECTING; + mContentSize = -1; + mCurrentOffset = offset; + + mDelegate->initiateConnection(mURI.c_str(), &mHeaders, offset); + + while (mState == CONNECTING) { + mCondition.wait(mLock); + } + + return mState == CONNECTED ? OK : mIOResult; +} + +void ChromiumHTTPDataSource::onConnectionEstablished( + int64_t contentSize, const char *contentType) { + Mutex::Autolock autoLock(mLock); + mState = CONNECTED; + mContentSize = (contentSize < 0) ? -1 : contentSize + mCurrentOffset; + mContentType = String8(contentType); + mCondition.broadcast(); +} + +void ChromiumHTTPDataSource::onConnectionFailed(status_t err) { + Mutex::Autolock autoLock(mLock); + mState = DISCONNECTED; + mCondition.broadcast(); + + mURI.clear(); + + mIOResult = err; + + clearDRMState_l(); +} + +void ChromiumHTTPDataSource::disconnect() { + Mutex::Autolock autoLock(mLock); + disconnect_l(); +} + +void ChromiumHTTPDataSource::disconnect_l() { + if (mState == DISCONNECTED) { + return; + } + + mState = DISCONNECTING; + mIOResult = -EINTR; + + mDelegate->initiateDisconnect(); + + while (mState == DISCONNECTING) { + mCondition.wait(mLock); + } + + CHECK_EQ((int)mState, (int)DISCONNECTED); +} + +status_t ChromiumHTTPDataSource::initCheck() const { + Mutex::Autolock autoLock(mLock); + + return mState == CONNECTED ? OK : NO_INIT; +} + +ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) { + Mutex::Autolock autoLock(mLock); + + if (mState != CONNECTED) { + return ERROR_NOT_CONNECTED; + } + + if (offset != mCurrentOffset) { + AString tmp = mURI; + KeyedVector<String8, String8> tmpHeaders = mHeaders; + + disconnect_l(); + + status_t err = connect_l(tmp.c_str(), &tmpHeaders, offset); + + if (err != OK) { + return err; + } + } + + mState = READING; + + int64_t startTimeUs = ALooper::GetNowUs(); + + mDelegate->initiateRead(data, size); + + while (mState == READING) { + mCondition.wait(mLock); + } + + if (mIOResult < OK) { + return mIOResult; + } + + if (mState == CONNECTED) { + int64_t delayUs = ALooper::GetNowUs() - startTimeUs; + + // The read operation was successful, mIOResult contains + // the number of bytes read. + addBandwidthMeasurement_l(mIOResult, delayUs); + + mCurrentOffset += mIOResult; + return mIOResult; + } + + return ERROR_IO; +} + +void ChromiumHTTPDataSource::onReadCompleted(ssize_t size) { + Mutex::Autolock autoLock(mLock); + + mIOResult = size; + + if (mState == READING) { + mState = CONNECTED; + mCondition.broadcast(); + } +} + +status_t ChromiumHTTPDataSource::getSize(off64_t *size) { + Mutex::Autolock autoLock(mLock); + + if (mContentSize < 0) { + return ERROR_UNSUPPORTED; + } + + *size = mContentSize; + + return OK; +} + +uint32_t ChromiumHTTPDataSource::flags() { + return kWantsPrefetching; +} + +// static +void ChromiumHTTPDataSource::InitiateRead( + ChromiumHTTPDataSource *me, void *data, size_t size) { + me->initiateRead(data, size); +} + +void ChromiumHTTPDataSource::initiateRead(void *data, size_t size) { + mDelegate->initiateRead(data, size); +} + +void ChromiumHTTPDataSource::onDisconnectComplete() { + Mutex::Autolock autoLock(mLock); + CHECK_EQ((int)mState, (int)DISCONNECTING); + + mState = DISCONNECTED; + mURI.clear(); + + mCondition.broadcast(); + + clearDRMState_l(); +} + +void ChromiumHTTPDataSource::addBandwidthMeasurement_l( + size_t numBytes, int64_t delayUs) { + BandwidthEntry entry; + entry.mDelayUs = delayUs; + entry.mNumBytes = numBytes; + mTotalTransferTimeUs += delayUs; + mTotalTransferBytes += numBytes; + + mBandwidthHistory.push_back(entry); + if (++mNumBandwidthHistoryItems > 100) { + BandwidthEntry *entry = &*mBandwidthHistory.begin(); + mTotalTransferTimeUs -= entry->mDelayUs; + mTotalTransferBytes -= entry->mNumBytes; + mBandwidthHistory.erase(mBandwidthHistory.begin()); + --mNumBandwidthHistoryItems; + } +} + +bool ChromiumHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) { + Mutex::Autolock autoLock(mLock); + + if (mNumBandwidthHistoryItems < 2) { + return false; + } + + *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs); + + return true; +} + +sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() { + Mutex::Autolock autoLock(mLock); + + if (mDrmManagerClient == NULL) { + mDrmManagerClient = new DrmManagerClient(); + } + + if (mDrmManagerClient == NULL) { + return NULL; + } + + if (mDecryptHandle == NULL) { + /* Note if redirect occurs, mUri is the redirect uri instead of the + * original one + */ + mDecryptHandle = mDrmManagerClient->openDecryptSession( + String8(mURI.c_str())); + } + + if (mDecryptHandle == NULL) { + delete mDrmManagerClient; + mDrmManagerClient = NULL; + } + + return mDecryptHandle; +} + +void ChromiumHTTPDataSource::getDrmInfo( + sp<DecryptHandle> &handle, DrmManagerClient **client) { + Mutex::Autolock autoLock(mLock); + + handle = mDecryptHandle; + *client = mDrmManagerClient; +} + +String8 ChromiumHTTPDataSource::getUri() { + Mutex::Autolock autoLock(mLock); + + return String8(mURI.c_str()); +} + +String8 ChromiumHTTPDataSource::getMIMEType() const { + Mutex::Autolock autoLock(mLock); + + return mContentType; +} + +void ChromiumHTTPDataSource::clearDRMState_l() { + if (mDecryptHandle != NULL) { + // To release mDecryptHandle + CHECK(mDrmManagerClient); + mDrmManagerClient->closeDecryptSession(mDecryptHandle); + mDecryptHandle = NULL; + } +} + +} // namespace android + diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp new file mode 100644 index 0000000..3e4e493 --- /dev/null +++ b/media/libstagefright/chromium_http/support.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ChromiumHTTPDataSourceSupport" +#include <utils/Log.h> + +#include <media/stagefright/foundation/AString.h> + +#include "support.h" + +#include "android/net/android_network_library_impl.h" +#include "base/thread.h" +#include "net/base/cert_verifier.h" +#include "net/base/host_resolver.h" +#include "net/base/ssl_config_service.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_cache.h" +#include "net/proxy/proxy_config_service_android.h" + +#include "include/ChromiumHTTPDataSource.h" + +#include <cutils/properties.h> +#include <media/stagefright/MediaErrors.h> + +namespace android { + +static Mutex gNetworkThreadLock; +static base::Thread *gNetworkThread = NULL; +static scoped_refptr<URLRequestContext> gReqContext; + +static void InitializeNetworkThreadIfNecessary() { + Mutex::Autolock autoLock(gNetworkThreadLock); + if (gNetworkThread == NULL) { + gNetworkThread = new base::Thread("network"); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + CHECK(gNetworkThread->StartWithOptions(options)); + + gReqContext = new SfRequestContext; + + net::AndroidNetworkLibrary::RegisterSharedInstance( + new SfNetworkLibrary); + } +} + +static void MY_LOGI(const char *s) { + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "%s", s); +} + +static void MY_LOGV(const char *s) { +#if !defined(LOG_NDEBUG) || LOG_NDEBUG == 0 + LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "%s", s); +#endif +} + +SfNetLog::SfNetLog() + : mNextID(1) { +} + +void SfNetLog::AddEntry( + EventType type, + const base::TimeTicks &time, + const Source &source, + EventPhase phase, + EventParameters *params) { +#if 0 + MY_LOGI(StringPrintf( + "AddEntry time=%s type=%s source=%s phase=%s\n", + TickCountToString(time).c_str(), + EventTypeToString(type), + SourceTypeToString(source.type), + EventPhaseToString(phase)).c_str()); +#endif +} + +uint32 SfNetLog::NextID() { + return mNextID++; +} + +net::NetLog::LogLevel SfNetLog::GetLogLevel() const { + return LOG_ALL; +} + +//////////////////////////////////////////////////////////////////////////////// + +SfRequestContext::SfRequestContext() { + AString ua; + ua.append("stagefright/1.2 (Linux;Android "); + +#if (PROPERTY_VALUE_MAX < 8) +#error "PROPERTY_VALUE_MAX must be at least 8" +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.release", value, "Unknown"); + ua.append(value); + ua.append(")"); + + mUserAgent = ua.c_str(); + + net_log_ = new SfNetLog; + + host_resolver_ = + net::CreateSystemHostResolver( + net::HostResolver::kDefaultParallelism, + NULL /* resolver_proc */, + net_log_); + + ssl_config_service_ = + net::SSLConfigService::CreateSystemSSLConfigService(); + + proxy_service_ = net::ProxyService::CreateWithoutProxyResolver( + new net::ProxyConfigServiceAndroid, net_log_); + + http_transaction_factory_ = new net::HttpCache( + host_resolver_, + new net::CertVerifier(), + dnsrr_resolver_, + dns_cert_checker_.get(), + proxy_service_.get(), + ssl_config_service_.get(), + net::HttpAuthHandlerFactory::CreateDefault(host_resolver_), + network_delegate_, + net_log_, + NULL); // backend_factory +} + +const std::string &SfRequestContext::GetUserAgent(const GURL &url) const { + return mUserAgent; +} + +//////////////////////////////////////////////////////////////////////////////// + +SfNetworkLibrary::SfNetworkLibrary() {} + +SfNetworkLibrary::VerifyResult SfNetworkLibrary::VerifyX509CertChain( + const std::vector<std::string>& cert_chain, + const std::string& hostname, + const std::string& auth_type) { + return VERIFY_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +SfDelegate::SfDelegate() + : mOwner(NULL), + mURLRequest(NULL), + mReadBuffer(new net::IOBufferWithSize(8192)), + mNumBytesRead(0), + mNumBytesTotal(0), + mDataDestination(NULL), + mAtEOS(false) { + InitializeNetworkThreadIfNecessary(); +} + +SfDelegate::~SfDelegate() { + CHECK(mURLRequest == NULL); +} + +void SfDelegate::setOwner(ChromiumHTTPDataSource *owner) { + mOwner = owner; +} + +void SfDelegate::OnReceivedRedirect( + net::URLRequest *request, const GURL &new_url, bool *defer_redirect) { + MY_LOGI("OnReceivedRedirect"); +} + +void SfDelegate::OnAuthRequired( + net::URLRequest *request, net::AuthChallengeInfo *auth_info) { + MY_LOGI("OnAuthRequired"); + + inherited::OnAuthRequired(request, auth_info); +} + +void SfDelegate::OnCertificateRequested( + net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info) { + MY_LOGI("OnCertificateRequested"); + + inherited::OnCertificateRequested(request, cert_request_info); +} + +void SfDelegate::OnSSLCertificateError( + net::URLRequest *request, int cert_error, net::X509Certificate *cert) { + fprintf(stderr, "OnSSLCertificateError cert_error=%d\n", cert_error); + + inherited::OnSSLCertificateError(request, cert_error, cert); +} + +void SfDelegate::OnGetCookies(net::URLRequest *request, bool blocked_by_policy) { + MY_LOGI("OnGetCookies"); +} + +void SfDelegate::OnSetCookie( + net::URLRequest *request, + const std::string &cookie_line, + const net::CookieOptions &options, + bool blocked_by_policy) { + MY_LOGI("OnSetCookie"); +} + +void SfDelegate::OnResponseStarted(net::URLRequest *request) { + if (request->status().status() != URLRequestStatus::SUCCESS) { + MY_LOGI(StringPrintf( + "Request failed with status %d and os_error %d", + request->status().status(), + request->status().os_error()).c_str()); + + delete mURLRequest; + mURLRequest = NULL; + + mOwner->onConnectionFailed(ERROR_IO); + return; + } else if (mRangeRequested && request->GetResponseCode() != 206) { + MY_LOGI(StringPrintf( + "We requested a content range, but server didn't " + "support that. (responded with %d)", + request->GetResponseCode()).c_str()); + + delete mURLRequest; + mURLRequest = NULL; + + mOwner->onConnectionFailed(-EPIPE); + return; + } else if ((request->GetResponseCode() / 100) != 2) { + MY_LOGI(StringPrintf( + "Server responded with http status %d", + request->GetResponseCode()).c_str()); + + delete mURLRequest; + mURLRequest = NULL; + + mOwner->onConnectionFailed(ERROR_IO); + return; + } + + MY_LOGV("OnResponseStarted"); + + std::string headers; + request->GetAllResponseHeaders(&headers); + + MY_LOGV(StringPrintf("response headers: %s", headers.c_str()).c_str()); + + std::string contentType; + request->GetResponseHeaderByName("Content-Type", &contentType); + + mOwner->onConnectionEstablished( + request->GetExpectedContentSize(), contentType.c_str()); +} + +void SfDelegate::OnReadCompleted(net::URLRequest *request, int bytes_read) { + if (bytes_read == -1) { + MY_LOGI(StringPrintf( + "OnReadCompleted, read failed, status %d", + request->status().status()).c_str()); + + mOwner->onReadCompleted(ERROR_IO); + return; + } + + MY_LOGV(StringPrintf("OnReadCompleted, read %d bytes", bytes_read).c_str()); + + if (bytes_read < 0) { + MY_LOGI(StringPrintf( + "Read failed w/ status %d\n", + request->status().status()).c_str()); + + mOwner->onReadCompleted(ERROR_IO); + return; + } else if (bytes_read == 0) { + mAtEOS = true; + mOwner->onReadCompleted(mNumBytesRead); + return; + } + + CHECK_GT(bytes_read, 0); + CHECK_LE(mNumBytesRead + bytes_read, mNumBytesTotal); + + memcpy((uint8_t *)mDataDestination + mNumBytesRead, + mReadBuffer->data(), + bytes_read); + + mNumBytesRead += bytes_read; + + readMore(request); +} + +void SfDelegate::readMore(net::URLRequest *request) { + while (mNumBytesRead < mNumBytesTotal) { + size_t copy = mNumBytesTotal - mNumBytesRead; + if (copy > mReadBuffer->size()) { + copy = mReadBuffer->size(); + } + + int n; + if (request->Read(mReadBuffer, copy, &n)) { + MY_LOGV(StringPrintf("Read %d bytes directly.", n).c_str()); + + CHECK_LE((size_t)n, copy); + + memcpy((uint8_t *)mDataDestination + mNumBytesRead, + mReadBuffer->data(), + n); + + mNumBytesRead += n; + + if (n == 0) { + mAtEOS = true; + break; + } + } else { + MY_LOGV("readMore pending read"); + + if (request->status().status() != URLRequestStatus::IO_PENDING) { + MY_LOGI(StringPrintf( + "Direct read failed w/ status %d\n", + request->status().status()).c_str()); + + mOwner->onReadCompleted(ERROR_IO); + return; + } + + return; + } + } + + mOwner->onReadCompleted(mNumBytesRead); +} + +void SfDelegate::initiateConnection( + const char *uri, + const KeyedVector<String8, String8> *headers, + off64_t offset) { + GURL url(uri); + + MessageLoop *loop = gNetworkThread->message_loop(); + loop->PostTask( + FROM_HERE, + NewRunnableFunction( + &SfDelegate::OnInitiateConnectionWrapper, + this, + url, + headers, + offset)); + +} + +// static +void SfDelegate::OnInitiateConnectionWrapper( + SfDelegate *me, GURL url, + const KeyedVector<String8, String8> *headers, + off64_t offset) { + me->onInitiateConnection(url, headers, offset); +} + +void SfDelegate::onInitiateConnection( + const GURL &url, + const KeyedVector<String8, String8> *extra, + off64_t offset) { + CHECK(mURLRequest == NULL); + + mURLRequest = new net::URLRequest(url, this); + mAtEOS = false; + + mRangeRequested = false; + + if (offset != 0 || extra != NULL) { + net::HttpRequestHeaders headers = + mURLRequest->extra_request_headers(); + + if (offset != 0) { + headers.AddHeaderFromString( + StringPrintf("Range: bytes=%lld-", offset).c_str()); + + mRangeRequested = true; + } + + if (extra != NULL) { + for (size_t i = 0; i < extra->size(); ++i) { + AString s; + s.append(extra->keyAt(i).string()); + s.append(": "); + s.append(extra->valueAt(i).string()); + + headers.AddHeaderFromString(s.c_str()); + } + } + + mURLRequest->SetExtraRequestHeaders(headers); + } + + mURLRequest->set_context(gReqContext); + + mURLRequest->Start(); +} + +void SfDelegate::initiateDisconnect() { + MessageLoop *loop = gNetworkThread->message_loop(); + loop->PostTask( + FROM_HERE, + NewRunnableFunction( + &SfDelegate::OnInitiateDisconnectWrapper, this)); +} + +// static +void SfDelegate::OnInitiateDisconnectWrapper(SfDelegate *me) { + me->onInitiateDisconnect(); +} + +void SfDelegate::onInitiateDisconnect() { + mURLRequest->Cancel(); + + delete mURLRequest; + mURLRequest = NULL; + + mOwner->onDisconnectComplete(); +} + +void SfDelegate::initiateRead(void *data, size_t size) { + MessageLoop *loop = gNetworkThread->message_loop(); + loop->PostTask( + FROM_HERE, + NewRunnableFunction( + &SfDelegate::OnInitiateReadWrapper, this, data, size)); +} + +// static +void SfDelegate::OnInitiateReadWrapper( + SfDelegate *me, void *data, size_t size) { + me->onInitiateRead(data, size); +} + +void SfDelegate::onInitiateRead(void *data, size_t size) { + CHECK(mURLRequest != NULL); + + mNumBytesRead = 0; + mNumBytesTotal = size; + mDataDestination = data; + + if (mAtEOS) { + mOwner->onReadCompleted(0); + return; + } + + readMore(mURLRequest); +} + +} // namespace android + diff --git a/media/libstagefright/chromium_http/support.h b/media/libstagefright/chromium_http/support.h new file mode 100644 index 0000000..4d03493 --- /dev/null +++ b/media/libstagefright/chromium_http/support.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SUPPORT_H_ + +#define SUPPORT_H_ + +#include <assert.h> + +#include "net/base/net_log.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/base/android_network_library.h" +#include "net/base/io_buffer.h" + +#include <utils/KeyedVector.h> +#include <utils/String8.h> + +namespace android { + +struct SfNetLog : public net::NetLog { + SfNetLog(); + + virtual void AddEntry( + EventType type, + const base::TimeTicks &time, + const Source &source, + EventPhase phase, + EventParameters *params); + + virtual uint32 NextID(); + virtual LogLevel GetLogLevel() const; + +private: + uint32 mNextID; + + DISALLOW_EVIL_CONSTRUCTORS(SfNetLog); +}; + +struct SfRequestContext : public URLRequestContext { + SfRequestContext(); + + virtual const std::string &GetUserAgent(const GURL &url) const; + +private: + std::string mUserAgent; + + DISALLOW_EVIL_CONSTRUCTORS(SfRequestContext); +}; + +// This is required for https support, we don't really verify certificates, +// we accept anything... +struct SfNetworkLibrary : public net::AndroidNetworkLibrary { + SfNetworkLibrary(); + + virtual VerifyResult VerifyX509CertChain( + const std::vector<std::string>& cert_chain, + const std::string& hostname, + const std::string& auth_type); + +private: + DISALLOW_EVIL_CONSTRUCTORS(SfNetworkLibrary); +}; + +struct ChromiumHTTPDataSource; + +struct SfDelegate : public net::URLRequest::Delegate { + SfDelegate(); + virtual ~SfDelegate(); + + void initiateConnection( + const char *uri, + const KeyedVector<String8, String8> *headers, + off64_t offset); + + void initiateDisconnect(); + void initiateRead(void *data, size_t size); + + void setOwner(ChromiumHTTPDataSource *mOwner); + + virtual void OnReceivedRedirect( + net::URLRequest *request, const GURL &new_url, bool *defer_redirect); + + virtual void OnAuthRequired( + net::URLRequest *request, net::AuthChallengeInfo *auth_info); + + virtual void OnCertificateRequested( + net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info); + + virtual void OnSSLCertificateError( + net::URLRequest *request, int cert_error, net::X509Certificate *cert); + + virtual void OnGetCookies(net::URLRequest *request, bool blocked_by_policy); + + virtual void OnSetCookie( + net::URLRequest *request, + const std::string &cookie_line, + const net::CookieOptions &options, + bool blocked_by_policy); + + virtual void OnResponseStarted(net::URLRequest *request); + + virtual void OnReadCompleted(net::URLRequest *request, int bytes_read); + +private: + typedef Delegate inherited; + + ChromiumHTTPDataSource *mOwner; + + net::URLRequest *mURLRequest; + scoped_refptr<net::IOBufferWithSize> mReadBuffer; + + size_t mNumBytesRead; + size_t mNumBytesTotal; + void *mDataDestination; + + bool mRangeRequested; + bool mAtEOS; + + void readMore(net::URLRequest *request); + + static void OnInitiateConnectionWrapper( + SfDelegate *me, + GURL url, + const KeyedVector<String8, String8> *headers, + off64_t offset); + + static void OnInitiateDisconnectWrapper(SfDelegate *me); + + static void OnInitiateReadWrapper( + SfDelegate *me, void *data, size_t size); + + void onInitiateConnection( + const GURL &url, + const KeyedVector<String8, String8> *headers, + off64_t offset); + + void onInitiateDisconnect(); + void onInitiateRead(void *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(SfDelegate); +}; + +} // namespace android + +#endif // SUPPORT_H_ diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp index 208431c..d2e3eaa 100644 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp @@ -234,6 +234,23 @@ status_t AACDecoder::read( mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels); CHECK(mNumDecodedBuffers > 0); + + if (decoderErr != MP4AUDEC_SUCCESS) { + // If decoding fails this early, the fields in mConfig may + // not be valid and we cannot recover. + + LOGE("Unable to decode aac content, decoder returned error %d", + decoderErr); + + buffer->release(); + buffer = NULL; + + mInputBuffer->release(); + mInputBuffer = NULL; + + return ERROR_UNSUPPORTED; + } + if (mNumDecodedBuffers == 1) { mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor; // Check on the sampling rate to see whether it is changed. diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk index 69e331f..359a2ec 100644 --- a/media/libstagefright/codecs/aacdec/Android.mk +++ b/media/libstagefright/codecs/aacdec/Android.mk @@ -143,14 +143,39 @@ LOCAL_SRC_FILES := \ unpack_idx.cpp \ window_tables_fxp.cpp \ pvmp4setaudioconfig.cpp \ - AACDecoder.cpp + AACDecoder.cpp \ LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG= -LOCAL_C_INCLUDES := frameworks/base/media/libstagefright/include +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ LOCAL_ARM_MODE := arm LOCAL_MODULE := libstagefright_aacdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAAC.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_aacdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_aacdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp new file mode 100644 index 0000000..7ce6128 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAAC" +#include <utils/Log.h> + +#include "SoftAAC.h" + +#include "pvmp4audiodecoder_api.h" + +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAAC::SoftAAC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mConfig(new tPVMP4AudioDecoderExternal), + mDecoderBuf(NULL), + mInputBufferCount(0), + mUpsamplingFactor(2), + mAnchorTimeUs(0), + mNumSamplesOutput(0), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAAC::~SoftAAC() { + free(mDecoderBuf); + mDecoderBuf = NULL; + + delete mConfig; + mConfig = NULL; +} + +void SoftAAC::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast<char *>("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftAAC::initDecoder() { + memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); + mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; + mConfig->aacPlusEnabled = 1; + + // The software decoder doesn't properly support mono output on + // AACplus files. Always output stereo. + mConfig->desiredChannels = 2; + + UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); + mDecoderBuf = malloc(memRequirements); + + Int err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + LOGE("Failed to initialize MP4 audio decoder"); + return UNKNOWN_ERROR; + } + + return OK; +} + +OMX_ERRORTYPE SoftAAC::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = 0; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; + aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + if (!isConfigured()) { + aacParams->nChannels = 1; + aacParams->nSampleRate = 44100; + aacParams->nFrameLength = 0; + } else { + aacParams->nChannels = mConfig->encodedChannels; + aacParams->nSampleRate = mConfig->samplingRate; + aacParams->nFrameLength = mConfig->frameLength; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mConfig->desiredChannels; + pcmParams->nSamplingRate = mConfig->samplingRate; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAAC::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAAC::isConfigured() const { + return mInputBufferCount > 0; +} + +void SoftAAC::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + if (portIndex == 0 && mInputBufferCount == 0) { + ++mInputBufferCount; + + BufferInfo *info = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *header = info->mHeader; + + mConfig->pInputBuffer = header->pBuffer + header->nOffset; + mConfig->inputBufferCurrentLength = header->nFilledLen; + mConfig->inputBufferMaxLength = 0; + + Int err = PVMP4AudioDecoderConfig(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err, NULL); + return; + } + + inQueue.erase(inQueue.begin()); + info->mOwnedByUs = false; + notifyEmptyBufferDone(header); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset; + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + mConfig->inputBufferMaxLength = 0; + mConfig->inputBufferUsedLength = 0; + mConfig->remainderBits = 0; + + mConfig->pOutputBuffer = + reinterpret_cast<Int16 *>(outHeader->pBuffer + outHeader->nOffset); + + mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; + mConfig->repositionFlag = false; + + Int32 prevSamplingRate = mConfig->samplingRate; + Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); + + /* + * AAC+/eAAC+ streams can be signalled in two ways: either explicitly + * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual + * rate system and the sampling rate in the final output is actually + * doubled compared with the core AAC decoder sampling rate. + * + * Explicit signalling is done by explicitly defining SBR audio object + * type in the bitstream. Implicit signalling is done by embedding + * SBR content in AAC extension payload specific to SBR, and hence + * requires an AAC decoder to perform pre-checks on actual audio frames. + * + * Thus, we could not say for sure whether a stream is + * AAC+/eAAC+ until the first data frame is decoded. + */ + if (mInputBufferCount <= 2) { + LOGV("audio/extended audio object type: %d + %d", + mConfig->audioObjectType, mConfig->extendedAudioObjectType); + LOGV("aac+ upsampling factor: %d desired channels: %d", + mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels); + + if (mInputBufferCount == 1) { + mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor; + // Check on the sampling rate to see whether it is changed. + if (mConfig->samplingRate != prevSamplingRate) { + LOGW("Sample rate was %d Hz, but now is %d Hz", + prevSamplingRate, mConfig->samplingRate); + + // We'll hold onto the input buffer and will decode + // it again once the output port has been reconfigured. + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + } else { // mInputBufferCount == 2 + if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC || + mConfig->extendedAudioObjectType == MP4AUDIO_LTP) { + if (mUpsamplingFactor == 2) { + // The stream turns out to be not aacPlus mode anyway + LOGW("Disable AAC+/eAAC+ since extended audio object " + "type is %d", + mConfig->extendedAudioObjectType); + mConfig->aacPlusEnabled = 0; + } + } else { + if (mUpsamplingFactor == 1) { + // aacPlus mode does not buy us anything, but to cause + // 1. CPU load to increase, and + // 2. a half speed of decoding + LOGW("Disable AAC+/eAAC+ since upsampling factor is 1"); + mConfig->aacPlusEnabled = 0; + } + } + } + } + + size_t numOutBytes = + mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; + + if (decoderErr == MP4AUDEC_SUCCESS) { + CHECK_LE(mConfig->inputBufferUsedLength, inHeader->nFilledLen); + + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + inHeader->nOffset += mConfig->inputBufferUsedLength; + } else { + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + } + + if (mUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], + &mConfig->pOutputBuffer[2048], + numOutBytes * 2); + } + numOutBytes *= 2; + } + + outHeader->nFilledLen = numOutBytes; + outHeader->nFlags = 0; + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate; + + mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftAAC::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + PVMP4AudioDecoderResetBuffer(mDecoderBuf); + } +} + +void SoftAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAAC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h new file mode 100644 index 0000000..963fd27 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AAC_H_ + +#define SOFT_AAC_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tPVMP4AudioDecoderExternal; + +namespace android { + +struct SoftAAC : public SimpleSoftOMXComponent { + SoftAAC(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAAC(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumBuffers = 4 + }; + + tPVMP4AudioDecoderExternal *mConfig; + void *mDecoderBuf; + + size_t mInputBufferCount; + size_t mUpsamplingFactor; + int64_t mAnchorTimeUs; + int64_t mNumSamplesOutput; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftAAC); +}; + +} // namespace android + +#endif // SOFT_AAC_H_ diff --git a/media/libstagefright/codecs/aacdec/sbr_dec.cpp b/media/libstagefright/codecs/aacdec/sbr_dec.cpp index 8fcc3ce..8519b17 100644 --- a/media/libstagefright/codecs/aacdec/sbr_dec.cpp +++ b/media/libstagefright/codecs/aacdec/sbr_dec.cpp @@ -1,5 +1,5 @@ /* ------------------------------------------------------------------ - * Copyright (C) 1998-2009 PacketVideo + * Copyright (C) 1998-2010 PacketVideo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -447,7 +447,12 @@ void sbr_dec(Int16 *inPcmData, if (xoverBand > sbrDec->highSubband) { - xoverBand = 32; /* error condition, default to upsampling mode */ + /* + * error condition, default to upsampling mode + * and make sure that the number of bands for xover does + * not exceed the number of high freq bands. + */ + xoverBand = (sbrDec->highSubband > 32)? 32: sbrDec->highSubband; } m = sbrDec->bufReadOffs + i; /* 2 + i */ @@ -558,18 +563,22 @@ void sbr_dec(Int16 *inPcmData, /* * Set Circular buffer for PS hybrid analysis */ + + int32_t *pt_temp = &scratch_mem[2][32]; + for (i = 0, j = 0; i < 3; i++) { - pv_memmove(&scratch_mem[2][32 + j ], + pv_memmove(&pt_temp[ j], hParametricStereoDec->hHybrid->mQmfBufferReal[i], HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferReal)); - pv_memmove(&scratch_mem[2][32 + j + 44], + pv_memmove(&pt_temp[ j + 44], hParametricStereoDec->hHybrid->mQmfBufferImag[i], HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferImag)); j += 88; } + pv_memset((void *)&qmf_PS_generated_Real[hParametricStereoDec->usb], 0, (64 - hParametricStereoDec->usb)*sizeof(*qmf_PS_generated_Real)); @@ -626,19 +635,23 @@ void sbr_dec(Int16 *inPcmData, * Save Circular buffer history used on PS hybrid analysis */ + + pt_temp = &scratch_mem[2][64]; + for (i = 0, j = 0; i < 3; i++) { pv_memmove(hParametricStereoDec->hHybrid->mQmfBufferReal[i], - &scratch_mem[2][ 64 + j ], + &pt_temp[ j], HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferReal)); pv_memmove(hParametricStereoDec->hHybrid->mQmfBufferImag[i], - &scratch_mem[2][ 64 + j + 44], + &pt_temp[ j + 44], HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferImag)); j += 88; } + pv_memmove(hFrameData->V, &circular_buffer_s[0], 1152*sizeof(*circular_buffer_s)); /* @@ -746,7 +759,12 @@ void sbr_dec(Int16 *inPcmData, if (xoverBand > sbrDec->highSubband) { - xoverBand = 32; /* error condition, default to upsampling mode */ + /* + * error condition, default to upsampling mode + * and make sure that the number of bands for xover does + * not exceed the number of high freq bands. + */ + xoverBand = (sbrDec->highSubband > 32)? 32: sbrDec->highSubband; } } else diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp index e4ff128..0bff52d 100644 --- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -84,7 +84,7 @@ status_t AACEncoder::initCheck() { params.sampleRate = mSampleRate; params.bitRate = mBitRate; params.nChannels = mChannels; - params.adtsUsed = 0; // For MP4 file, don't use adts format$ + params.adtsUsed = 0; // We add adts header in the file writer if needed. if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AAC_ENCPARAM, ¶ms)) { LOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk index cda4f9d..f9cc6a3 100644 --- a/media/libstagefright/codecs/aacenc/Android.mk +++ b/media/libstagefright/codecs/aacenc/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk -LOCAL_PRELINK_MODULE := false + LOCAL_SRC_FILES := basic_op/basicop2.c basic_op/oper_32b.c diff --git a/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c b/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c index 64d012d..774da7b 100644 --- a/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c +++ b/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c @@ -188,7 +188,7 @@ int main(int argc, char **argv) useData.memflag = VO_IMF_USERMEMOPERATOR;
useData.memData = (VO_PTR)(&moper);
// open encoder dll;
- handle = dlopen("/data/local/tmp/libvoAACEncv7.so", RTLD_NOW);
+ handle = dlopen("libstagefright.so", RTLD_NOW);
if(handle == 0)
{
printf("open dll error......");
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk index 52c9c07..ba3f4d2 100644 --- a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk @@ -1,24 +1,25 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := AAC_E_SAMPLES.c - -LOCAL_SRC_FILES += \ - ../../../Common/cmnMemory.c +LOCAL_SRC_FILES := \ + AAC_E_SAMPLES.c \ + ../../common/cmnMemory.c -LOCAL_MODULE := TestvoAACEnc +LOCAL_CFLAGS += $(VO_CFLAGS) -LOCAL_ARM_MODE := arm +LOCAL_MODULE_TAGS := debug + +LOCAL_MODULE := AACEncTest -LOCAL_STATIC_LIBRARIES := +LOCAL_ARM_MODE := arm -LOCAL_SHARED_LIBRARIES := libvoAACEnc +LOCAL_SHARED_LIBRARIES := \ + libstagefright \ + libdl LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/ \ - $(LOCAL_PATH)/../../../Common \ - $(LOCAL_PATH)/../../../Include \ + $(LOCAL_PATH)/ \ + $(LOCAL_PATH)/../../common \ + $(LOCAL_PATH)/../../common/include \ -LOCAL_CFLAGS := $(VO_CFLAGS) - include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile b/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile deleted file mode 100644 index 22c5dc1..0000000 --- a/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= exe
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= #ARMV5E
-
-
-
-# please specify the name of your module
-VOTARGET:= voAACEncTestv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../Release/
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/ms.mk b/media/libstagefright/codecs/aacenc/SampleCode/ms.mk deleted file mode 100644 index 771a569..0000000 --- a/media/libstagefright/codecs/aacenc/SampleCode/ms.mk +++ /dev/null @@ -1,23 +0,0 @@ -#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# please list all objects needed by your target here
-OBJS:=AAC_E_SAMPLES.o cmnMemory.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../ ../../../../include ../../../../Common
-
-
diff --git a/media/libstagefright/codecs/aacenc/Tools/doit.mk b/media/libstagefright/codecs/aacenc/Tools/doit.mk deleted file mode 100644 index dea0b0a..0000000 --- a/media/libstagefright/codecs/aacenc/Tools/doit.mk +++ /dev/null @@ -1,133 +0,0 @@ -#/* -# ** Copyright 2003-2010, VisualOn, Inc. -# ** -# ** 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. -# */ - -VERBOSE:=@ - - -VOMT ?= lib - -ifeq ($(VOMT), lib) -LIB_STATIC=$(VOTARGET).a -LIB_DYNAMIC=$(VOTARGET).so -endif - -ifeq ($(VOMT), exe) -TARGET=$(VOTARGET) -endif - -CFLAGS=$(VOCFLAGS) $(addprefix -I, $(VOSRCDIR)) -CPPFLAGS=$(VOCPPFLAGS) $(addprefix -I, $(VOSRCDIR)) -ifneq ($(VOTT), pc) -ASFLAGS=$(VOASFLAGS) $(addprefix -I, $(VOSRCDIR)) -endif - -LDFLAGS:=$(VOLDFLAGS) -VOTEDEPS+=$(VODEPLIBS) -VOTLDEPS+=$(VODEPLIBS) -VOSTCLIBS ?= - -vpath %.c $(VOSRCDIR) -vpath %.cpp $(VOSRCDIR) -ifneq ($(VOTT), pc) -vpath %.s $(VOSRCDIR) -endif - -ifeq ($(VOTT), pc) -BLTDIRS=$(VORELDIR)/Linux/static -BLTDIRD=$(VORELDIR)/Linux/shared -else -BLTDIRS=$(VORELDIR)/Google/$(VONJ)/lib/$(VOTT) -BLTDIRD=$(VORELDIR)/Google/$(VONJ)/so/$(VOTT) -endif - - -.PRECIOUS: $(OBJDIR)/%.o - -ifeq ($(VOMT), lib) -all: mkdirs $(LIB_STATIC) $(LIB_DYNAMIC) -mkdirs: $(OBJDIR) $(BLTDIRS) $(BLTDIRD) -else -all: mkdirs $(TARGET) -mkdirs: $(OBJDIR) -endif - -$(OBJDIR): - @if test ! -d $@; then \ - mkdir -p $@; \ - fi; - -ifeq ($(VOMT), lib) -$(BLTDIRS): - @if test ! -d $@; then \ - mkdir -p $@; \ - fi; -$(BLTDIRD): - @if test ! -d $@; then \ - mkdir -p $@; \ - fi; -endif - - -ifeq ($(VOMT), lib) -$(LIB_STATIC):$(OBJS) - $(AR) cr $@ $(OBJDIR)/*.o $(VOSTCLIBS) - $(RANLIB) $@ -ifneq ($(VODBG), yes) - #$(STRIP) $@ -endif - -$(LIB_DYNAMIC):$(OBJS) - $(GG) $(LDFLAGS) -o $@ $(OBJDIR)/*.o -Wl,--whole-archive $(VOSTCLIBS) -Wl,--no-whole-archive $(VOTLDEPS) -ifneq ($(VODBG), yes) - $(STRIP) $@ -endif - -else - -$(TARGET):$(OBJS) - $(GG) $(LDFLAGS) -o $@ $(OBJDIR)/*.o -Wl,--whole-archive $(VOSTCLIBS) -Wl,--no-whole-archive $(VOTEDEPS) -ifneq ($(VODBG), yes) - $(STRIP) $@ -endif - -endif - - -.SUFFIXES: .c .cpp .s .o -.c.o: - $(VERBOSE) $(CC) $(CFLAGS) -o $(OBJDIR)/$@ -c $< -#%.c:$(OBJDIR)/%.o -# $(VERBOSE) $(CC) $(CFLAGS) -o $@ -c $< -.cpp.o: - $(VERBOSE) $(GG) $(CPPFLAGS) -o $(OBJDIR)/$@ -c $< -ifneq ($(VOTT), pc) -.s.o: - $(VERBOSE) $(AS) $(ASFLAGS) -o $(OBJDIR)/$@ $< -endif - - -.PHONY: clean devel -clean: -ifeq ($(VOMT), lib) - -rm -fr $(OBJDIR) .*.sw* $(VOTARGET).* -else - -rm -fr $(OBJDIR) .*.sw* $(VOTARGET) -endif - -devel: - cp -a $(LIB_STATIC) $(BLTDIRS) - cp -a $(LIB_DYNAMIC) $(BLTDIRD) - diff --git a/media/libstagefright/codecs/aacenc/Tools/eclair.mk b/media/libstagefright/codecs/aacenc/Tools/eclair.mk deleted file mode 100644 index 1688361..0000000 --- a/media/libstagefright/codecs/aacenc/Tools/eclair.mk +++ /dev/null @@ -1,172 +0,0 @@ -#/* -# ** Copyright 2003-2010, VisualOn, Inc. -# ** -# ** 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. -# */ - -# special macro definitions for building -VOPREDEF=-DLINUX -D_LINUX - -VOPRJ ?= -VONJ ?= eclair -VOTT ?= v6 -# control the version to release out -# available: eva(evaluation), rel(release) -VOVER= -ifeq ($(VOVER), eva) -VOPREDEF+=-D__VOVER_EVA__ -endif - -# for debug or not: yes for debug, any other for release -VODBG?=ye - -# for detecting memory leak -VODML= -ifeq ($(VODML), yes) -VOPREDEF+=-DDMEMLEAK -endif - -VOPREDEF+=-D__VOTT_ARM__ -D__VONJ_ECLAIR__ -TCROOTPATH:=/opt/eclair -GCCVER:=4.4.0 -TCPATH:=$(TCROOTPATH)/prebuilt/linux-x86/toolchain/arm-eabi-$(GCCVER) -CCTPRE:=$(TCPATH)/bin/arm-eabi- -AS:=$(CCTPRE)as -AR:=$(CCTPRE)ar -NM:=$(CCTPRE)nm -CC:=$(CCTPRE)gcc -GG:=$(CCTPRE)g++ -LD:=$(CCTPRE)ld -SIZE:=$(CCTPRE)size -STRIP:=$(CCTPRE)strip -RANLIB:=$(CCTPRE)ranlib -OBJCOPY:=$(CCTPRE)objcopy -OBJDUMP:=$(CCTPRE)objdump -READELF:=$(CCTPRE)readelf -STRINGS:=$(CCTPRE)strings - -# target product dependcy -# available: dream, generic -VOTP:=sapphire-open -CCTLIB:=$(TCROOTPATH)/out/target/product/$(VOTP)/obj/lib -CCTINC:=-I$(TCROOTPATH)/system/core/include \ - -I$(TCROOTPATH)/hardware/libhardware/include \ - -I$(TCROOTPATH)/hardware/ril/include \ - -I$(TCROOTPATH)/hardware/libhardware_legacy/include \ - -I$(TCROOTPATH)/dalvik/libnativehelper/include \ - -I$(TCROOTPATH)/dalvik/libnativehelper/include/nativehelper \ - -I$(TCROOTPATH)/frameworks/base/include \ - -I$(TCROOTPATH)/frameworks/base/core/jni \ - -I$(TCROOTPATH)/frameworks/base/libs/audioflinger \ - -I$(TCROOTPATH)/external/skia/include \ - -I$(TCROOTPATH)/out/target/product/$(VOTP)/obj/include \ - -I$(TCROOTPATH)/bionic/libc/arch-arm/include \ - -I$(TCROOTPATH)/bionic/libc/include \ - -I$(TCROOTPATH)/bionic/libstdc++/include \ - -I$(TCROOTPATH)/bionic/libc/kernel/common \ - -I$(TCROOTPATH)/bionic/libc/kernel/arch-arm \ - -I$(TCROOTPATH)/bionic/libm/include \ - -I$(TCROOTPATH)/bionic/libm/include/arm \ - -I$(TCROOTPATH)/bionic/libthread_db/include \ - -I$(TCROOTPATH)/bionic/libm/arm \ - -I$(TCROOTPATH)/bionic/libm \ - -I$(TCROOTPATH)/frameworks/base/include/android_runtime - #-I$(TCROOTPATH)/out/target/product/$(VOTP)/obj/SHARED_LIBRARIES/libm_intermediates - -CCTCFLAGS:=-msoft-float -mthumb-interwork -fno-exceptions -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -fmessage-length=0 -finline-functions -finline-limit=600 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -fstrict-aliasing -funswitch-loops -#-fwide-exec-charset=charset=UTF-32 - -# for target exe -TELDFLAGS:=-nostdlib -Bdynamic -Wl,-T,$(TCROOTPATH)/build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -Wl,--no-undefined -Wl,-rpath-link=$(CCTLIB) -L$(CCTLIB) - -VOTEDEPS:=$(CCTLIB)/crtbegin_dynamic.o $(CCTLIB)/crtend_android.o $(TCPATH)/lib/gcc/arm-eabi/$(GCCVER)/interwork/libgcc.a -lc -lm - -# for target lib -TLLDFLAGS:=-nostdlib -Wl,-T,$(TCROOTPATH)/build/core/armelf.xsc -Wl,--gc-sections -Wl,-shared,-Bsymbolic -L$(CCTLIB) -Wl,--no-whole-archive -Wl,--no-undefined $(TCPATH)/lib/gcc/arm-eabi/$(GCCVER)/interwork/libgcc.a - -VOTLDEPS:=-lm -lc - - -ifeq ($(VOTT), v4) -VOCFLAGS:=-mtune=arm9tdmi -march=armv4t -VOASFLAGS:=-march=armv4t -mfpu=softfpa -endif - -ifeq ($(VOTT), v5) -VOCFLAGS:=-march=armv5te -VOASFLAGS:=-march=armv5te -mfpu=vfp -endif - -ifeq ($(VOTT), v5x) -VOCFLAGS:=-march=armv5te -mtune=xscale -VOASFLAGS:=-march=armv5te -mfpu=vfp -endif - -ifeq ($(VOTT), v6) -#VOCFLAGS:=-march=armv6 -mtune=arm1136jf-s -#VOASFLAGS:=-march=armv6 -VOCFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp -mfloat-abi=softfp -mapcs -mtpcs-leaf-frame -mlong-calls -VOASFLAGS:=-march=armv6j -mcpu=arm1136jf-s -mfpu=arm1136jf-s -mfloat-abi=softfp -mapcs-float -mapcs-reentrant -endif - -# -# global link options -VOLDFLAGS:=-Wl,-x,-X,--as-needed - - -ifeq ($(VOTT), v7) -VOCFLAGS+=-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp -VOASFLAGS+=-march=armv7-a -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -VOLDFLAGS+=-Wl,--fix-cortex-a8 -endif - -#global compiling options for ARM target -ifneq ($(VOTT), pc) -VOASFLAGS+=--strip-local-absolute -R -endif - - -ifeq ($(VODBG), yes) -VOCFLAGS+=-D_DEBUG -g -else -VOCFLAGS+=-DNDEBUG -O3 -endif - -VOCFLAGS+=$(VOPREDEF) $(VOMM) -Wall -fsigned-char -fomit-frame-pointer -fno-leading-underscore -fpic -fPIC -pipe -ftracer -fforce-addr -fno-bounds-check #-fvisibility=hidden #-fvisibility-inlines-hidden ##-ftree-loop-linear -mthumb -nostdinc -dD -fprefetch-loop-arrays - - -ifneq ($(VOTT), pc) -VOCFLAGS+=$(CCTCFLAGS) $(CCTINC) -VOCPPFLAGS:=-fno-rtti $(VOCFLAGS) - -ifeq ($(VOMT), exe) -VOLDFLAGS+=$(TELDFLAGS) -endif - -ifeq ($(VOMT), lib) -VOLDFLAGS+=$(TLLDFLAGS) -endif -else -VOCPPFLAGS:=$(VOCFLAGS) -ifeq ($(VOMT), lib) -VOLDFLAGS+=-shared -endif -endif - -ifeq ($(VODBG), yes) -#VOLDFLAGS:= -endif - -# where to place object files -OBJDIR=obj - diff --git a/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile b/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile deleted file mode 100644 index b4f63af..0000000 --- a/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v5
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= -DARMV5E -DARM_INASM -DARMV5_INASM
-
-
-
-# please specify the name of your module
-VOTARGET:=libvoAACEncv5
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=#-ldl -lstdc++
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile b/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile deleted file mode 100644 index cdce2c1..0000000 --- a/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM
-
-
-
-# please specify the name of your module
-VOTARGET:=libvoAACEncv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=#-ldl -lstdc++
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/build/eclair/makefile b/media/libstagefright/codecs/aacenc/build/eclair/makefile deleted file mode 100644 index 6bb3c13..0000000 --- a/media/libstagefright/codecs/aacenc/build/eclair/makefile +++ /dev/null @@ -1,40 +0,0 @@ -#/* -#** Copyright 2003-2010, VisualOn, Inc. -#** -#** 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. -#*/ - -# Just acting as Father Makefile of Modules -# please keep the name 'makefile' unchanged - -# Module Subdirs -VOMSD:=$(dir $(shell find . -name 'Makefile')) - -all: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir; \ - done - -.PHONY:clean devel -clean: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir clean; \ - done - -devel: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir devel; \ - done diff --git a/media/libstagefright/codecs/aacenc/build/ms.mk b/media/libstagefright/codecs/aacenc/build/ms.mk deleted file mode 100644 index b67efbc..0000000 --- a/media/libstagefright/codecs/aacenc/build/ms.mk +++ /dev/null @@ -1,42 +0,0 @@ -#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-
-# please list all objects needed by your target here
-OBJS:=basicop2.o oper_32b.o aac_rom.o aacenc.o aacenc_core.o adj_thr.o \
- band_nrg.o bit_cnt.o bitbuffer.o bitenc.o block_switch.o channel_map.o \
- dyn_bits.o grp_data.o interface.o line_pe.o memalign.o ms_stereo.o \
- pre_echo_control.o psy_configuration.o psy_main.o qc_main.o quantize.o sf_estim.o \
- spreading.o stat_bits.o tns.o transform.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../../../src \
- ../../../inc \
- ../../../basic_op\
- ../../../../../Include
-
-ifeq ($(VOTT), v5)
-OBJS+= AutoCorrelation_v5.o band_nrg_v5.o CalcWindowEnergy_v5.o \
- PrePostMDCT_v5.o R4R8First_v5.o Radix4FFT_v5.o
-VOSRCDIR+= ../../../src/asm/ARMV5E/
-endif
-
-ifeq ($(VOTT), v7)
-OBJS+= AutoCorrelation_v5.o band_nrg_v5.o CalcWindowEnergy_v5.o \
- PrePostMDCT_v7.o R4R8First_v7.o Radix4FFT_v7.o
-VOSRCDIR+= ../../../src/asm/ARMV5E/
-VOSRCDIR+= ../../../src/asm/ARMV7/
-endif
\ No newline at end of file diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp index fb300da..a11d46b 100644 --- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp +++ b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "AMRNBDecoder" +#include <utils/Log.h> + #include "AMRNBDecoder.h" #include "gsmamr_dec.h" @@ -154,18 +158,24 @@ status_t AMRNBDecoder::read( const uint8_t *inputPtr = (const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(); - size_t numBytesRead = + int32_t numBytesRead = AMRDecode(mState, (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), (UWord8 *)&inputPtr[1], static_cast<int16_t *>(buffer->data()), MIME_IETF); + if (numBytesRead == -1 ) { + LOGE("PV AMR decoder AMRDecode() call failed"); + buffer->release(); + buffer = NULL; + return ERROR_MALFORMED; + } ++numBytesRead; // Include the frame type header byte. buffer->set_range(0, kNumSamplesPerFrame * sizeof(int16_t)); - if (numBytesRead > mInputBuffer->range_length()) { + if (static_cast<size_t>(numBytesRead) > mInputBuffer->range_length()) { // This is bad, should never have happened, but did. Abort now. buffer->release(); diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index a545762..5862abc 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -52,3 +52,33 @@ LOCAL_CFLAGS := \ LOCAL_MODULE := libstagefright_amrnbdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAMR.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ + $(LOCAL_PATH)/../common \ + frameworks/base/media/libstagefright/codecs/amrwb/src \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_amrnbdec libstagefright_amrwbdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils \ + libstagefright_amrnb_common + +LOCAL_MODULE := libstagefright_soft_amrdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp new file mode 100644 index 0000000..c0a588f --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAMR" +#include <utils/Log.h> + +#include "SoftAMR.h" + +#include "gsmamr_dec.h" +#include "pvamrwbdecoder.h" + +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAMR::SoftAMR( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mMode(MODE_NARROW), + mState(NULL), + mDecoderBuf(NULL), + mDecoderCookie(NULL), + mInputBufferCount(0), + mAnchorTimeUs(0), + mNumSamplesOutput(0), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + if (!strcmp(name, "OMX.google.amrwb.decoder")) { + mMode = MODE_WIDE; + } else { + CHECK(!strcmp(name, "OMX.google.amrnb.decoder")); + } + + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAMR::~SoftAMR() { + if (mMode == MODE_NARROW) { + GSMDecodeFrameExit(&mState); + mState = NULL; + } else { + free(mDecoderBuf); + mDecoderBuf = NULL; + + mState = NULL; + mDecoderCookie = NULL; + } +} + +void SoftAMR::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + mMode == MODE_NARROW + ? const_cast<char *>("audio/amr") + : const_cast<char *>("audio/amrwb"); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + + def.nBufferSize = + (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB) + * sizeof(int16_t); + + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftAMR::initDecoder() { + if (mMode == MODE_NARROW) { + Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder"); + + if (err != 0) { + return UNKNOWN_ERROR; + } + } else { + int32_t memReq = pvDecoder_AmrWbMemRequirements(); + mDecoderBuf = malloc(memReq); + + pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie); + } + + return OK; +} + +OMX_ERRORTYPE SoftAMR::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (amrParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + amrParams->nChannels = 1; + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatConformance; + + if (!isConfigured()) { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused; + } else { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = + mMode == MODE_NARROW + ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->nChannels = 1; + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + + pcmParams->nSamplingRate = + (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB; + + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAMR::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (mMode == MODE_NARROW) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrnb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrwb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + const OMX_AUDIO_PARAM_AMRTYPE *aacParams = + (const OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAMR::isConfigured() const { + return mInputBufferCount > 0; +} + +static size_t getFrameSize(unsigned FT) { + static const size_t kFrameSizeWB[9] = { + 132, 177, 253, 285, 317, 365, 397, 461, 477 + }; + + size_t frameSize = kFrameSizeWB[FT]; + + // Round up bits to bytes and add 1 for the header byte. + frameSize = (frameSize + 7) / 8 + 1; + + return frameSize; +} + +void SoftAMR::onQueueFilled(OMX_U32 portIndex) { + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset; + int32_t numBytesRead; + + if (mMode == MODE_NARROW) { + numBytesRead = + AMRDecode(mState, + (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), + (UWord8 *)&inputPtr[1], + reinterpret_cast<int16_t *>(outHeader->pBuffer), + MIME_IETF); + + if (numBytesRead == -1) { + LOGE("PV AMR decoder AMRDecode() call failed"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + + ++numBytesRead; // Include the frame type header byte. + + if (static_cast<size_t>(numBytesRead) > inHeader->nFilledLen) { + // This is bad, should never have happened, but did. Abort now. + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + } else { + int16 mode = ((inputPtr[0] >> 3) & 0x0f); + size_t frameSize = getFrameSize(mode); + CHECK_GE(inHeader->nFilledLen, frameSize); + + int16 frameType; + RX_State_wb rx_state; + mime_unsorting( + const_cast<uint8_t *>(&inputPtr[1]), + mInputSampleBuffer, + &frameType, &mode, 1, &rx_state); + + int16_t *outPtr = (int16_t *)outHeader->pBuffer; + + int16_t numSamplesOutput; + pvDecoder_AmrWb( + mode, mInputSampleBuffer, + outPtr, + &numSamplesOutput, + mDecoderBuf, frameType, mDecoderCookie); + + CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB); + + for (int i = 0; i < kNumSamplesPerFrameWB; ++i) { + /* Delete the 2 LSBs (14-bit output) */ + outPtr[i] &= 0xfffC; + } + + numBytesRead = frameSize; + } + + inHeader->nOffset += numBytesRead; + inHeader->nFilledLen -= numBytesRead; + + outHeader->nFlags = 0; + outHeader->nOffset = 0; + + if (mMode == MODE_NARROW) { + outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / kSampleRateNB; + + mNumSamplesOutput += kNumSamplesPerFrameNB; + } else { + outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / kSampleRateWB; + + mNumSamplesOutput += kNumSamplesPerFrameWB; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) { +} + +void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAMR(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h new file mode 100644 index 0000000..9a596e5 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AMR_H_ + +#define SOFT_AMR_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftAMR : public SimpleSoftOMXComponent { + SoftAMR(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAMR(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumBuffers = 4, + kSampleRateNB = 8000, + kSampleRateWB = 16000, + kNumSamplesPerFrameNB = 160, + kNumSamplesPerFrameWB = 320, + }; + + enum { + MODE_NARROW, + MODE_WIDE + + } mMode; + + void *mState; + void *mDecoderBuf; + int16_t *mDecoderCookie; + + size_t mInputBufferCount; + int64_t mAnchorTimeUs; + int64_t mNumSamplesOutput; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + int16_t mInputSampleBuffer[477]; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftAMR); +}; + +} // namespace android + +#endif // SOFT_AMR_H_ + diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp index 2a21472..5b111ef 100644 --- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp +++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp @@ -177,7 +177,7 @@ status_t AMRWBDecoder::read( CHECK(mInputBuffer->range_length() >= frameSize); int16 frameType; - RX_State rx_state; + RX_State_wb rx_state; mime_unsorting( const_cast<uint8_t *>(&inputPtr[1]), mInputSampleBuffer, diff --git a/media/libstagefright/codecs/amrwb/src/mime_io.cpp b/media/libstagefright/codecs/amrwb/src/mime_io.cpp index 9ff8816..e1966c6 100644 --- a/media/libstagefright/codecs/amrwb/src/mime_io.cpp +++ b/media/libstagefright/codecs/amrwb/src/mime_io.cpp @@ -531,7 +531,7 @@ void mime_unsorting(uint8 unsorted_bits[], int16 * frame_type, int16 * mode, uint8 quality, - RX_State *st) + RX_State_wb *st) { int16 i; diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h index 433fc92..c40bc10 100644 --- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h +++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h @@ -101,7 +101,7 @@ typedef struct { int16 prev_ft; int16 prev_mode; -} RX_State; +} RX_State_wb; /*---------------------------------------------------------------------------- ; ENUMERATED TYPEDEF'S @@ -141,7 +141,7 @@ typedef struct int16 *frame_type, int16 *mode, uint8 q, - RX_State *st); + RX_State_wb *st); /*---------------------------------------------------------------------------- diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk index 4293287..5179380 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk -LOCAL_PRELINK_MODULE := false + LOCAL_SRC_FILES := \ AMRWBEncoder.cpp \ diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c b/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c index 792d3cc..5e71a5b 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c @@ -129,7 +129,7 @@ int encode( useData.memData = (VO_PTR)(&moper);
#ifdef LINUX
- handle = dlopen("/data/local/tmp/voAMRWBEnc.so", RTLD_NOW);
+ handle = dlopen("libstagefright.so", RTLD_NOW);
if(handle == 0)
{
printf("open dll error......");
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk index 7edb166..85ddceb 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk @@ -1,26 +1,26 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := AMRWB_E_SAMPLE.c - -LOCAL_SRC_FILES += \ - ../../../Common/cmnMemory.c +LOCAL_SRC_FILES := \ + AMRWB_E_SAMPLE.c \ + ../../common/cmnMemory.c -LOCAL_MODULE := TestvoAMRWBEnc +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := AMRWBEncTest LOCAL_ARM_MODE := arm -LOCAL_STATIC_LIBRARIES := +LOCAL_CFLAGS := $(VO_CFLAGS) -LOCAL_SHARED_LIBRARIES := libvoAMRWBEnc +LOCAL_SHARED_LIBRARIES := \ + libstagefright \ + libdl LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/ \ - $(LOCAL_PATH)/../../../Common \ - $(LOCAL_PATH)/../../../Include \ + $(LOCAL_PATH)/ \ + $(LOCAL_PATH)/../../common \ + $(LOCAL_PATH)/../../common/include -LOCAL_CFLAGS := $(VO_CFLAGS) - include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile b/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile deleted file mode 100644 index 55b876a..0000000 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v6
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= exe
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= #ARMV5E
-
-
-
-# please specify the name of your module
-VOTARGET:= voAMRWBEnc_Test
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl
-
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk deleted file mode 100644 index 74e8913..0000000 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk +++ /dev/null @@ -1,24 +0,0 @@ -#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-# please list all objects needed by your target here
-OBJS:=AMRWB_E_SAMPLE.o cmnMemory.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../ \
- ../../../../Common \
- ../../../../Include
-
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile deleted file mode 100644 index 58fda29..0000000 --- a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target type
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v5
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-ifeq ($(VOTT), v5)
-VOMM:=-DARM -DASM_OPT
-endif
-
-# please specify the name of your module
-VOTARGET:= libvoAMRWBEncv5
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl -lstdc++ -lcutils
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile deleted file mode 100644 index 5686411..0000000 --- a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target type
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-ifeq ($(VOTT), v7)
-VOMM:=-DARM -DARMV7 -DASM_OPT
-endif
-
-# please specify the name of your module
-VOTARGET:= libvoAMRWBEncv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl -lstdc++ -lcutils
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/makefile deleted file mode 100644 index 3473a1a..0000000 --- a/media/libstagefright/codecs/amrwbenc/build/eclair/makefile +++ /dev/null @@ -1,39 +0,0 @@ -#/* -# ** Copyright 2003-2010, VisualOn, Inc. -# ** -# ** 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. -# */ -# Just acting as Father Makefile of Modules -# please keep the name 'makefile' unchanged - -# Module Subdirs -VOMSD:=$(dir $(shell find . -name 'Makefile')) - -all: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir; \ - done - -.PHONY:clean devel -clean: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir clean; \ - done - -devel: - for dir in $(VOMSD); \ - do \ - $(MAKE) -C $$dir devel; \ - done diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk b/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk deleted file mode 100644 index bd6620c..0000000 --- a/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk +++ /dev/null @@ -1,43 +0,0 @@ -#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../../../inc \
- ../../../src \
- ../../../../../Include
-
-# please list all objects needed by your target here
-OBJS:= autocorr.o az_isp.o bits.o c2t64fx.o c4t64fx.o convolve.o cor_h_x.o decim54.o \
- deemph.o dtx.o g_pitch.o gpclip.o homing.o hp400.o hp50.o hp6k.o hp_wsp.o \
- int_lpc.o isp_az.o isp_isf.o lag_wind.o levinson.o log2.o lp_dec2.o math_op.o mem_align.o \
- oper_32b.o p_med_ol.o pit_shrp.o pitch_f4.o pred_lt4.o preemph.o q_gain2.o q_pulse.o \
- qisf_ns.o qpisf_2s.o random.o residu.o scale.o stream.o syn_filt.o updt_tar.o util.o \
- voAMRWBEnc.o voicefac.o wb_vad.o weight_a.o
-
-
-ifeq ($(VOTT), v5)
-OBJS += cor_h_vec_opt.o Deemph_32_opt.o Dot_p_opt.o Filt_6k_7k_opt.o residu_asm_opt.o \
- scale_sig_opt.o Syn_filt_32_opt.o syn_filt_opt.o pred_lt4_1_opt.o convolve_opt.o \
- Norm_Corr_opt.o
-VOSRCDIR+= ../../../src/asm/ARMV5E
-endif
-
-ifeq ($(VOTT), v7)
-OBJS+= cor_h_vec_neon.o Deemph_32_neon.o Dot_p_neon.o Filt_6k_7k_neon.o residu_asm_neon.o \
- scale_sig_neon.o Syn_filt_32_neon.o syn_filt_neon.o pred_lt4_1_neon.o convolve_neon.o \
- Norm_Corr_neon.o
-VOSRCDIR+= ../../../src/asm/ARMV7
-endif
-
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp index 5bbba35..490129f 100644 --- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp +++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp @@ -534,7 +534,8 @@ status_t AVCDecoder::read( default: { LOGE("Should not be here, unknown nalType %d", nalType); - CHECK(!"Should not be here"); + + err = ERROR_MALFORMED; break; } } diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk index 1b00347..4d4533b 100644 --- a/media/libstagefright/codecs/avc/dec/Android.mk +++ b/media/libstagefright/codecs/avc/dec/Android.mk @@ -3,25 +3,54 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ AVCDecoder.cpp \ - src/avcdec_api.cpp \ - src/avc_bitstream.cpp \ - src/header.cpp \ - src/itrans.cpp \ - src/pred_inter.cpp \ - src/pred_intra.cpp \ - src/residual.cpp \ - src/slice.cpp \ - src/vlc.cpp + src/avcdec_api.cpp \ + src/avc_bitstream.cpp \ + src/header.cpp \ + src/itrans.cpp \ + src/pred_inter.cpp \ + src/pred_intra.cpp \ + src/residual.cpp \ + src/slice.cpp \ + src/vlc.cpp LOCAL_MODULE := libstagefright_avcdec LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/src \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../common/include \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ $(TOP)/frameworks/base/media/libstagefright/include \ - $(TOP)/frameworks/base/include/media/stagefright/openmax + frameworks/base/include/media/stagefright/openmax \ LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAVC.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_avcdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_avc_common \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_avcdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp new file mode 100644 index 0000000..9f141ac --- /dev/null +++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAVC" +#include <utils/Log.h> + +#include "SoftAVC.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> + +#include "avcdec_api.h" +#include "avcdec_int.h" + +namespace android { + +static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 }; + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static int32_t Malloc(void *userData, int32_t size, int32_t attrs) { + return reinterpret_cast<int32_t>(malloc(size)); +} + +static void Free(void *userData, int32_t ptr) { + free(reinterpret_cast<void *>(ptr)); +} + +SoftAVC::SoftAVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mHandle(new tagAVCHandle), + mInputBufferCount(0), + mWidth(160), + mHeight(120), + mCropLeft(0), + mCropTop(0), + mCropRight(mWidth - 1), + mCropBottom(mHeight - 1), + mSPSSeen(false), + mPPSSeen(false), + mCurrentTimeUs(-1), + mEOSStatus(INPUT_DATA_AVAILABLE), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAVC::~SoftAVC() { + PVAVCCleanUpDecoder(mHandle); + + delete mHandle; + mHandle = NULL; +} + +void SoftAVC::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_AVC); + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.pNativeWindow = NULL; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW); + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; + def.format.video.pNativeWindow = NULL; + + def.nBufferSize = + (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; + + addPort(def); +} + +status_t SoftAVC::initDecoder() { + memset(mHandle, 0, sizeof(tagAVCHandle)); + mHandle->AVCObject = NULL; + mHandle->userData = this; + mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper; + mHandle->CBAVC_FrameBind = BindFrameWrapper; + mHandle->CBAVC_FrameUnbind = UnbindFrame; + mHandle->CBAVC_Malloc = Malloc; + mHandle->CBAVC_Free = Free; + + return OK; +} + +OMX_ERRORTYPE SoftAVC::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + } else { + CHECK_EQ(formatParams->nPortIndex, 1u); + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + formatParams->xFramerate = 0; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAVC::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "video_decoder.avc", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAVC::getConfig( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexConfigCommonOutputCrop: + { + OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; + + if (rectParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + rectParams->nLeft = mCropLeft; + rectParams->nTop = mCropTop; + rectParams->nWidth = mCropRight - mCropLeft + 1; + rectParams->nHeight = mCropBottom - mCropTop + 1; + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +static void findNALFragment( + const OMX_BUFFERHEADERTYPE *inHeader, + const uint8_t **fragPtr, size_t *fragSize) { + const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; + + size_t size = inHeader->nFilledLen; + + CHECK(size >= 4); + CHECK(!memcmp(kStartCode, data, 4)); + + size_t offset = 4; + while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) { + ++offset; + } + + *fragPtr = &data[4]; + if (offset + 3 >= size) { + *fragSize = size - 4; + } else { + *fragSize = offset - 4; + } +} + +void SoftAVC::onQueueFilled(OMX_U32 portIndex) { + if (mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) { + return; + } + + while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty()) + && outQueue.size() == kNumOutputBuffers) { + if (mEOSStatus == INPUT_EOS_SEEN) { + OMX_BUFFERHEADERTYPE *outHeader; + if (drainOutputBuffer(&outHeader)) { + List<BufferInfo *>::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + + outHeader->nOffset = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outHeader->nTimeStamp = 0; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + mEOSStatus = OUTPUT_FRAMES_FLUSHED; + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + mEOSStatus = INPUT_EOS_SEEN; + continue; + } + + mCurrentTimeUs = inHeader->nTimeStamp; + + const uint8_t *fragPtr; + size_t fragSize; + findNALFragment(inHeader, &fragPtr, &fragSize); + + bool releaseFragment; + OMX_BUFFERHEADERTYPE *outHeader; + status_t err = decodeFragment( + fragPtr, fragSize, + &releaseFragment, &outHeader); + + if (releaseFragment) { + CHECK_GE(inHeader->nFilledLen, fragSize + 4); + + inHeader->nOffset += fragSize + 4; + inHeader->nFilledLen -= fragSize + 4; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } + + if (outHeader != NULL) { + List<BufferInfo *>::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + if (err == INFO_FORMAT_CHANGED) { + return; + } + + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUndefined, err, NULL); + return; + } + } +} + +status_t SoftAVC::decodeFragment( + const uint8_t *fragPtr, size_t fragSize, + bool *releaseFragment, + OMX_BUFFERHEADERTYPE **outHeader) { + *releaseFragment = true; + *outHeader = NULL; + + int nalType; + int nalRefIdc; + AVCDec_Status res = + PVAVCDecGetNALType( + const_cast<uint8_t *>(fragPtr), fragSize, + &nalType, &nalRefIdc); + + if (res != AVCDEC_SUCCESS) { + LOGV("cannot determine nal type"); + return ERROR_MALFORMED; + } + + if (nalType != AVC_NALTYPE_SPS && nalType != AVC_NALTYPE_PPS + && (!mSPSSeen || !mPPSSeen)) { + // We haven't seen SPS or PPS yet. + return OK; + } + + switch (nalType) { + case AVC_NALTYPE_SPS: + { + mSPSSeen = true; + + res = PVAVCDecSeqParamSet( + mHandle, const_cast<uint8_t *>(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + return ERROR_MALFORMED; + } + + AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject; + + int32_t width = + (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16; + + int32_t height = + (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16; + + int32_t crop_left, crop_right, crop_top, crop_bottom; + if (pDecVid->seqParams[0]->frame_cropping_flag) + { + crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset; + crop_right = + width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1); + + if (pDecVid->seqParams[0]->frame_mbs_only_flag) + { + crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset; + crop_bottom = + height - + (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); + } + else + { + crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset; + crop_bottom = + height - + (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); + } + } else { + crop_bottom = height - 1; + crop_right = width - 1; + crop_top = crop_left = 0; + } + + status_t err = OK; + + if (mWidth != width || mHeight != height) { + mWidth = width; + mHeight = height; + + updatePortDefinitions(); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + + err = INFO_FORMAT_CHANGED; + } + + if (mCropLeft != crop_left + || mCropTop != crop_top + || mCropRight != crop_right + || mCropBottom != crop_bottom) { + mCropLeft = crop_left; + mCropTop = crop_top; + mCropRight = crop_right; + mCropBottom = crop_bottom; + + notify(OMX_EventPortSettingsChanged, + 1, + OMX_IndexConfigCommonOutputCrop, + NULL); + } + + return err; + } + + case AVC_NALTYPE_PPS: + { + mPPSSeen = true; + + res = PVAVCDecPicParamSet( + mHandle, const_cast<uint8_t *>(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + LOGV("PVAVCDecPicParamSet returned error %d", res); + return ERROR_MALFORMED; + } + + return OK; + } + + case AVC_NALTYPE_SLICE: + case AVC_NALTYPE_IDR: + { + res = PVAVCDecodeSlice( + mHandle, const_cast<uint8_t *>(fragPtr), + fragSize); + + if (res == AVCDEC_PICTURE_OUTPUT_READY) { + *releaseFragment = false; + + if (!drainOutputBuffer(outHeader)) { + return UNKNOWN_ERROR; + } + + return OK; + } + + if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) { + return OK; + } else { + LOGV("PVAVCDecodeSlice returned error %d", res); + return ERROR_MALFORMED; + } + } + + case AVC_NALTYPE_SEI: + { + res = PVAVCDecSEI( + mHandle, const_cast<uint8_t *>(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + return ERROR_MALFORMED; + } + + return OK; + } + + case AVC_NALTYPE_AUD: + case AVC_NALTYPE_FILL: + case AVC_NALTYPE_EOSEQ: + { + return OK; + } + + default: + { + LOGE("Should not be here, unknown nalType %d", nalType); + + return ERROR_MALFORMED; + } + } + + return OK; +} + +bool SoftAVC::drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader) { + int32_t index; + int32_t Release; + AVCFrameIO Output; + Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL; + AVCDec_Status status = + PVAVCDecGetOutput(mHandle, &index, &Release, &Output); + + if (status != AVCDEC_SUCCESS) { + return false; + } + + PortInfo *port = editPortInfo(1); + CHECK_GE(index, 0); + CHECK_LT((size_t)index, port->mBuffers.size()); + CHECK(port->mBuffers.editItemAt(index).mOwnedByUs); + + *outHeader = port->mBuffers.editItemAt(index).mHeader; + (*outHeader)->nOffset = 0; + (*outHeader)->nFilledLen = port->mDef.nBufferSize; + (*outHeader)->nFlags = 0; + + return true; +} + +void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + PVAVCDecReset(mHandle); + + mEOSStatus = INPUT_DATA_AVAILABLE; + } +} + +void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +void SoftAVC::updatePortDefinitions() { + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def = &editPortInfo(1)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def->nBufferSize = + (def->format.video.nFrameWidth + * def->format.video.nFrameHeight * 3) / 2; +} + +// static +int32_t SoftAVC::ActivateSPSWrapper( + void *userData, unsigned int sizeInMbs, unsigned int numBuffers) { + return static_cast<SoftAVC *>(userData)->activateSPS(sizeInMbs, numBuffers); +} + +// static +int32_t SoftAVC::BindFrameWrapper( + void *userData, int32_t index, uint8_t **yuv) { + return static_cast<SoftAVC *>(userData)->bindFrame(index, yuv); +} + +// static +void SoftAVC::UnbindFrame(void *userData, int32_t index) { +} + +int32_t SoftAVC::activateSPS( + unsigned int sizeInMbs, unsigned int numBuffers) { + PortInfo *port = editPortInfo(1); + CHECK_GE(port->mBuffers.size(), numBuffers); + CHECK_GE(port->mDef.nBufferSize, (sizeInMbs << 7) * 3); + + return 1; +} + +int32_t SoftAVC::bindFrame(int32_t index, uint8_t **yuv) { + PortInfo *port = editPortInfo(1); + + CHECK_GE(index, 0); + CHECK_LT((size_t)index, port->mBuffers.size()); + + BufferInfo *outBuffer = + &port->mBuffers.editItemAt(index); + + CHECK(outBuffer->mOwnedByUs); + + outBuffer->mHeader->nTimeStamp = mCurrentTimeUs; + *yuv = outBuffer->mHeader->pBuffer; + + return 1; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAVC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.h b/media/libstagefright/codecs/avc/dec/SoftAVC.h new file mode 100644 index 0000000..1594b4d --- /dev/null +++ b/media/libstagefright/codecs/avc/dec/SoftAVC.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AVC_H_ + +#define SOFT_AVC_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tagAVCHandle; + +namespace android { + +struct SoftAVC : public SimpleSoftOMXComponent { + SoftAVC(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAVC(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 18, + }; + + enum EOSStatus { + INPUT_DATA_AVAILABLE, + INPUT_EOS_SEEN, + OUTPUT_FRAMES_FLUSHED, + }; + + tagAVCHandle *mHandle; + + size_t mInputBufferCount; + + int32_t mWidth, mHeight; + int32_t mCropLeft, mCropTop, mCropRight, mCropBottom; + + bool mSPSSeen, mPPSSeen; + + int64_t mCurrentTimeUs; + + EOSStatus mEOSStatus; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + + status_t decodeFragment( + const uint8_t *fragPtr, size_t fragSize, + bool *releaseFrames, + OMX_BUFFERHEADERTYPE **outHeader); + + void updatePortDefinitions(); + bool drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader); + + static int32_t ActivateSPSWrapper( + void *userData, unsigned int sizeInMbs, unsigned int numBuffers); + + static int32_t BindFrameWrapper( + void *userData, int32_t index, uint8_t **yuv); + + static void UnbindFrame(void *userData, int32_t index); + + int32_t activateSPS( + unsigned int sizeInMbs, unsigned int numBuffers); + + int32_t bindFrame(int32_t index, uint8_t **yuv); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAVC); +}; + +} // namespace android + +#endif // SOFT_AVC_H_ + diff --git a/media/libstagefright/codecs/common/Android.mk b/media/libstagefright/codecs/common/Android.mk index fffb2ad..af8795a 100644 --- a/media/libstagefright/codecs/common/Android.mk +++ b/media/libstagefright/codecs/common/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_PRELINK_MODULE := false + LOCAL_SRC_FILES := cmnMemory.c diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk index cfb9fe4..6e98559 100644 --- a/media/libstagefright/codecs/g711/dec/Android.mk +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -10,3 +10,22 @@ LOCAL_C_INCLUDES := \ LOCAL_MODULE := libstagefright_g711dec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftG711.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_g711dec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp new file mode 100644 index 0000000..15e2c26 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftG711" +#include <utils/Log.h> + +#include "SoftG711.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftG711::SoftG711( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsMLaw(true), + mNumChannels(1), + mSignalledError(false) { + if (!strcmp(name, "OMX.google.g711.alaw.decoder")) { + mIsMLaw = false; + } else { + CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder")); + } + + initPorts(); +} + +SoftG711::~SoftG711() { +} + +void SoftG711::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast<char *>( + mIsMLaw + ? MEDIA_MIMETYPE_AUDIO_G711_MLAW + : MEDIA_MIMETYPE_AUDIO_G711_ALAW); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingG711; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +OMX_ERRORTYPE SoftG711::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = 8000; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftG711::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (mIsMLaw) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711mlaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711alaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftG711::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) { + LOGE("input buffer too large (%ld).", inHeader->nFilledLen); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; + + if (mIsMLaw) { + DecodeMLaw( + reinterpret_cast<int16_t *>(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } else { + DecodeALaw( + reinterpret_cast<int16_t *>(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } + + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t); + outHeader->nFlags = 0; + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + +// static +void SoftG711::DecodeALaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t ix = x ^ 0x55; + ix &= 0x7f; + + int32_t iexp = ix >> 4; + int32_t mant = ix & 0x0f; + + if (iexp > 0) { + mant += 16; + } + + mant = (mant << 4) + 8; + + if (iexp > 1) { + mant = mant << (iexp - 1); + } + + *out++ = (x > 127) ? mant : -mant; + } +} + +// static +void SoftG711::DecodeMLaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t mantissa = ~x; + int32_t exponent = (mantissa >> 4) & 7; + int32_t segment = exponent + 1; + mantissa &= 0x0f; + + int32_t step = 4 << segment; + + int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33; + + *out++ = (x < 0x80) ? -abs : abs; + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftG711(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h new file mode 100644 index 0000000..bff0c68 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_G711_H_ + +#define SOFT_G711_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftG711 : public SimpleSoftOMXComponent { + SoftG711(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftG711(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerFrame = 16384, + }; + + bool mIsMLaw; + OMX_U32 mNumChannels; + bool mSignalledError; + + void initPorts(); + + static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize); + static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize); + + DISALLOW_EVIL_CONSTRUCTORS(SoftG711); +}; + +} // namespace android + +#endif // SOFT_G711_H_ + diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk index 2d9bcc6..f1bec08 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk @@ -48,3 +48,29 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF= include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftMPEG4.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_m4vh263dec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_mpeg4dec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp new file mode 100644 index 0000000..13e1662 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftMPEG4" +#include <utils/Log.h> + +#include "SoftMPEG4.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> + +#include "mp4dec_api.h" + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftMPEG4::SoftMPEG4( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mMode(MODE_MPEG4), + mHandle(new tagvideoDecControls), + mInputBufferCount(0), + mWidth(352), + mHeight(288), + mCropLeft(0), + mCropTop(0), + mCropRight(mWidth - 1), + mCropBottom(mHeight - 1), + mSignalledError(false), + mInitialized(false), + mFramesConfigured(false), + mNumSamplesOutput(0), + mOutputPortSettingsChange(NONE) { + if (!strcmp(name, "OMX.google.h263.decoder")) { + mMode = MODE_H263; + } else { + CHECK(!strcmp(name, "OMX.google.mpeg4.decoder")); + } + + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftMPEG4::~SoftMPEG4() { + if (mInitialized) { + PVCleanUpVideoDecoder(mHandle); + } + + delete mHandle; + mHandle = NULL; +} + +void SoftMPEG4::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.video.cMIMEType = + (mMode == MODE_MPEG4) + ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4) + : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263); + + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + + def.format.video.eCompressionFormat = + mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263; + + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.pNativeWindow = NULL; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW); + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; + def.format.video.pNativeWindow = NULL; + + def.nBufferSize = + (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; + + addPort(def); +} + +status_t SoftMPEG4::initDecoder() { + memset(mHandle, 0, sizeof(tagvideoDecControls)); + return OK; +} + +OMX_ERRORTYPE SoftMPEG4::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + formatParams->eCompressionFormat = + (mMode == MODE_MPEG4) + ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263; + + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + } else { + CHECK_EQ(formatParams->nPortIndex, 1u); + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + formatParams->xFramerate = 0; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMPEG4::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (mMode == MODE_MPEG4) { + if (strncmp((const char *)roleParams->cRole, + "video_decoder.mpeg4", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "video_decoder.h263", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMPEG4::getConfig( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexConfigCommonOutputCrop: + { + OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; + + if (rectParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + rectParams->nLeft = mCropLeft; + rectParams->nTop = mCropTop; + rectParams->nWidth = mCropRight - mCropLeft + 1; + rectParams->nHeight = mCropBottom - mCropTop + 1; + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + PortInfo *port = editPortInfo(1); + + OMX_BUFFERHEADERTYPE *outHeader = + port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + ++mInputBufferCount; + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + List<BufferInfo *>::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset; + + if (!mInitialized) { + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + vol_data[0] = bitstream; + vol_size = inHeader->nFilledLen; + } + + MP4DecodingMode mode = + (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE; + + Bool success = PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode); + + if (!success) { + LOGW("PVInitVideoDecoder failed. Unsupported content?"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); + if (mode != actualMode) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + PVSetPostProcType((VideoDecControls *) mHandle, 0); + + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + mInitialized = true; + + if (mode == MPEG4_MODE && portSettingsChanged()) { + return; + } + + continue; + } + + if (!mFramesConfigured) { + PortInfo *port = editPortInfo(1); + OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader; + + PVSetReferenceYUV(mHandle, outHeader->pBuffer); + + mFramesConfigured = true; + } + + uint32_t timestamp = 0xFFFFFFFF; + int32_t bufferSize = inHeader->nFilledLen; + + uint32_t useExtTimestamp = 0; + if (PVDecodeVideoFrame( + mHandle, &bitstream, ×tamp, &bufferSize, + &useExtTimestamp, + outHeader->pBuffer) != PV_TRUE) { + LOGE("failed to decode video frame."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + if (portSettingsChanged()) { + return; + } + + outHeader->nTimeStamp = inHeader->nTimeStamp; + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + ++mInputBufferCount; + + outHeader->nOffset = 0; + outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; + outHeader->nFlags = 0; + + List<BufferInfo *>::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mNumSamplesOutput; + } +} + +bool SoftMPEG4::portSettingsChanged() { + int32_t disp_width, disp_height; + PVGetVideoDimensions(mHandle, &disp_width, &disp_height); + + int32_t buf_width, buf_height; + PVGetBufferDimensions(mHandle, &buf_width, &buf_height); + + CHECK_LE(disp_width, buf_width); + CHECK_LE(disp_height, buf_height); + + LOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d", + disp_width, disp_height, buf_width, buf_height); + + if (mCropRight != disp_width - 1 + || mCropBottom != disp_height - 1) { + mCropLeft = 0; + mCropTop = 0; + mCropRight = disp_width - 1; + mCropBottom = disp_height - 1; + + notify(OMX_EventPortSettingsChanged, + 1, + OMX_IndexConfigCommonOutputCrop, + NULL); + } + + if (buf_width != mWidth || buf_height != mHeight) { + mWidth = buf_width; + mHeight = buf_height; + + updatePortDefinitions(); + + if (mMode == MODE_H263) { + PVCleanUpVideoDecoder(mHandle); + + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + if (!PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, mWidth, mHeight, + H263_MODE)) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return true; + } + } + + mFramesConfigured = false; + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return true; + } + + return false; +} + +void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mInitialized) { + CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE); + } +} + +void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +void SoftMPEG4::updatePortDefinitions() { + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def = &editPortInfo(1)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def->nBufferSize = + (((def->format.video.nFrameWidth + 15) & -16) + * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMPEG4(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h new file mode 100644 index 0000000..dff08a7 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_MPEG4_H_ + +#define SOFT_MPEG4_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tagvideoDecControls; + +namespace android { + +struct SoftMPEG4 : public SimpleSoftOMXComponent { + SoftMPEG4(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMPEG4(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 2, + }; + + enum { + MODE_MPEG4, + MODE_H263, + + } mMode; + + tagvideoDecControls *mHandle; + + size_t mInputBufferCount; + + int32_t mWidth, mHeight; + int32_t mCropLeft, mCropTop, mCropRight, mCropBottom; + + bool mSignalledError; + bool mInitialized; + bool mFramesConfigured; + + int32_t mNumSamplesOutput; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + + void updatePortDefinitions(); + bool portSettingsChanged(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4); +}; + +} // namespace android + +#endif // SOFT_MPEG4_H_ + + diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk index 753500e..229988e 100644 --- a/media/libstagefright/codecs/mp3dec/Android.mk +++ b/media/libstagefright/codecs/mp3dec/Android.mk @@ -57,3 +57,26 @@ LOCAL_ARM_MODE := arm include $(BUILD_STATIC_LIBRARY) +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftMP3.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_mp3dec + +LOCAL_MODULE := libstagefright_soft_mp3dec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp index 59dd740..0ba42ff 100644 --- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp +++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "MP3Decoder" + #include "MP3Decoder.h" #include "include/pvmp3decoder_api.h" @@ -175,7 +178,12 @@ status_t MP3Decoder::read( != NO_DECODING_ERROR) { LOGV("mp3 decoder returned error %d", decoderErr); - if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) { + if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR || + mConfig->outputFrameSize == 0) { + + if (mConfig->outputFrameSize == 0) { + LOGE("Output frame size is 0"); + } buffer->release(); buffer = NULL; diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp new file mode 100644 index 0000000..f6770b0 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftMP3" +#include <utils/Log.h> + +#include "SoftMP3.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> + +#include "include/pvmp3decoder_api.h" + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftMP3::SoftMP3( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mConfig(new tPVMP3DecoderExternal), + mDecoderBuf(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumChannels(2), + mSamplingRate(44100), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + initDecoder(); +} + +SoftMP3::~SoftMP3() { + if (mDecoderBuf != NULL) { + free(mDecoderBuf); + mDecoderBuf = NULL; + } + + delete mConfig; + mConfig = NULL; +} + +void SoftMP3::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast<char *>(MEDIA_MIMETYPE_AUDIO_MPEG); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingMP3; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kOutputBufferSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +void SoftMP3::initDecoder() { + mConfig->equalizerType = flat; + mConfig->crcEnabled = false; + + uint32_t memRequirements = pvmp3_decoderMemRequirements(); + mDecoderBuf = malloc(memRequirements); + + pvmp3_InitDecoder(mConfig, mDecoderBuf); +} + +OMX_ERRORTYPE SoftMP3::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSamplingRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMP3::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.mp3", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftMP3::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; + + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + mConfig->inputBufferMaxLength = 0; + mConfig->inputBufferUsedLength = 0; + + mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); + + mConfig->pOutputBuffer = + reinterpret_cast<int16_t *>(outHeader->pBuffer); + + ERROR_CODE decoderErr; + if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) + != NO_DECODING_ERROR) { + LOGV("mp3 decoder returned error %d", decoderErr); + + if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR || + mConfig->outputFrameSize == 0) { + + if (mConfig->outputFrameSize == 0) { + LOGE("Output frame size is 0"); + } + + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + mSignalledError = true; + return; + } + + // This is recoverable, just ignore the current frame and + // play silence instead. + memset(outHeader->pBuffer, + 0, + mConfig->outputFrameSize * sizeof(int16_t)); + + mConfig->inputBufferUsedLength = inHeader->nFilledLen; + } else if (mConfig->samplingRate != mSamplingRate + || mConfig->num_channels != mNumChannels) { + mSamplingRate = mConfig->samplingRate; + mNumChannels = mConfig->num_channels; + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + outHeader->nOffset = 0; + outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; + + outHeader->nFlags = 0; + + CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); + + inHeader->nOffset += mConfig->inputBufferUsedLength; + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + + mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + +void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + pvmp3_InitDecoder(mConfig, mDecoderBuf); + } +} + +void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMP3(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h new file mode 100644 index 0000000..70d0682 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_MP3_H_ + +#define SOFT_MP3_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tPVMP3DecoderExternal; + +namespace android { + +struct SoftMP3 : public SimpleSoftOMXComponent { + SoftMP3(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMP3(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumBuffers = 4, + kOutputBufferSize = 4608 * 2 + }; + + tPVMP3DecoderExternal *mConfig; + void *mDecoderBuf; + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + + int32_t mNumChannels; + int32_t mSamplingRate; + + bool mConfigured; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + void initDecoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMP3); +}; + +} // namespace android + +#endif // SOFT_MP3_H_ + + diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk index b769f0d..832b885 100644 --- a/media/libstagefright/codecs/on2/dec/Android.mk +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -2,15 +2,42 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - VPXDecoder.cpp + VPXDecoder.cpp \ LOCAL_MODULE := libstagefright_vpxdec LOCAL_C_INCLUDES := \ $(TOP)/frameworks/base/media/libstagefright/include \ - $(TOP)/frameworks/base/include/media/stagefright/openmax \ + frameworks/base/include/media/stagefright/openmax \ $(TOP)/external/libvpx \ $(TOP)/external/libvpx/vpx_codec \ $(TOP)/external/libvpx/vpx_ports include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVPX.cpp + +LOCAL_C_INCLUDES := \ + $(TOP)/external/libvpx \ + $(TOP)/external/libvpx/vpx_codec \ + $(TOP)/external/libvpx/vpx_ports \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_vpxdec \ + libvpx + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_vpxdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp new file mode 100644 index 0000000..e9ce719 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftVPX" +#include <utils/Log.h> + +#include "SoftVPX.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> + +#include "vpx/vpx_decoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8dx.h" + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftVPX::SoftVPX( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mCtx(NULL), + mWidth(320), + mHeight(240), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVPX::~SoftVPX() { + vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); + delete (vpx_codec_ctx_t *)mCtx; + mCtx = NULL; +} + +void SoftVPX::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX); + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.pNativeWindow = NULL; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW); + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; + def.format.video.pNativeWindow = NULL; + + def.nBufferSize = + (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; + + addPort(def); +} + +status_t SoftVPX::initDecoder() { + mCtx = new vpx_codec_ctx_t; + vpx_codec_err_t vpx_err; + if ((vpx_err = vpx_codec_dec_init( + (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) { + LOGE("on2 decoder failed to initialize. (%d)", vpx_err); + return UNKNOWN_ERROR; + } + + return OK; +} + +OMX_ERRORTYPE SoftVPX::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + } else { + CHECK_EQ(formatParams->nPortIndex, 1u); + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + formatParams->xFramerate = 0; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVPX::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "video_decoder.vpx", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftVPX::onQueueFilled(OMX_U32 portIndex) { + if (mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (vpx_codec_decode( + (vpx_codec_ctx_t *)mCtx, + inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen, + NULL, + 0)) { + LOGE("on2 decoder failed to decode frame."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + vpx_codec_iter_t iter = NULL; + vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + + if (img != NULL) { + CHECK_EQ(img->fmt, IMG_FMT_I420); + + int32_t width = img->d_w; + int32_t height = img->d_h; + + if (width != mWidth || height != mHeight) { + mWidth = width; + mHeight = height; + + updatePortDefinitions(); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + outHeader->nOffset = 0; + outHeader->nFilledLen = (width * height * 3) / 2; + outHeader->nFlags = 0; + outHeader->nTimeStamp = inHeader->nTimeStamp; + + const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; + uint8_t *dst = outHeader->pBuffer; + for (size_t i = 0; i < img->d_h; ++i) { + memcpy(dst, srcLine, img->d_w); + + srcLine += img->stride[PLANE_Y]; + dst += img->d_w; + } + + srcLine = (const uint8_t *)img->planes[PLANE_U]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_U]; + dst += img->d_w / 2; + } + + srcLine = (const uint8_t *)img->planes[PLANE_V]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_V]; + dst += img->d_w / 2; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } +} + +void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { +} + +void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +void SoftVPX::updatePortDefinitions() { + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def = &editPortInfo(1)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def->nBufferSize = + (def->format.video.nFrameWidth + * def->format.video.nFrameHeight * 3) / 2; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVPX(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h new file mode 100644 index 0000000..3e814a2 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_VPX_H_ + +#define SOFT_VPX_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftVPX : public SimpleSoftOMXComponent { + SoftVPX(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftVPX(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumBuffers = 4 + }; + + void *mCtx; + + int32_t mWidth; + int32_t mHeight; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + + void updatePortDefinitions(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftVPX); +}; + +} // namespace android + +#endif // SOFT_VPX_H_ diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk index 5c768c8..9251229 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.mk +++ b/media/libstagefright/codecs/vorbis/dec/Android.mk @@ -6,8 +6,33 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := \ frameworks/base/media/libstagefright/include \ - external/tremolo + external/tremolo \ LOCAL_MODULE := libstagefright_vorbisdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVorbis.cpp + +LOCAL_C_INCLUDES := \ + external/tremolo \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_vorbisdec + +LOCAL_SHARED_LIBRARIES := \ + libvorbisidec libstagefright libstagefright_omx \ + libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_vorbisdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp new file mode 100644 index 0000000..4091111 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftVorbis" +#include <utils/Log.h> + +#include "SoftVorbis.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> + +extern "C" { + #include <Tremolo/codec_internal.h> + + int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); +} + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftVorbis::SoftVorbis( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mInputBufferCount(0), + mState(NULL), + mVi(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumFramesLeftOnPage(-1), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVorbis::~SoftVorbis() { + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + if (mVi != NULL) { + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } +} + +void SoftVorbis::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast<char *>(MEDIA_MIMETYPE_AUDIO_VORBIS); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftVorbis::initDecoder() { + return OK; +} + +OMX_ERRORTYPE SoftVorbis::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioVorbis: + { + OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + vorbisParams->nBitRate = 0; + vorbisParams->nMinBitRate = 0; + vorbisParams->nMaxBitRate = 0; + vorbisParams->nAudioBandWidth = 0; + vorbisParams->nQuality = 3; + vorbisParams->bManaged = OMX_FALSE; + vorbisParams->bDownmix = OMX_FALSE; + + if (!isConfigured()) { + vorbisParams->nChannels = 1; + vorbisParams->nSampleRate = 44100; + } else { + vorbisParams->nChannels = mVi->channels; + vorbisParams->nSampleRate = mVi->rate; + vorbisParams->nBitRate = mVi->bitrate_nominal; + vorbisParams->nMinBitRate = mVi->bitrate_lower; + vorbisParams->nMaxBitRate = mVi->bitrate_upper; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mVi->channels; + pcmParams->nSamplingRate = mVi->rate; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVorbis::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.vorbis", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioVorbis: + { + const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (const OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftVorbis::isConfigured() const { + return mInputBufferCount >= 2; +} + +static void makeBitReader( + const void *data, size_t size, + ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) { + buf->data = (uint8_t *)data; + buf->size = size; + buf->refcount = 1; + buf->ptr.owner = NULL; + + ref->buffer = buf; + ref->begin = 0; + ref->length = size; + ref->next = NULL; + + oggpack_readinit(bits, ref); +} + +void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + if (mOutputPortSettingsChange != NONE) { + return; + } + + if (portIndex == 0 && mInputBufferCount < 2) { + BufferInfo *info = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *header = info->mHeader; + + const uint8_t *data = header->pBuffer + header->nOffset; + size_t size = header->nFilledLen; + + ogg_buffer buf; + ogg_reference ref; + oggpack_buffer bits; + + makeBitReader( + (const uint8_t *)data + 7, size - 7, + &buf, &ref, &bits); + + if (mInputBufferCount == 0) { + CHECK(mVi == NULL); + mVi = new vorbis_info; + vorbis_info_init(mVi); + + CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits)); + } else { + CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits)); + + CHECK(mState == NULL); + mState = new vorbis_dsp_state; + CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + + inQueue.erase(inQueue.begin()); + info->mOwnedByUs = false; + notifyEmptyBufferDone(header); + + ++mInputBufferCount; + + return; + } + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + int32_t numPageSamples; + CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); + memcpy(&numPageSamples, + inHeader->pBuffer + + inHeader->nOffset + inHeader->nFilledLen - 4, + sizeof(numPageSamples)); + + if (numPageSamples >= 0) { + mNumFramesLeftOnPage = numPageSamples; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + inHeader->nFilledLen -= sizeof(numPageSamples);; + + ogg_buffer buf; + buf.data = inHeader->pBuffer + inHeader->nOffset; + buf.size = inHeader->nFilledLen; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = buf.size; + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = ref.length; + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + int numFrames = 0; + + int err = vorbis_dsp_synthesis(mState, &pack, 1); + if (err != 0) { + LOGW("vorbis_dsp_synthesis returned %d", err); + } else { + numFrames = vorbis_dsp_pcmout( + mState, (int16_t *)outHeader->pBuffer, + kMaxNumSamplesPerBuffer); + + if (numFrames < 0) { + LOGE("vorbis_dsp_pcmout returned %d", numFrames); + numFrames = 0; + } + } + + if (mNumFramesLeftOnPage >= 0) { + if (numFrames > mNumFramesLeftOnPage) { + LOGV("discarding %d frames at end of page", + numFrames - mNumFramesLeftOnPage); + numFrames = mNumFramesLeftOnPage; + } + mNumFramesLeftOnPage -= numFrames; + } + + outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels; + outHeader->nOffset = 0; + outHeader->nFlags = 0; + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumFramesOutput * 1000000ll) / mVi->rate; + + mNumFramesOutput += numFrames; + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mState != NULL) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + + mNumFramesOutput = 0; + vorbis_dsp_restart(mState); + } +} + +void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVorbis(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h new file mode 100644 index 0000000..e252f55 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_VORBIS_H_ + +#define SOFT_VORBIS_H_ + +#include "SimpleSoftOMXComponent.h" + +struct vorbis_dsp_state; +struct vorbis_info; + +namespace android { + +struct SoftVorbis : public SimpleSoftOMXComponent { + SoftVorbis(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftVorbis(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerBuffer = 8192 * 2 + }; + + size_t mInputBufferCount; + + vorbis_dsp_state *mState; + vorbis_info *mVi; + + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + int32_t mNumFramesLeftOnPage; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis); +}; + +} // namespace android + +#endif // SOFT_VORBIS_H_ + diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk index 62ba40f..702a7b4 100644 --- a/media/libstagefright/colorconversion/Android.mk +++ b/media/libstagefright/colorconversion/Android.mk @@ -9,6 +9,10 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ $(TOP)/hardware/msm7k +ifneq ($(filter crespo crespo4g,$(TARGET_DEVICE)),) +LOCAL_CFLAGS += -DTHIS_IS_CRESPO=1 +endif + LOCAL_MODULE:= libstagefright_color_conversion include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index 3b92e5d..4b72a53 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -187,8 +187,7 @@ status_t ColorConverter::convertCbYCrY( status_t ColorConverter::convertYUV420Planar( const BitmapParams &src, const BitmapParams &dst) { - if (!((dst.mWidth & 1) == 0 - && (src.mCropLeft & 1) == 0 + if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; @@ -196,8 +195,8 @@ status_t ColorConverter::convertYUV420Planar( uint8_t *kAdjustedClip = initClip(); - uint32_t *dst_ptr = (uint32_t *)dst.mBits - + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2; + uint16_t *dst_ptr = (uint16_t *)dst.mBits + + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_y = (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft; @@ -260,7 +259,11 @@ status_t ColorConverter::convertYUV420Planar( | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[b2] >> 3); - dst_ptr[x / 2] = (rgb2 << 16) | rgb1; + if (x + 1 < src.cropWidth()) { + *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; + } else { + dst_ptr[x] = rgb1; + } } src_y += src.mWidth; @@ -270,7 +273,7 @@ status_t ColorConverter::convertYUV420Planar( src_v += src.mWidth / 2; } - dst_ptr += dst.mWidth / 2; + dst_ptr += dst.mWidth; } return OK; diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 31afc43..1828ac8 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -56,9 +56,23 @@ SoftwareRenderer::SoftwareRenderer( } int halFormat; + size_t bufWidth, bufHeight; + switch (mColorFormat) { +#ifndef THIS_IS_CRESPO + case OMX_COLOR_FormatYUV420Planar: + { + halFormat = HAL_PIXEL_FORMAT_YV12; + bufWidth = (mWidth + 1) & ~1; + bufHeight = (mHeight + 1) & ~1; + break; + } +#endif + default: halFormat = HAL_PIXEL_FORMAT_RGB_565; + bufWidth = mWidth; + bufHeight = mHeight; mConverter = new ColorConverter( mColorFormat, OMX_COLOR_Format16bitRGB565); @@ -75,15 +89,17 @@ SoftwareRenderer::SoftwareRenderer( native_window_set_usage( mNativeWindow.get(), GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN - | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP)); - - CHECK_EQ(0, native_window_set_buffer_count(mNativeWindow.get(), 2)); + | GRALLOC_USAGE_HW_TEXTURE +#ifndef THIS_IS_CRESPO + | GRALLOC_USAGE_EXTERNAL_DISP +#endif + )); // Width must be multiple of 32??? CHECK_EQ(0, native_window_set_buffers_geometry( mNativeWindow.get(), - mCropRight - mCropLeft + 1, - mCropBottom - mCropTop + 1, + bufWidth, + bufHeight, halFormat)); uint32_t transform; @@ -99,6 +115,14 @@ SoftwareRenderer::SoftwareRenderer( CHECK_EQ(0, native_window_set_buffers_transform( mNativeWindow.get(), transform)); } + + android_native_rect_t crop; + crop.left = mCropLeft; + crop.top = mCropTop; + crop.right = mCropRight + 1; + crop.bottom = mCropBottom + 1; + + CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop)); } SoftwareRenderer::~SoftwareRenderer() { @@ -106,9 +130,14 @@ SoftwareRenderer::~SoftwareRenderer() { mConverter = NULL; } +static int ALIGN(int x, int y) { + // y must be a power of 2. + return (x + y - 1) & ~(y - 1); +} + void SoftwareRenderer::render( const void *data, size_t size, void *platformPrivate) { - android_native_buffer_t *buf; + ANativeWindowBuffer *buf; int err; if ((err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf)) != 0) { LOGW("Surface::dequeueBuffer returned error %d", err); @@ -129,14 +158,40 @@ void SoftwareRenderer::render( mConverter->convert( data, mWidth, mHeight, - mCropLeft, mCropTop, mCropRight, mCropBottom, + 0, 0, mWidth - 1, mHeight - 1, dst, buf->stride, buf->height, - 0, 0, - mCropRight - mCropLeft, - mCropBottom - mCropTop); + 0, 0, mWidth - 1, mHeight - 1); } else { - TRESPASS(); + CHECK_EQ(mColorFormat, OMX_COLOR_FormatYUV420Planar); + + const uint8_t *src_y = (const uint8_t *)data; + const uint8_t *src_u = (const uint8_t *)data + mWidth * mHeight; + const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2); + + uint8_t *dst_y = (uint8_t *)dst; + size_t dst_y_size = buf->stride * buf->height; + size_t dst_c_stride = ALIGN(buf->stride / 2, 16); + size_t dst_c_size = dst_c_stride * buf->height / 2; + uint8_t *dst_v = dst_y + dst_y_size; + uint8_t *dst_u = dst_v + dst_c_size; + + for (int y = 0; y < mHeight; ++y) { + memcpy(dst_y, src_y, mWidth); + + src_y += mWidth; + dst_y += buf->stride; + } + + for (int y = 0; y < (mHeight + 1) / 2; ++y) { + memcpy(dst_u, src_u, (mWidth + 1) / 2); + memcpy(dst_v, src_v, (mWidth + 1) / 2); + + src_u += mWidth / 2; + src_v += mWidth / 2; + dst_u += dst_c_stride; + dst_v += dst_c_stride; + } } CHECK_EQ(0, mapper.unlock(buf->handle)); diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index b7087f8..a5b316d 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -33,18 +33,30 @@ ALooperRoster gLooperRoster; struct ALooper::LooperThread : public Thread { LooperThread(ALooper *looper, bool canCallJava) : Thread(canCallJava), - mLooper(looper) { + mLooper(looper), + mThreadId(NULL) { + } + + virtual status_t readyToRun() { + mThreadId = androidGetThreadId(); + + return Thread::readyToRun(); } virtual bool threadLoop() { return mLooper->loop(); } + bool isCurrentThread() const { + return mThreadId == androidGetThreadId(); + } + protected: virtual ~LooperThread() {} private: ALooper *mLooper; + android_thread_id_t mThreadId; DISALLOW_EVIL_CONSTRUCTORS(LooperThread); }; @@ -136,7 +148,9 @@ status_t ALooper::stop() { mQueueChangedCondition.signal(); - if (!runningLocally) { + if (!runningLocally && !thread->isCurrentThread()) { + // If not running locally and this thread _is_ the looper thread, + // the loop() function will return and never be called again. thread->requestExitAndWait(); } @@ -197,6 +211,11 @@ bool ALooper::loop() { gLooperRoster.deliverMessage(event.mMessage); + // NOTE: It's important to note that at this point our "ALooper" object + // may no longer exist (its final reference may have gone away while + // delivering the message). We have made sure, however, that loop() + // won't be called again. + return true; } diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index 4e07f6f..d5025a1 100644 --- a/media/libstagefright/foundation/Android.mk +++ b/media/libstagefright/foundation/Android.mk @@ -25,6 +25,6 @@ LOCAL_CFLAGS += -Wno-multichar LOCAL_MODULE:= libstagefright_foundation -LOCAL_PRELINK_MODULE:= false + include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index f0cd6a0..012d9ad 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -23,7 +23,7 @@ #include "LiveDataSource.h" #include "include/M3UParser.h" -#include "include/NuHTTPDataSource.h" +#include "include/HTTPBase.h" #include <cutils/properties.h> #include <media/stagefright/foundation/hexdump.h> @@ -45,9 +45,9 @@ LiveSession::LiveSession(uint32_t flags) : mFlags(flags), mDataSource(new LiveDataSource), mHTTPDataSource( - new NuHTTPDataSource( + HTTPBase::Create( (mFlags & kFlagIncognito) - ? NuHTTPDataSource::kFlagIncognito + ? HTTPBase::kFlagIncognito : 0)), mPrevBandwidthIndex(-1), mLastPlaylistFetchTimeUs(-1), @@ -67,9 +67,17 @@ sp<DataSource> LiveSession::getDataSource() { return mDataSource; } -void LiveSession::connect(const char *url) { +void LiveSession::connect( + const char *url, const KeyedVector<String8, String8> *headers) { sp<AMessage> msg = new AMessage(kWhatConnect, id()); msg->setString("url", url); + + if (headers != NULL) { + msg->setPointer( + "headers", + new KeyedVector<String8, String8>(*headers)); + } + msg->post(); } @@ -144,6 +152,16 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { AString url; CHECK(msg->findString("url", &url)); + KeyedVector<String8, String8> *headers = NULL; + if (!msg->findPointer("headers", (void **)&headers)) { + mExtraHeaders.clear(); + } else { + mExtraHeaders = *headers; + + delete headers; + headers = NULL; + } + if (!(mFlags & kFlagIncognito)) { LOGI("onConnect '%s'", url.c_str()); } else { @@ -210,7 +228,8 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) { } } - status_t err = mHTTPDataSource->connect(url); + status_t err = mHTTPDataSource->connect( + url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); if (err != OK) { return err; @@ -625,7 +644,12 @@ status_t LiveSession::decryptBuffer( } else { key = new ABuffer(16); - sp<NuHTTPDataSource> keySource = new NuHTTPDataSource; + sp<HTTPBase> keySource = + HTTPBase::Create( + (mFlags & kFlagIncognito) + ? HTTPBase::kFlagIncognito + : 0); + status_t err = keySource->connect(keyURI.c_str()); if (err == OK) { diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h new file mode 100644 index 0000000..375a94d --- /dev/null +++ b/media/libstagefright/include/AVIExtractor.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AVI_EXTRACTOR_H_ + +#define AVI_EXTRACTOR_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <utils/Vector.h> + +namespace android { + +struct AVIExtractor : public MediaExtractor { + AVIExtractor(const sp<DataSource> &dataSource); + + virtual size_t countTracks(); + + virtual sp<MediaSource> getTrack(size_t index); + + virtual sp<MetaData> getTrackMetaData( + size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~AVIExtractor(); + +private: + struct AVISource; + + struct SampleInfo { + uint32_t mOffset; + bool mIsKey; + }; + + struct Track { + sp<MetaData> mMeta; + Vector<SampleInfo> mSamples; + uint32_t mRate; + uint32_t mScale; + + enum Kind { + AUDIO, + VIDEO, + OTHER + + } mKind; + + size_t mNumSyncSamples; + size_t mThumbnailSampleSize; + ssize_t mThumbnailSampleIndex; + size_t mMaxSampleSize; + }; + + sp<DataSource> mDataSource; + status_t mInitCheck; + Vector<Track> mTracks; + + off64_t mMovieOffset; + bool mFoundIndex; + bool mOffsetsAreAbsolute; + + ssize_t parseChunk(off64_t offset, off64_t size, int depth = 0); + status_t parseStreamHeader(off64_t offset, size_t size); + status_t parseStreamFormat(off64_t offset, size_t size); + status_t parseIndex(off64_t offset, size_t size); + + status_t parseHeaders(); + + status_t getSampleInfo( + size_t trackIndex, size_t sampleIndex, + off64_t *offset, size_t *size, bool *isKey); + + status_t getSampleIndexAtTime( + size_t trackIndex, + int64_t timeUs, MediaSource::ReadOptions::SeekMode mode, + size_t *sampleIndex) const; + + status_t addMPEG4CodecSpecificData(size_t trackIndex); + + static bool IsCorrectChunkType( + ssize_t trackIndex, Track::Kind kind, uint32_t chunkType); + + DISALLOW_EVIL_CONSTRUCTORS(AVIExtractor); +}; + +class String8; +struct AMessage; + +bool SniffAVI( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + +} // namespace android + +#endif // AVI_EXTRACTOR_H_ diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 3a21f25..3c9a121 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -18,7 +18,7 @@ #define AWESOME_PLAYER_H_ -#include "NuHTTPDataSource.h" +#include "HTTPBase.h" #include "TimedEventQueue.h" #include <media/MediaPlayerInterface.h> @@ -44,6 +44,7 @@ struct ARTSPController; class DrmManagerClinet; class DecryptHandle; +class TimedTextPlayer; struct WVMExtractor; struct AwesomeRenderer : public RefBase { @@ -90,44 +91,52 @@ struct AwesomePlayer { status_t getDuration(int64_t *durationUs); status_t getPosition(int64_t *positionUs); + status_t setParameter(int key, const Parcel &request); + status_t getParameter(int key, Parcel *reply); + status_t seekTo(int64_t timeUs); // This is a mask of MediaExtractor::Flags. uint32_t flags() const; - void postAudioEOS(); + void postAudioEOS(int64_t delayUs = 0ll); void postAudioSeekComplete(); + status_t setTimedTextTrackIndex(int32_t index); + private: friend struct AwesomeEvent; friend struct PreviewPlayer; enum { - PLAYING = 1, - LOOPING = 2, - FIRST_FRAME = 4, - PREPARING = 8, - PREPARED = 16, - AT_EOS = 32, - PREPARE_CANCELLED = 64, - CACHE_UNDERRUN = 128, - AUDIO_AT_EOS = 256, - VIDEO_AT_EOS = 512, - AUTO_LOOPING = 1024, + PLAYING = 0x01, + LOOPING = 0x02, + FIRST_FRAME = 0x04, + PREPARING = 0x08, + PREPARED = 0x10, + AT_EOS = 0x20, + PREPARE_CANCELLED = 0x40, + CACHE_UNDERRUN = 0x80, + AUDIO_AT_EOS = 0x0100, + VIDEO_AT_EOS = 0x0200, + AUTO_LOOPING = 0x0400, // We are basically done preparing but are currently buffering // sufficient data to begin playback and finish the preparation phase // for good. - PREPARING_CONNECTED = 2048, + PREPARING_CONNECTED = 0x0800, // We're triggering a single video event to display the first frame // after the seekpoint. - SEEK_PREVIEW = 4096, + SEEK_PREVIEW = 0x1000, + + AUDIO_RUNNING = 0x2000, + AUDIOPLAYER_STARTED = 0x4000, - AUDIO_RUNNING = 8192, - AUDIOPLAYER_STARTED = 16384, + INCOGNITO = 0x8000, - INCOGNITO = 32768, + TEXT_RUNNING = 0x10000, + TEXTPLAYER_STARTED = 0x20000, }; mutable Mutex mLock; @@ -165,7 +174,6 @@ private: uint32_t mFlags; uint32_t mExtractorFlags; - uint32_t mSinceLastDropped; int64_t mTimeSourceDeltaUs; int64_t mVideoTimeUs; @@ -205,13 +213,13 @@ private: void postVideoEvent_l(int64_t delayUs = -1); void postBufferingEvent_l(); void postStreamDoneEvent_l(status_t status); - void postCheckAudioStatusEvent_l(); + void postCheckAudioStatusEvent_l(int64_t delayUs); void postVideoLagEvent_l(); status_t play_l(); MediaBuffer *mVideoBuffer; - sp<NuHTTPDataSource> mConnectingDataSource; + sp<HTTPBase> mConnectingDataSource; sp<NuCachedSource2> mCachedSource; sp<ALooper> mLooper; @@ -219,7 +227,10 @@ private: sp<ARTSPController> mConnectingRTSPController; DrmManagerClient *mDrmManagerClient; - DecryptHandle *mDecryptHandle; + sp<DecryptHandle> mDecryptHandle; + + int64_t mLastVideoTimeUs; + TimedTextPlayer *mTextPlayer; sp<WVMExtractor> mWVMExtractor; @@ -244,6 +255,8 @@ private: void setVideoSource(sp<MediaSource> source); status_t initVideoDecoder(uint32_t flags = 0); + void addTextSource(sp<MediaSource> source); + void onStreamDone(); void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0); @@ -271,6 +284,10 @@ private: void ensureCacheIsFetching_l(); status_t startAudioPlayer_l(); + void postAudioSeekComplete_l(); + + void shutdownVideoDecoder_l(); + void setNativeWindow_l(const sp<ANativeWindow> &native); bool isStreamingHTTP() const; diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h new file mode 100644 index 0000000..0e2927d --- /dev/null +++ b/media/libstagefright/include/ChromiumHTTPDataSource.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CHROME_HTTP_DATA_SOURCE_H_ + +#define CHROME_HTTP_DATA_SOURCE_H_ + +#include <media/stagefright/foundation/AString.h> +#include <utils/threads.h> + +#include "HTTPBase.h" + +namespace android { + +struct SfDelegate; + +struct ChromiumHTTPDataSource : public HTTPBase { + ChromiumHTTPDataSource(uint32_t flags = 0); + + virtual status_t connect( + const char *uri, + const KeyedVector<String8, String8> *headers = NULL, + off64_t offset = 0); + + virtual void disconnect(); + + virtual status_t initCheck() const; + + virtual ssize_t readAt(off64_t offset, void *data, size_t size); + virtual status_t getSize(off64_t *size); + virtual uint32_t flags(); + + virtual bool estimateBandwidth(int32_t *bandwidth_bps); + + virtual sp<DecryptHandle> DrmInitialization(); + + virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client); + + virtual String8 getUri(); + + virtual String8 getMIMEType() const; + +protected: + virtual ~ChromiumHTTPDataSource(); + +private: + friend struct SfDelegate; + + enum State { + DISCONNECTED, + CONNECTING, + CONNECTED, + READING, + DISCONNECTING + }; + + struct BandwidthEntry { + int64_t mDelayUs; + size_t mNumBytes; + }; + + const uint32_t mFlags; + + mutable Mutex mLock; + Condition mCondition; + + State mState; + + SfDelegate *mDelegate; + + AString mURI; + KeyedVector<String8, String8> mHeaders; + + off64_t mCurrentOffset; + + // Any connection error or the result of a read operation + // (for the lattter this is the number of bytes read, if successful). + ssize_t mIOResult; + + int64_t mContentSize; + + String8 mContentType; + + List<BandwidthEntry> mBandwidthHistory; + size_t mNumBandwidthHistoryItems; + int64_t mTotalTransferTimeUs; + size_t mTotalTransferBytes; + + sp<DecryptHandle> mDecryptHandle; + DrmManagerClient *mDrmManagerClient; + + void disconnect_l(); + + status_t connect_l( + const char *uri, + const KeyedVector<String8, String8> *headers, + off64_t offset); + + static void InitiateRead( + ChromiumHTTPDataSource *me, void *data, size_t size); + + void initiateRead(void *data, size_t size); + + void onConnectionEstablished( + int64_t contentSize, const char *contentType); + + void onConnectionFailed(status_t err); + void onReadCompleted(ssize_t size); + void onDisconnectComplete(); + + void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs); + + void clearDRMState_l(); + + DISALLOW_EVIL_CONSTRUCTORS(ChromiumHTTPDataSource); +}; + +} // namespace android + +#endif // CHROME_HTTP_DATA_SOURCE_H_ diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h index 9881cc1..b4e4afb 100644 --- a/media/libstagefright/include/DRMExtractor.h +++ b/media/libstagefright/include/DRMExtractor.h @@ -45,7 +45,7 @@ private: sp<DataSource> mDataSource; sp<MediaExtractor> mOriginalExtractor; - DecryptHandle* mDecryptHandle; + sp<DecryptHandle> mDecryptHandle; DrmManagerClient* mDrmManagerClient; DRMExtractor(const DRMExtractor &); diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h new file mode 100644 index 0000000..6cec390 --- /dev/null +++ b/media/libstagefright/include/HTTPBase.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HTTP_BASE_H_ + +#define HTTP_BASE_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/DataSource.h> + +namespace android { + +struct HTTPBase : public DataSource { + enum Flags { + // Don't log any URLs. + kFlagIncognito = 1 + }; + + HTTPBase(); + + virtual status_t connect( + const char *uri, + const KeyedVector<String8, String8> *headers = NULL, + off64_t offset = 0) = 0; + + virtual void disconnect() = 0; + + // Returns true if bandwidth could successfully be estimated, + // false otherwise. + virtual bool estimateBandwidth(int32_t *bandwidth_bps) = 0; + + static sp<HTTPBase> Create(uint32_t flags = 0); + +private: + DISALLOW_EVIL_CONSTRUCTORS(HTTPBase); +}; + +} // namespace android + +#endif // HTTP_BASE_H_ diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h index 3fe5d4e..99abe64 100644 --- a/media/libstagefright/include/LiveSession.h +++ b/media/libstagefright/include/LiveSession.h @@ -20,13 +20,15 @@ #include <media/stagefright/foundation/AHandler.h> +#include <utils/String8.h> + namespace android { struct ABuffer; struct DataSource; struct LiveDataSource; struct M3UParser; -struct NuHTTPDataSource; +struct HTTPBase; struct LiveSession : public AHandler { enum Flags { @@ -37,7 +39,10 @@ struct LiveSession : public AHandler { sp<DataSource> getDataSource(); - void connect(const char *url); + void connect( + const char *url, + const KeyedVector<String8, String8> *headers = NULL); + void disconnect(); // Blocks until seek is complete. @@ -75,9 +80,11 @@ private: sp<LiveDataSource> mDataSource; - sp<NuHTTPDataSource> mHTTPDataSource; + sp<HTTPBase> mHTTPDataSource; AString mMasterURL; + KeyedVector<String8, String8> mExtraHeaders; + Vector<BandwidthItem> mBandwidthItems; KeyedVector<AString, sp<ABuffer> > mAESKeyForURI; diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h index ef71b8f..cf1146b 100644 --- a/media/libstagefright/include/MP3Extractor.h +++ b/media/libstagefright/include/MP3Extractor.h @@ -42,7 +42,7 @@ public: static bool get_mp3_frame_size( uint32_t header, size_t *frame_size, int *out_sampling_rate = NULL, int *out_channels = NULL, - int *out_bitrate = NULL); + int *out_bitrate = NULL, int *out_num_samples = NULL); private: status_t mInitCheck; diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 04e8a6a..3bd4c7e 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -57,7 +57,7 @@ private: }; sp<DataSource> mDataSource; - bool mHaveMetadata; + status_t mInitCheck; bool mHasVideo; Track *mFirstTrack, *mLastTrack; @@ -90,6 +90,8 @@ private: status_t parseTrackHeader(off64_t data_offset, off64_t data_size); + Track *findTrackByMimePrefix(const char *mimePrefix); + MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); }; diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h index 022804c..ed3e265c 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -37,9 +37,12 @@ struct NuCachedSource2 : public DataSource { virtual status_t getSize(off64_t *size); virtual uint32_t flags(); - virtual DecryptHandle* DrmInitialization(); - virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); + virtual sp<DecryptHandle> DrmInitialization(); + virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client); virtual String8 getUri(); + + virtual String8 getMIMEType() const; + //////////////////////////////////////////////////////////////////////////// size_t cachedSize(); @@ -93,7 +96,9 @@ private: status_t seekInternal_l(off64_t offset); size_t approxDataRemaining_l(status_t *finalStatus); - void restartPrefetcherIfNecessary_l(bool ignoreLowWaterThreshold = false); + + void restartPrefetcherIfNecessary_l( + bool ignoreLowWaterThreshold = false, bool force = false); DISALLOW_EVIL_CONSTRUCTORS(NuCachedSource2); }; diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h index 0d68234..2ab1f19 100644 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ b/media/libstagefright/include/NuHTTPDataSource.h @@ -18,28 +18,24 @@ #define NU_HTTP_DATA_SOURCE_H_ -#include <media/stagefright/DataSource.h> #include <utils/List.h> #include <utils/String8.h> #include <utils/threads.h> #include "HTTPStream.h" +#include "include/HTTPBase.h" namespace android { -struct NuHTTPDataSource : public DataSource { - enum Flags { - // Don't log any URLs. - kFlagIncognito = 1 - }; +struct NuHTTPDataSource : public HTTPBase { NuHTTPDataSource(uint32_t flags = 0); - status_t connect( + virtual status_t connect( const char *uri, const KeyedVector<String8, String8> *headers = NULL, off64_t offset = 0); - void disconnect(); + virtual void disconnect(); virtual status_t initCheck() const; @@ -49,12 +45,14 @@ struct NuHTTPDataSource : public DataSource { // Returns true if bandwidth could successfully be estimated, // false otherwise. - bool estimateBandwidth(int32_t *bandwidth_bps); + virtual bool estimateBandwidth(int32_t *bandwidth_bps); - virtual DecryptHandle* DrmInitialization(); - virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); + virtual sp<DecryptHandle> DrmInitialization(); + virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client); virtual String8 getUri(); + virtual String8 getMIMEType() const; + protected: virtual ~NuHTTPDataSource(); @@ -89,6 +87,8 @@ private: bool mContentLengthValid; bool mHasChunkedTransferEncoding; + String8 mContentType; + // The number of data bytes in the current chunk before any subsequent // chunk header (or -1 if no more chunks). ssize_t mChunkDataBytesLeft; @@ -97,9 +97,8 @@ private: size_t mNumBandwidthHistoryItems; int64_t mTotalTransferTimeUs; size_t mTotalTransferBytes; - int64_t mPrevBandwidthMeasureTimeUs; - DecryptHandle *mDecryptHandle; + sp<DecryptHandle> mDecryptHandle; DrmManagerClient *mDrmManagerClient; status_t connect( diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index 2f95de9..f44e0a2 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -63,7 +63,7 @@ public: uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *decodingTime, + uint32_t *compositionTime, bool *isSyncSample = NULL); enum { @@ -107,6 +107,12 @@ private: uint32_t mTimeToSampleCount; uint32_t *mTimeToSample; + struct SampleTimeEntry { + uint32_t mSampleIndex; + uint32_t mCompositionTime; + }; + SampleTimeEntry *mSampleTimeEntries; + uint32_t *mCompositionTimeDeltaEntries; size_t mNumCompositionTimeDeltaEntries; @@ -130,6 +136,10 @@ private: uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const; + static int CompareIncreasingTime(const void *, const void *); + + void buildSampleEntriesTable(); + SampleTable(const SampleTable &); SampleTable &operator=(const SampleTable &); }; diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h new file mode 100644 index 0000000..2a29a7d --- /dev/null +++ b/media/libstagefright/include/SimpleSoftOMXComponent.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIMPLE_SOFT_OMX_COMPONENT_H_ + +#define SIMPLE_SOFT_OMX_COMPONENT_H_ + +#include "SoftOMXComponent.h" + +#include <media/stagefright/foundation/AHandlerReflector.h> +#include <utils/RefBase.h> +#include <utils/threads.h> +#include <utils/Vector.h> + +namespace android { + +struct ALooper; + +struct SimpleSoftOMXComponent : public SoftOMXComponent { + SimpleSoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual ~SimpleSoftOMXComponent(); + + void onMessageReceived(const sp<AMessage> &msg); + +protected: + struct BufferInfo { + OMX_BUFFERHEADERTYPE *mHeader; + bool mOwnedByUs; + }; + + struct PortInfo { + OMX_PARAM_PORTDEFINITIONTYPE mDef; + Vector<BufferInfo> mBuffers; + List<BufferInfo *> mQueue; + + enum { + NONE, + DISABLING, + ENABLING, + } mTransition; + }; + + void addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + List<BufferInfo *> &getPortQueue(OMX_U32 portIndex); + + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + + PortInfo *editPortInfo(OMX_U32 portIndex); + +private: + enum { + kWhatSendCommand, + kWhatEmptyThisBuffer, + kWhatFillThisBuffer, + }; + + Mutex mLock; + + sp<ALooper> mLooper; + sp<AHandlerReflector<SimpleSoftOMXComponent> > mHandler; + + OMX_STATETYPE mState; + OMX_STATETYPE mTargetState; + + Vector<PortInfo> mPorts; + + bool isSetParameterAllowed( + OMX_INDEXTYPE index, const OMX_PTR params) const; + + virtual OMX_ERRORTYPE sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data); + + virtual OMX_ERRORTYPE getParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + virtual OMX_ERRORTYPE allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + virtual OMX_ERRORTYPE freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state); + + void onSendCommand(OMX_COMMANDTYPE cmd, OMX_U32 param); + void onChangeState(OMX_STATETYPE state); + void onPortEnable(OMX_U32 portIndex, bool enable); + void onPortFlush(OMX_U32 portIndex, bool sendFlushComplete); + + void checkTransitions(); + + DISALLOW_EVIL_CONSTRUCTORS(SimpleSoftOMXComponent); +}; + +} // namespace android + +#endif // SIMPLE_SOFT_OMX_COMPONENT_H_ diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h new file mode 100644 index 0000000..053bc22 --- /dev/null +++ b/media/libstagefright/include/SoftOMXComponent.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_OMX_COMPONENT_H_ + +#define SOFT_OMX_COMPONENT_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AString.h> +#include <utils/RefBase.h> + +#include <OMX_Component.h> + +namespace android { + +struct SoftOMXComponent : public RefBase { + SoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE initCheck() const; + + void setLibHandle(void *libHandle); + void *libHandle() const; + +protected: + virtual ~SoftOMXComponent(); + + const char *name() const; + + void notify( + OMX_EVENTTYPE event, + OMX_U32 data1, OMX_U32 data2, OMX_PTR data); + + void notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header); + void notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header); + + virtual OMX_ERRORTYPE sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data); + + virtual OMX_ERRORTYPE getParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getConfig( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setConfig( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getExtensionIndex( + const char *name, OMX_INDEXTYPE *index); + + virtual OMX_ERRORTYPE useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + virtual OMX_ERRORTYPE allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + virtual OMX_ERRORTYPE freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state); + +private: + AString mName; + const OMX_CALLBACKTYPE *mCallbacks; + OMX_COMPONENTTYPE *mComponent; + + void *mLibHandle; + + static OMX_ERRORTYPE SendCommandWrapper( + OMX_HANDLETYPE component, + OMX_COMMANDTYPE cmd, + OMX_U32 param, + OMX_PTR data); + + static OMX_ERRORTYPE GetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE SetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE GetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE SetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE GetExtensionIndexWrapper( + OMX_HANDLETYPE component, + OMX_STRING name, + OMX_INDEXTYPE *index); + + static OMX_ERRORTYPE UseBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + static OMX_ERRORTYPE AllocateBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + static OMX_ERRORTYPE FreeBufferWrapper( + OMX_HANDLETYPE component, + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE EmptyThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE FillThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE GetStateWrapper( + OMX_HANDLETYPE component, + OMX_STATETYPE *state); + + DISALLOW_EVIL_CONSTRUCTORS(SoftOMXComponent); +}; + +} // namespace android + +#endif // SOFT_OMX_COMPONENT_H_ diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h index 07b1ec8..b02ed0e 100644 --- a/media/libstagefright/include/StagefrightMetadataRetriever.h +++ b/media/libstagefright/include/StagefrightMetadataRetriever.h @@ -32,7 +32,10 @@ struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface { StagefrightMetadataRetriever(); virtual ~StagefrightMetadataRetriever(); - virtual status_t setDataSource(const char *url); + virtual status_t setDataSource( + const char *url, + const KeyedVector<String8, String8> *headers); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option); diff --git a/media/libstagefright/include/TimedTextPlayer.h b/media/libstagefright/include/TimedTextPlayer.h new file mode 100644 index 0000000..ac41b4f --- /dev/null +++ b/media/libstagefright/include/TimedTextPlayer.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIMEDTEXT_PLAYER_H_ + +#define TIMEDTEXT_PLAYER_H_ + +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/foundation/ABase.h> + +#include "include/TimedEventQueue.h" + +namespace android { + +class MediaSource; +class AwesomePlayer; +class MediaBuffer; + +class TimedTextPlayer { +public: + TimedTextPlayer(AwesomePlayer *observer, + const wp<MediaPlayerBase> &listener, + TimedEventQueue *queue); + + virtual ~TimedTextPlayer(); + + // index: the index of the text track which will + // be turned on + status_t start(uint8_t index); + + void pause(); + + void resume(); + + status_t seekTo(int64_t time_us); + + void addTextSource(sp<MediaSource> source); + + status_t setTimedTextTrackIndex(int32_t index); + +private: + Mutex mLock; + + sp<MediaSource> mSource; + + bool mSeeking; + int64_t mSeekTimeUs; + + bool mStarted; + + sp<TimedEventQueue::Event> mTextEvent; + bool mTextEventPending; + + TimedEventQueue *mQueue; + + wp<MediaPlayerBase> mListener; + AwesomePlayer *mObserver; + + MediaBuffer *mTextBuffer; + Parcel mData; + + Vector<sp<MediaSource> > mTextTrackVector; + + void reset(); + + void onTextEvent(); + void postTextEvent(int64_t delayUs = -1); + void cancelTextEvent(); + + void notifyListener( + int msg, const void *data = NULL, size_t size = 0); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer); +}; + +} // namespace android + +#endif // TIMEDTEXT_PLAYER_H_ diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 642835a..e1b9991 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -60,7 +60,10 @@ struct DataSourceReader : public mkvparser::IMkvReader { virtual int Length(long long* total, long long* available) { off64_t size; if (mSource->getSize(&size) != OK) { - return -1; + *total = -1; + *available = (long long)((1ull << 63) - 1); + + return 0; } if (total) { @@ -84,7 +87,7 @@ private: //////////////////////////////////////////////////////////////////////////////// struct BlockIterator { - BlockIterator(mkvparser::Segment *segment, unsigned long trackNum); + BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum); bool eos() const; @@ -96,11 +99,14 @@ struct BlockIterator { int64_t blockTimeUs() const; private: - mkvparser::Segment *mSegment; + MatroskaExtractor *mExtractor; unsigned long mTrackNum; - mkvparser::Cluster *mCluster; + const mkvparser::Cluster *mCluster; const mkvparser::BlockEntry *mBlockEntry; + long mBlockEntryIndex; + + void advance_l(); BlockIterator(const BlockIterator &); BlockIterator &operator=(const BlockIterator &); @@ -150,7 +156,7 @@ MatroskaSource::MatroskaSource( : mExtractor(extractor), mTrackIndex(index), mType(OTHER), - mBlockIter(mExtractor->mSegment, + mBlockIter(mExtractor.get(), mExtractor->mTracks.itemAt(index).mTrackNum), mNALSizeLen(0) { sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta; @@ -199,11 +205,12 @@ sp<MetaData> MatroskaSource::getFormat() { //////////////////////////////////////////////////////////////////////////////// BlockIterator::BlockIterator( - mkvparser::Segment *segment, unsigned long trackNum) - : mSegment(segment), + MatroskaExtractor *extractor, unsigned long trackNum) + : mExtractor(extractor), mTrackNum(trackNum), mCluster(NULL), - mBlockEntry(NULL) { + mBlockEntry(NULL), + mBlockEntryIndex(0) { reset(); } @@ -212,45 +219,100 @@ bool BlockIterator::eos() const { } void BlockIterator::advance() { - while (!eos()) { - if (mBlockEntry != NULL) { - mBlockEntry = mCluster->GetNext(mBlockEntry); - } else if (mCluster != NULL) { - mCluster = mSegment->GetNext(mCluster); + Mutex::Autolock autoLock(mExtractor->mLock); + advance_l(); +} + +void BlockIterator::advance_l() { + for (;;) { + long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry); + LOGV("GetEntry returned %ld", res); + + long long pos; + long len; + if (res < 0) { + // Need to parse this cluster some more + + CHECK_EQ(res, mkvparser::E_BUFFER_NOT_FULL); + + res = mCluster->Parse(pos, len); + LOGV("Parse returned %ld", res); + + if (res < 0) { + // I/O error + + LOGE("Cluster::Parse returned result %ld", res); - if (eos()) { + mCluster = NULL; break; } - mBlockEntry = mCluster->GetFirst(); + continue; + } else if (res == 0) { + // We're done with this cluster + + const mkvparser::Cluster *nextCluster; + res = mExtractor->mSegment->ParseNext( + mCluster, nextCluster, pos, len); + LOGV("ParseNext returned %ld", res); + + if (res > 0) { + // EOF + + mCluster = NULL; + break; + } + + CHECK_EQ(res, 0); + CHECK(nextCluster != NULL); + CHECK(!nextCluster->EOS()); + + mCluster = nextCluster; + + res = mCluster->Parse(pos, len); + LOGV("Parse (2) returned %ld", res); + CHECK_GE(res, 0); + + mBlockEntryIndex = 0; + continue; } - if (mBlockEntry != NULL - && mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) { + CHECK(mBlockEntry != NULL); + CHECK(mBlockEntry->GetBlock() != NULL); + ++mBlockEntryIndex; + + if (mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) { break; } } } void BlockIterator::reset() { - mCluster = mSegment->GetFirst(); - mBlockEntry = mCluster->GetFirst(); + Mutex::Autolock autoLock(mExtractor->mLock); - while (!eos() && block()->GetTrackNumber() != mTrackNum) { - advance(); - } + mCluster = mExtractor->mSegment->GetFirst(); + mBlockEntry = NULL; + mBlockEntryIndex = 0; + + do { + advance_l(); + } while (!eos() && block()->GetTrackNumber() != mTrackNum); } void BlockIterator::seek(int64_t seekTimeUs) { - mCluster = mSegment->FindCluster(seekTimeUs * 1000ll); - mBlockEntry = mCluster != NULL ? mCluster->GetFirst() : NULL; + Mutex::Autolock autoLock(mExtractor->mLock); + + mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll); + mBlockEntry = NULL; + mBlockEntryIndex = 0; - while (!eos() && block()->GetTrackNumber() != mTrackNum) { - advance(); + do { + advance_l(); } + while (!eos() && block()->GetTrackNumber() != mTrackNum); while (!eos() && !mBlockEntry->GetBlock()->IsKey()) { - advance(); + advance_l(); } } @@ -291,16 +353,6 @@ void MatroskaSource::clearPendingFrames() { } } -#define BAIL(err) \ - do { \ - if (bigbuf) { \ - bigbuf->release(); \ - bigbuf = NULL; \ - } \ - \ - return err; \ - } while (0) - status_t MatroskaSource::readBlock() { CHECK(mPendingFrames.empty()); @@ -310,181 +362,39 @@ status_t MatroskaSource::readBlock() { const mkvparser::Block *block = mBlockIter.block(); - size_t size = block->GetSize(); int64_t timeUs = mBlockIter.blockTimeUs(); - int32_t isSync = block->IsKey(); - - MediaBuffer *bigbuf = new MediaBuffer(size); - - long res = block->Read( - mExtractor->mReader, (unsigned char *)bigbuf->data()); - - if (res != 0) { - bigbuf->release(); - bigbuf = NULL; - - return ERROR_END_OF_STREAM; - } - - mBlockIter.advance(); - bigbuf->meta_data()->setInt64(kKeyTime, timeUs); - bigbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); + for (int i = 0; i < block->GetFrameCount(); ++i) { + const mkvparser::Block::Frame &frame = block->GetFrame(i); - unsigned lacing = (block->Flags() >> 1) & 3; - - if (lacing == 0) { - mPendingFrames.push_back(bigbuf); - return OK; - } - - LOGV("lacing = %u, size = %d", lacing, size); - - const uint8_t *data = (const uint8_t *)bigbuf->data(); - // hexdump(data, size); - - if (size == 0) { - BAIL(ERROR_MALFORMED); - } - - unsigned numFrames = (unsigned)data[0] + 1; - ++data; - --size; - - Vector<uint64_t> frameSizes; - - switch (lacing) { - case 1: // Xiph - { - for (size_t i = 0; i < numFrames - 1; ++i) { - size_t frameSize = 0; - uint8_t byte; - do { - if (size == 0) { - BAIL(ERROR_MALFORMED); - } - byte = data[0]; - ++data; - --size; - - frameSize += byte; - } while (byte == 0xff); - - frameSizes.push(frameSize); - } - - break; - } - - case 2: // fixed-size - { - if ((size % numFrames) != 0) { - BAIL(ERROR_MALFORMED); - } - - size_t frameSize = size / numFrames; - for (size_t i = 0; i < numFrames - 1; ++i) { - frameSizes.push(frameSize); - } - - break; - } - - case 3: // EBML - { - uint64_t lastFrameSize = 0; - for (size_t i = 0; i < numFrames - 1; ++i) { - uint8_t byte; - - if (size == 0) { - BAIL(ERROR_MALFORMED); - } - byte = data[0]; - ++data; - --size; - - size_t numLeadingZeroes = clz(byte); - - uint64_t frameSize = byte & ~(0x80 >> numLeadingZeroes); - for (size_t j = 0; j < numLeadingZeroes; ++j) { - if (size == 0) { - BAIL(ERROR_MALFORMED); - } - - frameSize = frameSize << 8; - frameSize |= data[0]; - ++data; - --size; - } - - if (i == 0) { - frameSizes.push(frameSize); - } else { - size_t shift = - 7 - numLeadingZeroes + 8 * numLeadingZeroes; - - int64_t delta = - (int64_t)frameSize - (1ll << (shift - 1)) + 1; - - frameSize = lastFrameSize + delta; - - frameSizes.push(frameSize); - } - - lastFrameSize = frameSize; - } - break; - } - - default: - TRESPASS(); - } - -#if 0 - AString out; - for (size_t i = 0; i < frameSizes.size(); ++i) { - if (i > 0) { - out.append(", "); - } - out.append(StringPrintf("%llu", frameSizes.itemAt(i))); - } - LOGV("sizes = [%s]", out.c_str()); -#endif + MediaBuffer *mbuf = new MediaBuffer(frame.len); + mbuf->meta_data()->setInt64(kKeyTime, timeUs); + mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); - for (size_t i = 0; i < frameSizes.size(); ++i) { - uint64_t frameSize = frameSizes.itemAt(i); + long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data()); + if (n != 0) { + mPendingFrames.clear(); - if (size < frameSize) { - BAIL(ERROR_MALFORMED); + mBlockIter.advance(); + return ERROR_IO; } - MediaBuffer *mbuf = new MediaBuffer(frameSize); - mbuf->meta_data()->setInt64(kKeyTime, timeUs); - mbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); - memcpy(mbuf->data(), data, frameSize); mPendingFrames.push_back(mbuf); - - data += frameSize; - size -= frameSize; } - size_t offset = bigbuf->range_length() - size; - bigbuf->set_range(offset, size); - - mPendingFrames.push_back(bigbuf); + mBlockIter.advance(); return OK; } -#undef BAIL - status_t MatroskaSource::read( MediaBuffer **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { + if (options && options->getSeekTo(&seekTimeUs, &mode) + && !mExtractor->isLiveStreaming()) { clearPendingFrames(); mBlockIter.seek(seekTimeUs); } @@ -584,6 +494,13 @@ MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) mReader(new DataSourceReader(mDataSource)), mSegment(NULL), mExtractedThumbnails(false) { + off64_t size; + mIsLiveStreaming = + (mDataSource->flags() + & (DataSource::kWantsPrefetching + | DataSource::kIsCachingDataSource)) + && mDataSource->getSize(&size) != OK; + mkvparser::EBMLHeader ebmlHeader; long long pos; if (ebmlHeader.Parse(mReader, pos) < 0) { @@ -598,7 +515,16 @@ MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) return; } - ret = mSegment->Load(); + if (isLiveStreaming()) { + ret = mSegment->ParseHeaders(); + CHECK_EQ(ret, 0); + + long len; + ret = mSegment->LoadCluster(pos, len); + CHECK_EQ(ret, 0); + } else { + ret = mSegment->Load(); + } if (ret < 0) { delete mSegment; @@ -635,7 +561,8 @@ sp<MetaData> MatroskaExtractor::getTrackMetaData( return NULL; } - if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails) { + if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails + && !isLiveStreaming()) { findThumbnails(); mExtractedThumbnails = true; } @@ -643,6 +570,10 @@ sp<MetaData> MatroskaExtractor::getTrackMetaData( return mTracks.itemAt(index).mMeta; } +bool MatroskaExtractor::isLiveStreaming() const { + return mIsLiveStreaming; +} + static void addESDSFromAudioSpecificInfo( const sp<MetaData> &meta, const void *asi, size_t asiSize) { static const uint8_t kStaticESDS[] = { @@ -800,7 +731,7 @@ void MatroskaExtractor::findThumbnails() { continue; } - BlockIterator iter(mSegment, info->mTrackNum); + BlockIterator iter(this, info->mTrackNum); int32_t i = 0; int64_t thumbnailTimeUs = 0; size_t maxBlockSize = 0; @@ -808,7 +739,11 @@ void MatroskaExtractor::findThumbnails() { if (iter.block()->IsKey()) { ++i; - size_t blockSize = iter.block()->GetSize(); + size_t blockSize = 0; + for (int i = 0; i < iter.block()->GetFrameCount(); ++i) { + blockSize += iter.block()->GetFrame(i).len; + } + if (blockSize > maxBlockSize) { maxBlockSize = blockSize; thumbnailTimeUs = iter.blockTimeUs(); @@ -827,6 +762,15 @@ sp<MetaData> MatroskaExtractor::getMetaData() { return meta; } +uint32_t MatroskaExtractor::flags() const { + uint32_t x = CAN_PAUSE; + if (!isLiveStreaming()) { + x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK; + } + + return x; +} + bool SniffMatroska( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h index fa20b84..38ebd61 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.h +++ b/media/libstagefright/matroska/MatroskaExtractor.h @@ -20,6 +20,7 @@ #include <media/stagefright/MediaExtractor.h> #include <utils/Vector.h> +#include <utils/threads.h> namespace mkvparser { struct Segment; @@ -45,26 +46,34 @@ struct MatroskaExtractor : public MediaExtractor { virtual sp<MetaData> getMetaData(); + virtual uint32_t flags() const; + protected: virtual ~MatroskaExtractor(); private: friend struct MatroskaSource; + friend struct BlockIterator; struct TrackInfo { unsigned long mTrackNum; sp<MetaData> mMeta; }; + + Mutex mLock; Vector<TrackInfo> mTracks; sp<DataSource> mDataSource; DataSourceReader *mReader; mkvparser::Segment *mSegment; bool mExtractedThumbnails; + bool mIsLiveStreaming; void addTracks(); void findThumbnails(); + bool isLiveStreaming() const; + MatroskaExtractor(const MatroskaExtractor &); MatroskaExtractor &operator=(const MatroskaExtractor &); }; diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 6e069c8..08ad6f3 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,41 +1,28 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(BUILD_WITHOUT_PV),true) -# Set up the OpenCore variables. -include external/opencore/Config.mk -LOCAL_C_INCLUDES := $(PV_INCLUDES) -LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY) -endif - LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_SRC_FILES:= \ - OMX.cpp \ + OMX.cpp \ OMXComponentBase.cpp \ + OMXMaster.cpp \ OMXNodeInstance.cpp \ - OMXMaster.cpp - -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SRC_FILES += \ - OMXPVCodecsPlugin.cpp -else -LOCAL_CFLAGS += -DNO_OPENCORE -endif - -LOCAL_C_INCLUDES += $(TOP)/frameworks/base/include/media/stagefright/openmax - -LOCAL_SHARED_LIBRARIES := \ - libbinder \ - libmedia \ - libutils \ - libui \ - libcutils \ - -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SHARED_LIBRARIES += \ - libopencore_common -endif + SimpleSoftOMXComponent.cpp \ + SoftOMXComponent.cpp \ + SoftOMXPlugin.cpp \ + +LOCAL_C_INCLUDES += \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libmedia \ + libutils \ + libui \ + libcutils \ + libstagefright_foundation \ ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) LOCAL_LDLIBS += -lpthread -ldl @@ -49,5 +36,6 @@ LOCAL_MODULE:= libstagefright_omx include $(BUILD_SHARED_LIBRARY) -include $(call all-makefiles-under,$(LOCAL_PATH)) +################################################################################ +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp index 56b169a..545e6d4 100644 --- a/media/libstagefright/omx/OMXMaster.cpp +++ b/media/libstagefright/omx/OMXMaster.cpp @@ -20,23 +20,18 @@ #include "OMXMaster.h" +#include "SoftOMXPlugin.h" + #include <dlfcn.h> #include <media/stagefright/MediaDebug.h> -#ifndef NO_OPENCORE -#include "OMXPVCodecsPlugin.h" -#endif - namespace android { OMXMaster::OMXMaster() : mVendorLibHandle(NULL) { addVendorPlugin(); - -#ifndef NO_OPENCORE - addPlugin(new OMXPVCodecsPlugin); -#endif + addPlugin(new SoftOMXPlugin); } OMXMaster::~OMXMaster() { @@ -49,7 +44,11 @@ OMXMaster::~OMXMaster() { } void OMXMaster::addVendorPlugin() { - mVendorLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW); + addPlugin("libstagefrighthw.so"); +} + +void OMXMaster::addPlugin(const char *libname) { + mVendorLibHandle = dlopen(libname, RTLD_NOW); if (mVendorLibHandle == NULL) { return; diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h index 7ba8d18..feee1f9 100644 --- a/media/libstagefright/omx/OMXMaster.h +++ b/media/libstagefright/omx/OMXMaster.h @@ -58,6 +58,7 @@ private: void *mVendorLibHandle; void addVendorPlugin(); + void addPlugin(const char *libname); void addPlugin(OMXPluginBase *plugin); void clearPlugins(); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 6cbd599..8462988 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #define LOG_TAG "OMXNodeInstance" #include <utils/Log.h> @@ -234,6 +234,7 @@ status_t OMXNodeInstance::getParameter( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params); + return StatusFromOMXError(err); } diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp b/media/libstagefright/omx/OMXPVCodecsPlugin.cpp deleted file mode 100644 index d1f5be3..0000000 --- a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "OMXPVCodecsPlugin.h" - -#include "pv_omxcore.h" - -#include <media/stagefright/MediaDebug.h> - -namespace android { - -OMXPVCodecsPlugin::OMXPVCodecsPlugin() { - OMX_MasterInit(); -} - -OMXPVCodecsPlugin::~OMXPVCodecsPlugin() { - OMX_MasterDeinit(); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::makeComponentInstance( - const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component) { - return OMX_MasterGetHandle( - reinterpret_cast<OMX_HANDLETYPE *>(component), - const_cast<char *>(name), - appData, - const_cast<OMX_CALLBACKTYPE *>(callbacks)); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::destroyComponentInstance( - OMX_COMPONENTTYPE *component) { - return OMX_MasterFreeHandle(component); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::enumerateComponents( - OMX_STRING name, - size_t size, - OMX_U32 index) { - return OMX_MasterComponentNameEnum(name, size, index); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::getRolesOfComponent( - const char *name, - Vector<String8> *roles) { - roles->clear(); - - OMX_U32 numRoles; - OMX_ERRORTYPE err = - OMX_MasterGetRolesOfComponent( - const_cast<char *>(name), - &numRoles, - NULL); - - if (err != OMX_ErrorNone) { - return err; - } - - if (numRoles > 0) { - OMX_U8 **array = new OMX_U8 *[numRoles]; - for (OMX_U32 i = 0; i < numRoles; ++i) { - array[i] = new OMX_U8[OMX_MAX_STRINGNAME_SIZE]; - } - - OMX_U32 numRoles2; - err = OMX_MasterGetRolesOfComponent( - const_cast<char *>(name), &numRoles2, array); - - CHECK_EQ(err, OMX_ErrorNone); - CHECK_EQ(numRoles, numRoles2); - - for (OMX_U32 i = 0; i < numRoles; ++i) { - String8 s((const char *)array[i]); - roles->push(s); - - delete[] array[i]; - array[i] = NULL; - } - - delete[] array; - array = NULL; - } - - return OMX_ErrorNone; -} - -} // namespace android diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp new file mode 100644 index 0000000..179b2a0 --- /dev/null +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SimpleSoftOMXComponent" +#include <utils/Log.h> + +#include "include/SimpleSoftOMXComponent.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> + +namespace android { + +SimpleSoftOMXComponent::SimpleSoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftOMXComponent(name, callbacks, appData, component), + mLooper(new ALooper), + mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)), + mState(OMX_StateLoaded), + mTargetState(OMX_StateLoaded) { + mLooper->setName(name); + mLooper->registerHandler(mHandler); + + mLooper->start( + false, // runOnCallingThread + false, // canCallJava + PRIORITY_AUDIO); +} + +SimpleSoftOMXComponent::~SimpleSoftOMXComponent() { + mLooper->unregisterHandler(mHandler->id()); + mLooper->stop(); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { + CHECK(data == NULL); + + sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id()); + msg->setInt32("cmd", cmd); + msg->setInt32("param", param); + msg->post(); + + return OMX_ErrorNone; +} + +bool SimpleSoftOMXComponent::isSetParameterAllowed( + OMX_INDEXTYPE index, const OMX_PTR params) const { + if (mState == OMX_StateLoaded) { + return true; + } + + OMX_U32 portIndex; + + switch (index) { + case OMX_IndexParamPortDefinition: + { + portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex; + break; + } + + case OMX_IndexParamAudioPcm: + { + portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex; + break; + } + + case OMX_IndexParamAudioAac: + { + portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex; + break; + } + + default: + return false; + } + + CHECK(portIndex < mPorts.size()); + + return !mPorts.itemAt(portIndex).mDef.bEnabled; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + Mutex::Autolock autoLock(mLock); + return internalGetParameter(index, params); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + Mutex::Autolock autoLock(mLock); + + CHECK(isSetParameterAllowed(index, params)); + + return internalSetParameter(index, params); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (defParams->nPortIndex >= mPorts.size() + || defParams->nSize + != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUndefined; + } + + const PortInfo *port = + &mPorts.itemAt(defParams->nPortIndex); + + memcpy(defParams, &port->mDef, sizeof(port->mDef)); + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (defParams->nPortIndex >= mPorts.size() + || defParams->nSize + != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUndefined; + } + + PortInfo *port = + &mPorts.editItemAt(defParams->nPortIndex); + + if (defParams->nBufferSize != port->mDef.nBufferSize) { + CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize); + port->mDef.nBufferSize = defParams->nBufferSize; + } + + if (defParams->nBufferCountActual + != port->mDef.nBufferCountActual) { + CHECK_GE(defParams->nBufferCountActual, + port->mDef.nBufferCountMin); + + port->mDef.nBufferCountActual = defParams->nBufferCountActual; + } + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( + OMX_BUFFERHEADERTYPE **header, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + Mutex::Autolock autoLock(mLock); + CHECK_LT(portIndex, mPorts.size()); + + *header = new OMX_BUFFERHEADERTYPE; + (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE); + (*header)->nVersion.s.nVersionMajor = 1; + (*header)->nVersion.s.nVersionMinor = 0; + (*header)->nVersion.s.nRevision = 0; + (*header)->nVersion.s.nStep = 0; + (*header)->pBuffer = ptr; + (*header)->nAllocLen = size; + (*header)->nFilledLen = 0; + (*header)->nOffset = 0; + (*header)->pAppPrivate = appPrivate; + (*header)->pPlatformPrivate = NULL; + (*header)->pInputPortPrivate = NULL; + (*header)->pOutputPortPrivate = NULL; + (*header)->hMarkTargetComponent = NULL; + (*header)->pMarkData = NULL; + (*header)->nTickCount = 0; + (*header)->nTimeStamp = 0; + (*header)->nFlags = 0; + (*header)->nOutputPortIndex = portIndex; + (*header)->nInputPortIndex = portIndex; + + PortInfo *port = &mPorts.editItemAt(portIndex); + + CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE); + + CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual); + + port->mBuffers.push(); + + BufferInfo *buffer = + &port->mBuffers.editItemAt(port->mBuffers.size() - 1); + + buffer->mHeader = *header; + buffer->mOwnedByUs = false; + + if (port->mBuffers.size() == port->mDef.nBufferCountActual) { + port->mDef.bPopulated = OMX_TRUE; + checkTransitions(); + } + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer( + OMX_BUFFERHEADERTYPE **header, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + OMX_U8 *ptr = new OMX_U8[size]; + + OMX_ERRORTYPE err = + useBuffer(header, portIndex, appPrivate, size, ptr); + + if (err != OMX_ErrorNone) { + delete[] ptr; + ptr = NULL; + + return err; + } + + CHECK((*header)->pPlatformPrivate == NULL); + (*header)->pPlatformPrivate = ptr; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *header) { + Mutex::Autolock autoLock(mLock); + + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + +#if 0 // XXX + CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded) + || port->mDef.bEnabled == OMX_FALSE); +#endif + + bool found = false; + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (buffer->mHeader == header) { + CHECK(!buffer->mOwnedByUs); + + if (header->pPlatformPrivate != NULL) { + // This buffer's data was allocated by us. + CHECK(header->pPlatformPrivate == header->pBuffer); + + delete[] header->pBuffer; + header->pBuffer = NULL; + } + + delete header; + header = NULL; + + port->mBuffers.removeAt(i); + port->mDef.bPopulated = OMX_FALSE; + + checkTransitions(); + + found = true; + break; + } + } + + CHECK(found); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id()); + msg->setPointer("header", buffer); + msg->post(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler->id()); + msg->setPointer("header", buffer); + msg->post(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) { + Mutex::Autolock autoLock(mLock); + + *state = mState; + + return OMX_ErrorNone; +} + +void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) { + Mutex::Autolock autoLock(mLock); + + switch (msg->what()) { + case kWhatSendCommand: + { + int32_t cmd, param; + CHECK(msg->findInt32("cmd", &cmd)); + CHECK(msg->findInt32("param", ¶m)); + + onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param); + break; + } + + case kWhatEmptyThisBuffer: + case kWhatFillThisBuffer: + { + OMX_BUFFERHEADERTYPE *header; + CHECK(msg->findPointer("header", (void **)&header)); + + CHECK(mState == OMX_StateExecuting && mTargetState == mState); + + bool found = false; + for (size_t i = 0; i < mPorts.size(); ++i) { + PortInfo *port = &mPorts.editItemAt(i); + + for (size_t j = 0; j < port->mBuffers.size(); ++j) { + BufferInfo *buffer = &port->mBuffers.editItemAt(j); + + if (buffer->mHeader == header) { + CHECK(!buffer->mOwnedByUs); + + buffer->mOwnedByUs = true; + + CHECK((msg->what() == kWhatEmptyThisBuffer + && port->mDef.eDir == OMX_DirInput) + || (port->mDef.eDir == OMX_DirOutput)); + + port->mQueue.push_back(buffer); + onQueueFilled(i); + + found = true; + break; + } + } + } + + CHECK(found); + break; + } + + default: + TRESPASS(); + break; + } +} + +void SimpleSoftOMXComponent::onSendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param) { + switch (cmd) { + case OMX_CommandStateSet: + { + onChangeState((OMX_STATETYPE)param); + break; + } + + case OMX_CommandPortEnable: + case OMX_CommandPortDisable: + { + onPortEnable(param, cmd == OMX_CommandPortEnable); + break; + } + + case OMX_CommandFlush: + { + onPortFlush(param, true /* sendFlushComplete */); + break; + } + + default: + TRESPASS(); + break; + } +} + +void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { + // We shouldn't be in a state transition already. + CHECK_EQ((int)mState, (int)mTargetState); + + switch (mState) { + case OMX_StateLoaded: + CHECK_EQ((int)state, (int)OMX_StateIdle); + break; + case OMX_StateIdle: + CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting); + break; + case OMX_StateExecuting: + { + CHECK_EQ((int)state, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + onPortFlush(i, false /* sendFlushComplete */); + } + + mState = OMX_StateIdle; + notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL); + break; + } + + default: + TRESPASS(); + } + + mTargetState = state; + + checkTransitions(); +} + +void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); + CHECK(port->mDef.bEnabled == !enable); + + if (!enable) { + port->mDef.bEnabled = OMX_FALSE; + port->mTransition = PortInfo::DISABLING; + + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (buffer->mOwnedByUs) { + buffer->mOwnedByUs = false; + + if (port->mDef.eDir == OMX_DirInput) { + notifyEmptyBufferDone(buffer->mHeader); + } else { + CHECK_EQ(port->mDef.eDir, OMX_DirOutput); + notifyFillBufferDone(buffer->mHeader); + } + } + } + + port->mQueue.clear(); + } else { + port->mTransition = PortInfo::ENABLING; + } + + checkTransitions(); +} + +void SimpleSoftOMXComponent::onPortFlush( + OMX_U32 portIndex, bool sendFlushComplete) { + if (portIndex == OMX_ALL) { + for (size_t i = 0; i < mPorts.size(); ++i) { + onPortFlush(i, sendFlushComplete); + } + + if (sendFlushComplete) { + notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL); + } + + return; + } + + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); + + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (!buffer->mOwnedByUs) { + continue; + } + + buffer->mHeader->nFilledLen = 0; + buffer->mHeader->nOffset = 0; + buffer->mHeader->nFlags = 0; + + buffer->mOwnedByUs = false; + + if (port->mDef.eDir == OMX_DirInput) { + notifyEmptyBufferDone(buffer->mHeader); + } else { + CHECK_EQ(port->mDef.eDir, OMX_DirOutput); + + notifyFillBufferDone(buffer->mHeader); + } + } + + port->mQueue.clear(); + + if (sendFlushComplete) { + notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL); + + onPortFlushCompleted(portIndex); + } +} + +void SimpleSoftOMXComponent::checkTransitions() { + if (mState != mTargetState) { + bool transitionComplete = true; + + if (mState == OMX_StateLoaded) { + CHECK_EQ((int)mTargetState, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + const PortInfo &port = mPorts.itemAt(i); + if (port.mDef.bEnabled == OMX_FALSE) { + continue; + } + + if (port.mDef.bPopulated == OMX_FALSE) { + transitionComplete = false; + break; + } + } + } else if (mTargetState == OMX_StateLoaded) { + CHECK_EQ((int)mState, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + const PortInfo &port = mPorts.itemAt(i); + if (port.mDef.bEnabled == OMX_FALSE) { + continue; + } + + size_t n = port.mBuffers.size(); + + if (n > 0) { + CHECK_LE(n, port.mDef.nBufferCountActual); + + if (n == port.mDef.nBufferCountActual) { + CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE); + } else { + CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE); + } + + transitionComplete = false; + break; + } + } + } + + if (transitionComplete) { + mState = mTargetState; + + notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); + } + } + + for (size_t i = 0; i < mPorts.size(); ++i) { + PortInfo *port = &mPorts.editItemAt(i); + + if (port->mTransition == PortInfo::DISABLING) { + if (port->mBuffers.empty()) { + LOGV("Port %d now disabled.", i); + + port->mTransition = PortInfo::NONE; + notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL); + + onPortEnableCompleted(i, false /* enabled */); + } + } else if (port->mTransition == PortInfo::ENABLING) { + if (port->mDef.bPopulated == OMX_TRUE) { + LOGV("Port %d now enabled.", i); + + port->mTransition = PortInfo::NONE; + port->mDef.bEnabled = OMX_TRUE; + notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL); + + onPortEnableCompleted(i, true /* enabled */); + } + } + } +} + +void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) { + CHECK_EQ(def.nPortIndex, mPorts.size()); + + mPorts.push(); + PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1); + info->mDef = def; + info->mTransition = PortInfo::NONE; +} + +void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) { +} + +void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) { +} + +void SimpleSoftOMXComponent::onPortEnableCompleted( + OMX_U32 portIndex, bool enabled) { +} + +List<SimpleSoftOMXComponent::BufferInfo *> & +SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) { + CHECK_LT(portIndex, mPorts.size()); + return mPorts.editItemAt(portIndex).mQueue; +} + +SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo( + OMX_U32 portIndex) { + CHECK_LT(portIndex, mPorts.size()); + return &mPorts.editItemAt(portIndex); +} + +} // namespace android diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp new file mode 100644 index 0000000..b1c34dc --- /dev/null +++ b/media/libstagefright/omx/SoftOMXComponent.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftOMXComponent" +#include <utils/Log.h> + +#include "include/SoftOMXComponent.h" + +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +SoftOMXComponent::SoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : mName(name), + mCallbacks(callbacks), + mComponent(new OMX_COMPONENTTYPE), + mLibHandle(NULL) { + mComponent->nSize = sizeof(*mComponent); + mComponent->nVersion.s.nVersionMajor = 1; + mComponent->nVersion.s.nVersionMinor = 0; + mComponent->nVersion.s.nRevision = 0; + mComponent->nVersion.s.nStep = 0; + mComponent->pComponentPrivate = this; + mComponent->pApplicationPrivate = appData; + + mComponent->GetComponentVersion = NULL; + mComponent->SendCommand = SendCommandWrapper; + mComponent->GetParameter = GetParameterWrapper; + mComponent->SetParameter = SetParameterWrapper; + mComponent->GetConfig = GetConfigWrapper; + mComponent->SetConfig = SetConfigWrapper; + mComponent->GetExtensionIndex = GetExtensionIndexWrapper; + mComponent->GetState = GetStateWrapper; + mComponent->ComponentTunnelRequest = NULL; + mComponent->UseBuffer = UseBufferWrapper; + mComponent->AllocateBuffer = AllocateBufferWrapper; + mComponent->FreeBuffer = FreeBufferWrapper; + mComponent->EmptyThisBuffer = EmptyThisBufferWrapper; + mComponent->FillThisBuffer = FillThisBufferWrapper; + mComponent->SetCallbacks = NULL; + mComponent->ComponentDeInit = NULL; + mComponent->UseEGLImage = NULL; + mComponent->ComponentRoleEnum = NULL; + + *component = mComponent; +} + +SoftOMXComponent::~SoftOMXComponent() { + delete mComponent; + mComponent = NULL; +} + +void SoftOMXComponent::setLibHandle(void *libHandle) { + CHECK(libHandle != NULL); + mLibHandle = libHandle; +} + +void *SoftOMXComponent::libHandle() const { + return mLibHandle; +} + +OMX_ERRORTYPE SoftOMXComponent::initCheck() const { + return OMX_ErrorNone; +} + +const char *SoftOMXComponent::name() const { + return mName.c_str(); +} + +void SoftOMXComponent::notify( + OMX_EVENTTYPE event, + OMX_U32 data1, OMX_U32 data2, OMX_PTR data) { + (*mCallbacks->EventHandler)( + mComponent, + mComponent->pApplicationPrivate, + event, + data1, + data2, + data); +} + +void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) { + (*mCallbacks->EmptyBufferDone)( + mComponent, mComponent->pApplicationPrivate, header); +} + +void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) { + (*mCallbacks->FillBufferDone)( + mComponent, mComponent->pApplicationPrivate, header); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SendCommandWrapper( + OMX_HANDLETYPE component, + OMX_COMMANDTYPE cmd, + OMX_U32 param, + OMX_PTR data) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->sendCommand(cmd, param, data); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getParameter(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->setParameter(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getConfig(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->setConfig(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetExtensionIndexWrapper( + OMX_HANDLETYPE component, + OMX_STRING name, + OMX_INDEXTYPE *index) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getExtensionIndex(name, index); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::UseBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->useBuffer(buffer, portIndex, appPrivate, size, ptr); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::AllocateBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->allocateBuffer(buffer, portIndex, appPrivate, size); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::FreeBufferWrapper( + OMX_HANDLETYPE component, + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->freeBuffer(portIndex, buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::EmptyThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->emptyThisBuffer(buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::FillThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->fillThisBuffer(buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetStateWrapper( + OMX_HANDLETYPE component, + OMX_STATETYPE *state) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getState(state); +} + +//////////////////////////////////////////////////////////////////////////////// + +OMX_ERRORTYPE SoftOMXComponent::sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::setParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getConfig( + OMX_INDEXTYPE index, OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::setConfig( + OMX_INDEXTYPE index, const OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE *state) { + return OMX_ErrorUndefined; +} + +} // namespace android diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp new file mode 100644 index 0000000..6bd6624 --- /dev/null +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftOMXPlugin" +#include <utils/Log.h> + +#include "SoftOMXPlugin.h" +#include "include/SoftOMXComponent.h" + +#include <media/stagefright/foundation/AString.h> + +#include <dlfcn.h> + +namespace android { + +static const struct { + const char *mName; + const char *mLibNameSuffix; + const char *mRole; + +} kComponents[] = { + { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" }, + { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" }, + { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" }, + { "OMX.google.avc.decoder", "avcdec", "video_decoder.avc" }, + { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, + { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, + { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, + { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" }, + { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" }, + { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, + { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, +}; + +static const size_t kNumComponents = + sizeof(kComponents) / sizeof(kComponents[0]); + +SoftOMXPlugin::SoftOMXPlugin() { +} + +OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + LOGV("makeComponentInstance '%s'", name); + + for (size_t i = 0; i < kNumComponents; ++i) { + if (strcmp(name, kComponents[i].mName)) { + continue; + } + + AString libName = "libstagefright_soft_"; + libName.append(kComponents[i].mLibNameSuffix); + libName.append(".so"); + + void *libHandle = dlopen(libName.c_str(), RTLD_NOW); + + if (libHandle == NULL) { + LOGE("unable to dlopen %s", libName.c_str()); + + return OMX_ErrorComponentNotFound; + } + + typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( + const char *, const OMX_CALLBACKTYPE *, + OMX_PTR, OMX_COMPONENTTYPE **); + + CreateSoftOMXComponentFunc createSoftOMXComponent = + (CreateSoftOMXComponentFunc)dlsym( + libHandle, + "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" + "PvPP17OMX_COMPONENTTYPE"); + + if (createSoftOMXComponent == NULL) { + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorComponentNotFound; + } + + sp<SoftOMXComponent> codec = + (*createSoftOMXComponent)(name, callbacks, appData, component); + + if (codec == NULL) { + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorInsufficientResources; + } + + OMX_ERRORTYPE err = codec->initCheck(); + if (err != OMX_ErrorNone) { + dlclose(libHandle); + libHandle = NULL; + + return err; + } + + codec->incStrong(this); + codec->setLibHandle(libHandle); + + return OMX_ErrorNone; + } + + return OMX_ErrorInvalidComponentName; +} + +OMX_ERRORTYPE SoftOMXPlugin::destroyComponentInstance( + OMX_COMPONENTTYPE *component) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + void *libHandle = me->libHandle(); + + me->decStrong(this); + me = NULL; + + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents( + OMX_STRING name, + size_t size, + OMX_U32 index) { + if (index >= kNumComponents) { + return OMX_ErrorNoMore; + } + + strcpy(name, kComponents[index].mName); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftOMXPlugin::getRolesOfComponent( + const char *name, + Vector<String8> *roles) { + for (size_t i = 0; i < kNumComponents; ++i) { + if (strcmp(name, kComponents[i].mName)) { + continue; + } + + roles->clear(); + roles->push(String8(kComponents[i].mRole)); + + return OMX_ErrorNone; + } + + return OMX_ErrorInvalidComponentName; +} + +} // namespace android diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.h b/media/libstagefright/omx/SoftOMXPlugin.h index c133232..f93c323 100644 --- a/media/libstagefright/omx/OMXPVCodecsPlugin.h +++ b/media/libstagefright/omx/SoftOMXPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,17 @@ * limitations under the License. */ -#ifndef OMX_PV_CODECS_PLUGIN_H_ +#ifndef SOFT_OMX_PLUGIN_H_ -#define OMX_PV_CODECS_PLUGIN_H_ +#define SOFT_OMX_PLUGIN_H_ +#include <media/stagefright/foundation/ABase.h> #include <media/stagefright/OMXPluginBase.h> namespace android { -struct OMXPVCodecsPlugin : public OMXPluginBase { - OMXPVCodecsPlugin(); - virtual ~OMXPVCodecsPlugin(); +struct SoftOMXPlugin : public OMXPluginBase { + SoftOMXPlugin(); virtual OMX_ERRORTYPE makeComponentInstance( const char *name, @@ -45,10 +45,9 @@ struct OMXPVCodecsPlugin : public OMXPluginBase { Vector<String8> *roles); private: - OMXPVCodecsPlugin(const OMXPVCodecsPlugin &); - OMXPVCodecsPlugin &operator=(const OMXPVCodecsPlugin &); + DISALLOW_EVIL_CONSTRUCTORS(SoftOMXPlugin); }; } // namespace android -#endif // OMX_PV_CODECS_PLUGIN_H_ +#endif // SOFT_OMX_PLUGIN_H_ diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 4f28855..a404f1f 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -29,6 +29,7 @@ #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> @@ -454,6 +455,7 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "video_decoder.avc", "video/avc" }, { "video_decoder.mpeg4", "video/mp4v-es" }, { "video_decoder.h263", "video/3gpp" }, + { "video_decoder.vpx", "video/x-vnd.on2.vp8" }, // we appear to use this as a synonym to amrnb. { "audio_decoder.amr", "audio/3gpp" }, @@ -461,7 +463,10 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "audio_decoder.amrnb", "audio/3gpp" }, { "audio_decoder.amrwb", "audio/amr-wb" }, { "audio_decoder.aac", "audio/mp4a-latm" }, - { "audio_decoder.mp3", "audio/mpeg" } + { "audio_decoder.mp3", "audio/mpeg" }, + { "audio_decoder.vorbis", "audio/vorbis" }, + { "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW }, + { "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW }, }; for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) { @@ -487,14 +492,20 @@ static const char *GetURLForMime(const char *mime) { { "audio/3gpp", "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" }, { "audio/amr-wb", - "file:///sdcard/media_api/music_perf/AMRWB/" - "NIN_AMR-WB_15.85kbps_16kbps.amr" }, + "file:///sdcard/media_api/music/" + "AI_AMR-WB_12.65kbps(13kbps)_16khz_mono_NMC.awb" }, { "audio/mp4a-latm", - "file:///sdcard/media_api/music_perf/AAC/" - "WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4" }, + "file:///sdcard/media_api/video/H264_AAC.3gp" }, { "audio/mpeg", - "file:///sdcard/media_api/music_perf/MP3/" - "WC_256kbps_44.1khz_mono_CBR_DPA.mp3" } + "file:///sdcard/media_api/music/MP3CBR.mp3" }, + { "audio/vorbis", + "file:///sdcard/media_api/metaDataTestMedias/OGG/" + "When You Say Nothing At All.ogg" }, + { "video/x-vnd.on2.vp8", + "file:///sdcard/media_api/webm/big-buck-bunny_trailer.webm" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, + "file:///sdcard/M1F1-mulaw-AFsp.wav" }, }; for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) { @@ -626,8 +637,10 @@ status_t Harness::testSeek( requestedSeekTimeUs, requestedSeekTimeUs / 1E6); } - MediaBuffer *buffer; - options.setSeekTo(requestedSeekTimeUs); + MediaBuffer *buffer = NULL; + options.setSeekTo( + requestedSeekTimeUs, MediaSource::ReadOptions::SEEK_NEXT_SYNC); + if (seekSource->read(&buffer, &options) != OK) { CHECK_EQ(buffer, NULL); actualSeekTimeUs = -1; @@ -746,6 +759,10 @@ status_t Harness::testAll() { const IOMX::ComponentInfo &info = *it; const char *componentName = info.mName.string(); + if (strncmp(componentName, "OMX.google.", 11)) { + continue; + } + for (List<String8>::const_iterator role_it = info.mRoles.begin(); role_it != info.mRoles.end(); ++role_it) { const char *componentRole = (*role_it).string(); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 0740515..c4e0cdc 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -20,6 +20,8 @@ #include "ARTSPConnection.h" +#include <cutils/properties.h> + #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -44,6 +46,7 @@ ARTSPConnection::ARTSPConnection() mConnectionID(0), mNextCSeq(0), mReceiveResponseEventPending(false) { + MakeUserAgent(&mUserAgent); } ARTSPConnection::~ARTSPConnection() { @@ -378,6 +381,7 @@ void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) { reply->setString("original-request", request.c_str(), request.size()); addAuthentication(&request); + addUserAgent(&request); // Find the boundary between headers and the body. ssize_t i = request.find("\r\n\r\n"); @@ -979,4 +983,27 @@ void ARTSPConnection::addAuthentication(AString *request) { #endif } +// static +void ARTSPConnection::MakeUserAgent(AString *userAgent) { + userAgent->clear(); + userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android "); + +#if (PROPERTY_VALUE_MAX < 8) +#error "PROPERTY_VALUE_MAX must be at least 8" +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.release", value, "Unknown"); + userAgent->append(value); + userAgent->append(")\r\n"); +} + +void ARTSPConnection::addUserAgent(AString *request) const { + // Find the boundary between headers and the body. + ssize_t i = request->find("\r\n\r\n"); + CHECK_GE(i, 0); + + request->insert(mUserAgent, i + 2); +} + } // namespace android diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index 0fecf3c..ac2e3ae 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -87,6 +87,8 @@ private: sp<AMessage> mObserveBinaryMessage; + AString mUserAgent; + void onConnect(const sp<AMessage> &msg); void onDisconnect(const sp<AMessage> &msg); void onCompleteConnection(const sp<AMessage> &msg); @@ -106,6 +108,8 @@ private: bool parseAuthMethod(const sp<ARTSPResponse> &response); void addAuthentication(AString *request); + void addUserAgent(AString *request) const; + status_t findPendingRequest( const sp<ARTSPResponse> &response, ssize_t *index) const; @@ -114,6 +118,8 @@ private: static bool ParseSingleUnsignedLong( const char *from, unsigned long *x); + static void MakeUserAgent(AString *userAgent); + DISALLOW_EVIL_CONSTRUCTORS(ARTSPConnection); }; diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk index 7697e3c..a4253f6 100644 --- a/media/libstagefright/yuv/Android.mk +++ b/media/libstagefright/yuv/Android.mk @@ -10,6 +10,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libstagefright_yuv -LOCAL_PRELINK_MODULE := false + include $(BUILD_SHARED_LIBRARY) diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java index 34025f6..90be041 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java @@ -744,7 +744,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media assertNotNull(msg + ": could not create AudioEffect", effect); byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET); byte[] value = new byte[2]; - if (effect.getParameter(param, value) == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(param, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -777,8 +777,8 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media 0); assertNotNull(msg + ": could not create AudioEffect", effect); int[] value = new int[1]; - if (effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError( + effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -811,8 +811,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media 0); assertNotNull(msg + ": could not create AudioEffect", effect); short[] value = new short[1]; - if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -845,8 +844,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media 0); assertNotNull(msg + ": could not create AudioEffect", effect); byte[] value = new byte[2]; - if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -881,8 +879,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media int[] param = new int[1]; int[] value = new int[1]; param[0] = EnvironmentalReverb.PARAM_DECAY_TIME; - if (effect.getParameter(param, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(param, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -917,8 +914,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media int[] param = new int[1]; short[] value = new short[1]; param[0] = Equalizer.PARAM_CURRENT_PRESET; - if (effect.getParameter(param, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(param, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -953,8 +949,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media int[] param = new int[1]; byte[] value = new byte[2]; param[0] = Equalizer.PARAM_CURRENT_PRESET; - if (effect.getParameter(param, value) - == AudioEffect.SUCCESS) { + if (!AudioEffect.isError(effect.getParameter(param, value))) { result = true; } } catch (IllegalArgumentException e) { @@ -1082,8 +1077,8 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media short[] value = new short[1]; status = effect2.getParameter(Equalizer.PARAM_CURRENT_PRESET, value); - assertEquals(msg + ": Effect2 getParameter failed", - AudioEffect.SUCCESS, status); + assertFalse(msg + ": Effect2 getParameter failed", + AudioEffect.isError(status)); assertEquals(msg + ": Effect1 changed parameter", (short)0, value[0]); @@ -1278,7 +1273,7 @@ public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<Media byte[] cmd = new byte[0]; byte[] reply = new byte[4]; int status = effect.command(3, cmd, reply); - assertEquals(msg + ": command failed", AudioEffect.SUCCESS, status); + assertFalse(msg + ": command failed", AudioEffect.isError(status)); assertTrue(msg + ": effect not enabled", effect.getEnabled()); result = true; } catch (IllegalStateException e) { diff --git a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java index df1b375..33db2dd 100644 --- a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java +++ b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java @@ -28,7 +28,6 @@ import android.media.AudioSystem; import android.media.AudioManager; import android.media.SoundPool; import android.media.SoundPool.OnLoadCompleteListener; -import android.util.Config; import android.util.Log; import java.util.HashMap; import java.lang.Math; diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk index 10367cf..c655ae6 100644 --- a/media/tests/players/Android.mk +++ b/media/tests/players/Android.mk @@ -24,6 +24,6 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= invoke_mock_media_player LOCAL_MODULE_TAGS := tests eng -LOCAL_PRELINK_MODULE:= false + include $(BUILD_SHARED_LIBRARY) diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp index d571106..ed3051b 100644 --- a/media/tests/players/invoke_mock_media_player.cpp +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -84,6 +84,9 @@ class Player: public MediaPlayerBase virtual status_t setLooping(int loop) {return OK;} virtual player_type playerType() {return TEST_PLAYER;} virtual status_t invoke(const Parcel& request, Parcel *reply); + virtual status_t setParameter(int key, const Parcel &request) {return OK;} + virtual status_t getParameter(int key, Parcel *reply) {return OK;} + private: // Take a request, copy it to the reply. |
