diff options
Diffstat (limited to 'media')
85 files changed, 7268 insertions, 1601 deletions
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 5f7f36f..02d6b66 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -68,7 +68,21 @@ public class MediaMetadataRetriever * @param headers the headers to be sent together with the request for the data * @throws IllegalArgumentException If the URI is invalid. */ - public native void setDataSource(String uri, Map<String, String> headers) + 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; /** diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 3f799cf..84f588e 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -799,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 diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index edefb22..961ee1e 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -347,6 +347,40 @@ public class MediaRecorder } /** + * 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. + * + * {@hide} + */ + public void setGeoData(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 = "Unsupported latitude: " + latitude; + throw new IllegalArgumentException(msg); + } + if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { + String msg = "Unsupported longitude: " + longitude; + 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 +801,71 @@ 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; + /** + * {@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 +910,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 +930,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/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java index 47bb8c9..3272fed 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -22,7 +22,10 @@ import android.os.ParcelFileDescriptor; import android.util.Log; /** - * This class represents an MTP or PTP device connected on the USB host bus. + * This class represents an MTP or PTP device connected on the USB host bus. An application can + * instantiate an object of this type, by referencing an attached {@link + * android.hardware.usb.UsbDevice} and then use methods in this class to get information about the + * device and objects stored on it, as well as open the connection and transfer data. */ public final class MtpDevice { diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 33146e7..21a18ca 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -29,12 +29,15 @@ public class MtpStorage { private final String mPath; private final String mDescription; private final long mReserveSpace; + private final boolean mRemovable; - public MtpStorage(int id, String path, String description, long reserveSpace) { + public MtpStorage(int id, String path, String description, + long reserveSpace, boolean removable) { mStorageId = id; mPath = path; mDescription = description; mReserveSpace = reserveSpace; + mRemovable = removable; } /** @@ -86,4 +89,12 @@ public class MtpStorage { return mReserveSpace; } + /** + * Returns true if the storage is removable. + * + * @return is removable + */ + public final boolean isRemovable() { + return mRemovable; + } } diff --git a/media/java/android/mtp/package.html b/media/java/android/mtp/package.html new file mode 100644 index 0000000..6bd9229 --- /dev/null +++ b/media/java/android/mtp/package.html @@ -0,0 +1,8 @@ +<html> +<body> +<p>Provides APIs that let you interact directly with connected cameras and other devices, using the +PTP (Picture Transfer Protocol) subset of the MTP (Media Transfer Protocol) specification. Your +application can receive notifications when devices are attached and removed, manage files and +storage on those devices, and transfer files and metadata from the devices.</p> +</body> +</html>
\ No newline at end of file diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 77cedd5..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 \ diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 2ba9b3f..73aea2a 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -28,6 +28,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "android_media_Utils.h" using namespace android; @@ -79,7 +80,9 @@ static void setRetriever(JNIEnv* env, jobject thiz, int retriever) static void android_media_MediaMetadataRetriever_setDataSourceAndHeaders( - JNIEnv *env, jobject thiz, jstring path, jobject headers) { + JNIEnv *env, jobject thiz, jstring path, + jobjectArray keys, jobjectArray values) { + LOGV("setDataSource"); MediaMetadataRetriever* retriever = getRetriever(env, thiz); if (retriever == 0) { @@ -103,7 +106,6 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( } String8 pathStr(tmp); - env->ReleaseStringUTFChars(path, tmp); tmp = NULL; @@ -114,119 +116,26 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( 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"); - if (mapClass == NULL) { - return; - } - - jmethodID entrySet = - env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); - if (entrySet == NULL) { - return; - } - - jobject set = env->CallObjectMethod(headers, entrySet); - if (set == NULL) { - return; - } - - // Obtain an iterator over the Set - jclass setClass = env->FindClass("java/util/Set"); - if (setClass == NULL) { - return; - } - - jmethodID iterator = - env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); - if (iterator == NULL) { - return; - } - - jobject iter = env->CallObjectMethod(set, iterator); - if (iter == NULL) { - return; - } - - // Get the Iterator method IDs - jclass iteratorClass = env->FindClass("java/util/Iterator"); - if (iteratorClass == NULL) { - return; - } - jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); - if (hasNext == NULL) { - return; - } - - jmethodID next = - env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); - if (next == NULL) { - return; - } - - // Get the Entry class method IDs - jclass entryClass = env->FindClass("java/util/Map$Entry"); - if (entryClass == NULL) { - return; - } - - jmethodID getKey = - env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); - if (getKey == NULL) { - return; - } - - jmethodID getValue = - env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); - if (getValue == NULL) { - return; - } - - // 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 - return; - } - - const char* valueStr = env->GetStringUTFChars(value, NULL); - if (!valueStr) { // Out of memory - env->ReleaseStringUTFChars(key, keyStr); - return; - } - - headersVector.add(String8(keyStr), String8(valueStr)); - - env->DeleteLocalRef(entry); - env->ReleaseStringUTFChars(key, keyStr); - env->DeleteLocalRef(key); - env->ReleaseStringUTFChars(value, valueStr); - env->DeleteLocalRef(value); - } - + if (!ConvertKeyValueArraysToKeyedVector( + env, keys, values, &headersVector)) { + return; } - process_media_retriever_call( env, retriever->setDataSource( - pathStr.string(), headers ? &headersVector : NULL), + 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); + env, thiz, path, NULL, NULL); } static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) @@ -539,7 +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/util/Map;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders}, + + { + "_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 8763ebd..b03aa38 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -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> @@ -186,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); @@ -207,112 +211,18 @@ android_media_MediaPlayer_setDataSourceAndHeaders( env->ReleaseStringUTFChars(path, tmp); tmp = NULL; - // headers is a Map<String, String>. - // We build a similar KeyedVector out of it. + // We build a KeyedVector out of the key and val arrays KeyedVector<String8, String8> headersVector; - if (headers) { - // Get the Map's entry Set. - jclass mapClass = env->FindClass("java/util/Map"); - if (mapClass == NULL) { - return; - } - - jmethodID entrySet = - env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); - if (entrySet == NULL) { - return; - } - - jobject set = env->CallObjectMethod(headers, entrySet); - if (set == NULL) { - return; - } - - // Obtain an iterator over the Set - jclass setClass = env->FindClass("java/util/Set"); - if (setClass == NULL) { - return; - } - - jmethodID iterator = - env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); - if (iterator == NULL) { - return; - } - - jobject iter = env->CallObjectMethod(set, iterator); - if (iter == NULL) { - return; - } - - // Get the Iterator method IDs - jclass iteratorClass = env->FindClass("java/util/Iterator"); - if (iteratorClass == NULL) { - return; - } - - jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); - if (hasNext == NULL) { - return; - } - - jmethodID next = - env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); - if (next == NULL) { - return; - } - - // Get the Entry class method IDs - jclass entryClass = env->FindClass("java/util/Map$Entry"); - if (entryClass == NULL) { - return; - } - - jmethodID getKey = - env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); - if (getKey == NULL) { - return; - } - - jmethodID getValue = - env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); - if (getValue == NULL) { - return; - } - - // 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 - return; - } - - const char* valueStr = env->GetStringUTFChars(value, NULL); - if (!valueStr) { // Out of memory - env->ReleaseStringUTFChars(key, keyStr); - return; - } - - headersVector.add(String8(keyStr), String8(valueStr)); - - env->DeleteLocalRef(entry); - env->ReleaseStringUTFChars(key, keyStr); - env->DeleteLocalRef(key); - env->ReleaseStringUTFChars(value, valueStr); - env->DeleteLocalRef(value); - } - + if (!ConvertKeyValueArraysToKeyedVector( + env, keys, values, &headersVector)) { + return; } LOGV("setDataSource: path %s", pathStr); status_t opStatus = mp->setDataSource( pathStr, - headers ? &headersVector : NULL); + headersVector.size() > 0? &headersVector : NULL); process_media_player_call( env, thiz, opStatus, "java/io/IOException", @@ -322,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 @@ -851,7 +761,13 @@ android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobj 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}, diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 66cb71c..2f7d7ee 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -33,7 +33,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" -#include <hardware/audio.h> +#include <system/audio.h> // ---------------------------------------------------------------------------- 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_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 84c2c7e..4d84cb7 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -47,6 +47,7 @@ static jfieldID field_MtpStorage_storageId; static jfieldID field_MtpStorage_path; static jfieldID field_MtpStorage_description; static jfieldID field_MtpStorage_reserveSpace; +static jfieldID field_MtpStorage_removable; static Mutex sMutex; @@ -245,12 +246,13 @@ android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path); jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description); jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace); + jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable); const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr != NULL) { const char *descriptionStr = env->GetStringUTFChars(description, NULL); if (descriptionStr != NULL) { - MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace); + MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace, removable); thread->addStorage(storage); env->ReleaseStringUTFChars(path, pathStr); env->ReleaseStringUTFChars(description, descriptionStr); @@ -322,7 +324,12 @@ int register_android_mtp_MtpServer(JNIEnv *env) } field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J"); if (field_MtpStorage_reserveSpace == NULL) { - LOGE("Can't find MtpStorage.mStorageId"); + LOGE("Can't find MtpStorage.mReserveSpace"); + return -1; + } + field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z"); + if (field_MtpStorage_removable == NULL) { + LOGE("Can't find MtpStorage.mRemovable"); return -1; } clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz); diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index bfbf04a..3ea13a6 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -27,7 +27,7 @@ #include <media/AudioTrack.h> #include <media/mediaplayer.h> -#include <hardware/audio.h> +#include <system/audio.h> #include "SoundPool.h" #include "SoundPoolThread.h" diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 8438714..446e3df 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -37,7 +37,7 @@ #include <utils/Timers.h> #include <utils/Atomic.h> -#include <hardware/audio.h> +#include <system/audio.h> #include <cutils/bitops.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index e08a55b..8a180d8 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -23,7 +23,7 @@ #include <media/IAudioPolicyService.h> #include <math.h> -#include <hardware/audio.h> +#include <system/audio.h> // ---------------------------------------------------------------------------- // the sim build doesn't have gettid diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 2673df9..7520ed9 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -39,7 +39,7 @@ #include <cutils/bitops.h> -#include <hardware/audio.h> +#include <system/audio.h> #include <hardware/audio_policy.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 88a9ae0..9fbcee0 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -25,7 +25,7 @@ #include <media/IAudioPolicyService.h> -#include <hardware/audio.h> +#include <system/audio.h> namespace android { 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/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 28e07ff..7b7ba74 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -37,7 +37,7 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> -#include <hardware/audio.h> +#include <system/audio.h> namespace android { diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 3b2cf10..d51c946 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -53,7 +53,7 @@ #include <media/AudioTrack.h> #include <media/MemoryLeakTrackUtil.h> -#include <hardware/audio.h> +#include <system/audio.h> #include <private/android_filesystem_config.h> diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 6c4071f..8bab471 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -30,7 +30,7 @@ #include <media/MediaPlayerInterface.h> #include <media/Metadata.h> -#include <hardware/audio.h> +#include <system/audio.h> namespace android { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 5a47384..29cc019 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -35,7 +35,7 @@ #include <media/AudioTrack.h> -#include <hardware/audio.h> +#include <system/audio.h> #include "MediaRecorderClient.h" #include "MediaPlayerService.h" diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 37a3db3..589c625 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -30,7 +30,7 @@ #include <sys/types.h> #include <sys/stat.h> -#include <hardware/audio.h> +#include <system/audio.h> #include "MidiFile.h" diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index c1687c4..978571c 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -47,7 +47,7 @@ #include <ctype.h> #include <unistd.h> -#include <hardware/audio.h> +#include <system/audio.h> #include "ARTPWriter.h" @@ -593,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()); @@ -621,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)) { @@ -1412,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); } @@ -1419,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; @@ -1625,6 +1665,7 @@ status_t StagefrightRecorder::reset() { mAudioTimeScale = -1; mVideoTimeScale = -1; mCameraId = 0; + mStartTimeOffsetMs = -1; mVideoEncoderProfile = -1; mVideoEncoderLevel = -1; mMaxFileDurationUs = 0; @@ -1638,6 +1679,8 @@ status_t StagefrightRecorder::reset() { mIsMetaDataStoredInVideoBuffers = false; mEncoderProfiles = MediaProfiles::getInstance(); mRotationDegrees = 0; + mLatitudex10000 = -3600000; + mLongitudex10000 = -3600000; mOutputFd = -1; mOutputFdAux = -1; @@ -1711,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 c3e6d65..aa67aa7 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -22,7 +22,7 @@ #include <camera/CameraParameters.h> #include <utils/String8.h> -#include <hardware/audio.h> +#include <system/audio.h> namespace android { @@ -97,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; @@ -160,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/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/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/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index e52f6d1..4189354 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -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/Android.mk b/media/libstagefright/Android.mk index 10ce00c..f731dfb 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -3,6 +3,8 @@ include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk +BUILD_WITH_SOFTWARE_DECODERS := false + LOCAL_SRC_FILES:= \ ACodec.cpp \ AACExtractor.cpp \ @@ -45,7 +47,6 @@ LOCAL_SRC_FILES:= \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ - ThreadedSource.cpp \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ @@ -82,28 +83,39 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ -180,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 69f9c23..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!!!"); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index cccd0b7..b45e5d3 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -831,6 +831,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( @@ -840,6 +842,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(); } @@ -933,7 +942,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. @@ -1957,6 +1968,10 @@ void AwesomePlayer::postAudioEOS(int64_t delayUs) { void AwesomePlayer::postAudioSeekComplete() { Mutex::Autolock autoLock(mLock); + postAudioSeekComplete_l(); +} + +void AwesomePlayer::postAudioSeekComplete_l() { postCheckAudioStatusEvent_l(0 /* delayUs */); } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index e13b67e..9dc83a6 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -45,6 +45,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 @@ -63,12 +64,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: @@ -84,6 +86,7 @@ private: 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 @@ -156,6 +159,8 @@ private: bool mReachedEOS; int64_t mStartTimestampUs; + int64_t mStartTimeRealUs; + int64_t mFirstSampleTimeRealUs; int64_t mPreviousTrackTimeUs; int64_t mTrackEveryTimeDurationUs; @@ -187,12 +192,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); @@ -214,6 +216,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 &); @@ -230,7 +257,11 @@ 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 | O_RDWR); if (mFd >= 0) { @@ -249,7 +280,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() { @@ -450,20 +485,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; @@ -643,43 +665,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) { @@ -709,9 +700,76 @@ 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(const 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(); +} + +void MPEG4Writer::sendSessionSummary() { + 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; @@ -874,6 +932,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); } @@ -1156,18 +1285,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); } @@ -1175,8 +1299,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(); @@ -1205,6 +1327,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; } } @@ -1248,6 +1377,8 @@ status_t MPEG4Writer::startWriterThread() { it != mTracks.end(); ++it) { ChunkInfo info; info.mTrack = *it; + info.mPrevChunkTimestampUs = 0; + info.mMaxInterChunkDurUs = 0; mChunkInfos.push_back(info); } @@ -1271,6 +1402,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)) { @@ -1295,10 +1427,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); @@ -1329,6 +1466,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); @@ -1766,6 +1904,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; @@ -1903,7 +2042,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; @@ -1998,7 +2138,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()) { @@ -2017,7 +2157,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 != @@ -2040,7 +2184,7 @@ status_t MPEG4Writer::Track::threadEntry() { mOwner->trackProgressStatus(mTrackId, -1, err); // Last chunk - if (mOwner->numTracks() == 1) { + if (!hasMultipleTracks) { addOneStscTableEntry(1, mNumSamples); } else if (!mChunkSamples.empty()) { addOneStscTableEntry(++nChunks, mChunkSamples.size()); @@ -2067,6 +2211,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) { @@ -2079,6 +2226,51 @@ status_t MPEG4Writer::Track::threadEntry() { return err; } +void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { + 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); + } + + 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 && @@ -2169,388 +2361,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 +} + +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 +} - mOwner->writeInt32(width << 16); // 32-bit fixed-point value - mOwner->writeInt32(height << 16); // 32-bit fixed-point value +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/MetaData.cpp b/media/libstagefright/MetaData.cpp index 884f3b4..aac94a6 100644 --- a/media/libstagefright/MetaData.cpp +++ b/media/libstagefright/MetaData.cpp @@ -83,7 +83,7 @@ bool MetaData::setRect( return setData(key, TYPE_RECT, &r, sizeof(r)); } -bool MetaData::findCString(uint32_t key, const char **value) { +bool MetaData::findCString(uint32_t key, const char **value) const { uint32_t type; const void *data; size_t size; @@ -96,7 +96,7 @@ bool MetaData::findCString(uint32_t key, const char **value) { return true; } -bool MetaData::findInt32(uint32_t key, int32_t *value) { +bool MetaData::findInt32(uint32_t key, int32_t *value) const { uint32_t type; const void *data; size_t size; @@ -111,7 +111,7 @@ bool MetaData::findInt32(uint32_t key, int32_t *value) { return true; } -bool MetaData::findInt64(uint32_t key, int64_t *value) { +bool MetaData::findInt64(uint32_t key, int64_t *value) const { uint32_t type; const void *data; size_t size; @@ -126,7 +126,7 @@ bool MetaData::findInt64(uint32_t key, int64_t *value) { return true; } -bool MetaData::findFloat(uint32_t key, float *value) { +bool MetaData::findFloat(uint32_t key, float *value) const { uint32_t type; const void *data; size_t size; @@ -141,7 +141,7 @@ bool MetaData::findFloat(uint32_t key, float *value) { return true; } -bool MetaData::findPointer(uint32_t key, void **value) { +bool MetaData::findPointer(uint32_t key, void **value) const { uint32_t type; const void *data; size_t size; @@ -159,7 +159,7 @@ bool MetaData::findPointer(uint32_t key, void **value) { bool MetaData::findRect( uint32_t key, int32_t *left, int32_t *top, - int32_t *right, int32_t *bottom) { + int32_t *right, int32_t *bottom) const { uint32_t type; const void *data; size_t size; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 78d13b2..cd5bdfb 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; @@ -2899,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; @@ -3224,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); @@ -4013,6 +4077,13 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { numChannels, params.nChannels); } + if (sampleRate != params.nSamplingRate) { + LOGW("Codec outputs at different sampling rate than " + "what the input stream contains (contains data at " + "%d Hz, codec outputs %d Hz)", + sampleRate, params.nSamplingRate); + } + mOutputFormat->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); @@ -4025,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); 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 ef4d3d0..eb135ab 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -220,7 +220,7 @@ status_t SampleTable::setSampleSizeParams( return ERROR_MALFORMED; } - mSampleSizeFieldSize = mDefaultSampleSize & 0xf; + mSampleSizeFieldSize = mDefaultSampleSize & 0xff; mDefaultSampleSize = 0; if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8 diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 76f47f7..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() { 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/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/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/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/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/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 1b6b02f..a4e8ee4 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -89,8 +89,6 @@ SoftwareRenderer::SoftwareRenderer( 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)); - // Width must be multiple of 32??? CHECK_EQ(0, native_window_set_buffers_geometry( mNativeWindow.get(), diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index fd3ddf7..835d2bb 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -281,6 +281,7 @@ private: void ensureCacheIsFetching_l(); status_t startAudioPlayer_l(); + void postAudioSeekComplete_l(); void shutdownVideoDecoder_l(); void setNativeWindow_l(const sp<ANativeWindow> &native); 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/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 cdce772..8462988 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -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 54c0d77..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) { @@ -492,7 +497,15 @@ static const char *GetURLForMime(const char *mime) { { "audio/mp4a-latm", "file:///sdcard/media_api/video/H264_AAC.3gp" }, { "audio/mpeg", - "file:///sdcard/media_api/music/MP3CBR.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) { @@ -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/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp index fff0b5f..fef8066 100644 --- a/media/mtp/MtpStorage.cpp +++ b/media/mtp/MtpStorage.cpp @@ -33,12 +33,13 @@ namespace android { MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, - const char* description, uint64_t reserveSpace) + const char* description, uint64_t reserveSpace, bool removable) : mStorageID(id), mFilePath(filePath), mDescription(description), mMaxCapacity(0), - mReserveSpace(reserveSpace) + mReserveSpace(reserveSpace), + mRemovable(removable) { LOGV("MtpStorage id: %d path: %s\n", id, filePath); } @@ -47,7 +48,7 @@ MtpStorage::~MtpStorage() { } int MtpStorage::getType() const { - return MTP_STORAGE_FIXED_RAM; + return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM); } int MtpStorage::getFileSystemType() const { diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h index d6ad25f..3e4f40d 100644 --- a/media/mtp/MtpStorage.h +++ b/media/mtp/MtpStorage.h @@ -33,10 +33,12 @@ private: uint64_t mMaxCapacity; // amount of free space to leave unallocated uint64_t mReserveSpace; + bool mRemovable; public: MtpStorage(MtpStorageID id, const char* filePath, - const char* description, uint64_t reserveSpace); + const char* description, uint64_t reserveSpace, + bool removable); virtual ~MtpStorage(); inline MtpStorageID getStorageID() const { return mStorageID; } @@ -47,6 +49,7 @@ public: uint64_t getFreeSpace(); const char* getDescription() const; inline const char* getPath() const { return (const char *)mFilePath; } + inline bool isRemovable() const { return mRemovable; } }; }; // namespace android |