diff options
46 files changed, 703 insertions, 285 deletions
diff --git a/api/9.xml b/api/9.xml index 3056e47ebfb8..27629b7fb1b9 100644 --- a/api/9.xml +++ b/api/9.xml @@ -73399,16 +73399,6 @@ visibility="public" > </field> -<field name="CAMERA_ID_DEFAULT" - type="int" - transient="false" - volatile="false" - static="true" - final="false" - deprecated="not deprecated" - visibility="public" -> -</field> </class> <interface name="Camera.AutoFocusCallback" abstract="true" @@ -86967,6 +86957,19 @@ <parameter name="listener" type="android.media.MediaRecorder.OnInfoListener"> </parameter> </method> +<method name="setOrientationHint" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="degrees" type="int"> +</parameter> +</method> <method name="setOutputFile" return="void" abstract="false" diff --git a/api/current.xml b/api/current.xml index 833fb6bd6603..3b9ab1a0edfc 100644 --- a/api/current.xml +++ b/api/current.xml @@ -73399,16 +73399,6 @@ visibility="public" > </field> -<field name="CAMERA_ID_DEFAULT" - type="int" - transient="false" - volatile="false" - static="true" - final="false" - deprecated="not deprecated" - visibility="public" -> -</field> </class> <interface name="Camera.AutoFocusCallback" abstract="true" @@ -86967,6 +86957,19 @@ <parameter name="listener" type="android.media.MediaRecorder.OnInfoListener"> </parameter> </method> +<method name="setOrientationHint" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="degrees" type="int"> +</parameter> +</method> <method name="setOutputFile" return="void" abstract="false" @@ -93860,7 +93863,7 @@ type="android.net.SSLCertificateSocketFactory" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="handshakeTimeoutMillis" type="int"> @@ -95960,7 +95963,7 @@ type="android.net.http.SslCertificate" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="issuedTo" type="java.lang.String"> @@ -96027,7 +96030,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -96049,7 +96052,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -172461,7 +172464,7 @@ abstract="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <constructor name="EventLogTags" @@ -202792,7 +202795,7 @@ synchronized="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -203338,7 +203341,7 @@ synchronized="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="flag" type="boolean"> @@ -205463,7 +205466,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="view" type="android.webkit.WebView"> @@ -213996,7 +213999,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -411701,7 +411704,7 @@ abstract="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <method name="getLength" @@ -412190,7 +412193,7 @@ abstract="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <method name="characters" @@ -412405,7 +412408,7 @@ abstract="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <implements name="org.xml.sax.DTDHandler"> @@ -412872,7 +412875,7 @@ abstract="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <method name="parse" @@ -414304,7 +414307,7 @@ abstract="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <implements name="org.xml.sax.AttributeList"> @@ -415793,7 +415796,7 @@ abstract="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <method name="makeParser" diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 275e2eb4a8c8..378189e6fcbe 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -211,8 +211,7 @@ public class Camera { * blocking the main application UI thread. * * @param cameraId the hardware camera to access, between 0 and - * {@link #getNumberOfCameras()}-1. Use {@link #CAMERA_ID_DEFAULT} - * to access the default camera. + * {@link #getNumberOfCameras()}-1. * @return a new Camera object, connected, locked and ready for use. * @throws RuntimeException if connection to the camera service fails (for * example, if the camera is in use by another process). @@ -222,18 +221,21 @@ public class Camera { } /** - * The id for the default camera. - * @see #open(int) - */ - public static int CAMERA_ID_DEFAULT = 0; - - /** - * Equivalent to Camera.open(Camera.CAMERA_ID_DEFAULT). - * Creates a new Camera object to access the default camera. + * Creates a new Camera object to access the first back-facing camera on the + * device. If the device does not have a back-facing camera, this returns + * null. * @see #open(int) */ public static Camera open() { - return new Camera(CAMERA_ID_DEFAULT); + int numberOfCameras = getNumberOfCameras(); + CameraInfo cameraInfo = new CameraInfo(); + for (int i = 0; i < numberOfCameras; i++) { + getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { + return new Camera(i); + } + } + return null; } Camera(int cameraId) { diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index b822b27f98d3..63ac21f7cdb2 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -102,6 +102,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private final boolean mSecure; /** @deprecated Use {@link #getDefault(int)} instead. */ + @Deprecated public SSLCertificateSocketFactory(int handshakeTimeoutMillis) { this(handshakeTimeoutMillis, null, true); } diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java index c29926c04be8..30f25a2cd309 100644 --- a/core/java/android/net/http/SslCertificate.java +++ b/core/java/android/net/http/SslCertificate.java @@ -112,6 +112,7 @@ public class SslCertificate { * @param validNotAfter The not-after date from the certificate validity period in ISO 8601 format * @deprecated Use {@link #SslCertificate(String, String, Date, Date)} */ + @Deprecated public SslCertificate( String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) { this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter)); @@ -157,6 +158,7 @@ public class SslCertificate { * * @deprecated Use {@link #getValidNotBeforeDate()} */ + @Deprecated public String getValidNotBefore() { return formatDate(mValidNotBefore); } @@ -175,6 +177,7 @@ public class SslCertificate { * * @deprecated Use {@link #getValidNotAfterDate()} */ + @Deprecated public String getValidNotAfter() { return formatDate(mValidNotAfter); } diff --git a/core/java/android/util/EventLogTags.java b/core/java/android/util/EventLogTags.java index 5cf5332a5622..8c18417286fd 100644 --- a/core/java/android/util/EventLogTags.java +++ b/core/java/android/util/EventLogTags.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; * @deprecated This class is no longer functional. * Use {@link android.util.EventLog} instead. */ +@Deprecated public class EventLogTags { public static class Description { public final int mTag; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index acda3e184775..65904972d396 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6533,11 +6533,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque(); - final boolean translucentWindow = attachInfo != null && attachInfo.mTranslucentWindow; + final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache; if (width <= 0 || height <= 0 || // Projected bitmap size in bytes - (width * height * (opaque && !translucentWindow ? 2 : 4) > + (width * height * (opaque && !use32BitCache ? 2 : 4) > ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; @@ -6567,7 +6567,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { // Optimization for translucent windows // If the window is translucent, use a 32 bits bitmap to benefit from memcpy() - quality = translucentWindow ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; } // Try to cleanup memory @@ -6581,7 +6581,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); } - if (opaque && translucentWindow) bitmap.setHasAlpha(false); + if (opaque && use32BitCache) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the @@ -9315,9 +9315,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int mWindowTop; /** - * Indicates whether the window is translucent/transparent + * Indicates whether views need to use 32-bit drawing caches */ - boolean mTranslucentWindow; + boolean mUse32BitDrawingCache; /** * For windows that are full-screen but using insets to layout inside diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 9c249ceeab27..c58207ee9999 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -754,7 +754,8 @@ public final class ViewRoot extends Handler implements ViewParent, // object is not initialized to its backing store, but soon it // will be (assuming the window is visible). attachInfo.mSurface = mSurface; - attachInfo.mTranslucentWindow = PixelFormat.formatHasAlpha(lp.format); + attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) || + lp.format == PixelFormat.RGBX_8888; attachInfo.mHasWindowFocus = false; attachInfo.mWindowVisibility = viewVisibility; attachInfo.mRecomputeGlobalAttributes = false; @@ -1301,6 +1302,7 @@ public final class ViewRoot extends Handler implements ViewParent, // Need to make sure we re-evaluate the window attributes next // time around, to ensure the window has the correct format. mWindowAttributesChanged = true; + requestLayout(); } } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index da0c61b2dc51..4d70e7cddb78 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1029,6 +1029,7 @@ public class WebSettings { * @deprecated This method has been deprecated in favor of * {@link #setPluginState} */ + @Deprecated public synchronized void setPluginsEnabled(boolean flag) { setPluginState(PluginState.ON); } @@ -1211,6 +1212,7 @@ public class WebSettings { * @return True if plugins are enabled. * @deprecated This method has been replaced by {@link #getPluginState} */ + @Deprecated public synchronized boolean getPluginsEnabled() { return mPluginState == PluginState.ON; } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 02c721009b10..1f8eeba9016c 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -89,6 +89,7 @@ public class WebViewClient { * @deprecated This method is no longer called. When the WebView encounters * a redirect loop, it will cancel the load. */ + @Deprecated public void onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg) { cancelMsg.sendToTarget(); diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 82b7c4fb1393..e1a1894f9432 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3626,6 +3626,7 @@ public class ListView extends AbsListView { * * @deprecated Use {@link #getCheckedItemIds()} instead. */ + @Deprecated public long[] getCheckItemIds() { // Use new behavior that correctly handles stable ID mapping. if (mAdapter != null && mAdapter.hasStableIds()) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b30b6b1d3841..41c973614b82 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3758,7 +3758,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would // allow to test for hasSelection in onFocusChanged, which would trigger a // startTextSelectionMode here. TODO - if (selectionController != null && hasSelection()) { + if (this instanceof ExtractEditText && selectionController != null && hasSelection()) { startTextSelectionMode(); } @@ -4819,6 +4819,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void beginBatchEdit() { + mInBatchEditControllers = true; final InputMethodState ims = mInputMethodState; if (ims != null) { int nesting = ++ims.mBatchEditNesting; @@ -4841,6 +4842,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void endBatchEdit() { + mInBatchEditControllers = false; final InputMethodState ims = mInputMethodState; if (ims != null) { int nesting = --ims.mBatchEditNesting; @@ -6747,10 +6749,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Restore previous selection Selection.setSelection((Spannable)mText, prevStart, prevEnd); - if (mSelectionModifierCursorController != null && - !mSelectionModifierCursorController.isShowing()) { + if (hasSelectionController() && !getSelectionController().isShowing()) { // If the anchors aren't showing, revive them. - mSelectionModifierCursorController.show(); + getSelectionController().show(); } else { // Tapping inside the selection displays the cut/copy/paste context menu // as long as the anchors are already showing. @@ -6761,12 +6762,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Tapping outside stops selection mode, if any stopTextSelectionMode(); - if (mInsertionPointCursorController != null && mText.length() > 0) { - mInsertionPointCursorController.show(); + if (hasInsertionController() && mText.length() > 0) { + getInsertionController().show(); } } - } else if (hasSelection() && mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.show(); + } else if (hasSelection() && hasSelectionController()) { + getSelectionController().show(); } } @@ -6800,11 +6801,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.onTouchEvent(event); + if (hasInsertionController()) { + getInsertionController().onTouchEvent(event); } - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.onTouchEvent(event); + if (hasSelectionController()) { + getSelectionController().onTouchEvent(event); } // Reset this state; it will be re-set if super.onTouchEvent @@ -6826,11 +6827,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) { - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.onTouchEvent(event); + if (hasInsertionController()) { + getInsertionController().onTouchEvent(event); } - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.onTouchEvent(event); + if (hasSelectionController()) { + getSelectionController().onTouchEvent(event); } boolean handled = false; @@ -6892,19 +6893,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // TODO Add an extra android:cursorController flag to disable the controller? - if (windowSupportsHandles && mCursorVisible && mLayout != null) { - if (mInsertionPointCursorController == null) { - mInsertionPointCursorController = new InsertionPointCursorController(); - } - } else { + mInsertionControllerEnabled = windowSupportsHandles && mCursorVisible && mLayout != null; + mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() && + mLayout != null; + + if (!mInsertionControllerEnabled) { mInsertionPointCursorController = null; } - if (windowSupportsHandles && textCanBeSelected() && mLayout != null) { - if (mSelectionModifierCursorController == null) { - mSelectionModifierCursorController = new SelectionModifierCursorController(); - } - } else { + if (!mSelectionControllerEnabled) { // Stop selection mode if the controller becomes unavailable. stopTextSelectionMode(); mSelectionModifierCursorController = null; @@ -7535,10 +7532,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case ID_SELECT_ALL: Selection.setSelection((Spannable) mText, 0, mText.length()); startTextSelectionMode(); + getSelectionController().show(); return true; case ID_START_SELECTING_TEXT: startTextSelectionMode(); + getSelectionController().show(); return true; case ID_CUT: @@ -7648,7 +7647,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void startTextSelectionMode() { if (!mIsInTextSelectionMode) { - if (mSelectionModifierCursorController == null) { + if (!hasSelectionController()) { Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled."); return; } @@ -7658,7 +7657,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } selectCurrentWord(); - mSelectionModifierCursorController.show(); mIsInTextSelectionMode = true; } } @@ -7837,6 +7835,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } + if (isInBatchEditMode()) { + return false; + } + final int extendedPaddingTop = getExtendedPaddingTop(); final int extendedPaddingBottom = getExtendedPaddingBottom(); final int compoundPaddingLeft = getCompoundPaddingLeft(); @@ -7876,7 +7878,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPositionY = y - TextView.this.mScrollY; if (isPositionVisible()) { int[] coords = null; - if (mContainer.isShowing()){ + if (mContainer.isShowing()) { coords = mTempCoords; TextView.this.getLocationInWindow(coords); mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY, @@ -8065,6 +8067,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void show() { + if (isInBatchEditMode()) { + return; + } + mIsShowing = true; updatePosition(); mStartHandle.show(); @@ -8128,6 +8134,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void updatePosition() { + if (!isShowing()) { + return; + } + final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); @@ -8288,6 +8298,62 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return getOffsetForHorizontal(line, x); } + /** + * @return True if this view supports insertion handles. + */ + boolean hasInsertionController() { + return mInsertionControllerEnabled; + } + + /** + * @return True if this view supports selection handles. + */ + boolean hasSelectionController() { + return mSelectionControllerEnabled; + } + + CursorController getInsertionController() { + if (!mInsertionControllerEnabled) { + return null; + } + + if (mInsertionPointCursorController == null) { + mInsertionPointCursorController = new InsertionPointCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + if (observer != null) { + observer.addOnTouchModeChangeListener(mInsertionPointCursorController); + } + } + + return mInsertionPointCursorController; + } + + CursorController getSelectionController() { + if (!mSelectionControllerEnabled) { + return null; + } + + if (mSelectionModifierCursorController == null) { + mSelectionModifierCursorController = new SelectionModifierCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + if (observer != null) { + observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); + } + } + + return mSelectionModifierCursorController; + } + + boolean isInBatchEditMode() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + return ims.mBatchEditNesting > 0; + } + return mInBatchEditControllers; + } + @ViewDebug.ExportedProperty private CharSequence mText; private CharSequence mTransformed; @@ -8319,6 +8385,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Cursor Controllers. Null when disabled. private CursorController mInsertionPointCursorController; private CursorController mSelectionModifierCursorController; + private boolean mInsertionControllerEnabled; + private boolean mSelectionControllerEnabled; + private boolean mInBatchEditControllers; private boolean mIsInTextSelectionMode = false; // These are needed to desambiguate a long click. If the long click comes from ones of these, we // select from the current cursor position. Otherwise, select from long pressed position. diff --git a/core/jni/android_nfc.h b/core/jni/android_nfc.h index df660f2ee3b3..de0ddde1d716 100644 --- a/core/jni/android_nfc.h +++ b/core/jni/android_nfc.h @@ -22,8 +22,17 @@ #ifndef __ANDROID_NFC_H__ #define __ANDROID_NFC_H__ +#define LOG_TAG "NdefMessage" +#include <utils/Log.h> + extern "C" { +#if 0 + #define TRACE(...) LOG(LOG_DEBUG, "NdefMessage", __VA_ARGS__) +#else + #define TRACE(...) +#endif + typedef struct phFriNfc_NdefRecord { uint8_t Flags; uint8_t Tnf; diff --git a/core/jni/android_nfc_NdefMessage.cpp b/core/jni/android_nfc_NdefMessage.cpp index eaf989d2f2e1..9beef2a97a5e 100644 --- a/core/jni/android_nfc_NdefMessage.cpp +++ b/core/jni/android_nfc_NdefMessage.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "NdefMessage" - #include <stdlib.h> #include "jni.h" @@ -23,8 +21,6 @@ #include "android_nfc.h" -#include <utils/Log.h> - namespace android { static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, @@ -53,7 +49,7 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, return -1; /* Get the number of records in the message so we can allocate buffers */ - LOGD("phFriNfc_NdefRecord_GetRecords(NULL)"); + TRACE("phFriNfc_NdefRecord_GetRecords(NULL)"); status = phFriNfc_NdefRecord_GetRecords((uint8_t *)raw_msg, (uint32_t)raw_msg_size, NULL, NULL, &num_of_records); @@ -62,9 +58,7 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, LOGE("phFriNfc_NdefRecord_GetRecords(NULL) returned 0x%04x", status); goto end; } - LOGD("phFriNfc_NdefRecord_GetRecords(NULL) returned 0x%04x", status); - - LOGD("found %d records in message", num_of_records); + TRACE("phFriNfc_NdefRecord_GetRecords(NULL) returned 0x%04x, with %d records", status, num_of_records); is_chunked = (uint8_t*)malloc(num_of_records); if (is_chunked == NULL) @@ -74,7 +68,7 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, goto end; /* Now, actually retrieve records position in message */ - LOGD("phFriNfc_NdefRecord_GetRecords()"); + TRACE("phFriNfc_NdefRecord_GetRecords()"); status = phFriNfc_NdefRecord_GetRecords((uint8_t *)raw_msg, (uint32_t)raw_msg_size, records, is_chunked, &num_of_records); @@ -83,7 +77,7 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, LOGE("phFriNfc_NdefRecord_GetRecords() returned 0x%04x", status); goto end; } - LOGD("phFriNfc_NdefRecord_GetRecords() returned 0x%04x", status); + TRACE("phFriNfc_NdefRecord_GetRecords() returned 0x%04x, with %d records", status, num_of_records); /* Build NDEF records array */ record_cls = e->FindClass("android/nfc/NdefRecord"); @@ -94,13 +88,11 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, ctor = e->GetMethodID(record_cls, "<init>", "(S[B[B[B)V"); - LOGD("NFC_Number of records = %d\n", num_of_records); - for (i = 0; i < num_of_records; i++) { jbyteArray type, id, payload; jobject new_record; - LOGD("phFriNfc_NdefRecord_Parse()"); + TRACE("phFriNfc_NdefRecord_Parse()"); status = phFriNfc_NdefRecord_Parse(&record, records[i]); @@ -108,7 +100,7 @@ static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, LOGE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); goto end; } - LOGD("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); + TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); type = e->NewByteArray(record.TypeLength); if (type == NULL) { diff --git a/core/jni/android_nfc_NdefRecord.cpp b/core/jni/android_nfc_NdefRecord.cpp index 0a3a5193fff7..a7a80c8b1b98 100644 --- a/core/jni/android_nfc_NdefRecord.cpp +++ b/core/jni/android_nfc_NdefRecord.cpp @@ -54,7 +54,7 @@ static jbyteArray android_nfc_NdefRecord_generate( if (buf == NULL) goto end; - LOGD("phFriNfc_NdefRecord_Generate()"); + TRACE("phFriNfc_NdefRecord_Generate()"); status = phFriNfc_NdefRecord_Generate(&record, buf, buf_size, &record_size); @@ -63,7 +63,7 @@ static jbyteArray android_nfc_NdefRecord_generate( LOGE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status); goto end; } - LOGD("phFriNfc_NdefRecord_Generate() returned 0x%04x", status); + TRACE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status); result = e->NewByteArray(record_size); if (result == NULL) @@ -104,13 +104,13 @@ static jint android_nfc_NdefRecord_parseNdefRecord(JNIEnv *e, jobject o, goto clean_and_return; } - LOGD("phFriNfc_NdefRecord_Parse()"); + TRACE("phFriNfc_NdefRecord_Parse()"); status = phFriNfc_NdefRecord_Parse(&record, (uint8_t *)raw_record); if (status) { LOGE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); goto clean_and_return; } - LOGD("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); + TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); /* Set TNF field */ mTnf = e->GetFieldID(record_cls, "mTnf", "S"); diff --git a/core/res/res/drawable-hdpi/textfield_pressed.9.png b/core/res/res/drawable-hdpi/textfield_pressed.9.png Binary files differindex 296d3daa6c00..6537be05a599 100755..100644 --- a/core/res/res/drawable-hdpi/textfield_pressed.9.png +++ b/core/res/res/drawable-hdpi/textfield_pressed.9.png diff --git a/core/res/res/drawable-mdpi/textfield_pressed.9.png b/core/res/res/drawable-mdpi/textfield_pressed.9.png Binary files differindex 6d03e1e3f390..9524fec0791c 100644 --- a/core/res/res/drawable-mdpi/textfield_pressed.9.png +++ b/core/res/res/drawable-mdpi/textfield_pressed.9.png diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index 2812ae834ae0..3dda5ee9b7ca 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -19,52 +19,15 @@ sdk.linux_checksum=TODO @jd:body +<p>Here's an overview of the steps you must follow to set up the Android SDK:</p> -<h2 id="quickstart">Quick Start</h2> +<ol> + <li>Prepare your development computer and ensure it meets the system requirements.</li> + <li>Install the SDK starter package from the table above.</li> + <li>Install the ADT Plugin for Eclipse (if you'll be developing in Eclipse).</li> + <li>Add Android platforms and other components to your SDK.</li> + <li>Explore the contents of the Android SDK (optional).</li> +</ol> -<p><strong>1. Prepare your development computer</strong></p> - -<p>Read the <a href="{@docRoot}sdk/requirements.html">System Requirements</a> -document and make sure that your development computer meets the hardware and -software requirements for the Android SDK. Install any additional software -needed before downloading the Android SDK. In particular, you may need to -install the <a href="http://java.sun.com/javase/downloads/index.jsp">JDK</a> - (version 5 or 6 required) and <a href="http://www.eclipse.org/downloads/">Eclipse</a> - (version 3.4 or 3.5, needed only if you want develop using the ADT Plugin). - -<p><strong>2. Download and install the SDK starter package</strong></p> - -<p>Download a starter package from the table above onto your development computer. -If you're using Windows, we recommend that you download the installer (the {@code .exe} file), -which will launch a Wizard to guide you through the installation and check your computer for -required software. Otherwise, download the SDK starter package ({@code .zip} or {@code .tgz}) -appropriate for your system, unpack it to a safe location, then add the location to your PATH -environment variable. </p> - -<p><strong>3. Install the ADT Plugin for Eclipse</strong></p> - -<p>If you are developing in Eclipse, add a new remote update site with the URL -<code>https://dl-ssl.google.com/android/eclipse/</code>. Install the Android -Development Tools (ADT) Plugin from that site, restart Eclipse, and set the "Android" -preferences in Eclipse to point to the Android SDK directory (installed in the previous step). For -detailed instructions to setup Eclipse, see <a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin -for Eclipse</a>.</p> - -<p><strong>4. Add Android platforms and other components to your SDK</strong></p> - -<p>Launch the <em>Android SDK and AVD Manager</em> by executing {@code SDK Manager.exe} (Windows) or -{@code android} (Mac/Linux) from the SDK's {@code tools/} directory (if you used the Windows -installer, this is launched for you when the Wizard is complete). Add some Android platforms -(such as Android 1.6 and Android 2.3) and other components (such as documentation) to your SDK. If -you aren't sure what to add, see <a -href="installing.html#which">Recommended Components</a></p> - -<p><strong>Done!</strong></p> - -<p>To write your first Android application, see the <a -href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> tutorial. Also see <a -href="{@docRoot}sdk/installing.html#NextSteps">Next -Steps</a> for other suggestions about how to get started.</p> - -<p>For a more detailed guide to installing and setting up the SDK, read <a +<p>To get started, download the appropriate package from the table above, then read the guide to <a href="installing.html">Installing the SDK</a>.</p> diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd index 5b5c4f492095..2f1918124aaf 100644 --- a/docs/html/sdk/installing.jd +++ b/docs/html/sdk/installing.jd @@ -58,7 +58,7 @@ function toggleDiv(link) { <ol> <li><a href="#which">Recommended Components</a></li> </ol></li> - <li><a href="#sdkContents">5. Exploring the SDK</a></li> + <li><a href="#sdkContents">5. Exploring the SDK (Optional)</a></li> <li><a href="#NextSteps">Next Steps</a></li> <li><a href="#troubleshooting">Troubleshooting</a></li> </ol> @@ -112,7 +112,7 @@ RCP version of Eclipse is recommended.</p> development environment—it includes only the core SDK Tools, which you can use to download the rest of the SDK components (such as the latest Android platform).</p> -<p>You can get the latest version of the SDK starter package from the <a +<p>If you haven't already, get the latest version of the SDK starter package from the <a href="{@docRoot}sdk/index.html">SDK download page</a>.</p> <p>If you downloaded a {@code .zip} or {@code .tgz} package (instead of the SDK installer), unpack @@ -379,10 +379,10 @@ you with the components you've just installed.</p> href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a> document. </p> -<h2 id="sdkContents">Step 5. Exploring the SDK</h2> +<h2 id="sdkContents">Step 5. Exploring the SDK (Optional)</h2> <p>Once you've installed the SDK and downloaded the platforms, documentation, -and add-ons that you need, open the SDK directory and take a look at what's +and add-ons that you need, we suggest that you open the SDK directory and take a look at what's inside.</p> <p>The table below describes the full SDK directory contents, with components diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd index 11e6642ceb2c..0f3634561401 100644 --- a/docs/html/sdk/ndk/index.jd +++ b/docs/html/sdk/ndk/index.jd @@ -84,34 +84,56 @@ padding: .25em 1em; <dd> <ul> + + <li>A new toolchain (based on GCC 4.4.3), which generates better code, and can also now +be used as a standalone cross-compiler, for people who want to build their stuff with +<code>./configure && make</code>. See +docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided, +but the 4.2.1 binaries were removed.</li> + + <li>Support for prebuilt static and shared libraries (docs/PREBUILTS.html), module +exports and imports to make sharing and reuse of third-party modules much easier +(docs/IMPORT-MODULE.html explains why).</li> + + <li>A C++ STL implementation (based on STLport) is now provided as a helper module. It +can be used either as a static or shared library (details and usage exemple under +sources/android/stlport/README). <strong>Note:</strong> For now, C++ Exceptions and RTTI are still +not supported.</li> + + <li>Improvements to the <code>cpufeatures</code> helper library to deal with buggy +kernel that incorrectly report they run on an ARMv7 CPU (while the device really is an ARMv6). We +recommend developers that use it to simply rebuild their applications to benefit from it, then +upload to Market.</li> + <li>Adds support for native activities, which allows you to write completely native applications.</li> <li>Adds an EGL library that lets you create and manage OpenGL ES textures and services.</li> - <li>Provides an interface that lets you write a native text-to-speech engine.</li> - <li>Adds native support for the following: <ul> - <li>the input subsystem (such as the keyboard and touch screen)</li> + <li>Input subsystem (such as the keyboard and touch screen)</li> - <li>the window and surface subsystem.</li> + <li>Window and surface subsystem</li> - <li>audio APIs based on the OpenSL ES standard that support playback and recording - as well as control over platform audio effects.</li> + <li>Audio APIs based on the OpenSL ES standard that support playback and recording + as well as control over platform audio effects</li> - <li>event loop APIs to wait for things such as input and sensor events.</li> + <li>Event loop APIs to wait for things such as input and sensor events</li> - <li>accessing assets packaged in an <code>.apk</code> file.</li> + <li>Access to assets packaged in the <code>.apk</code></li> - <li>accessing sensor data (accelerometer, compass, gyroscope, etc).</li> - - <li>provides sample applications, <code>native-plasma</code> and - <code>native-activity</code>, to demonstrate how to write a native activity.</li> + <li>Access to sensor data (accelerometer, compass, gyroscope, etc.)</li> </ul> </li> + + <li>New sample applications, <code>native-plasma</code> and + <code>native-activity</code>, to demonstrate how to write a native activity.</li> + + <li>Plus many bugfixes and other small improvements; see docs/CHANGES.html for a more +detailed list of changes.</li> </ul> </dd> </dl> diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 2f61cbe1cabd..f79476677f4b 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -115,7 +115,8 @@ public: const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) = 0; + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees) = 0; // Note: These methods are _not_ virtual, it exists as a wrapper around // the virtual "createRenderer" method above facilitating extraction @@ -125,14 +126,16 @@ public: const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees); sp<IOMXRenderer> createRendererFromJavaSurface( JNIEnv *env, jobject javaSurface, const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees); }; struct omx_message { diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h index 221c6796306f..63f11d19ebec 100644 --- a/include/media/stagefright/HardwareAPI.h +++ b/include/media/stagefright/HardwareAPI.h @@ -32,6 +32,14 @@ extern android::VideoRenderer *createRenderer( size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight); +extern android::VideoRenderer *createRendererWithRotation( + const android::sp<android::ISurface> &surface, + const char *componentName, + OMX_COLOR_FORMATTYPE colorFormat, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees); + extern android::OMXPluginBase *createOMXPlugin(); #endif // HARDWARE_API_H_ diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h index bb469e565f7b..7bf07eb11e49 100644 --- a/include/media/stagefright/MPEG4Writer.h +++ b/include/media/stagefright/MPEG4Writer.h @@ -154,6 +154,7 @@ private: bool exceedsFileDurationLimit(); bool isFileStreamable() const; void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK); + void writeCompositionMatrix(int32_t degrees); MPEG4Writer(const MPEG4Writer &); MPEG4Writer &operator=(const MPEG4Writer &); diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index d2bd9f2dd14d..29bfc4aaf588 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -32,6 +32,7 @@ enum { kKeyMIMEType = 'mime', // cstring kKeyWidth = 'widt', // int32_t kKeyHeight = 'heig', // int32_t + kKeyRotation = 'rotA', // int32_t (angle in degrees) kKeyIFramesInterval = 'ifiv', // int32_t kKeyStride = 'strd', // int32_t kKeySliceHeight = 'slht', // int32_t @@ -90,6 +91,7 @@ enum { // Track authoring progress status // kKeyTrackTimeStatus is used to track progress in elapsed time kKeyTrackTimeStatus = 'tktm', // int64_t + kKeyRotationDegree = 'rdge', // int32_t (clockwise, in degree) kKeyNotRealTime = 'ntrt', // bool (int32_t) diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h index 9c35274bafa6..3aff0c6acaa4 100644 --- a/include/private/media/VideoFrame.h +++ b/include/private/media/VideoFrame.h @@ -120,6 +120,7 @@ public: uint32_t mDisplayHeight; uint32_t mSize; // Number of bytes in mData uint8_t* mData; // Actual binary data + int32_t mRotationAngle; // rotation angle, clockwise }; }; // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 12db90885c4b..1994f6a4325f 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -289,7 +289,7 @@ private: void flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { - Rect const* p = cur; + Rect const* p = span.editArray(); Rect const* q = head; if (p->top == q->bottom) { merge = true; diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index a27df575749f..4004c7637c1a 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -16,6 +16,9 @@ package android.media; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; + /** * The CamcorderProfile class is used to retrieve the * predefined camcorder profile settings for camcorder applications. @@ -119,12 +122,21 @@ public class CamcorderProfile public int audioChannels; /** - * Returns the camcorder profile for the default camera at the given - * quality level. + * Returns the camcorder profile for the first back-facing camera on the + * device at the given quality level. If the device has no back-facing + * camera, this returns null. * @param quality the target quality level for the camcorder profile */ public static CamcorderProfile get(int quality) { - return get(0, quality); + int numberOfCameras = Camera.getNumberOfCameras(); + CameraInfo cameraInfo = new CameraInfo(); + for (int i = 0; i < numberOfCameras; i++) { + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { + return get(i, quality); + } + } + return null; } /** diff --git a/media/java/android/media/CameraProfile.java b/media/java/android/media/CameraProfile.java index 6a0be08defdd..905e2d2d5de3 100644 --- a/media/java/android/media/CameraProfile.java +++ b/media/java/android/media/CameraProfile.java @@ -16,6 +16,9 @@ package android.media; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; + import java.util.Arrays; import java.util.HashMap; @@ -46,12 +49,21 @@ public class CameraProfile /** * Returns a pre-defined still image capture (jpeg) quality level * used for the given quality level in the Camera application for - * the default camera. + * the first back-facing camera on the device. If the device has no + * back-facing camera, this returns 0. * * @param quality The target quality level */ public static int getJpegEncodingQualityParameter(int quality) { - return getJpegEncodingQualityParameter(0, quality); + int numberOfCameras = Camera.getNumberOfCameras(); + CameraInfo cameraInfo = new CameraInfo(); + for (int i = 0; i < numberOfCameras; i++) { + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { + return getJpegEncodingQualityParameter(i, quality); + } + } + return 0; } /** diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index b38124ecc401..ecabae8c775f 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -285,6 +285,31 @@ public class MediaRecorder } /** + * Sets the orientation hint for output video playback. + * This method should be called before prepare(). This method will not + * trigger the source video frame to rotate during video recording, but to + * add a composition matrix containing the rotation angle in the output + * video if the output format is OutputFormat.THREE_GPP or + * OutputFormat.MPEG_4 so that a video player can choose the proper + * orientation for playback. Note that some video players may choose + * to ignore the compostion matrix in a video during playback. + * + * @param degrees the angle to be rotated clockwise in degrees. + * The supported angles are 0, 90, 180, and 270 degrees. + * @throws IllegalArgumentException if the angle is not supported. + * + */ + public void setOrientationHint(int degrees) { + if (degrees != 0 && + degrees != 90 && + degrees != 180 && + degrees != 270) { + throw new IllegalArgumentException("Unsupported angle: " + degrees); + } + setParameter(String.format("video-param-rotation-angle-degrees=%d", degrees)); + } + + /** * Sets the format of the output file produced during recording. Call this * after setAudioSource()/setVideoSource() but before prepare(). * diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index efea8020613e..63e9dc867569 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -36,6 +36,7 @@ struct fields_t { jfieldID context; jclass bitmapClazz; jmethodID bitmapConstructor; + jmethodID createBitmapMethod; }; static fields_t fields; @@ -174,6 +175,41 @@ static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jo return NULL; } + jobject matrix = NULL; + if (videoFrame->mRotationAngle != 0) { + LOGD("Create a rotation matrix: %d degrees", videoFrame->mRotationAngle); + jclass matrixClazz = env->FindClass("android/graphics/Matrix"); + if (matrixClazz == NULL) { + jniThrowException(env, "java/lang/RuntimeException", + "Can't find android/graphics/Matrix"); + return NULL; + } + jmethodID matrixConstructor = + env->GetMethodID(matrixClazz, "<init>", "()V"); + if (matrixConstructor == NULL) { + jniThrowException(env, "java/lang/RuntimeException", + "Can't find Matrix constructor"); + return NULL; + } + matrix = + env->NewObject(matrixClazz, matrixConstructor); + if (matrix == NULL) { + LOGE("Could not create a Matrix object"); + return NULL; + } + + LOGV("Rotate the matrix: %d degrees", videoFrame->mRotationAngle); + jmethodID setRotateMethod = + env->GetMethodID(matrixClazz, "setRotate", "(F)V"); + if (setRotateMethod == NULL) { + jniThrowException(env, "java/lang/RuntimeException", + "Can't find Matrix setRotate method"); + return NULL; + } + env->CallVoidMethod(matrix, setRotateMethod, 1.0 * videoFrame->mRotationAngle); + env->DeleteLocalRef(matrixClazz); + } + // Create a SkBitmap to hold the pixels SkBitmap *bitmap = new SkBitmap(); if (bitmap == NULL) { @@ -191,7 +227,19 @@ static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jo // Since internally SkBitmap uses reference count to manage the reference to // its pixels, it is important that the pixels (along with SkBitmap) be // available after creating the Bitmap is returned to Java app. - return env->NewObject(fields.bitmapClazz, fields.bitmapConstructor, (int) bitmap, true, NULL, -1); + jobject jSrcBitmap = env->NewObject(fields.bitmapClazz, + fields.bitmapConstructor, (int) bitmap, true, NULL, -1); + + LOGV("Return a new bitmap constructed with the rotation matrix"); + return env->CallStaticObjectMethod( + fields.bitmapClazz, fields.createBitmapMethod, + jSrcBitmap, // source Bitmap + 0, // x + 0, // y + videoFrame->mDisplayWidth, // width + videoFrame->mDisplayHeight, // height + matrix, // transform matrix + false); // filter } static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz) @@ -291,6 +339,15 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env) jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor"); return; } + fields.createBitmapMethod = + env->GetStaticMethodID(fields.bitmapClazz, "createBitmap", + "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)" + "Landroid/graphics/Bitmap;"); + if (fields.createBitmapMethod == NULL) { + jniThrowException(env, "java/lang/RuntimeException", + "Can't find Bitmap.createBitmap method"); + return; + } } static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz) diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index f3804b8cfd49..ae6c2bf93785 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -38,11 +38,13 @@ sp<IOMXRenderer> IOMX::createRenderer( const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees) { return createRenderer( surface->getISurface(), componentName, colorFormat, encodedWidth, encodedHeight, - displayWidth, displayHeight); + displayWidth, displayHeight, + rotationDegrees); } sp<IOMXRenderer> IOMX::createRendererFromJavaSurface( @@ -50,7 +52,8 @@ sp<IOMXRenderer> IOMX::createRendererFromJavaSurface( const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees) { jclass surfaceClass = env->FindClass("android/view/Surface"); if (surfaceClass == NULL) { LOGE("Can't find android/view/Surface"); @@ -67,7 +70,8 @@ sp<IOMXRenderer> IOMX::createRendererFromJavaSurface( return createRenderer( surface, componentName, colorFormat, encodedWidth, - encodedHeight, displayWidth, displayHeight); + encodedHeight, displayWidth, displayHeight, + rotationDegrees); } class BpOMX : public BpInterface<IOMX> { @@ -349,7 +353,8 @@ public: const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); @@ -360,6 +365,7 @@ public: data.writeInt32(encodedHeight); data.writeInt32(displayWidth); data.writeInt32(displayHeight); + data.writeInt32(rotationDegrees); remote()->transact(CREATE_RENDERER, data, &reply); @@ -682,11 +688,13 @@ status_t BnOMX::onTransact( size_t encodedHeight = (size_t)data.readInt32(); size_t displayWidth = (size_t)data.readInt32(); size_t displayHeight = (size_t)data.readInt32(); + int32_t rotationDegrees = data.readInt32(); sp<IOMXRenderer> renderer = createRenderer(isurface, componentName, colorFormat, encodedWidth, encodedHeight, - displayWidth, displayHeight); + displayWidth, displayHeight, + rotationDegrees); reply->writeStrongBinder(renderer->asBinder()); diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index ca229fa52ba2..39fce81d6c7e 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -253,6 +253,8 @@ sp<IMemory> MetadataRetrieverClient::captureFrame() frameCopy->mDisplayWidth = frame->mDisplayWidth; frameCopy->mDisplayHeight = frame->mDisplayHeight; frameCopy->mSize = frame->mSize; + frameCopy->mRotationAngle = frame->mRotationAngle; + LOGV("rotation: %d", frameCopy->mRotationAngle); frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame); memcpy(frameCopy->mData, frame->mData, frame->mSize); delete frame; // Fix memory leakage diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index d37d83d3472e..553648da0358 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -340,6 +340,17 @@ status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { return OK; } +// Always rotate clockwise, and only support 0, 90, 180 and 270 for now. +status_t StagefrightRecorder::setParamVideoRotation(int32_t degrees) { + LOGV("setParamVideoRotation: %d", degrees); + if (degrees < 0 || degrees % 90 != 0) { + LOGE("Unsupported video rotation angle: %d", degrees); + return BAD_VALUE; + } + mRotationDegrees = degrees % 360; + return OK; +} + status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { LOGV("setParamMaxFileDurationUs: %lld us", timeUs); if (timeUs <= 0) { @@ -532,6 +543,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &video_bitrate)) { return setParamVideoEncodingBitRate(video_bitrate); } + } else if (key == "video-param-rotation-angle-degrees") { + int32_t degrees; + if (safe_strtoi32(value.string(), °rees)) { + return setParamVideoRotation(degrees); + } } else if (key == "video-param-i-frames-interval") { int32_t seconds; if (safe_strtoi32(value.string(), &seconds)) { @@ -1105,6 +1121,9 @@ status_t StagefrightRecorder::startMPEG4Recording() { if (mTrackEveryTimeDurationUs > 0) { meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs); } + if (mRotationDegrees != 0) { + meta->setInt32(kKeyRotationDegree, mRotationDegrees); + } writer->setListener(mListener); mWriter = writer; return mWriter->start(meta.get()); @@ -1187,6 +1206,7 @@ status_t StagefrightRecorder::reset() { mMaxFileDurationUs = 0; mMaxFileSizeBytes = 0; mTrackEveryTimeDurationUs = 0; + mRotationDegrees = 0; mEncoderProfiles = MediaProfiles::getInstance(); mOutputFd = -1; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index ad0dfa05fda8..e42df2e82108 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -91,6 +91,7 @@ private: int64_t mMaxFileSizeBytes; int64_t mMaxFileDurationUs; int64_t mTrackEveryTimeDurationUs; + int32_t mRotationDegrees; // Clockwise String8 mParams; int mOutputFd; @@ -120,6 +121,7 @@ private: status_t setParamVideoEncoderLevel(int32_t level); status_t setParamVideoCameraId(int32_t cameraId); status_t setParamVideoTimeScale(int32_t timeScale); + status_t setParamVideoRotation(int32_t degrees); status_t setParamTrackTimeStatus(int64_t timeDurationUs); status_t setParamInterleaveDuration(int32_t durationUs); status_t setParam64BitFileOffset(bool use64BitFileOffset); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 064a00cc0fd6..66eb7ee59fab 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -103,12 +103,14 @@ struct AwesomeLocalRenderer : public AwesomeRenderer { OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight) + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees) : mTarget(NULL), mLibHandle(NULL) { init(previewOnly, componentName, colorFormat, surface, displayWidth, - displayHeight, decodedWidth, decodedHeight); + displayHeight, decodedWidth, decodedHeight, + rotationDegrees); } virtual void render(MediaBuffer *buffer) { @@ -141,7 +143,8 @@ private: OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight); + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees); AwesomeLocalRenderer(const AwesomeLocalRenderer &); AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);; @@ -153,7 +156,8 @@ void AwesomeLocalRenderer::init( OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight) { + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees) { if (!previewOnly) { // We will stick to the vanilla software-color-converting renderer // for "previewOnly" mode, to avoid unneccessarily switching overlays @@ -162,6 +166,14 @@ void AwesomeLocalRenderer::init( mLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW); if (mLibHandle) { + typedef VideoRenderer *(*CreateRendererWithRotationFunc)( + const sp<ISurface> &surface, + const char *componentName, + OMX_COLOR_FORMATTYPE colorFormat, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees); + typedef VideoRenderer *(*CreateRendererFunc)( const sp<ISurface> &surface, const char *componentName, @@ -169,17 +181,36 @@ void AwesomeLocalRenderer::init( size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight); - CreateRendererFunc func = - (CreateRendererFunc)dlsym( + CreateRendererWithRotationFunc funcWithRotation = + (CreateRendererWithRotationFunc)dlsym( mLibHandle, - "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" - "OMX_COLOR_FORMATTYPEjjjj"); + "_Z26createRendererWithRotationRKN7android2spINS_8" + "ISurfaceEEEPKc20OMX_COLOR_FORMATTYPEjjjji"); - if (func) { + if (funcWithRotation) { mTarget = - (*func)(surface, componentName, colorFormat, - displayWidth, displayHeight, - decodedWidth, decodedHeight); + (*funcWithRotation)( + surface, componentName, colorFormat, + displayWidth, displayHeight, + decodedWidth, decodedHeight, + rotationDegrees); + } else { + if (rotationDegrees != 0) { + LOGW("renderer does not support rotation."); + } + + CreateRendererFunc func = + (CreateRendererFunc)dlsym( + mLibHandle, + "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" + "OMX_COLOR_FORMATTYPEjjjj"); + + if (func) { + mTarget = + (*func)(surface, componentName, colorFormat, + displayWidth, displayHeight, + decodedWidth, decodedHeight); + } } } } @@ -187,7 +218,7 @@ void AwesomeLocalRenderer::init( if (mTarget == NULL) { mTarget = new SoftwareRenderer( colorFormat, surface, displayWidth, displayHeight, - decodedWidth, decodedHeight); + decodedWidth, decodedHeight, rotationDegrees); } } @@ -785,6 +816,12 @@ void AwesomePlayer::initRenderer_l() { CHECK(meta->findInt32(kKeyWidth, &decodedWidth)); CHECK(meta->findInt32(kKeyHeight, &decodedHeight)); + int32_t rotationDegrees; + if (!mVideoTrack->getFormat()->findInt32( + kKeyRotation, &rotationDegrees)) { + rotationDegrees = 0; + } + mVideoRenderer.clear(); // Must ensure that mVideoRenderer's destructor is actually executed @@ -800,7 +837,8 @@ void AwesomePlayer::initRenderer_l() { mISurface, component, (OMX_COLOR_FORMATTYPE)format, decodedWidth, decodedHeight, - mVideoWidth, mVideoHeight)); + mVideoWidth, mVideoHeight, + rotationDegrees)); } else { // Other decoders are instantiated locally and as a consequence // allocate their buffers in local address space. @@ -810,7 +848,7 @@ void AwesomePlayer::initRenderer_l() { (OMX_COLOR_FORMATTYPE)format, mISurface, mVideoWidth, mVideoHeight, - decodedWidth, decodedHeight); + decodedWidth, decodedHeight, rotationDegrees); } } } @@ -1625,7 +1663,22 @@ void AwesomePlayer::finishAsyncPrepare_l() { if (mVideoWidth < 0 || mVideoHeight < 0) { notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0); } else { - notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight); + int32_t rotationDegrees; + if (!mVideoTrack->getFormat()->findInt32( + kKeyRotation, &rotationDegrees)) { + rotationDegrees = 0; + } + +#if 1 + if (rotationDegrees == 90 || rotationDegrees == 270) { + notifyListener_l( + MEDIA_SET_VIDEO_SIZE, mVideoHeight, mVideoWidth); + } else +#endif + { + notifyListener_l( + MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight); + } } notifyListener_l(MEDIA_PREPARED); @@ -1757,7 +1810,8 @@ status_t AwesomePlayer::resume() { state->mVideoWidth, state->mVideoHeight, state->mDecodedWidth, - state->mDecodedHeight); + state->mDecodedHeight, + 0); mVideoRendererIsPreview = true; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index f4047087c544..2154f2f5fc69 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -27,11 +27,11 @@ #include <stdlib.h> #include <string.h> +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/DataSource.h> #include "include/ESDS.h" #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> @@ -579,52 +579,9 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { case FOURCC('t', 'k', 'h', 'd'): { - if (chunk_data_size < 4) { - return ERROR_MALFORMED; - } - - uint8_t version; - if (mDataSource->readAt(data_offset, &version, 1) < 1) { - return ERROR_IO; - } - - uint64_t ctime, mtime, duration; - int32_t id; - uint32_t width, height; - - if (version == 1) { - if (chunk_data_size != 36 + 60) { - return ERROR_MALFORMED; - } - - uint8_t buffer[36 + 60]; - if (mDataSource->readAt( - data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { - return ERROR_IO; - } - - ctime = U64_AT(&buffer[4]); - mtime = U64_AT(&buffer[12]); - id = U32_AT(&buffer[20]); - duration = U64_AT(&buffer[28]); - width = U32_AT(&buffer[88]); - height = U32_AT(&buffer[92]); - } else if (version == 0) { - if (chunk_data_size != 24 + 60) { - return ERROR_MALFORMED; - } - - uint8_t buffer[24 + 60]; - if (mDataSource->readAt( - data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { - return ERROR_IO; - } - ctime = U32_AT(&buffer[4]); - mtime = U32_AT(&buffer[8]); - id = U32_AT(&buffer[12]); - duration = U32_AT(&buffer[20]); - width = U32_AT(&buffer[76]); - height = U32_AT(&buffer[80]); + status_t err; + if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) { + return err; } *offset += chunk_size; @@ -1073,6 +1030,89 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return OK; } +status_t MPEG4Extractor::parseTrackHeader( + off_t data_offset, off_t data_size) { + if (data_size < 4) { + return ERROR_MALFORMED; + } + + uint8_t version; + if (mDataSource->readAt(data_offset, &version, 1) < 1) { + return ERROR_IO; + } + + size_t dynSize = (version == 1) ? 36 : 24; + + uint8_t buffer[36 + 60]; + + if (data_size != (off_t)dynSize + 60) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, data_size) < (ssize_t)data_size) { + return ERROR_IO; + } + + uint64_t ctime, mtime, duration; + int32_t id; + + if (version == 1) { + ctime = U64_AT(&buffer[4]); + mtime = U64_AT(&buffer[12]); + id = U32_AT(&buffer[20]); + duration = U64_AT(&buffer[28]); + } else if (version == 0) { + ctime = U32_AT(&buffer[4]); + mtime = U32_AT(&buffer[8]); + id = U32_AT(&buffer[12]); + duration = U32_AT(&buffer[20]); + } + + size_t matrixOffset = dynSize + 16; + int32_t a00 = U32_AT(&buffer[matrixOffset]); + int32_t a01 = U32_AT(&buffer[matrixOffset + 4]); + int32_t dx = U32_AT(&buffer[matrixOffset + 8]); + int32_t a10 = U32_AT(&buffer[matrixOffset + 12]); + int32_t a11 = U32_AT(&buffer[matrixOffset + 16]); + int32_t dy = U32_AT(&buffer[matrixOffset + 20]); + +#if 0 + LOGI("x' = %.2f * x + %.2f * y + %.2f", + a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f); + LOGI("y' = %.2f * x + %.2f * y + %.2f", + a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f); +#endif + + uint32_t rotationDegrees; + + static const int32_t kFixedOne = 0x10000; + if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) { + // Identity, no rotation + rotationDegrees = 0; + } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) { + rotationDegrees = 90; + } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) { + rotationDegrees = 270; + } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) { + rotationDegrees = 180; + } else { + LOGW("We only support 0,90,180,270 degree rotation matrices"); + rotationDegrees = 0; + } + + if (rotationDegrees != 0) { + mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees); + } + +#if 0 + uint32_t width = U32_AT(&buffer[dynSize + 52]); + uint32_t height = U32_AT(&buffer[dynSize + 56]); +#endif + + return OK; +} + status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) { if (size < 4) { return ERROR_MALFORMED; @@ -1386,7 +1426,7 @@ MPEG4Source::MPEG4Source( const uint8_t *ptr = (const uint8_t *)data; CHECK(size >= 7); - CHECK_EQ(ptr[0], 1); // configurationVersion == 1 + CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 // The number of bytes used to encode the length of a NAL unit. mNALLengthSize = 1 + (ptr[4] & 3); @@ -1534,7 +1574,7 @@ status_t MPEG4Source::read( } uint32_t sampleTime; - CHECK_EQ(OK, mSampleTable->getMetaDataForSample( + CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample( sampleIndex, NULL, NULL, &sampleTime)); if (mode == ReadOptions::SEEK_CLOSEST) { @@ -1581,7 +1621,7 @@ status_t MPEG4Source::read( err = mGroup->acquire_buffer(&mBuffer); if (err != OK) { - CHECK_EQ(mBuffer, NULL); + CHECK(mBuffer == NULL); return err; } } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a15c2741352c..cbb160402431 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -202,6 +202,7 @@ private: // Simple validation on the codec specific data status_t checkCodecSpecificData() const; + int32_t mRotation; void updateTrackSizeEstimate(); void addOneStscTableEntry(size_t chunkId, size_t sampleId); @@ -519,6 +520,58 @@ void MPEG4Writer::stopWriterThread() { pthread_join(mThread, &dummy); } +/* + * MP4 file standard defines a composition matrix: + * | a b u | + * | c d v | + * | x y w | + * + * the element in the matrix is stored in the following + * order: {a, b, u, c, d, v, x, y, w}, + * where a, b, c, d, x, and y is in 16.16 format, while + * u, v and w is in 2.30 format. + */ +void MPEG4Writer::writeCompositionMatrix(int degrees) { + LOGV("writeCompositionMatrix"); + uint32_t a = 0x00010000; + uint32_t b = 0; + uint32_t c = 0; + uint32_t d = 0x00010000; + switch (degrees) { + case 0: + break; + case 90: + a = 0; + b = 0x00010000; + c = 0xFFFF0000; + d = 0; + break; + case 180: + a = 0xFFFF0000; + d = 0xFFFF0000; + break; + case 270: + a = 0; + b = 0xFFFF0000; + c = 0x00010000; + d = 0; + break; + default: + CHECK(!"Should never reach this unknown rotation"); + break; + } + + writeInt32(a); // a + writeInt32(b); // b + writeInt32(0); // u + writeInt32(c); // c + writeInt32(d); // d + writeInt32(0); // v + writeInt32(0); // x + writeInt32(0); // y + writeInt32(0x40000000); // w +} + status_t MPEG4Writer::stop() { if (mFile == NULL) { return OK; @@ -584,15 +637,7 @@ status_t MPEG4Writer::stop() { writeInt16(0); // reserved writeInt32(0); // reserved writeInt32(0); // reserved - writeInt32(0x10000); // matrix - writeInt32(0); - writeInt32(0); - writeInt32(0); - writeInt32(0x10000); - writeInt32(0); - writeInt32(0); - writeInt32(0); - writeInt32(0x40000000); + writeCompositionMatrix(0); writeInt32(0); // predefined writeInt32(0); // predefined writeInt32(0); // predefined @@ -885,7 +930,8 @@ MPEG4Writer::Track::Track( mCodecSpecificData(NULL), mCodecSpecificDataSize(0), mGotAllCodecSpecificData(false), - mReachedEOS(false) { + mReachedEOS(false), + mRotation(0) { getCodecSpecificDataFromInputFormatIfPossible(); const char *mime; @@ -1178,6 +1224,11 @@ status_t MPEG4Writer::Track::start(MetaData *params) { startTimeUs = 0; } + int32_t rotationDegrees; + if (!mIsAudio && params && params->findInt32(kKeyRotationDegree, &rotationDegrees)) { + mRotation = rotationDegrees; + } + mIsRealTimeRecording = true; { int32_t isNotRealTime; @@ -2071,15 +2122,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume mOwner->writeInt16(0); // reserved - mOwner->writeInt32(0x10000); // matrix - mOwner->writeInt32(0); - mOwner->writeInt32(0); - mOwner->writeInt32(0); - mOwner->writeInt32(0x10000); - mOwner->writeInt32(0); - mOwner->writeInt32(0); - mOwner->writeInt32(0); - mOwner->writeInt32(0x40000000); + mOwner->writeCompositionMatrix(mRotation); if (mIsAudio) { mOwner->writeInt32(0); diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index a800a93f23fc..9b2dec9b389a 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -191,6 +191,11 @@ static VideoFrame *extractVideoFrameWithCodecFlags( CHECK(meta->findInt32(kKeyWidth, &width)); CHECK(meta->findInt32(kKeyHeight, &height)); + int32_t rotationAngle; + if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) { + rotationAngle = 0; // By default, no rotation + } + VideoFrame *frame = new VideoFrame; frame->mWidth = width; frame->mHeight = height; @@ -198,6 +203,7 @@ static VideoFrame *extractVideoFrameWithCodecFlags( frame->mDisplayHeight = height; frame->mSize = width * height * 2; frame->mData = new uint8_t[frame->mSize]; + frame->mRotationAngle = rotationAngle; int32_t srcFormat; CHECK(meta->findInt32(kKeyColorFormat, &srcFormat)); diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index a6dbf6912ee1..86ad85bf3d15 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -30,7 +30,8 @@ SoftwareRenderer::SoftwareRenderer( OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight) + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees) : mColorFormat(colorFormat), mConverter(colorFormat, OMX_COLOR_Format16bitRGB565), mISurface(surface), @@ -56,10 +57,20 @@ SoftwareRenderer::SoftwareRenderer( CHECK(mMemoryHeap->heapID() >= 0); CHECK(mConverter.isValid()); + uint32_t orientation; + switch (rotationDegrees) { + case 0: orientation = ISurface::BufferHeap::ROT_0; break; + case 90: orientation = ISurface::BufferHeap::ROT_90; break; + case 180: orientation = ISurface::BufferHeap::ROT_180; break; + case 270: orientation = ISurface::BufferHeap::ROT_270; break; + default: orientation = ISurface::BufferHeap::ROT_0; break; + } + ISurface::BufferHeap bufferHeap( mDisplayWidth, mDisplayHeight, mDecodedWidth, mDecodedHeight, PIXEL_FORMAT_RGB_565, + orientation, 0, mMemoryHeap); status_t err = mISurface->registerBuffers(bufferHeap); diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 1c9cc7e5b3a2..2610b0e2c965 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -71,6 +71,8 @@ private: static status_t verifyTrack(Track *track); + status_t parseTrackHeader(off_t data_offset, off_t data_size); + MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); }; diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index c99da5977784..72ab5aaf389e 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -92,7 +92,8 @@ public: const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees); virtual void binderDied(const wp<IBinder> &the_late_who); diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index 9eed089ce9d4..25c9df71d9f9 100644 --- a/media/libstagefright/include/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -33,7 +33,8 @@ public: OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight); + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees = 0); virtual ~SoftwareRenderer(); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index c927da182d57..63af26a6ccb4 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -459,7 +459,8 @@ sp<IOMXRenderer> OMX::createRenderer( const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { + size_t displayWidth, size_t displayHeight, + int32_t rotationDegrees) { Mutex::Autolock autoLock(mLock); VideoRenderer *impl = NULL; @@ -467,6 +468,14 @@ sp<IOMXRenderer> OMX::createRenderer( void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW); if (libHandle) { + typedef VideoRenderer *(*CreateRendererWithRotationFunc)( + const sp<ISurface> &surface, + const char *componentName, + OMX_COLOR_FORMATTYPE colorFormat, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight, + int32_t rotationDegrees); + typedef VideoRenderer *(*CreateRendererFunc)( const sp<ISurface> &surface, const char *componentName, @@ -474,22 +483,35 @@ sp<IOMXRenderer> OMX::createRenderer( size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight); - CreateRendererFunc func = - (CreateRendererFunc)dlsym( + CreateRendererWithRotationFunc funcWithRotation = + (CreateRendererWithRotationFunc)dlsym( libHandle, - "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" - "OMX_COLOR_FORMATTYPEjjjj"); - - if (func) { - impl = (*func)(surface, componentName, colorFormat, - displayWidth, displayHeight, encodedWidth, encodedHeight); - - if (impl) { - impl = new SharedVideoRenderer(libHandle, impl); - libHandle = NULL; + "_Z26createRendererWithRotationRKN7android2spINS_8" + "ISurfaceEEEPKc20OMX_COLOR_FORMATTYPEjjjji"); + + if (funcWithRotation) { + impl = (*funcWithRotation)( + surface, componentName, colorFormat, + displayWidth, displayHeight, encodedWidth, encodedHeight, + rotationDegrees); + } else { + CreateRendererFunc func = + (CreateRendererFunc)dlsym( + libHandle, + "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" + "OMX_COLOR_FORMATTYPEjjjj"); + + if (func) { + impl = (*func)(surface, componentName, colorFormat, + displayWidth, displayHeight, encodedWidth, encodedHeight); } } + if (impl) { + impl = new SharedVideoRenderer(libHandle, impl); + libHandle = NULL; + } + if (libHandle) { dlclose(libHandle); libHandle = NULL; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index e204e0402aaa..c9ab9922482e 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -175,10 +175,11 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) for (size_t i=0 ; i<mSensorList.size() ; i++) { const Sensor& s(mSensorList[i]); const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle())); - snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, last=<%5.1f,%5.1f,%5.1f>)\n", + snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, maxRate=%.2fHz, last=<%5.1f,%5.1f,%5.1f>)\n", s.getName().string(), s.getVendor().string(), s.getHandle(), + s.getMinDelay() ? (1000000.0f / s.getMinDelay()) : 0.0f, e.data[0], e.data[1], e.data[2]); result.append(buffer); } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 9f37799fe7d9..dfb1c0e15c63 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -49,8 +49,8 @@ class SensorService : { friend class BinderService<SensorService>; - static const nsecs_t MINIMUM_EVENTS_PERIOD = 10000000; // 10ms - static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 200 ms + static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz + static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 5 Hz SensorService(); virtual ~SensorService(); diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index 42bf983fe7fd..aea106224bda 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -27,15 +27,25 @@ int receiver(int fd, int events, void* data) sp<SensorEventQueue> q((SensorEventQueue*)data); ssize_t n; ASensorEvent buffer[8]; + + static nsecs_t oldTimeStamp = 0; + while ((n = q->read(buffer, 8)) > 0) { for (int i=0 ; i<n ; i++) { - if (buffer[i].type == Sensor::TYPE_ACCELEROMETER) { + if (buffer[i].type == Sensor::TYPE_GYROSCOPE) { printf("time=%lld, value=<%5.1f,%5.1f,%5.1f>\n", buffer[i].timestamp, buffer[i].acceleration.x, buffer[i].acceleration.y, buffer[i].acceleration.z); } + + if (oldTimeStamp) { + float t = float(buffer[i].timestamp - oldTimeStamp) / s2ns(1); + printf("%f ms (%f Hz)\n", t*1000, 1.0/t); + } + oldTimeStamp = buffer[i].timestamp; + } } if (n<0 && n != -EAGAIN) { @@ -56,7 +66,7 @@ int main(int argc, char** argv) sp<SensorEventQueue> q = mgr.createEventQueue(); printf("queue=%p\n", q.get()); - Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER); + Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_GYROSCOPE); printf("accelerometer=%p (%s)\n", accelerometer, accelerometer->getName().string()); q->enableSensor(accelerometer); |