From 70742854ac298d9d78c5e231d93e798ee4bd84f5 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 28 Sep 2012 03:33:29 -0700 Subject: [PATCH 01/21] Bug 784908: Part 0: Make this test actually test session history instead of scroll port clamping. irc-r=roc --- docshell/test/file_bug590573_1.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docshell/test/file_bug590573_1.html b/docshell/test/file_bug590573_1.html index 40da53f1a55..850d418bde1 100644 --- a/docshell/test/file_bug590573_1.html +++ b/docshell/test/file_bug590573_1.html @@ -2,7 +2,7 @@ -
This is a very tall div.
+
This is a very tall div.
From f73db9f5dad8895027dba7b01be7a6ffc6bf2a70 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Fri, 28 Sep 2012 22:16:34 -0400 Subject: [PATCH 02/21] Bug 784908: Part 1: Change names of FrameMetrics variables to be more descriptive, add documentation, change some coordinate spaces. r=roc --- .../BrowserElementScrolling.js | 24 +-- dom/ipc/TabChild.cpp | 28 +-- gfx/2d/BaseRect.h | 15 ++ gfx/layers/FrameMetrics.h | 133 ++++++++++++-- gfx/layers/Layers.cpp | 13 +- gfx/layers/basic/BasicTiledThebesLayer.cpp | 2 +- gfx/layers/ipc/AsyncPanZoomController.cpp | 165 ++++++++---------- gfx/layers/ipc/AsyncPanZoomController.h | 20 +-- gfx/layers/ipc/Axis.cpp | 62 ++++--- gfx/layers/ipc/Axis.h | 4 +- gfx/layers/ipc/CompositorParent.cpp | 40 +++-- gfx/layers/opengl/ReusableTileStoreOGL.cpp | 12 +- ipc/glue/IPCMessageUtils.h | 12 +- layout/base/nsDisplayList.cpp | 31 ++-- layout/ipc/RenderFrameParent.cpp | 14 +- 15 files changed, 362 insertions(+), 213 deletions(-) diff --git a/dom/browser-element/BrowserElementScrolling.js b/dom/browser-element/BrowserElementScrolling.js index 06ac4f3c9c7..daac3f65824 100644 --- a/dom/browser-element/BrowserElementScrolling.js +++ b/dom/browser-element/BrowserElementScrolling.js @@ -206,38 +206,38 @@ const ContentPanning = { let metrics = data.json; let displayPort = metrics.displayPort; - let screenWidth = metrics.screenSize.width; - let screenHeight = metrics.screenSize.height; + let compositionWidth = metrics.compositionBounds.width; + let compositionHeight = metrics.compositionBounds.height; let x = metrics.x; let y = metrics.y; this._zoom = metrics.zoom; this._viewport = new Rect(x, y, - screenWidth / metrics.zoom, - screenHeight / metrics.zoom); + compositionWidth / metrics.zoom, + compositionHeight / metrics.zoom); this._cssPageRect = new Rect(metrics.cssPageRect.x, metrics.cssPageRect.y, metrics.cssPageRect.width, metrics.cssPageRect.height); let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - if (this._screenWidth != screenWidth || this._screenHeight != screenHeight) { - cwu.setCSSViewport(screenWidth, screenHeight); - this._screenWidth = screenWidth; - this._screenHeight = screenHeight; + if (this._compositionWidth != compositionWidth || this._compositionHeight != compositionHeight) { + cwu.setCSSViewport(compositionWidth, compositionHeight); + this._compositionWidth = compositionWidth; + this._compositionHeight = compositionHeight; } // Set scroll position cwu.setScrollPositionClampingScrollPortSize( - screenWidth / metrics.zoom, screenHeight / metrics.zoom); + compositionWidth / metrics.zoom, compositionHeight / metrics.zoom); content.scrollTo(x, y); cwu.setResolution(displayPort.resolution, displayPort.resolution); let element = null; if (content.document && (element = content.document.documentElement)) { - cwu.setDisplayPortForElement(displayPort.left, - displayPort.top, + cwu.setDisplayPortForElement(displayPort.x, + displayPort.y, displayPort.width, displayPort.height, element); @@ -272,7 +272,7 @@ const ContentPanning = { let cssPageRect = this._cssPageRect; let viewport = this._viewport; - let bRect = new Rect(Math.max(cssPageRect.left, rect.x - margin), + let bRect = new Rect(Math.max(cssPageRect.x, rect.x - margin), rect.y, rect.w + 2 * margin, rect.h); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index df0d4fa0ca0..0c1d1b14fd0 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -843,27 +843,29 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) } nsCString data; - data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mViewportScrollOffset.x)); - data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mViewportScrollOffset.y)); + data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x)); + data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y)); // We don't treat the x and y scales any differently for this // semi-platform-specific code. data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mResolution.width); data += nsPrintfCString(", \"displayPort\" : "); - data += nsPrintfCString("{ \"left\" : %d", aFrameMetrics.mDisplayPort.X()); - data += nsPrintfCString(", \"top\" : %d", aFrameMetrics.mDisplayPort.Y()); - data += nsPrintfCString(", \"width\" : %d", aFrameMetrics.mDisplayPort.Width()); - data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mDisplayPort.Height()); + data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x); + data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y); + data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mDisplayPort.width); + data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mDisplayPort.height); data += nsPrintfCString(", \"resolution\" : %f", aFrameMetrics.mResolution.width); data += nsPrintfCString(" }"); - data += nsPrintfCString(", \"screenSize\" : "); - data += nsPrintfCString("{ \"width\" : %d", aFrameMetrics.mViewport.width); - data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mViewport.height); + data += nsPrintfCString(", \"compositionBounds\" : "); + data += nsPrintfCString("{ \"x\" : %d", aFrameMetrics.mCompositionBounds.x); + data += nsPrintfCString(", \"y\" : %d", aFrameMetrics.mCompositionBounds.y); + data += nsPrintfCString(", \"width\" : %d", aFrameMetrics.mCompositionBounds.width); + data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mCompositionBounds.height); data += nsPrintfCString(" }"); data += nsPrintfCString(", \"cssPageRect\" : "); - data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mCSSContentRect.x); - data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mCSSContentRect.y); - data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mCSSContentRect.width); - data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mCSSContentRect.height); + data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mScrollableRect.x); + data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mScrollableRect.y); + data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mScrollableRect.width); + data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mScrollableRect.height); data += nsPrintfCString(" }"); data += nsPrintfCString(" }"); diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h index 7f88336a368..e6cce2dc659 100644 --- a/gfx/2d/BaseRect.h +++ b/gfx/2d/BaseRect.h @@ -403,6 +403,21 @@ struct BaseRect { width = right - x; height = bottom - y; } + // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is + // the largest integer-coordinate rectangle contained by the unrounded result. + void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); } + // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so + // that the result is the largest integer-coordinate rectangle contained by the + // unrounded result. + void ScaleInverseRoundIn(double aXScale, double aYScale) + { + T right = static_cast(floor(double(XMost()) / aXScale)); + T bottom = static_cast(floor(double(YMost()) / aYScale)); + x = static_cast(ceil(double(x) / aXScale)); + y = static_cast(ceil(double(y) / aYScale)); + width = gfx_max(0, right - x); + height = gfx_max(0, bottom - y); + } /** * Clamp aPoint to this rectangle. It is allowed to end up on any diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index d5a09bfc8a6..c7a4c65cd26 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -30,12 +30,15 @@ public: // will begin at. FrameMetrics() - : mViewport(0, 0, 0, 0) + : mCompositionBounds(0, 0, 0, 0) , mContentRect(0, 0, 0, 0) - , mViewportScrollOffset(0, 0) + , mDisplayPort(0, 0, 0, 0) + , mViewport(0, 0, 0, 0) + , mScrollOffset(0, 0) , mScrollId(NULL_SCROLL_ID) - , mCSSContentRect(0, 0, 0, 0) + , mScrollableRect(0, 0, 0, 0) , mResolution(1, 1) + , mDevPixelsPerCSSPixel(1) , mMayHaveTouchListeners(false) {} @@ -44,7 +47,7 @@ public: bool operator==(const FrameMetrics& aOther) const { return (mViewport.IsEqualEdges(aOther.mViewport) && - mViewportScrollOffset == aOther.mViewportScrollOffset && + mScrollOffset == aOther.mScrollOffset && mDisplayPort.IsEqualEdges(aOther.mDisplayPort) && mScrollId == aOther.mScrollId); } @@ -68,21 +71,129 @@ public: return mScrollId != NULL_SCROLL_ID; } - // These are all in layer coordinate space. - nsIntRect mViewport; + gfxSize LayersPixelsPerCSSPixel() const + { + return mResolution * mDevPixelsPerCSSPixel; + } + + gfx::Point GetScrollOffsetInLayerPixels() const + { + return gfx::Point(mScrollOffset.x * LayersPixelsPerCSSPixel().width, + mScrollOffset.y * LayersPixelsPerCSSPixel().height); + } + + // --------------------------------------------------------------------------- + // The following metrics are all in widget space/device pixels. + // + + // This is the area within the widget that we're compositing to, which means + // that it is the visible region of this frame. It is not relative to + // anything. + // So { 0, 0, [compositeArea.width], [compositeArea.height] }. + // + // This is useful because, on mobile, the viewport and composition dimensions + // are not always the same. In this case, we calculate the displayport using + // an area bigger than the region we're compositing to. If we used the + // viewport dimensions to calculate the displayport, we'd run into situations + // where we're prerendering the wrong regions and the content may be clipped, + // or too much of it prerendered. If the displayport is the same as the + // viewport, there is no need for this and we can just use the viewport + // instead. + // + // This is only valid on the root layer. Nested iframes do not need this + // metric as they do not have a displayport set. See bug 775452. + nsIntRect mCompositionBounds; + + // |mScrollableRect|, stored in device pixels. DECPRECATED, DO NOT USE. + // + // This is valid on any layer where |mScrollableRect| is, though it may be + // more lazily maintained than |mScrollableRect|. That is, when + // |mScrollableRect| is updated, this may lag. For this reason, it's better to + // use |mScrollableRect| for any control logic. + // + // FIXME/bug 785929: Is this really necessary? Can it not be calculated from + // |mScrollableRect| whenever it's needed? nsIntRect mContentRect; - gfx::Point mViewportScrollOffset; - nsIntRect mDisplayPort; + + // --------------------------------------------------------------------------- + // The following metrics are all in CSS pixels. They are not in any uniform + // space, so each is explained separately. + // + + // The area of a frame's contents that has been painted, relative to the + // viewport. It is in the same coordinate space as |mViewport|. For example, + // if it is at 0,0, then it's at the same place at the viewport, which is at + // the top-left in the layer, and at the same place as the scroll offset of + // the document. + // + // Note that this is structured in such a way that it doesn't depend on the + // method layout uses to scroll content. + // + // May be larger or smaller than |mScrollableRect|. + // + // To pre-render a margin of 100 CSS pixels around the window, + // { x = -100, y = - 100, + // width = window.innerWidth + 100, height = window.innerHeight + 100 } + // + // This is only valid on the root layer. Nested iframes do not have a + // displayport set on them. See bug 775452. + gfx::Rect mDisplayPort; + + // The CSS viewport, which is the dimensions we're using to constrain the + // element of this frame, relative to the top-left of the layer. Note + // that its offset is structured in such a way that it doesn't depend on the + // method layout uses to scroll content. + // + // This is mainly useful on the root layer, however nested iframes can have + // their own viewport, which will just be the size of the window of the + // iframe. For layers that don't correspond to a document, this metric is + // meaningless and invalid. + gfx::Rect mViewport; + + // The position of the top-left of the CSS viewport, relative to the document + // (or the document relative to the viewport, if that helps understand it). + // + // Thus it is relative to the document. It is in the same coordinate space as + // |mScrollableRect|, but a different coordinate space than |mViewport| and + // |mDisplayPort|. + // + // It is required that the rect: + // { x = mScrollOffset.x, y = mScrollOffset.y, + // width = mCompositionBounds.x / mResolution.width, + // height = mCompositionBounds.y / mResolution.height } + // Be within |mScrollableRect|. + // + // This is valid for any layer, but is always relative to this frame and + // not any parents, regardless of parent transforms. + gfx::Point mScrollOffset; + + // A unique ID assigned to each scrollable frame (unless this is + // ROOT_SCROLL_ID, in which case it is not unique). ViewID mScrollId; - // Consumers often want to know the origin/size before scaling to pixels - // so we record this as well. - gfx::Rect mCSSContentRect; + // The scrollable bounds of a frame. This is determined by reflow. + // For the top-level |window|, + // { x = window.scrollX, y = window.scrollY, // could be 0, 0 + // width = window.innerWidth, height = window.innerHeight } + // + // This is relative to the document. It is in the same coordinate space as + // |mScrollOffset|, but a different coordinate space than |mViewport| and + // |mDisplayPort|. Note also that this coordinate system is understood by + // window.scrollTo(). + // + // This is valid on any layer unless it has no content. + gfx::Rect mScrollableRect; // This represents the resolution at which the associated layer // will been rendered. gfxSize mResolution; + // The conversion factor between CSS pixels and device pixels for this frame. + // This can vary based on a variety of things, such as reflowing-zoom. The + // conversion factor for device pixels to layers pixels is just the + // resolution. + float mDevPixelsPerCSSPixel; + // Whether or not this frame may have touch listeners. bool mMayHaveTouchListeners; }; diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 547475f3329..515ae8e991f 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -143,6 +143,17 @@ AppendToString(nsACString& s, const nsIntRect& r, return s += sfx; } +nsACString& +AppendToString(nsACString& s, const Rect& r, + const char* pfx="", const char* sfx="") +{ + s += pfx; + s.AppendPrintf( + "(x=%f, y=%f, w=%f, h=%f)", + r.x, r.y, r.width, r.height); + return s += sfx; +} + nsACString& AppendToString(nsACString& s, const nsIntRegion& r, const char* pfx="", const char* sfx="") @@ -173,7 +184,7 @@ AppendToString(nsACString& s, const FrameMetrics& m, { s += pfx; AppendToString(s, m.mViewport, "{ viewport="); - AppendToString(s, m.mViewportScrollOffset, " viewportScroll="); + AppendToString(s, m.mScrollOffset, " viewportScroll="); AppendToString(s, m.mDisplayPort, " displayport="); AppendToString(s, m.mScrollId, " scrollId=", " }"); return s += sfx; diff --git a/gfx/layers/basic/BasicTiledThebesLayer.cpp b/gfx/layers/basic/BasicTiledThebesLayer.cpp index d41380af96d..b786a140f2e 100644 --- a/gfx/layers/basic/BasicTiledThebesLayer.cpp +++ b/gfx/layers/basic/BasicTiledThebesLayer.cpp @@ -253,7 +253,7 @@ BasicTiledThebesLayer::PaintThebes(gfxContext* aContext, Layer* primaryScrollable = BasicManager()->GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); - scrollOffset = metrics.mViewportScrollOffset; + scrollOffset = metrics.mScrollOffset; } int32_t scrollDiffX = scrollOffset.x - mLastScrollOffset.x; int32_t scrollDiffY = scrollOffset.y - mLastScrollOffset.y; diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 216e5d5ae7a..32193aadd38 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -138,10 +138,10 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent, { MonitorAutoLock monitor(mMonitor); currentZoom = mFrameMetrics.mResolution.width; - currentScrollOffset = gfx::Point(mFrameMetrics.mViewportScrollOffset.x, - mFrameMetrics.mViewportScrollOffset.y); - lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mViewportScrollOffset.x, - mLastContentPaintMetrics.mViewportScrollOffset.y); + currentScrollOffset = gfx::Point(mFrameMetrics.mScrollOffset.x, + mFrameMetrics.mScrollOffset.y); + lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mScrollOffset.x, + mLastContentPaintMetrics.mScrollOffset.y); } nsEventStatus status; @@ -669,10 +669,10 @@ void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorPa } void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) { - gfx::Point newOffset(mFrameMetrics.mViewportScrollOffset.x + aOffset.x, - mFrameMetrics.mViewportScrollOffset.y + aOffset.y); + gfx::Point newOffset(mFrameMetrics.mScrollOffset.x + aOffset.x, + mFrameMetrics.mScrollOffset.y + aOffset.y); FrameMetrics metrics(mFrameMetrics); - metrics.mViewportScrollOffset = newOffset; + metrics.mScrollOffset = newOffset; mFrameMetrics = metrics; } @@ -682,12 +682,12 @@ void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) { float scale = mFrameMetrics.mResolution.width; // The page rect is the css page rect scaled by the current zoom. - pageSize.ScaleRoundOut(1 / scale); + pageSize.ScaleInverseRoundOut(scale); // Round the page rect so we don't get any truncation, then get the nsIntRect // from this. metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y, pageSize.width, pageSize.height); - metrics.mCSSContentRect = aCSSPageRect; + metrics.mScrollableRect = aCSSPageRect; mFrameMetrics = metrics; } @@ -703,19 +703,19 @@ void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFoc // Force a recalculation of the page rect based on the new zoom and the // current CSS page rect (which is unchanged since it's not affected by zoom). - SetPageRect(mFrameMetrics.mCSSContentRect); + SetPageRect(mFrameMetrics.mScrollableRect); - gfx::Point scrollOffset = metrics.mViewportScrollOffset; + gfx::Point scrollOffset = metrics.mScrollOffset; scrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale; scrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale; - metrics.mViewportScrollOffset = scrollOffset; + metrics.mScrollOffset = scrollOffset; mFrameMetrics = metrics; } -bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aViewport, +bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBounds, float aVelocity, float* aDisplayPortOffset, float* aDisplayPortLength) @@ -724,77 +724,50 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aViewport, const float MAX_SKATE_SIZE_MULTIPLIER = 4.0f; if (fabsf(aVelocity) > MIN_SKATE_SPEED) { - *aDisplayPortLength = aViewport * clamped(fabsf(aVelocity), + *aDisplayPortLength = aCompositionBounds * clamped(fabsf(aVelocity), MIN_SKATE_SIZE_MULTIPLIER, MAX_SKATE_SIZE_MULTIPLIER); - *aDisplayPortOffset = aVelocity > 0 ? 0 : aViewport - *aDisplayPortLength; + *aDisplayPortOffset = aVelocity > 0 ? 0 : aCompositionBounds - *aDisplayPortLength; return true; } return false; } -const nsIntRect AsyncPanZoomController::CalculatePendingDisplayPort() { +const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() { float scale = mFrameMetrics.mResolution.width; - nsIntRect viewport = mFrameMetrics.mViewport; - viewport.ScaleRoundIn(1 / scale); + nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; + compositionBounds.ScaleInverseRoundIn(scale); - gfx::Point scrollOffset = mFrameMetrics.mViewportScrollOffset; + gfx::Point scrollOffset = mFrameMetrics.mScrollOffset; gfx::Point velocity = GetVelocityVector(); - // The displayport is relative to the current scroll offset. Here's a little - // diagram to make it easier to see: - // - // - - - - - // | | - // ************* - // * | | * - // - -*- @------ -*- - - // | * |=====| * | - // * |=====| * - // | * |=====| * | - // - -*- ------- -*- - - // * | | * - // ************* - // | | - // - - - - - // - // The full --- area with === inside it is the actual viewport rect, the *** area - // is the displayport, and the - - - area is an imaginary additional page on all 4 - // borders of the actual page. Notice that the displayport intersects half-way with - // each of the imaginary extra pages. The @ symbol at the top left of the - // viewport marks the current scroll offset. From the @ symbol to the far left - // and far top, it is clear that this distance is 1/4 of the displayport's - // height/width dimension. const float STATIONARY_SIZE_MULTIPLIER = 2.0f; gfx::Rect displayPort(0, 0, - viewport.width * STATIONARY_SIZE_MULTIPLIER, - viewport.height * STATIONARY_SIZE_MULTIPLIER); + compositionBounds.width * STATIONARY_SIZE_MULTIPLIER, + compositionBounds.height * STATIONARY_SIZE_MULTIPLIER); // If there's motion along an axis of movement, and it's above a threshold, // then we want to paint a larger area in the direction of that motion so that // it's less likely to checkerboard. bool enlargedX = EnlargeDisplayPortAlongAxis( - viewport.width, velocity.x, &displayPort.x, &displayPort.width); + compositionBounds.width, velocity.x, &displayPort.x, &displayPort.width); bool enlargedY = EnlargeDisplayPortAlongAxis( - viewport.height, velocity.y, &displayPort.y, &displayPort.height); + compositionBounds.height, velocity.y, &displayPort.y, &displayPort.height); if (!enlargedX && !enlargedY) { displayPort.x = -displayPort.width / 4; displayPort.y = -displayPort.height / 4; } else if (!enlargedX) { - displayPort.width = viewport.width; + displayPort.width = compositionBounds.width; } else if (!enlargedY) { - displayPort.height = viewport.height; + displayPort.height = compositionBounds.height; } gfx::Rect shiftedDisplayPort = displayPort; shiftedDisplayPort.MoveBy(scrollOffset.x, scrollOffset.y); - displayPort = shiftedDisplayPort.Intersect(mFrameMetrics.mCSSContentRect); + displayPort = shiftedDisplayPort.Intersect(mFrameMetrics.mScrollableRect); displayPort.MoveBy(-scrollOffset.x, -scrollOffset.y); - // Round the displayport so we don't get any truncation, then get the nsIntRect - // from this. - displayPort.Round(); - return nsIntRect(displayPort.x, displayPort.y, displayPort.width, displayPort.height); + return displayPort; } void AsyncPanZoomController::SetDPI(int aDPI) { @@ -814,22 +787,13 @@ void AsyncPanZoomController::ScheduleComposite() { void AsyncPanZoomController::RequestContentRepaint() { mFrameMetrics.mDisplayPort = CalculatePendingDisplayPort(); - gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mViewportScrollOffset, - newScrollOffset = mFrameMetrics.mViewportScrollOffset; + gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mScrollOffset, + newScrollOffset = mFrameMetrics.mScrollOffset; // If we're trying to paint what we already think is painted, discard this // request since it's a pointless paint. - nsRect oldDisplayPort = nsRect( - mLastPaintRequestMetrics.mDisplayPort.x, - mLastPaintRequestMetrics.mDisplayPort.y, - mLastPaintRequestMetrics.mDisplayPort.width, - mLastPaintRequestMetrics.mDisplayPort.height); - - gfx::Rect newDisplayPort = gfx::Rect( - mFrameMetrics.mDisplayPort.x, - mFrameMetrics.mDisplayPort.y, - mFrameMetrics.mDisplayPort.width, - mFrameMetrics.mDisplayPort.height); + gfx::Rect oldDisplayPort = mLastPaintRequestMetrics.mDisplayPort; + gfx::Rect newDisplayPort = mFrameMetrics.mDisplayPort; oldDisplayPort.MoveBy(oldScrollOffset.x, oldScrollOffset.y); newDisplayPort.MoveBy(newScrollOffset.x, newScrollOffset.y); @@ -892,11 +856,11 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa mEndZoomToMetrics.mResolution.width * sampledPosition + mStartZoomToMetrics.mResolution.width * (1 - sampledPosition); - mFrameMetrics.mViewportScrollOffset = gfx::Point( - mEndZoomToMetrics.mViewportScrollOffset.x * sampledPosition + - mStartZoomToMetrics.mViewportScrollOffset.x * (1 - sampledPosition), - mEndZoomToMetrics.mViewportScrollOffset.y * sampledPosition + - mStartZoomToMetrics.mViewportScrollOffset.y * (1 - sampledPosition) + mFrameMetrics.mScrollOffset = gfx::Point( + mEndZoomToMetrics.mScrollOffset.x * sampledPosition + + mStartZoomToMetrics.mScrollOffset.x * (1 - sampledPosition), + mEndZoomToMetrics.mScrollOffset.y * sampledPosition + + mStartZoomToMetrics.mScrollOffset.y * (1 - sampledPosition) ); requestAnimationFrame = true; @@ -920,10 +884,10 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa localScaleY = mFrameMetrics.mResolution.height; if (frame.IsScrollable()) { - metricsScrollOffset = frame.mViewportScrollOffset; + metricsScrollOffset = frame.GetScrollOffsetInLayerPixels(); } - scrollOffset = mFrameMetrics.mViewportScrollOffset; + scrollOffset = mFrameMetrics.mScrollOffset; } nsIntPoint scrollCompensation( @@ -974,8 +938,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr case WAITING_LISTENERS: // FIXME/bug 784908: Scroll offset is stored in layer pixels in the rest // of the layers code, but we want it in CSS pixels. - mFrameMetrics.mViewportScrollOffset = - aViewportFrame.mViewportScrollOffset / aViewportFrame.mResolution.width; + mFrameMetrics.mScrollOffset = + aViewportFrame.mScrollOffset / aViewportFrame.mResolution.width; break; // Don't clobber if we're in other states. default: @@ -988,10 +952,15 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mX.CancelTouch(); mY.CancelTouch(); + + // The composition bounds are not stored within the layers code, so we have + // to reset them back to what they were every time we overwrite them. + nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; mFrameMetrics = aViewportFrame; + mFrameMetrics.mCompositionBounds = compositionBounds; mFrameMetrics.mResolution.width = 1 / mFrameMetrics.mResolution.width; mFrameMetrics.mResolution.height = 1 / mFrameMetrics.mResolution.height; - SetPageRect(mFrameMetrics.mCSSContentRect); + SetPageRect(mFrameMetrics.mScrollableRect); // Bug 776413/fixme: Request a repaint as soon as a page is loaded so that // we get a larger displayport. This is very bad because we're wasting a @@ -999,9 +968,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr RequestContentRepaint(); mState = NOTHING; - } else if (!mFrameMetrics.mCSSContentRect.IsEqualEdges(aViewportFrame.mCSSContentRect)) { - mFrameMetrics.mCSSContentRect = aViewportFrame.mCSSContentRect; - SetPageRect(mFrameMetrics.mCSSContentRect); + } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) { + mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect; + SetPageRect(mFrameMetrics.mScrollableRect); } } @@ -1010,10 +979,10 @@ const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() { return mFrameMetrics; } -void AsyncPanZoomController::UpdateViewportSize(int aWidth, int aHeight) { +void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositionBounds) { MonitorAutoLock mon(mMonitor); FrameMetrics metrics = GetFrameMetrics(); - metrics.mViewport = nsIntRect(0, 0, aWidth, aHeight); + metrics.mCompositionBounds = aCompositionBounds; mFrameMetrics = metrics; } @@ -1032,27 +1001,29 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) { { MonitorAutoLock mon(mMonitor); - nsIntRect viewport = mFrameMetrics.mViewport; - gfx::Rect cssPageRect = mFrameMetrics.mCSSContentRect; - gfx::Point scrollOffset = mFrameMetrics.mViewportScrollOffset; + nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; + gfx::Rect cssPageRect = mFrameMetrics.mScrollableRect; + gfx::Point scrollOffset = mFrameMetrics.mScrollOffset; // If the rect is empty, treat it as a request to zoom out to the full page // size. if (zoomToRect.IsEmpty()) { - nsIntRect cssViewport = viewport; - cssViewport.ScaleRoundIn(1 / mFrameMetrics.mResolution.width); - cssViewport.MoveBy(nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y))); + // composition bounds in CSS coordinates + nsIntRect cssCompositionBounds = compositionBounds; + cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mResolution.width); + cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y); - float y = mFrameMetrics.mViewportScrollOffset.y; - float newHeight = cssViewport.height * cssPageRect.width / cssViewport.width; - float dh = cssViewport.height - newHeight; + float y = mFrameMetrics.mScrollOffset.y; + float newHeight = + cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width; + float dh = cssCompositionBounds.height - newHeight; zoomToRect = gfx::Rect(0.0f, y + dh/2, cssPageRect.width, y + dh/2 + newHeight); } else { - float targetRatio = float(viewport.width) / float(viewport.height); + float targetRatio = float(compositionBounds.width) / float(compositionBounds.height); float rectRatio = zoomToRect.width / zoomToRect.height; if (fabsf(targetRatio - rectRatio) < EPSILON) { @@ -1073,24 +1044,24 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) { } mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = - NS_MIN(viewport.width / zoomToRect.width, viewport.height / zoomToRect.height); + NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height); mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = clamped(mEndZoomToMetrics.mResolution.width, MIN_ZOOM, MAX_ZOOM); // Recalculate the zoom to rect using the new dimensions. - zoomToRect.width = viewport.width / mEndZoomToMetrics.mResolution.width; - zoomToRect.height = viewport.height / mEndZoomToMetrics.mResolution.height; + zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mResolution.width; + zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mResolution.height; // Clamp the zoom to rect to the CSS rect to make sure it fits. zoomToRect = zoomToRect.Intersect(cssPageRect); // Do one final recalculation to get the resolution. mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = - NS_MAX(viewport.width / zoomToRect.width, viewport.height / zoomToRect.height); + NS_MAX(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height); mStartZoomToMetrics = mFrameMetrics; - mEndZoomToMetrics.mViewportScrollOffset = + mEndZoomToMetrics.mScrollOffset = gfx::Point(zoomToRect.x, zoomToRect.y); mAnimationStartTime = TimeStamp::Now(); diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index ab130771deb..375cedecc01 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -97,17 +97,13 @@ public: nsInputEvent* aOutEvent); /** - * Updates the viewport size, i.e. the dimensions of the frame (not - * necessarily the screen) content will actually be rendered onto in device - * pixels for example, a subframe will not take the entire screen, but we - * still want to know how big it is in device pixels. Ideally we want to be - * using CSS pixels everywhere inside here, but in this case we need to know - * how large of a displayport to set so we use these dimensions plus some - * extra. - * - * XXX: Use nsIntRect instead. + * Updates the composition bounds, i.e. the dimensions of the final size of + * the frame this is tied to during composition onto, in device pixels. In + * general, this will just be: + * { x = 0, y = 0, width = surface.width, height = surface.height }, however + * there is no hard requirement for this. */ - void UpdateViewportSize(int aWidth, int aHeight); + void UpdateCompositionBounds(const nsIntRect& aCompositionBounds); /** * We have found a scrollable subframe, so disable our machinery until we hit @@ -350,7 +346,7 @@ protected: * a larger area than the screen so that when you scroll down, you don't * checkerboard immediately. */ - const nsIntRect CalculatePendingDisplayPort(); + const gfx::Rect CalculatePendingDisplayPort(); /** * Attempts to enlarge the displayport along a single axis. Returns whether or @@ -360,7 +356,7 @@ protected: * |aDisplayPortLength|. If enlarged, these will be updated with the new * metrics. */ - bool EnlargeDisplayPortAlongAxis(float aViewport, float aVelocity, + bool EnlargeDisplayPortAlongAxis(float aCompositionBounds, float aVelocity, float* aDisplayPortOffset, float* aDisplayPortLength); /** diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/ipc/Axis.cpp index 9046998f63d..e520a791b3e 100644 --- a/gfx/layers/ipc/Axis.cpp +++ b/gfx/layers/ipc/Axis.cpp @@ -140,12 +140,12 @@ bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) { } Axis::Overscroll Axis::GetOverscroll() { - // If the current pan takes the viewport to the left of or above the current + // If the current pan takes the window to the left of or above the current // page rect. bool minus = GetOrigin() < GetPageStart(); - // If the current pan takes the viewport to the right of or below the current + // If the current pan takes the window to the right of or below the current // page rect. - bool plus = GetViewportEnd() > GetPageEnd(); + bool plus = GetCompositionEnd() > GetPageEnd(); if (minus && plus) { return OVERSCROLL_BOTH; } @@ -161,19 +161,20 @@ Axis::Overscroll Axis::GetOverscroll() { float Axis::GetExcess() { switch (GetOverscroll()) { case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart(); - case OVERSCROLL_PLUS: return GetViewportEnd() - GetPageEnd(); - case OVERSCROLL_BOTH: return (GetViewportEnd() - GetPageEnd()) + (GetPageStart() - GetOrigin()); + case OVERSCROLL_PLUS: return GetCompositionEnd() - GetPageEnd(); + case OVERSCROLL_BOTH: return (GetCompositionEnd() - GetPageEnd()) + + (GetPageStart() - GetOrigin()); default: return 0; } } Axis::Overscroll Axis::DisplacementWillOverscroll(int32_t aDisplacement) { - // If the current pan plus a displacement takes the viewport to the left of or + // If the current pan plus a displacement takes the window to the left of or // above the current page rect. bool minus = GetOrigin() + aDisplacement < GetPageStart(); - // If the current pan plus a displacement takes the viewport to the right of or + // If the current pan plus a displacement takes the window to the right of or // below the current page rect. - bool plus = GetViewportEnd() + aDisplacement > GetPageEnd(); + bool plus = GetCompositionEnd() + aDisplacement > GetPageEnd(); if (minus && plus) { return OVERSCROLL_BOTH; } @@ -189,7 +190,7 @@ Axis::Overscroll Axis::DisplacementWillOverscroll(int32_t aDisplacement) { float Axis::DisplacementWillOverscrollAmount(int32_t aDisplacement) { switch (DisplacementWillOverscroll(aDisplacement)) { case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart(); - case OVERSCROLL_PLUS: return (GetViewportEnd() + aDisplacement) - GetPageEnd(); + case OVERSCROLL_PLUS: return (GetCompositionEnd() + aDisplacement) - GetPageEnd(); // Don't handle overscrolled in both directions; a displacement can't cause // this, it must have already been zoomed out too far. default: return 0; @@ -201,7 +202,7 @@ Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, int32_t aFocus) { bool both = ScaleWillOverscrollBothSides(aScale); bool minus = originAfterScale < GetPageStart() * aScale; - bool plus = (originAfterScale + GetViewportLength()) > GetPageEnd() * aScale; + bool plus = (originAfterScale + GetCompositionLength()) > GetPageEnd() * aScale; if ((minus && plus) || both) { return OVERSCROLL_BOTH; @@ -219,7 +220,8 @@ float Axis::ScaleWillOverscrollAmount(float aScale, int32_t aFocus) { float originAfterScale = (GetOrigin() + aFocus) * aScale - aFocus; switch (ScaleWillOverscroll(aScale, aFocus)) { case OVERSCROLL_MINUS: return originAfterScale - GetPageStart() * aScale; - case OVERSCROLL_PLUS: return (originAfterScale + GetViewportLength()) - GetPageEnd() * aScale; + case OVERSCROLL_PLUS: return (originAfterScale + GetCompositionLength()) - + NS_lround(GetPageEnd() * aScale); // Don't handle OVERSCROLL_BOTH. Client code is expected to deal with it. default: return 0; } @@ -229,8 +231,8 @@ float Axis::GetVelocity() { return mVelocity; } -float Axis::GetViewportEnd() { - return GetOrigin() + GetViewportLength(); +float Axis::GetCompositionEnd() { + return GetOrigin() + GetCompositionLength(); } float Axis::GetPageEnd() { @@ -238,40 +240,44 @@ float Axis::GetPageEnd() { } float Axis::GetOrigin() { - gfx::Point origin = mAsyncPanZoomController->GetFrameMetrics().mViewportScrollOffset; + gfx::Point origin = mAsyncPanZoomController->GetFrameMetrics().mScrollOffset; return GetPointOffset(origin); } -float Axis::GetViewportLength() { - nsIntRect viewport = mAsyncPanZoomController->GetFrameMetrics().mViewport; - gfx::Rect scaledViewport = gfx::Rect(viewport.x, viewport.y, viewport.width, viewport.height); - scaledViewport.ScaleRoundIn(1 / mAsyncPanZoomController->GetFrameMetrics().mResolution.width); - return GetRectLength(scaledViewport); +float Axis::GetCompositionLength() { + nsIntRect compositionBounds = + mAsyncPanZoomController->GetFrameMetrics().mCompositionBounds; + gfx::Rect scaledCompositionBounds = + gfx::Rect(compositionBounds.x, compositionBounds.y, + compositionBounds.width, compositionBounds.height); + scaledCompositionBounds.ScaleInverseRoundIn( + mAsyncPanZoomController->GetFrameMetrics().mResolution.width); + return GetRectLength(scaledCompositionBounds); } float Axis::GetPageStart() { - gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mCSSContentRect; + gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect; return GetRectOffset(pageRect); } float Axis::GetPageLength() { - gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mCSSContentRect; + gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect; return GetRectLength(pageRect); } bool Axis::ScaleWillOverscrollBothSides(float aScale) { const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics(); - gfx::Rect cssContentRect = metrics.mCSSContentRect; + gfx::Rect cssContentRect = metrics.mScrollableRect; float currentScale = metrics.mResolution.width; - gfx::Rect viewport = gfx::Rect(metrics.mViewport.x, - metrics.mViewport.y, - metrics.mViewport.width, - metrics.mViewport.height); - viewport.ScaleRoundIn(1 / (currentScale * aScale)); + nsIntRect compositionBounds = metrics.mCompositionBounds; + gfx::Rect scaledCompositionBounds = + gfx::Rect(compositionBounds.x, compositionBounds.y, + compositionBounds.width, compositionBounds.height); + scaledCompositionBounds.ScaleInverseRoundIn(currentScale * aScale); - return GetRectLength(cssContentRect) < GetRectLength(viewport); + return GetRectLength(cssContentRect) < GetRectLength(scaledCompositionBounds); } AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController) diff --git a/gfx/layers/ipc/Axis.h b/gfx/layers/ipc/Axis.h index 9a9cbe32383..9d953492c8c 100644 --- a/gfx/layers/ipc/Axis.h +++ b/gfx/layers/ipc/Axis.h @@ -165,10 +165,10 @@ public: bool ScaleWillOverscrollBothSides(float aScale); float GetOrigin(); - float GetViewportLength(); + float GetCompositionLength(); float GetPageStart(); float GetPageLength(); - float GetViewportEnd(); + float GetCompositionEnd(); float GetPageEnd(); virtual float GetPointOffset(const gfx::Point& aPoint) = 0; diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 766ee88f56d..af6251adc2b 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -467,8 +467,8 @@ private: } const FrameMetrics& fm = c->GetFrameMetrics(); gfx3DMatrix m(aContainer->GetTransform()); - m.Translate(gfxPoint3D(-fm.mViewportScrollOffset.x, - -fm.mViewportScrollOffset.y, 0)); + m.Translate(gfxPoint3D(-fm.GetScrollOffsetInLayerPixels().x, + -fm.GetScrollOffsetInLayerPixels().y, 0)); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective @@ -787,30 +787,43 @@ CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame) float rootScaleX = rootTransform.GetXScale(), rootScaleY = rootTransform.GetYScale(); + // The ratio of layers pixels to device pixels. The Java + // compositor wants to see values in units of device pixels, so we + // map our FrameMetrics values to that space. This is not exposed + // as a FrameMetrics helper because it's a deprecated conversion. + float devPixelRatioX = 1 / rootScaleX, devPixelRatioY = 1 / rootScaleY; + + gfx::Point scrollOffsetLayersPixels(metrics.GetScrollOffsetInLayerPixels()); + nsIntPoint scrollOffsetDevPixels( + NS_lround(scrollOffsetLayersPixels.x * devPixelRatioX), + NS_lround(scrollOffsetLayersPixels.y * devPixelRatioY)); if (mIsFirstPaint) { mContentRect = metrics.mContentRect; - const gfx::Point& scrollOffset = metrics.mViewportScrollOffset; - SetFirstPaintViewport(nsIntPoint(NS_lround(scrollOffset.x), - NS_lround(scrollOffset.y)), + SetFirstPaintViewport(scrollOffsetDevPixels, 1/rootScaleX, mContentRect, - metrics.mCSSContentRect); + metrics.mScrollableRect); mIsFirstPaint = false; } else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) { mContentRect = metrics.mContentRect; - SetPageRect(metrics.mCSSContentRect); + SetPageRect(metrics.mScrollableRect); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. // Calculate the absolute display port to send to Java - nsIntRect displayPort = metrics.mDisplayPort; - gfx::Point scrollOffset = metrics.mViewportScrollOffset; - displayPort.x += NS_lround(scrollOffset.x); - displayPort.y += NS_lround(scrollOffset.y); + gfx::Rect displayPortLayersPixels(metrics.mDisplayPort); + nsIntRect displayPortDevPixels( + NS_lround(displayPortLayersPixels.x * devPixelRatioX), + NS_lround(displayPortLayersPixels.y * devPixelRatioY), + NS_lround(displayPortLayersPixels.width * devPixelRatioX), + NS_lround(displayPortLayersPixels.height * devPixelRatioY)); - SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated, + displayPortDevPixels.x += scrollOffsetDevPixels.x; + displayPortDevPixels.y += scrollOffsetDevPixels.y; + + SyncViewportInfo(displayPortDevPixels, 1/rootScaleX, mLayersUpdated, mScrollOffset, mXScale, mYScale); mLayersUpdated = false; @@ -825,8 +838,7 @@ CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame) nsIntPoint metricsScrollOffset(0, 0); if (metrics.IsScrollable()) { - metricsScrollOffset = - nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y)); + metricsScrollOffset = scrollOffsetDevPixels; } nsIntPoint scrollCompensation( diff --git a/gfx/layers/opengl/ReusableTileStoreOGL.cpp b/gfx/layers/opengl/ReusableTileStoreOGL.cpp index e153a58919a..4be94b40c74 100644 --- a/gfx/layers/opengl/ReusableTileStoreOGL.cpp +++ b/gfx/layers/opengl/ReusableTileStoreOGL.cpp @@ -37,7 +37,9 @@ ReusableTileStoreOGL::InvalidateTiles(TiledThebesLayerOGL* aLayer, // This will be incorrect when the transform involves rotation, but // it'd be quite hard to retain invalid tiles correctly in this // situation anyway. - renderBounds = parent->GetEffectiveTransform().TransformBounds(gfxRect(metrics.mDisplayPort)); + renderBounds = parent->GetEffectiveTransform().TransformBounds( + gfxRect(metrics.mDisplayPort.x, metrics.mDisplayPort.y, + metrics.mDisplayPort.width, metrics.mDisplayPort.height)); break; } } @@ -217,10 +219,14 @@ ReusableTileStoreOGL::DrawTiles(TiledThebesLayerOGL* aLayer, scrollableLayer = parent; if (!parentMetrics.mDisplayPort.IsEmpty() && scrollableLayer) { displayPort = parent->GetEffectiveTransform(). - TransformBounds(gfxRect(parentMetrics.mDisplayPort)); + TransformBounds(gfxRect( + parentMetrics.mDisplayPort.x, parentMetrics.mDisplayPort.y, + parentMetrics.mDisplayPort.width, parentMetrics.mDisplayPort.height)); const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics(); const nsIntSize& contentSize = metrics.mContentRect.Size(); - const gfx::Point& scrollOffset = metrics.mViewportScrollOffset; + gfx::Point scrollOffset = + gfx::Point(metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().width, + metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().height); const nsIntPoint& contentOrigin = metrics.mContentRect.TopLeft() - nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y)); gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y, diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 7115b3fab48..187de99069d 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -960,25 +960,29 @@ struct ParamTraits static void Write(Message* aMsg, const paramType& aParam) { - WriteParam(aMsg, aParam.mCSSContentRect); + WriteParam(aMsg, aParam.mScrollableRect); WriteParam(aMsg, aParam.mViewport); WriteParam(aMsg, aParam.mContentRect); - WriteParam(aMsg, aParam.mViewportScrollOffset); + WriteParam(aMsg, aParam.mScrollOffset); WriteParam(aMsg, aParam.mDisplayPort); + WriteParam(aMsg, aParam.mCompositionBounds); WriteParam(aMsg, aParam.mScrollId); WriteParam(aMsg, aParam.mResolution); + WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); WriteParam(aMsg, aParam.mMayHaveTouchListeners); } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { - return (ReadParam(aMsg, aIter, &aResult->mCSSContentRect) && + return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) && ReadParam(aMsg, aIter, &aResult->mViewport) && ReadParam(aMsg, aIter, &aResult->mContentRect) && - ReadParam(aMsg, aIter, &aResult->mViewportScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mScrollOffset) && ReadParam(aMsg, aIter, &aResult->mDisplayPort) && + ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && ReadParam(aMsg, aIter, &aResult->mScrollId) && ReadParam(aMsg, aIter, &aResult->mResolution) && + ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners)); } }; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 487d3e7d63b..8950a74498c 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -575,6 +575,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, bool aMayHaveTouchListeners) { nsPresContext* presContext = aForFrame->PresContext(); int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); + float auPerCSSPixel = nsPresContext::AppUnitsPerCSSPixel(); nsIntRect visible = aVisibleRect.ScaleToNearestPixels( aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel); @@ -582,12 +583,18 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, FrameMetrics metrics; - metrics.mViewport = aViewport.ScaleToNearestPixels( - aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel); + metrics.mViewport = mozilla::gfx::Rect( + NSAppUnitsToDoublePixels(aViewport.x, auPerDevPixel), + NSAppUnitsToDoublePixels(aViewport.y, auPerDevPixel), + NSAppUnitsToDoublePixels(aViewport.width, auPerDevPixel), + NSAppUnitsToDoublePixels(aViewport.height, auPerDevPixel)); if (aDisplayPort) { - metrics.mDisplayPort = aDisplayPort->ScaleToNearestPixels( - aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel); + metrics.mDisplayPort = mozilla::gfx::Rect( + NSAppUnitsToDoublePixels(aDisplayPort->x, auPerDevPixel), + NSAppUnitsToDoublePixels(aDisplayPort->y, auPerDevPixel), + NSAppUnitsToDoublePixels(aDisplayPort->width, auPerDevPixel), + NSAppUnitsToDoublePixels(aDisplayPort->height, auPerDevPixel)); } nsIScrollableFrame* scrollableFrame = nullptr; @@ -598,7 +605,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, nsRect contentBounds = scrollableFrame->GetScrollRange(); contentBounds.width += scrollableFrame->GetScrollPortRect().width; contentBounds.height += scrollableFrame->GetScrollPortRect().height; - metrics.mCSSContentRect = + metrics.mScrollableRect = mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x), nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y), nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width), @@ -606,13 +613,13 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, metrics.mContentRect = contentBounds.ScaleToNearestPixels( aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel); nsPoint scrollPosition = scrollableFrame->GetScrollPosition(); - metrics.mViewportScrollOffset = mozilla::gfx::Point( - NSAppUnitsToDoublePixels(scrollPosition.x, auPerDevPixel) * aContainerParameters.mXScale, - NSAppUnitsToDoublePixels(scrollPosition.y, auPerDevPixel) * aContainerParameters.mYScale); + metrics.mScrollOffset = mozilla::gfx::Point( + NSAppUnitsToDoublePixels(scrollPosition.x, auPerCSSPixel), + NSAppUnitsToDoublePixels(scrollPosition.y, auPerCSSPixel)); } else { nsRect contentBounds = aForFrame->GetRect(); - metrics.mCSSContentRect = + metrics.mScrollableRect = mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x), nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y), nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width), @@ -626,6 +633,8 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, nsIPresShell* presShell = presContext->GetPresShell(); metrics.mResolution = gfxSize(presShell->GetXResolution(), presShell->GetYResolution()); + metrics.mDevPixelsPerCSSPixel = auPerCSSPixel / auPerDevPixel; + metrics.mMayHaveTouchListeners = aMayHaveTouchListeners; aRoot->SetFrameMetrics(metrics); @@ -1070,8 +1079,10 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder, } } + nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize()); + RecordFrameMetrics(aForFrame, rootScrollFrame, - root, mVisibleRect, mVisibleRect, + root, mVisibleRect, viewport, (usingDisplayport ? &displayport : nullptr), id, containerParameters, mayHaveTouchListeners); if (usingDisplayport && diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index 28fd8829dde..5f34079eae5 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -158,7 +158,7 @@ ComputeShadowTreeTransform(nsIFrame* aContainerFrame, nsIntPoint scrollOffset = aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel); // metricsScrollOffset is in layer coordinates. - gfx::Point metricsScrollOffset = aMetrics->mViewportScrollOffset; + gfx::Point metricsScrollOffset = aMetrics->GetScrollOffsetInLayerPixels(); nsIntPoint roundedMetricsScrollOffset = nsIntPoint(NS_lround(metricsScrollOffset.x), NS_lround(metricsScrollOffset.y)); @@ -217,7 +217,9 @@ BuildListForLayer(Layer* aLayer, nsRect bounds; { nscoord auPerDevPixel = aSubdocFrame->PresContext()->AppUnitsPerDevPixel(); - bounds = metrics->mViewport.ToAppUnits(auPerDevPixel); + gfx::Rect viewport = metrics->mViewport; + bounds = nsIntRect(viewport.x, viewport.y, + viewport.width, viewport.height).ToAppUnits(auPerDevPixel); ApplyTransform(bounds, tmpTransform, auPerDevPixel); } @@ -362,6 +364,7 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews, if (metrics.IsScrollable()) { nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent() ->PresContext()->AppUnitsPerDevPixel(); + nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel; nsContentView* view = FindViewForId(oldContentViews, scrollId); if (view) { // View already exists. Be sure to propagate scales for any values @@ -392,8 +395,8 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews, // The default scale is 1, so no need to propagate scale down. ViewConfig config; config.mScrollOffset = nsPoint( - NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.x, auPerDevPixel) * aXScale, - NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.y, auPerDevPixel) * aYScale); + NSIntPixelsToAppUnits(metrics.mScrollOffset.x, auPerCSSPixel) * aXScale, + NSIntPixelsToAppUnits(metrics.mScrollOffset.y, auPerCSSPixel) * aYScale); view = new nsContentView(aFrameLoader, scrollId, config); view->mParentScaleX = aAccConfigXScale; view->mParentScaleY = aAccConfigYScale; @@ -728,7 +731,8 @@ void RenderFrameParent::NotifyDimensionsChanged(int width, int height) { if (mPanZoomController) { - mPanZoomController->UpdateViewportSize(width, height); + mPanZoomController->UpdateCompositionBounds( + nsIntRect(0, 0, width, height)); } } From 2f62aa80d8db698896ac167c789e1bc1cb27f385 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Fri, 28 Sep 2012 22:16:36 -0400 Subject: [PATCH 03/21] Bug 784908: Part 2: Add a call to UpdateDimensions when a tab is first created r=cjones --- dom/ipc/TabParent.cpp | 18 ++++++++++++++++++ dom/ipc/TabParent.h | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 578566942e8..da75f24e89d 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -83,6 +83,7 @@ TabParent::TabParent(mozIApplication* aApp, bool aIsBrowserElement) , mIMECompositionStart(0) , mIMESeqno(0) , mEventCaptureDepth(0) + , mDimensions(0, 0) , mDPI(0) , mIsBrowserElement(aIsBrowserElement) , mShown(false) @@ -229,6 +230,7 @@ TabParent::Show(const nsIntSize& size) { // sigh mShown = true; + mDimensions = size; unused << SendShow(size); } @@ -239,6 +241,7 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size) if (RenderFrameParent* rfp = GetRenderFrame()) { rfp->NotifyDimensionsChanged(size.width, size.height); } + mDimensions = size; } void @@ -1172,6 +1175,21 @@ TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener, return true; } +bool +TabParent::RecvPRenderFrameConstructor(PRenderFrameParent* actor, + ScrollingBehavior* scrolling, + LayersBackend* backend, + int32_t* maxTextureSize, + uint64_t* layersId) +{ + RenderFrameParent* rfp = GetRenderFrame(); + if (mDimensions != nsIntSize() && rfp) { + rfp->NotifyDimensionsChanged(mDimensions.width, mDimensions.height); + } + + return true; +} + bool TabParent::RecvZoomToRect(const gfxRect& aRect) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index b676620c0d7..0645e539f7b 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -94,6 +94,11 @@ public: virtual bool RecvMoveFocus(const bool& aForward); virtual bool RecvEvent(const RemoteDOMEvent& aEvent); + virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* actor, + ScrollingBehavior* scrolling, + LayersBackend* backend, + int32_t* maxTextureSize, + uint64_t* layersId); virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener, const nsString& aURL, const nsString& aName, @@ -269,6 +274,7 @@ protected: // The number of event series we're currently capturing. int32_t mEventCaptureDepth; + nsIntSize mDimensions; float mDPI; bool mIsBrowserElement; bool mShown; From 2c2aabf8882bb2d0a77bb47ef066e18f60dbbe7c Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Fri, 28 Sep 2012 22:16:38 -0400 Subject: [PATCH 04/21] Bug 784908: Part 3: Distinguish resolution from a new zoom field on FrameMetrics r=roc --- dom/ipc/TabChild.cpp | 2 +- gfx/layers/FrameMetrics.h | 27 ++++++- gfx/layers/ipc/AsyncPanZoomController.cpp | 86 ++++++++++++----------- gfx/layers/ipc/AsyncPanZoomController.h | 8 +++ gfx/layers/ipc/Axis.cpp | 4 +- ipc/glue/IPCMessageUtils.h | 2 + 6 files changed, 82 insertions(+), 47 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 0c1d1b14fd0..d9d383bfe29 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -847,7 +847,7 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y)); // We don't treat the x and y scales any differently for this // semi-platform-specific code. - data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mResolution.width); + data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mZoom.width); data += nsPrintfCString(", \"displayPort\" : "); data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x); data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y); diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index c7a4c65cd26..2db60db9be7 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -184,10 +184,33 @@ public: // This is valid on any layer unless it has no content. gfx::Rect mScrollableRect; - // This represents the resolution at which the associated layer - // will been rendered. + // --------------------------------------------------------------------------- + // The following metrics are dimensionless. + // + + // The resolution, along both axes, that the current frame has been painted + // at. + // + // Every time this frame is composited and the compositor samples its + // transform, this metric is used to create a transform which is + // post-multiplied into the parent's transform. Since this only happens when + // we walk the layer tree, the resulting transform isn't stored here. Thus the + // resolution of parent layers is opaque to this metric. gfxSize mResolution; + // The amount we are currently zooming the frame. This is distinct from + // |mResolution| as we can paint a frame at a different resolution than we + // zoom it at. This is useful in situations where we want to zoom a frame + // without forcing a repaint. At steady state, this and |mResolution| will be + // equal. + // + // Every time this frame is composited and the compositor samples its + // transform, this metric is used to create a transform which is + // post-multiplied into the parent's transform. Since this only happens when + // we walk the layer tree, the resulting transform isn't stored here. Thus the + // zoom of parent layers is opaque to this metric. + gfxSize mZoom; + // The conversion factor between CSS pixels and device pixels for this frame. // This can vary based on a variety of things, such as reflowing-zoom. The // conversion factor for device pixels to layers pixels is just the diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 32193aadd38..78b306016f8 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -137,7 +137,7 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent, gfx::Point currentScrollOffset, lastScrollOffset; { MonitorAutoLock monitor(mMonitor); - currentZoom = mFrameMetrics.mResolution.width; + currentZoom = mFrameMetrics.mZoom.width; currentScrollOffset = gfx::Point(mFrameMetrics.mScrollOffset.x, mFrameMetrics.mScrollOffset.y); lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mScrollOffset.x, @@ -284,8 +284,13 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent case ANIMATING_ZOOM: // We just interrupted a double-tap animation, so force a redraw in case // this touchstart is just a tap that doesn't end up triggering a redraw. - RequestContentRepaint(); - ScheduleComposite(); + { + MonitorAutoLock monitor(mMonitor); + // Bring the resolution back in sync with the zoom. + SetZoomAndResolution(mFrameMetrics.mZoom.width); + RequestContentRepaint(); + ScheduleComposite(); + } // Fall through. case FLING: CancelAnimation(); @@ -422,7 +427,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { { MonitorAutoLock monitor(mMonitor); - float scale = mFrameMetrics.mResolution.width; + float scale = mFrameMetrics.mZoom.width; nsIntPoint focusPoint = aEvent.mFocusPoint; float xFocusChange = (mLastZoomFocus.x - focusPoint.x) / scale, yFocusChange = (mLastZoomFocus.y - focusPoint.y) / scale; @@ -529,7 +534,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven gfx::Point point = WidgetSpaceToCompensatedViewportSpace( gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y), - mFrameMetrics.mResolution.width); + mFrameMetrics.mZoom.width); mGeckoContentController->HandleSingleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y))); return nsEventStatus_eConsumeNoDefault; } @@ -547,7 +552,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) gfx::Point point = WidgetSpaceToCompensatedViewportSpace( gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y), - mFrameMetrics.mResolution.width); + mFrameMetrics.mZoom.width); mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y))); return nsEventStatus_eConsumeNoDefault; } @@ -614,7 +619,7 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) { // We want to inversely scale it because when you're zoomed further in, a // larger swipe should move you a shorter distance. - float inverseScale = 1 / mFrameMetrics.mResolution.width; + float inverseScale = 1 / mFrameMetrics.mZoom.width; int32_t xDisplacement = mX.GetDisplacementForDuration(inverseScale, timeDelta); int32_t yDisplacement = mY.GetDisplacementForDuration(inverseScale, timeDelta); @@ -649,7 +654,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) { // We want to inversely scale it because when you're zoomed further in, a // larger swipe should move you a shorter distance. - float inverseScale = 1 / mFrameMetrics.mResolution.width; + float inverseScale = 1 / mFrameMetrics.mZoom.width; ScrollBy(gfx::Point( mX.GetDisplacementForDuration(inverseScale, aDelta), @@ -679,7 +684,7 @@ void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) { void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) { FrameMetrics metrics = mFrameMetrics; gfx::Rect pageSize = aCSSPageRect; - float scale = mFrameMetrics.mResolution.width; + float scale = mFrameMetrics.mZoom.width; // The page rect is the css page rect scaled by the current zoom. pageSize.ScaleInverseRoundOut(scale); @@ -693,26 +698,17 @@ void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) { } void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) { - FrameMetrics metrics(mFrameMetrics); + float scaleFactor = aScale / mFrameMetrics.mZoom.width, + oldScale = mFrameMetrics.mZoom.width; - // Don't set the scale to the inputted value, but rather multiply it in. - float scaleFactor = aScale / metrics.mResolution.width, - oldScale = metrics.mResolution.width; - - metrics.mResolution.width = metrics.mResolution.height = aScale; + SetZoomAndResolution(aScale); // Force a recalculation of the page rect based on the new zoom and the // current CSS page rect (which is unchanged since it's not affected by zoom). SetPageRect(mFrameMetrics.mScrollableRect); - gfx::Point scrollOffset = metrics.mScrollOffset; - - scrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale; - scrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale; - - metrics.mScrollOffset = scrollOffset; - - mFrameMetrics = metrics; + mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale; + mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale; } bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBounds, @@ -733,7 +729,7 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBound } const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() { - float scale = mFrameMetrics.mResolution.width; + float scale = mFrameMetrics.mZoom.width; nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; compositionBounds.ScaleInverseRoundIn(scale); @@ -852,9 +848,9 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa } double sampledPosition = gComputedTimingFunction->GetValue(animPosition); - mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height = - mEndZoomToMetrics.mResolution.width * sampledPosition + - mStartZoomToMetrics.mResolution.width * (1 - sampledPosition); + mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = + mEndZoomToMetrics.mZoom.width * sampledPosition + + mStartZoomToMetrics.mZoom.width * (1 - sampledPosition); mFrameMetrics.mScrollOffset = gfx::Point( mEndZoomToMetrics.mScrollOffset.x * sampledPosition + @@ -866,6 +862,8 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa requestAnimationFrame = true; if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) { + // Bring the resolution in sync with the zoom. + SetZoomAndResolution(mFrameMetrics.mZoom.width); mState = NOTHING; RequestContentRepaint(); } @@ -880,8 +878,8 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa // transformed due to touches like panning or pinching. Eventually, the root // layer transform will become this during runtime, but we must wait for Gecko // to repaint. - localScaleX = mFrameMetrics.mResolution.width; - localScaleY = mFrameMetrics.mResolution.height; + localScaleX = mFrameMetrics.mZoom.width; + localScaleY = mFrameMetrics.mZoom.height; if (frame.IsScrollable()) { metricsScrollOffset = frame.GetScrollOffsetInLayerPixels(); @@ -936,10 +934,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr case FLING: case TOUCHING: case WAITING_LISTENERS: - // FIXME/bug 784908: Scroll offset is stored in layer pixels in the rest - // of the layers code, but we want it in CSS pixels. - mFrameMetrics.mScrollOffset = - aViewportFrame.mScrollOffset / aViewportFrame.mResolution.width; + mFrameMetrics.mScrollOffset = aViewportFrame.mScrollOffset; break; // Don't clobber if we're in other states. default: @@ -958,8 +953,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; mFrameMetrics = aViewportFrame; mFrameMetrics.mCompositionBounds = compositionBounds; - mFrameMetrics.mResolution.width = 1 / mFrameMetrics.mResolution.width; - mFrameMetrics.mResolution.height = 1 / mFrameMetrics.mResolution.height; + + // On first paint, we want to bring zoom back in sync with resolution. + mFrameMetrics.mZoom = mFrameMetrics.mResolution; SetPageRect(mFrameMetrics.mScrollableRect); // Bug 776413/fixme: Request a repaint as soon as a page is loaded so that @@ -1010,7 +1006,7 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) { if (zoomToRect.IsEmpty()) { // composition bounds in CSS coordinates nsIntRect cssCompositionBounds = compositionBounds; - cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mResolution.width); + cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mZoom.width); cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y); float y = mFrameMetrics.mScrollOffset.y; @@ -1043,21 +1039,21 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) { zoomToRect = zoomToRect.Intersect(cssPageRect); } - mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = + mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height = NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height); - mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = - clamped(mEndZoomToMetrics.mResolution.width, MIN_ZOOM, MAX_ZOOM); + mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height = + clamped(mEndZoomToMetrics.mZoom.width, MIN_ZOOM, MAX_ZOOM); // Recalculate the zoom to rect using the new dimensions. - zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mResolution.width; - zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mResolution.height; + zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width; + zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mZoom.height; // Clamp the zoom to rect to the CSS rect to make sure it fits. zoomToRect = zoomToRect.Intersect(cssPageRect); // Do one final recalculation to get the resolution. - mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height = + mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height = NS_MAX(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height); mStartZoomToMetrics = mFrameMetrics; @@ -1115,5 +1111,11 @@ void AsyncPanZoomController::TimeoutTouchListeners() { ContentReceivedTouch(false); } +void AsyncPanZoomController::SetZoomAndResolution(float aScale) { + mMonitor.AssertCurrentThreadOwns(); + mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height = + mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale; +} + } } diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index 375cedecc01..370149d7f82 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -392,6 +392,14 @@ protected: */ void TimeoutTouchListeners(); + /** + * Utility function that sets the zoom and resolution simultaneously. This is + * useful when we want to repaint at the current zoom level. + * + * *** The monitor must be held while calling this. + */ + void SetZoomAndResolution(float aScale); + private: enum PanZoomState { NOTHING, /* no touch-start events received */ diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/ipc/Axis.cpp index e520a791b3e..4fef1cc9302 100644 --- a/gfx/layers/ipc/Axis.cpp +++ b/gfx/layers/ipc/Axis.cpp @@ -251,7 +251,7 @@ float Axis::GetCompositionLength() { gfx::Rect(compositionBounds.x, compositionBounds.y, compositionBounds.width, compositionBounds.height); scaledCompositionBounds.ScaleInverseRoundIn( - mAsyncPanZoomController->GetFrameMetrics().mResolution.width); + mAsyncPanZoomController->GetFrameMetrics().mZoom.width); return GetRectLength(scaledCompositionBounds); } @@ -270,7 +270,7 @@ bool Axis::ScaleWillOverscrollBothSides(float aScale) { gfx::Rect cssContentRect = metrics.mScrollableRect; - float currentScale = metrics.mResolution.width; + float currentScale = metrics.mZoom.width; nsIntRect compositionBounds = metrics.mCompositionBounds; gfx::Rect scaledCompositionBounds = gfx::Rect(compositionBounds.x, compositionBounds.y, diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 187de99069d..89e81b1e82b 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -968,6 +968,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mCompositionBounds); WriteParam(aMsg, aParam.mScrollId); WriteParam(aMsg, aParam.mResolution); + WriteParam(aMsg, aParam.mZoom); WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); WriteParam(aMsg, aParam.mMayHaveTouchListeners); } @@ -982,6 +983,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && ReadParam(aMsg, aIter, &aResult->mScrollId) && ReadParam(aMsg, aIter, &aResult->mResolution) && + ReadParam(aMsg, aIter, &aResult->mZoom) && ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners)); } From 4f2bfeb032baa13f5132bf195fd460fa9f2e1c6f Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Fri, 28 Sep 2012 22:18:18 -0400 Subject: [PATCH 05/21] Bug 746502: Add support for on B2G/async panning and zooming r=cjones,smaug --- .../BrowserElementScrolling.js | 22 -- dom/ipc/PBrowser.ipdl | 6 + dom/ipc/TabChild.cpp | 349 +++++++++++++++++- dom/ipc/TabChild.h | 28 ++ dom/ipc/TabParent.cpp | 11 + dom/ipc/TabParent.h | 3 + gfx/layers/ipc/AsyncPanZoomController.cpp | 92 +++-- gfx/layers/ipc/AsyncPanZoomController.h | 50 ++- layout/ipc/RenderFrameParent.cpp | 8 + layout/ipc/RenderFrameParent.h | 2 + 10 files changed, 497 insertions(+), 74 deletions(-) diff --git a/dom/browser-element/BrowserElementScrolling.js b/dom/browser-element/BrowserElementScrolling.js index daac3f65824..0061aeb2e9e 100644 --- a/dom/browser-element/BrowserElementScrolling.js +++ b/dom/browser-element/BrowserElementScrolling.js @@ -220,28 +220,6 @@ const ContentPanning = { metrics.cssPageRect.y, metrics.cssPageRect.width, metrics.cssPageRect.height); - - let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - if (this._compositionWidth != compositionWidth || this._compositionHeight != compositionHeight) { - cwu.setCSSViewport(compositionWidth, compositionHeight); - this._compositionWidth = compositionWidth; - this._compositionHeight = compositionHeight; - } - - // Set scroll position - cwu.setScrollPositionClampingScrollPortSize( - compositionWidth / metrics.zoom, compositionHeight / metrics.zoom); - content.scrollTo(x, y); - cwu.setResolution(displayPort.resolution, displayPort.resolution); - - let element = null; - if (content.document && (element = content.document.documentElement)) { - cwu.setDisplayPortForElement(displayPort.x, - displayPort.y, - displayPort.width, - displayPort.height, - element); - } }, _recvDoubleTap: function(data) { diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index c76315d351e..bc8562201aa 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -266,6 +266,12 @@ parent: */ ContentReceivedTouch(bool aPreventDefault); + /** + * Updates any zoom constraints on the parent and anything tied to it. This + * is useful for control logic that resides outside of the remote browser. + */ + UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom); + __delete__(); child: diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index d9d383bfe29..1d53c6eb815 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/PContentChild.h" #include "mozilla/dom/PContentDialogChild.h" #include "mozilla/ipc/DocumentRendererChild.h" +#include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/PLayersChild.h" #include "mozilla/layout/RenderFrameChild.h" @@ -29,10 +30,12 @@ #include "nsContentUtils.h" #include "nsEmbedCID.h" #include "nsEventListenerManager.h" +#include "nsGenericElement.h" #include "nsIAppsService.h" #include "nsIBaseWindow.h" #include "nsIComponentManager.h" #include "nsIDOMClassInfo.h" +#include "nsIDOMElement.h" #include "nsIDOMEvent.h" #include "nsIDOMWindow.h" #include "nsIDOMWindowUtils.h" @@ -50,6 +53,8 @@ #include "nsIServiceManager.h" #include "nsISupportsImpl.h" #include "nsIURI.h" +#include "nsIURIFixup.h" +#include "nsCDefaultURIFixup.h" #include "nsIView.h" #include "nsIWebBrowser.h" #include "nsIWebBrowserFocus.h" @@ -85,6 +90,12 @@ using namespace mozilla::widget; NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener) +static const nsIntSize kDefaultViewportSize(980, 480); + +static const char CANCEL_DEFAULT_PAN_ZOOM[] = "cancel-default-pan-zoom"; +static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect"; +static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; + NS_IMETHODIMP ContentListener::HandleEvent(nsIDOMEvent* aEvent) { @@ -151,28 +162,45 @@ TabChild::TabChild(uint32_t aChromeFlags, bool aIsBrowserElement, , mTabChildGlobal(nullptr) , mChromeFlags(aChromeFlags) , mOuterRect(0, 0, 0, 0) + , mInnerSize(0, 0) + , mOldViewportWidth(0.0f) , mLastBackgroundColor(NS_RGB(255, 255, 255)) , mAppId(aAppId) , mDidFakeShow(false) , mIsBrowserElement(aIsBrowserElement) , mNotified(false) + , mContentDocumentIsDisplayed(false) , mTriedBrowserInit(false) { printf("creating %d!\n", NS_IsMainThread()); } -nsresult +NS_IMETHODIMP +TabChild::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString eventType; + aEvent->GetType(eventType); + if (eventType.EqualsLiteral("DOMMetaAdded")) { + // This meta data may or may not have been a meta viewport tag. If it was, + // we should handle it immediately. + HandlePossibleMetaViewportChange(); + } + + return NS_OK; +} + +NS_IMETHODIMP TabChild::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { - if (!strcmp(aTopic, "cancel-default-pan-zoom")) { + if (!strcmp(aTopic, CANCEL_DEFAULT_PAN_ZOOM)) { nsCOMPtr docShell(do_QueryInterface(aSubject)); nsCOMPtr tabChild(GetTabChildFrom(docShell)); if (tabChild == this) { mRemoteFrame->CancelDefaultPanZoom(); } - } else if (!strcmp(aTopic, "browser-zoom-to-rect")) { + } else if (!strcmp(aTopic, BROWSER_ZOOM_TO_RECT)) { nsCOMPtr docShell(do_QueryInterface(aSubject)); nsCOMPtr tabChild(GetTabChildFrom(docShell)); if (tabChild == this) { @@ -182,11 +210,262 @@ TabChild::Observe(nsISupports *aSubject, &rect.x, &rect.y, &rect.width, &rect.height); SendZoomToRect(rect); } + } else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) { + if (IsAsyncPanZoomEnabled()) { + nsCOMPtr subject(do_QueryInterface(aSubject)); + nsCOMPtr domDoc; + mWebNav->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc(do_QueryInterface(domDoc)); + + if (SameCOMIdentity(subject, doc)) { + nsCOMPtr utils(GetDOMWindowUtils()); + + mContentDocumentIsDisplayed = true; + + // Reset CSS viewport and zoom to default on new page, then calculate them + // properly using the actual metadata from the page. + SetCSSViewport(kDefaultViewportSize.width, kDefaultViewportSize.height); + + // Calculate a really simple resolution that we probably won't be + // keeping, as well as putting the scroll offset back to the top-left of + // the page. + float resolution = float(mInnerSize.width) / float(kDefaultViewportSize.width); + mLastMetrics.mZoom.width = mLastMetrics.mZoom.height = + mLastMetrics.mResolution.width = mLastMetrics.mResolution.height = + resolution; + mLastMetrics.mScrollOffset = gfx::Point(0, 0); + utils->SetResolution(resolution, resolution); + + HandlePossibleMetaViewportChange(); + } + } } return NS_OK; } +NS_IMETHODIMP +TabChild::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *aLocation, + uint32_t aFlags) +{ + if (!IsAsyncPanZoomEnabled()) { + return NS_OK; + } + + nsCOMPtr window; + aWebProgress->GetDOMWindow(getter_AddRefs(window)); + if (!window) { + return NS_OK; + } + + nsCOMPtr progressDoc; + window->GetDocument(getter_AddRefs(progressDoc)); + if (!progressDoc) { + return NS_OK; + } + + nsCOMPtr domDoc; + mWebNav->GetDocument(getter_AddRefs(domDoc)); + if (!domDoc || !SameCOMIdentity(domDoc, progressDoc)) { + return NS_OK; + } + + nsCOMPtr urifixup(do_GetService(NS_URIFIXUP_CONTRACTID)); + if (!urifixup) { + return NS_OK; + } + + nsCOMPtr exposableURI; + urifixup->CreateExposableURI(aLocation, getter_AddRefs(exposableURI)); + if (!exposableURI) { + return NS_OK; + } + + if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { + mContentDocumentIsDisplayed = false; + } else if (mLastURI != nullptr) { + bool exposableEqualsLast, exposableEqualsNew; + exposableURI->Equals(mLastURI.get(), &exposableEqualsLast); + exposableURI->Equals(aLocation, &exposableEqualsNew); + if (exposableEqualsLast && !exposableEqualsNew) { + mContentDocumentIsDisplayed = false; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const PRUnichar* aMessage) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) +{ + NS_NOTREACHED("not implemented in TabChild"); + return NS_OK; +} + +void +TabChild::SetCSSViewport(float aWidth, float aHeight) +{ + mOldViewportWidth = aWidth; + + if (mContentDocumentIsDisplayed) { + nsCOMPtr utils(GetDOMWindowUtils()); + utils->SetCSSViewport(aWidth, aHeight); + } +} + +void +TabChild::HandlePossibleMetaViewportChange() +{ + if (!IsAsyncPanZoomEnabled()) { + return; + } + + nsCOMPtr domDoc; + mWebNav->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr document(do_QueryInterface(domDoc)); + + nsCOMPtr utils(GetDOMWindowUtils()); + + ViewportInfo viewportMetaData = + nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height); + SendUpdateZoomConstraints(viewportMetaData.allowZoom, + viewportMetaData.minZoom, + viewportMetaData.maxZoom); + + float screenW = mInnerSize.width; + float screenH = mInnerSize.height; + float viewportW = viewportMetaData.width; + float viewportH = viewportMetaData.height; + + // We're not being displayed in any way; don't bother doing anything because + // that will just confuse future adjustments. + if (!screenW || !screenH) { + return; + } + + // Make sure the viewport height is not shorter than the window when the page + // is zoomed out to show its full width. Note that before we set the viewport + // width, the "full width" of the page isn't properly defined, so that's why + // we have to call SetCSSViewport twice - once to set the width, and the + // second time to figure out the height based on the layout at that width. + float oldBrowserWidth = mOldViewportWidth; + mLastMetrics.mViewport.width = viewportMetaData.width; + mLastMetrics.mViewport.height = viewportMetaData.height; + if (!oldBrowserWidth) { + oldBrowserWidth = kDefaultViewportSize.width; + } + SetCSSViewport(viewportW, viewportH); + + // If this page has not been painted yet, then this must be getting run + // because a meta-viewport element was added (via the DOMMetaAdded handler). + // in this case, we should not do anything that forces a reflow (see bug + // 759678) such as requesting the page size or sending a viewport update. this + // code will get run again in the before-first-paint handler and that point we + // will run though all of it. the reason we even bother executing up to this + // point on the DOMMetaAdded handler is so that scripts that use + // window.innerWidth before they are painted have a correct value (bug + // 771575). + if (!mContentDocumentIsDisplayed) { + return; + } + + float minScale = 1.0f; + + nsCOMPtr htmlDOMElement = do_QueryInterface(document->GetHtmlElement()); + nsCOMPtr bodyDOMElement = do_QueryInterface(document->GetBodyElement()); + + PRInt32 htmlWidth = 0, htmlHeight = 0; + if (htmlDOMElement) { + htmlDOMElement->GetScrollWidth(&htmlWidth); + htmlDOMElement->GetScrollHeight(&htmlHeight); + } + PRInt32 bodyWidth = 0, bodyHeight = 0; + if (bodyDOMElement) { + bodyDOMElement->GetScrollWidth(&bodyWidth); + bodyDOMElement->GetScrollHeight(&bodyHeight); + } + + float pageWidth = NS_MAX(htmlWidth, bodyWidth); + float pageHeight = NS_MAX(htmlHeight, bodyHeight); + + minScale = mInnerSize.width / pageWidth; + minScale = clamped((double)minScale, viewportMetaData.minZoom, viewportMetaData.maxZoom); + + viewportH = NS_MAX(viewportH, screenH / minScale); + SetCSSViewport(viewportW, viewportH); + + // This change to the zoom accounts for all types of changes I can conceive: + // 1. screen size changes, CSS viewport does not (pages with no meta viewport + // or a fixed size viewport) + // 2. screen size changes, CSS viewport also does (pages with a device-width + // viewport) + // 3. screen size remains constant, but CSS viewport changes (meta viewport + // tag is added or removed) + // 4. neither screen size nor CSS viewport changes + // + // In all of these cases, we maintain how much actual content is visible + // within the screen width. Note that "actual content" may be different with + // respect to CSS pixels because of the CSS viewport size changing. + int32_t oldScreenWidth = mLastMetrics.mCompositionBounds.width; + if (!oldScreenWidth) { + oldScreenWidth = mInnerSize.width; + } + float zoomScale = (screenW * oldBrowserWidth) / (oldScreenWidth * viewportW); + + float zoom = clamped(double(mLastMetrics.mZoom.width * zoomScale), + viewportMetaData.minZoom, viewportMetaData.maxZoom); + utils->SetResolution(zoom, zoom); + + FrameMetrics metrics(mLastMetrics); + metrics.mViewport = gfx::Rect(0.0f, 0.0f, viewportW, viewportH); + metrics.mScrollableRect = gfx::Rect(0.0f, 0.0f, pageWidth, pageHeight); + metrics.mCompositionBounds = nsIntRect(0, 0, mInnerSize.width, mInnerSize.height); + metrics.mZoom.width = metrics.mZoom.height = + metrics.mResolution.width = metrics.mResolution.height = zoom; + metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort( + metrics, + gfx::Point(0.0f, 0.0f)); + // Force a repaint with these metrics. This, among other things, sets the + // displayport, so we start with async painting. + RecvUpdateFrame(metrics); +} + nsresult TabChild::Init() { @@ -238,6 +517,12 @@ TabChild::Init() "DNS prefetching enable step."); } + nsCOMPtr docShell = do_GetInterface(mWebNav); + MOZ_ASSERT(docShell); + nsCOMPtr webProgress = do_GetInterface(docShell); + NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE); + webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION); + return NS_OK; } @@ -266,8 +551,11 @@ NS_INTERFACE_MAP_BEGIN(TabChild) NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsITabChild) NS_INTERFACE_MAP_ENTRY(nsIDialogCreator) + NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference) NS_INTERFACE_MAP_END @@ -799,6 +1087,7 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size) mOuterRect.width = rect.width; mOuterRect.height = rect.height; + mInnerSize = size; mWidget->Resize(0, 0, size.width, size.height, true); @@ -806,6 +1095,8 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size) baseWin->SetPositionAndSize(0, 0, size.width, size.height, true); + HandlePossibleMetaViewportChange(); + return true; } @@ -842,6 +1133,8 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) return true; } + // The BrowserElementScrolling helper must know about these updated metrics + // for other functions it performs, such as double tap handling. nsCString data; data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x)); data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y)); @@ -871,6 +1164,32 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data); + nsCOMPtr utils(GetDOMWindowUtils()); + nsCOMPtr window = do_GetInterface(mWebNav); + + utils->SetScrollPositionClampingScrollPortSize( + aFrameMetrics.mCompositionBounds.width / aFrameMetrics.mZoom.width, + aFrameMetrics.mCompositionBounds.height / aFrameMetrics.mZoom.width); + window->ScrollTo(aFrameMetrics.mScrollOffset.x, + aFrameMetrics.mScrollOffset.y); + utils->SetResolution(aFrameMetrics.mResolution.width, + aFrameMetrics.mResolution.width); + + nsCOMPtr domDoc; + nsCOMPtr docElement; + mWebNav->GetDocument(getter_AddRefs(domDoc)); + if (domDoc) { + domDoc->GetDocumentElement(getter_AddRefs(docElement)); + if (docElement) { + utils->SetDisplayPortForElement( + aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y, + aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height, + docElement); + } + } + + mLastMetrics = aFrameMetrics; + return true; } @@ -928,8 +1247,7 @@ TabChild::RecvMouseEvent(const nsString& aType, const int32_t& aModifiers, const bool& aIgnoreRootScrollFrame) { - nsCOMPtr window = do_GetInterface(mWebNav); - nsCOMPtr utils = do_GetInterface(window); + nsCOMPtr utils(GetDOMWindowUtils()); NS_ENSURE_TRUE(utils, true); utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, 0, 0); @@ -1030,8 +1348,7 @@ TabChild::RecvKeyEvent(const nsString& aType, const int32_t& aModifiers, const bool& aPreventDefault) { - nsCOMPtr window = do_GetInterface(mWebNav); - nsCOMPtr utils = do_GetInterface(window); + nsCOMPtr utils(GetDOMWindowUtils()); NS_ENSURE_TRUE(utils, true); bool ignored = false; utils->SendKeyEvent(aType, aKeyCode, aCharCode, @@ -1281,6 +1598,13 @@ TabChild::RecvDestroy() ); } + nsCOMPtr observerService = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); + + observerService->RemoveObserver(this, CANCEL_DEFAULT_PAN_ZOOM); + observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT); + observerService->RemoveObserver(this, BEFORE_FIRST_PAINT); + // XXX what other code in ~TabChild() should we be running here? DestroyWindow(); @@ -1319,7 +1643,7 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading) mTabChildGlobal = scope; nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIDOMEventTarget*, scope); - + NS_ENSURE_TRUE(InitTabChildGlobalInternal(scopeSupports), false); scope->Init(); @@ -1327,6 +1651,8 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading) nsCOMPtr root = do_QueryInterface(chromeHandler); NS_ENSURE_TRUE(root, false); root->SetParentTarget(scope); + + chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), this, false); } if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) { @@ -1391,10 +1717,13 @@ TabChild::InitRenderingState() if (observerService) { observerService->AddObserver(this, - "cancel-default-pan-zoom", + CANCEL_DEFAULT_PAN_ZOOM, false); observerService->AddObserver(this, - "browser-zoom-to-rect", + BROWSER_ZOOM_TO_RECT, + false); + observerService->AddObserver(this, + BEFORE_FIRST_PAINT, false); } diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 1e6a0b232ab..aeae753937a 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -36,9 +36,11 @@ #include "nsNetUtil.h" #include "nsFrameMessageManager.h" #include "nsIScriptContext.h" +#include "nsIWebProgressListener.h" #include "nsDOMEventTargetHelper.h" #include "nsIDialogCreator.h" #include "nsIDialogParamBlock.h" +#include "nsIDOMWindowUtils.h" #include "nsIPresShell.h" #include "nsIPrincipal.h" #include "nsIScriptObjectPrincipal.h" @@ -141,6 +143,8 @@ class TabChild : public PBrowserChild, public nsIWebBrowserChromeFocus, public nsIInterfaceRequestor, public nsIWindowProvider, + public nsIDOMEventListener, + public nsIWebProgressListener, public nsSupportsWeakReference, public nsIDialogCreator, public nsITabChild, @@ -175,6 +179,8 @@ public: NS_DECL_NSIWEBBROWSERCHROMEFOCUS NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIWINDOWPROVIDER + NS_DECL_NSIDOMEVENTLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER NS_DECL_NSIDIALOGCREATOR NS_DECL_NSITABCHILD NS_DECL_NSIOBSERVER @@ -328,6 +334,16 @@ private: // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow(). void DoFakeShow(); + // Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state + // variables local to this class before setting it. + void SetCSSViewport(float aX, float aY); + + // Recalculates the display state, including the CSS viewport. This should + // be called whenever we believe the meta viewport data on a document may + // have changed. If it didn't change, this function doesn't do anything. + // However, it should not be called all the time as it is fairly expensive. + void HandlePossibleMetaViewportChange(); + // Wraps up a JSON object as a structured clone and sends it to the browser // chrome script. // @@ -347,18 +363,30 @@ private: bool* aWindowIsNew, nsIDOMWindow** aReturn); + nsIDOMWindowUtils* GetDOMWindowUtils() + { + nsCOMPtr window = do_GetInterface(mWebNav); + nsCOMPtr utils = do_GetInterface(window); + return utils; + } + nsCOMPtr mWebNav; nsCOMPtr mWidget; + nsCOMPtr mLastURI; + FrameMetrics mLastMetrics; RenderFrameChild* mRemoteFrame; nsRefPtr mTabChildGlobal; uint32_t mChromeFlags; nsIntRect mOuterRect; + nsIntSize mInnerSize; + float mOldViewportWidth; nscolor mLastBackgroundColor; ScrollingBehavior mScrolling; uint32_t mAppId; bool mDidFakeShow; bool mIsBrowserElement; bool mNotified; + bool mContentDocumentIsDisplayed; bool mTriedBrowserInit; DISALLOW_EVIL_CONSTRUCTORS(TabChild); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index da75f24e89d..13a05addbe6 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1199,6 +1199,17 @@ TabParent::RecvZoomToRect(const gfxRect& aRect) return true; } +bool +TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom, + const float& aMinZoom, + const float& aMaxZoom) +{ + if (RenderFrameParent* rfp = GetRenderFrame()) { + rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom); + } + return true; +} + bool TabParent::RecvContentReceivedTouch(const bool& aPreventDefault) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 0645e539f7b..285d145c020 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -136,6 +136,9 @@ public: virtual bool RecvGetDPI(float* aValue); virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue); virtual bool RecvZoomToRect(const gfxRect& aRect); + virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom, + const float& aMinZoom, + const float& aMaxZoom); virtual bool RecvContentReceivedTouch(const bool& aPreventDefault); virtual PContentDialogParent* AllocPContentDialog(const uint32_t& aType, const nsCString& aName, diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 78b306016f8..2420d96ab27 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -85,6 +85,9 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon mTouchListenerTimeoutTask(nullptr), mX(this), mY(this), + mAllowZoom(true), + mMinZoom(MIN_ZOOM), + mMaxZoom(MAX_ZOOM), mMonitor("AsyncPanZoomController"), mLastSampleTime(TimeStamp::Now()), mState(NOTHING), @@ -409,6 +412,10 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven } nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) { + if (!mAllowZoom) { + return nsEventStatus_eConsumeNoDefault; + } + SetState(PINCHING); mLastZoomFocus = aEvent.mFocusPoint; @@ -416,6 +423,10 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve } nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { + if (mState != PINCHING) { + return nsEventStatus_eConsumeNoDefault; + } + float prevSpan = aEvent.mPreviousSpan; if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) { // We're still handling it; we've just decided to throw this event away. @@ -447,14 +458,14 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { float neededDisplacementX = 0, neededDisplacementY = 0; // Only do the scaling if we won't go over 8x zoom in or out. - bool doScale = (scale < MAX_ZOOM && spanRatio > 1.0f) || (scale > MIN_ZOOM && spanRatio < 1.0f); + bool doScale = (scale < mMaxZoom && spanRatio > 1.0f) || (scale > mMinZoom && spanRatio < 1.0f); // If this zoom will take it over 8x zoom in either direction, but it's not // already there, then normalize it. - if (scale * spanRatio > MAX_ZOOM) { - spanRatio = scale / MAX_ZOOM; - } else if (scale * spanRatio < MIN_ZOOM) { - spanRatio = scale / MIN_ZOOM; + if (scale * spanRatio > mMaxZoom) { + spanRatio = scale / mMaxZoom; + } else if (scale * spanRatio < mMinZoom) { + spanRatio = scale / mMinZoom; } if (doScale) { @@ -550,10 +561,13 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) if (mGeckoContentController) { MonitorAutoLock monitor(mMonitor); - gfx::Point point = WidgetSpaceToCompensatedViewportSpace( - gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y), - mFrameMetrics.mZoom.width); - mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y))); + if (mAllowZoom) { + gfx::Point point = WidgetSpaceToCompensatedViewportSpace( + gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y), + mFrameMetrics.mZoom.width); + mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y))); + } + return nsEventStatus_eConsumeNoDefault; } return nsEventStatus_eIgnore; @@ -728,13 +742,15 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBound return false; } -const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() { - float scale = mFrameMetrics.mZoom.width; - nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds; +const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort( + const FrameMetrics& aFrameMetrics, + const gfx::Point& aVelocity) +{ + float scale = aFrameMetrics.mZoom.width; + nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds; compositionBounds.ScaleInverseRoundIn(scale); - gfx::Point scrollOffset = mFrameMetrics.mScrollOffset; - gfx::Point velocity = GetVelocityVector(); + gfx::Point scrollOffset = aFrameMetrics.mScrollOffset; const float STATIONARY_SIZE_MULTIPLIER = 2.0f; gfx::Rect displayPort(0, 0, @@ -745,9 +761,9 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() { // then we want to paint a larger area in the direction of that motion so that // it's less likely to checkerboard. bool enlargedX = EnlargeDisplayPortAlongAxis( - compositionBounds.width, velocity.x, &displayPort.x, &displayPort.width); + compositionBounds.width, aVelocity.x, &displayPort.x, &displayPort.width); bool enlargedY = EnlargeDisplayPortAlongAxis( - compositionBounds.height, velocity.y, &displayPort.y, &displayPort.height); + compositionBounds.height, aVelocity.y, &displayPort.y, &displayPort.height); if (!enlargedX && !enlargedY) { displayPort.x = -displayPort.width / 4; @@ -760,7 +776,7 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() { gfx::Rect shiftedDisplayPort = displayPort; shiftedDisplayPort.MoveBy(scrollOffset.x, scrollOffset.y); - displayPort = shiftedDisplayPort.Intersect(mFrameMetrics.mScrollableRect); + displayPort = shiftedDisplayPort.Intersect(aFrameMetrics.mScrollableRect); displayPort.MoveBy(-scrollOffset.x, -scrollOffset.y); return displayPort; @@ -781,7 +797,8 @@ void AsyncPanZoomController::ScheduleComposite() { } void AsyncPanZoomController::RequestContentRepaint() { - mFrameMetrics.mDisplayPort = CalculatePendingDisplayPort(); + mFrameMetrics.mDisplayPort = + CalculatePendingDisplayPort(mFrameMetrics, GetVelocityVector()); gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mScrollOffset, newScrollOffset = mFrameMetrics.mScrollOffset; @@ -958,11 +975,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mFrameMetrics.mZoom = mFrameMetrics.mResolution; SetPageRect(mFrameMetrics.mScrollableRect); - // Bug 776413/fixme: Request a repaint as soon as a page is loaded so that - // we get a larger displayport. This is very bad because we're wasting a - // paint and not initializating the displayport correctly. - RequestContentRepaint(); - mState = NOTHING; } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) { mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect; @@ -977,9 +989,25 @@ const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() { void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositionBounds) { MonitorAutoLock mon(mMonitor); - FrameMetrics metrics = GetFrameMetrics(); - metrics.mCompositionBounds = aCompositionBounds; - mFrameMetrics = metrics; + + nsIntRect oldCompositionBounds = mFrameMetrics.mCompositionBounds; + mFrameMetrics.mCompositionBounds = aCompositionBounds; + + // If the window had 0 dimensions before, or does now, we don't want to + // repaint or update the zoom since we'll run into rendering issues and/or + // divide-by-zero. This manifests itself as the screen flashing. If the page + // has gone out of view, the buffer will be cleared elsewhere anyways. + if (aCompositionBounds.width && aCompositionBounds.height && + oldCompositionBounds.width && oldCompositionBounds.height) { + // Alter the zoom such that we can see the same width of the page as we used + // to be able to. + SetZoomAndResolution(mFrameMetrics.mResolution.width * + aCompositionBounds.width / + oldCompositionBounds.width); + + // Repaint on a rotation so that our new resolution gets properly updated. + RequestContentRepaint(); + } } void AsyncPanZoomController::CancelDefaultPanZoom() { @@ -1043,7 +1071,9 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) { NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height); mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height = - clamped(mEndZoomToMetrics.mZoom.width, MIN_ZOOM, MAX_ZOOM); + clamped(float(mEndZoomToMetrics.mZoom.width), + mMinZoom, + mMaxZoom); // Recalculate the zoom to rect using the new dimensions. zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width; @@ -1117,5 +1147,13 @@ void AsyncPanZoomController::SetZoomAndResolution(float aScale) { mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale; } +void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom, + float aMinZoom, + float aMaxZoom) { + mAllowZoom = aAllowZoom; + mMinZoom = aMinZoom; + mMaxZoom = aMaxZoom; +} + } } diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index 370149d7f82..c9b9b337ec9 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -14,6 +14,7 @@ #include "mozilla/TimeStamp.h" #include "InputData.h" #include "Axis.h" +#include "nsContentUtils.h" #include "base/message_loop.h" @@ -130,6 +131,13 @@ public: */ void ContentReceivedTouch(bool aPreventDefault); + /** + * Updates any zoom constraints contained in the tag. + * We try to obey everything it asks us elsewhere, but here we only handle + * minimum-scale, maximum-scale, and user-scalable. + */ + void UpdateZoomConstraints(bool aAllowZoom, float aMinScale, float aMaxScale); + // -------------------------------------------------------------------------- // These methods must only be called on the compositor thread. // @@ -189,6 +197,16 @@ public: */ int GetDPI(); + /** + * Recalculates the displayport. Ideally, this should paint an area bigger + * than the composite-to dimensions so that when you scroll down, you don't + * checkerboard immediately. This includes a bunch of logic, including + * algorithms to bias painting in the direction of the velocity. + */ + static const gfx::Rect CalculatePendingDisplayPort( + const FrameMetrics& aFrameMetrics, + const gfx::Point& aVelocity); + protected: /** * Internal handler for ReceiveInputEvent(). Does all the actual work. @@ -339,15 +357,6 @@ protected: */ void TrackTouch(const MultiTouchInput& aEvent); - /** - * Recalculates the displayport. Ideally, this should paint an area bigger - * than the actual screen. The viewport refers to the size of the screen, - * while the displayport is the area actually painted by Gecko. We paint - * a larger area than the screen so that when you scroll down, you don't - * checkerboard immediately. - */ - const gfx::Rect CalculatePendingDisplayPort(); - /** * Attempts to enlarge the displayport along a single axis. Returns whether or * not the displayport was enlarged. This will fail in circumstances where the @@ -356,8 +365,10 @@ protected: * |aDisplayPortLength|. If enlarged, these will be updated with the new * metrics. */ - bool EnlargeDisplayPortAlongAxis(float aCompositionBounds, float aVelocity, - float* aDisplayPortOffset, float* aDisplayPortLength); + static bool EnlargeDisplayPortAlongAxis(float aCompositionBounds, + float aVelocity, + float* aDisplayPortOffset, + float* aDisplayPortLength); /** * Utility function to send updated FrameMetrics to Gecko so that it can paint @@ -471,10 +482,19 @@ private: AxisX mX; AxisY mY; - // Protects |mFrameMetrics|, |mLastContentPaintMetrics| and |mState|. Before - // manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the monitor - // should be held. When setting |mState|, either the SetState() function can - // be used, or the monitor can be held and then |mState| updated. + // Most up-to-date constraints on zooming. These should always be reasonable + // values; for example, allowing a min zoom of 0.0 can cause very bad things + // to happen. + bool mAllowZoom; + float mMinZoom; + float mMaxZoom; + + // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, |mState| and + // |mMetaViewportInfo|. Before manipulating |mFrameMetrics| or + // |mLastContentPaintMetrics|, the monitor should be held. When setting + // |mState|, either the SetState() function can be used, or the monitor can be + // held and then |mState| updated. |mMetaViewportInfo| should be updated + // using UpdateMetaViewport(). Monitor mMonitor; // The last time the compositor has sampled the content transform for this diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index 5f34079eae5..9fbff4f416e 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -913,6 +913,14 @@ RenderFrameParent::ContentReceivedTouch(bool aPreventDefault) } } +void +RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom) +{ + if (mPanZoomController) { + mPanZoomController->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom); + } +} + } // namespace layout } // namespace mozilla diff --git a/layout/ipc/RenderFrameParent.h b/layout/ipc/RenderFrameParent.h index afb6b78afb7..dd13148f047 100644 --- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -101,6 +101,8 @@ public: void ContentReceivedTouch(bool aPreventDefault); + void UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom); + protected: void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; From 6ba91c2842b93f83f66e9c6e0181f88021c51c6f Mon Sep 17 00:00:00 2001 From: Stephen Horlander Date: Fri, 28 Sep 2012 17:47:38 -0700 Subject: [PATCH 06/21] Bug 783778 - Update Google favicon in the search bar. r=dolske --- browser/locales/en-US/searchplugins/google.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/locales/en-US/searchplugins/google.xml b/browser/locales/en-US/searchplugins/google.xml index 4cd56d30fba..a285056fbae 100644 --- a/browser/locales/en-US/searchplugins/google.xml +++ b/browser/locales/en-US/searchplugins/google.xml @@ -16,7 +16,7 @@ Google Google Search UTF-8 -data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVR42pWTzUsCYRCH9y9zu3SooCCkjhIRRLeIykXokiWCJ7PvDpZRlz6si1lIQZ3SQxQdOhREpgSm0JeQvfu0+i6I7LKLh4F5h5nnnRl+o6jTdHn8omAYbVqhXqvYFXcEBKFDwcoZZB8B4LkEB9cwGGmFKHb01A1EU9JXzfdvDYZi1lwLwBcVAIwsNWPesIwls7gDtB2Z7N9ujVe+IX2LO2AgItB1OL9vJqsmILDrOoK02IkBAdYy4FsQJC5h+VQCHQDWTqYSgo8fuHuRxS4Ae3stQ7UGE5ttAHqCUgfxC7m4ryrowOyeO6CxqHwZxtYFqtYc5+kNan/gDTsAeueEIRj7n/rmRQMwueUAGF0VAAT3rQBTC0Y3DoDOGbm00icML4oWHYSTgo0MFqjlmPpDgqMcFCuQf4erBzjOwXjcriu9qHg0uutO2+es6fl67T9ptebvFRjBVgAAAABJRU5ErkJggg== #expand __GOOGLE_PARAMS__ From c061bf878dc8d8dda36b96e1990dbf88a668ff24 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Fri, 28 Sep 2012 22:52:11 -0400 Subject: [PATCH 07/21] Bug 792212 - Don't attempt to migrate profiles if using a custom profile;r=gbrown It seems that doing so triggers a race condition which can result in the fennec reftests failing, if the shared preferences associated with profile migration or the gecko application is not correctly initialized. This is more or less a band aid solution, but it addresses the problem until we can find the root cause. --- mobile/android/base/GeckoApp.java | 7 +++++-- mobile/android/base/GeckoProfile.java | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 45cf3b067e1..8900d58b3e0 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -156,6 +156,7 @@ abstract public class GeckoApp public static int mOrientation; private boolean mIsRestoringActivity; private String mCurrentResponse = ""; + public static boolean sIsUsingCustomProfile = false; private PromptService mPromptService; private Favicons mFavicons; @@ -1554,6 +1555,7 @@ abstract public class GeckoApp if (profileName == null) profileName = "default"; } + GeckoApp.sIsUsingCustomProfile = true; } if (profileName != null || profilePath != null) { mProfile = GeckoProfile.get(this, profileName, profilePath); @@ -2252,7 +2254,8 @@ abstract public class GeckoApp ProfileMigrator profileMigrator = new ProfileMigrator(app); // Do a migration run on the first start after an upgrade. - if (!profileMigrator.hasMigrationRun()) { + if (!GeckoApp.sIsUsingCustomProfile && + !profileMigrator.hasMigrationRun()) { // Show the "Setting up Fennec" screen if this takes // a while. final SetupScreen setupScreen = new SetupScreen(app); @@ -2296,7 +2299,7 @@ abstract public class GeckoApp private void checkMigrateSync() { final File profileDir = getProfile().getDir(); - if (profileDir != null) { + if (!GeckoApp.sIsUsingCustomProfile && profileDir != null) { final GeckoApp app = GeckoApp.mAppContext; ProfileMigrator profileMigrator = new ProfileMigrator(app); if (!profileMigrator.hasSyncMigrated()) { diff --git a/mobile/android/base/GeckoProfile.java b/mobile/android/base/GeckoProfile.java index f04d957ed04..16fc0fa8a78 100644 --- a/mobile/android/base/GeckoProfile.java +++ b/mobile/android/base/GeckoProfile.java @@ -148,7 +148,8 @@ public final class GeckoProfile { try { // Check for old profiles that may need migration. ProfileMigrator profileMigrator = new ProfileMigrator(mContext); - if (!profileMigrator.isProfileMoved()) { + if (!GeckoApp.sIsUsingCustomProfile && + !profileMigrator.isProfileMoved()) { Log.i(LOGTAG, "New installation or update, checking for old profiles."); profileMigrator.launchMoveProfile(); } From bc898245f6a2c840ae650fb49f846eefafbf7e69 Mon Sep 17 00:00:00 2001 From: George Wright Date: Fri, 28 Sep 2012 21:25:24 -0400 Subject: [PATCH 08/21] Bug 795538 - Ensure we use the correct colour (and alpha) for the clamp values r=mattwoodrow --- .../src/effects/gradients/SkGradientShader.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/gfx/skia/src/effects/gradients/SkGradientShader.cpp b/gfx/skia/src/effects/gradients/SkGradientShader.cpp index 79e7202d66b..45de4e306f4 100644 --- a/gfx/skia/src/effects/gradients/SkGradientShader.cpp +++ b/gfx/skia/src/effects/gradients/SkGradientShader.cpp @@ -473,15 +473,17 @@ const SkPMColor* SkGradientShaderBase::getCache32() const { } // Write the clamp colours into the first and last entries of fCache32 - fCache32[kCache32ClampLower] = SkPackARGB32(fCacheAlpha, - SkColorGetR(fOrigColors[0]), - SkColorGetG(fOrigColors[0]), - SkColorGetB(fOrigColors[0])); + fCache32[kCache32ClampLower] = SkPremultiplyARGBInline(SkMulDiv255Round(SkColorGetA(fOrigColors[0]), + fCacheAlpha), + SkColorGetR(fOrigColors[0]), + SkColorGetG(fOrigColors[0]), + SkColorGetB(fOrigColors[0])); - fCache32[kCache32ClampUpper] = SkPackARGB32(fCacheAlpha, - SkColorGetR(fOrigColors[fColorCount - 1]), - SkColorGetG(fOrigColors[fColorCount - 1]), - SkColorGetB(fOrigColors[fColorCount - 1])); + fCache32[kCache32ClampUpper] = SkPremultiplyARGBInline(SkMulDiv255Round(SkColorGetA(fOrigColors[fColorCount - 1]), + fCacheAlpha), + SkColorGetR(fOrigColors[fColorCount - 1]), + SkColorGetG(fOrigColors[fColorCount - 1]), + SkColorGetB(fOrigColors[fColorCount - 1])); return fCache32; } From e1fe34fe36089c937e7c419387cb03c266c10c6d Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Wed, 12 Sep 2012 14:19:30 +0800 Subject: [PATCH 09/21] Bug 790547 - Part 1: IDL for sendEventDownload. r=philikon sr=sicking --- dom/icc/interfaces/SimToolKit.idl | 48 +++++++++++++++++++ dom/icc/interfaces/nsIDOMIccManager.idl | 19 +++++++- .../nsIMobileConnectionProvider.idl | 4 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/dom/icc/interfaces/SimToolKit.idl b/dom/icc/interfaces/SimToolKit.idl index 12e84110216..cbbd4926395 100644 --- a/dom/icc/interfaces/SimToolKit.idl +++ b/dom/icc/interfaces/SimToolKit.idl @@ -255,6 +255,54 @@ dictionary MozStkSetUpEventList jsval eventList; // unsigned short [] }; +dictionary MozStkLocationInfo +{ + /** + * Mobile Country Code (MCC) of the current serving operator. + */ + unsigned short mcc; + + /** + * Mobile Network Code (MNC) of the current serving operator. + */ + unsigned short mnc; + + /** + * Mobile Location Area Code (LAC) for the current serving operator. + */ + unsigned short gsmLocationAreaCode; + + /** + * Mobile Cell ID for the current serving operator. + */ + unsigned long gsmCellId; +}; + +dictionary MozStkLocationEvent +{ + /** + * The type of this event. + * It shall be nsIDOMMozIccManager.STK_EVENT_TYPE_LOCATION_STATUS; + */ + unsigned short eventType; + + /** + * Indicate current service state of the MS with one of the values listed + * below: + * - nsIDOMMozIccManager.STK_SERVICE_STATE_NORMAL + * - nsIDOMMozIccManager.STK_SERVICE_STATE_LIMITED + * - nsIDOMMozIccManager.STK_SERVICE_STATE_UNAVAILABLE + */ + unsigned short locationStatus; + + /** + * See MozStkLocationInfo. + * This value shall only be provided if the locationStatus indicates + * 'STK_SERVICE_STATE_NORMAL'. + */ + jsval locationInfo; +}; + dictionary MozStkCommand { /** diff --git a/dom/icc/interfaces/nsIDOMIccManager.idl b/dom/icc/interfaces/nsIDOMIccManager.idl index b2a653eee65..c5686799977 100644 --- a/dom/icc/interfaces/nsIDOMIccManager.idl +++ b/dom/icc/interfaces/nsIDOMIccManager.idl @@ -7,7 +7,7 @@ interface nsIDOMEventListener; -[scriptable, builtinclass, uuid(2eace3f9-6aa4-491b-820e-7d69ce7b3f02)] +[scriptable, builtinclass, uuid(b45c3873-88bd-4ae9-9c9b-10ee58ca5389)] interface nsIDOMMozIccManager : nsIDOMEventTarget { /** @@ -162,6 +162,13 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget const unsigned short STK_EVENT_TYPE_BROWSING_STATUS = 0x0f; const unsigned short STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGED = 0x10; + /** + * The service state of STK Location Status. + */ + const unsigned short STK_SERVICE_STATE_NORMAL = 0x00; + const unsigned short STK_SERVICE_STATE_LIMITED = 0x01; + const unsigned short STK_SERVICE_STATE_UNAVAILABLE = 0x02; + /** * Send the response back to ICC after an attempt to execute STK Proactive * Command. @@ -185,6 +192,16 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget void sendStkMenuSelection(in unsigned short itemIdentifier, in boolean helpRequested); + /** + * Send "Event Download" Envelope command to ICC. + * ICC will not respond with any data for this command. + * + * @param event + * one of events below: + * - MozStkLocationEvent + */ + void sendStkEventDownload(in jsval event); + /** * The 'stkcommand' event is notified whenever STK Proactive Command is * issued from ICC. diff --git a/dom/network/interfaces/nsIMobileConnectionProvider.idl b/dom/network/interfaces/nsIMobileConnectionProvider.idl index 024b828c2d8..67f92e1578f 100644 --- a/dom/network/interfaces/nsIMobileConnectionProvider.idl +++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl @@ -14,7 +14,7 @@ interface nsIDOMWindow; * XPCOM component (in the content process) that provides the mobile * network information. */ -[scriptable, uuid(63787ba1-5091-450b-8810-d321a8b4f77a)] +[scriptable, uuid(bc1a82aa-2a1f-4a27-a5e7-614d06b72e0a)] interface nsIMobileConnectionProvider : nsISupports { readonly attribute DOMString cardState; @@ -40,4 +40,6 @@ interface nsIMobileConnectionProvider : nsISupports void sendStkMenuSelection(in nsIDOMWindow window, in unsigned short itemIdentifier, in boolean helpRequested); + void sendStkEventDownload(in nsIDOMWindow window, + in jsval event); }; From 38a7a2683440a9a4dcc78db90c109fcb22c925a2 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Wed, 12 Sep 2012 15:24:58 +0800 Subject: [PATCH 10/21] Bug 790547 - Part 2: Add SendEventDownload in IccManager. r=smaug --- dom/icc/src/IccManager.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dom/icc/src/IccManager.cpp b/dom/icc/src/IccManager.cpp index 9cd3068ebb0..0ea9eb2dbc4 100644 --- a/dom/icc/src/IccManager.cpp +++ b/dom/icc/src/IccManager.cpp @@ -137,6 +137,17 @@ IccManager::SendStkMenuSelection(uint16_t aItemIdentifier, bool aHelpRequested) return NS_OK; } +NS_IMETHODIMP +IccManager::SendStkEventDownload(const JS::Value& aEvent) +{ + if (!mProvider) { + return NS_ERROR_FAILURE; + } + + mProvider->SendStkEventDownload(GetOwner(), aEvent); + return NS_OK; +} + nsresult IccManager::InternalDispatchEvent(const nsAString& aType) { From 626307bf9225d1cc748227c784180113d0bdc718 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 11 Sep 2012 10:34:36 +0800 Subject: [PATCH 11/21] Bug 790547 - Part 3: Add send Location Status Event in RIL. r=philikon --- dom/system/gonk/RILContentHelper.js | 9 ++ dom/system/gonk/RadioInterfaceLayer.js | 9 ++ dom/system/gonk/ril_consts.js | 7 ++ dom/system/gonk/ril_worker.js | 145 +++++++++++++++++++++++-- 4 files changed, 162 insertions(+), 8 deletions(-) diff --git a/dom/system/gonk/RILContentHelper.js b/dom/system/gonk/RILContentHelper.js index 4aed9d0a31f..1f73c31084f 100644 --- a/dom/system/gonk/RILContentHelper.js +++ b/dom/system/gonk/RILContentHelper.js @@ -453,6 +453,15 @@ RILContentHelper.prototype = { helpRequested: helpRequested}); }, + sendStkEventDownload: function sendStkEventDownload(window, + event) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + cpmm.sendAsyncMessage("RIL:SendStkEventDownload", {event: event}); + }, + _telephonyCallbacks: null, _voicemailCallbacks: null, _enumerateTelephonyCallbacks: null, diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index bbfa06803d6..fd1e93a2b6d 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -73,6 +73,7 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [ "RIL:CancelUSSD", "RIL:SendStkResponse", "RIL:SendStkMenuSelection", + "RIL:SendStkEventDownload", ]; XPCOMUtils.defineLazyServiceGetter(this, "gSmsService", @@ -384,6 +385,9 @@ RadioInterfaceLayer.prototype = { case "RIL:SendStkMenuSelection": this.sendStkMenuSelection(msg.json); break; + case "RIL:SendStkEventDownload": + this.sendStkEventDownload(msg.json); + break; } }, @@ -1530,6 +1534,11 @@ RadioInterfaceLayer.prototype = { this.worker.postMessage(message); }, + sendStkEventDownload: function sendStkEventDownload(message) { + message.rilMessageType = "sendStkEventDownload"; + this.worker.postMessage(message); + }, + get microphoneMuted() { return gAudioManager.microphoneMuted; }, diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 01b749b6c1e..9c2767c3fde 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -540,8 +540,10 @@ const COMPREHENSIONTLV_TAG_TEXT_STRING = 0x0d; const COMPREHENSIONTLV_TAG_ITEM = 0x0f; const COMPREHENSIONTLV_TAG_ITEM_ID = 0x10; const COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11; +const COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13; const COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15; const COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17; +const COMPREHENSIONTLV_TAG_LOCATION_STATUS = 0x1b; const COMPREHENSIONTLV_TAG_EVENT_LIST = 0x19; const COMPREHENSIONTLV_TAG_ICON_ID = 0x1e; const COMPREHENSIONTLV_TAG_ICON_ID_LIST = 0x1f; @@ -725,6 +727,11 @@ const STK_EVENT_TYPE_LOCAL_CONNECTION = 0x0d; const STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGED = 0x0e; const STK_EVENT_TYPE_BROWSING_STATUS = 0x0f; +// STK Service state of Location Status. +const STK_SERVICE_STATE_NORMAL = 0x00; +const STK_SERVICE_STATE_LIMITED = 0x01; +const STK_SERVICE_STATE_UNAVAILABLE = 0x02; + /** * (U)SIM Services. * diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index c8323b7c610..d3a38e01623 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -51,6 +51,16 @@ const PARCEL_SIZE_SIZE = UINT32_SIZE; const PDU_HEX_OCTET_SIZE = 4; +const TLV_COMMAND_DETAILS_SIZE = 5; +const TLV_DEVICE_ID_SIZE = 4; +const TLV_RESULT_SIZE = 3; +const TLV_ITEM_ID_SIZE = 3; +const TLV_HELP_REQUESTED_SIZE = 2; +const TLV_EVENT_LIST_SIZE = 3; +const TLV_LOCATION_STATUS_SIZE = 3; +const TLV_LOCATION_INFO_GSM_SIZE = 9; +const TLV_LOCATION_INFO_UMTS_SIZE = 11; + const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"]; let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = libcutils.property_get("ro.moz.ril.callstate_extra_int"); @@ -2257,10 +2267,10 @@ let RIL = { } // 1 octets = 2 chars. - let size = (5 + /* Size of Command Details TLV */ - 4 + /* Size of Device Identifier TLV */ - 3 + /* Size of Result */ - (response.itemIdentifier ? 3 : 0) + + let size = (TLV_COMMAND_DETAILS_SIZE + + TLV_DEVICE_ID_SIZE + + TLV_RESULT_SIZE + + (response.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) + (textLen ? textLen + 3 : 0)) * 2; Buf.writeUint32(size); @@ -2365,6 +2375,29 @@ let RIL = { this.sendICCEnvelopeCommand(command); }, + /** + * Send STK Envelope(Event Download) command. + * @param event + */ + sendStkEventDownload: function sendStkEventDownload(command) { + command.tag = BER_EVENT_DOWNLOAD_TAG; + command.eventList = command.event.eventType; + switch (command.eventList) { + case STK_EVENT_TYPE_LOCATION_STATUS: + command.deviceId = { + sourceId :STK_DEVICE_ID_ME, + destinationId: STK_DEVICE_ID_SIM + }; + command.locationStatus = command.event.locationStatus; + // Location info should only be provided when locationStatus is normal. + if (command.locationStatus == STK_SERVICE_STATE_NORMAL) { + command.locationInfo = command.event.locationInfo; + } + break; + } + this.sendICCEnvelopeCommand(command); + }, + /** * Send REQUEST_STK_SEND_ENVELOPE_COMMAND to ICC. * @@ -2372,12 +2405,25 @@ let RIL = { * @patam deviceId * @param [optioanl] itemIdentifier * @param [optional] helpRequested + * @param [optional] eventList + * @param [optional] locationStatus + * @param [optional] locationInfo */ sendICCEnvelopeCommand: function sendICCEnvelopeCommand(options) { + if (DEBUG) { + debug("Stk Envelope " + JSON.stringify(options)); + } let token = Buf.newParcel(REQUEST_STK_SEND_ENVELOPE_COMMAND); - let berLen = 4 + /* Size of Device Identifier TLV */ - (options.itemIdentifier ? 3 : 0) + - (options.helpRequested ? 2 : 0); + let berLen = TLV_DEVICE_ID_SIZE + /* Size of Device Identifier TLV */ + (options.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) + + (options.helpRequested ? TLV_HELP_REQUESTED_SIZE : 0) + + (options.eventList ? TLV_EVENT_LIST_SIZE : 0) + + (options.locationStatus ? TLV_LOCATION_STATUS_SIZE : 0) + + (options.locationInfo ? + (options.locationInfo.gsmCellId > 0xffff ? + TLV_LOCATION_INFO_UMTS_SIZE : + TLV_LOCATION_INFO_GSM_SIZE) : + 0); let size = (2 + berLen) * 2; Buf.writeUint32(size); @@ -2409,6 +2455,28 @@ let RIL = { // Help Request doesn't have value } + // Event List + if (options.eventList) { + GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_EVENT_LIST | + COMPREHENSIONTLV_FLAG_CR); + GsmPDUHelper.writeHexOctet(1); + GsmPDUHelper.writeHexOctet(options.eventList); + } + + // Location Status + if (options.locationStatus) { + let len = options.locationStatus.length; + GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_LOCATION_STATUS | + COMPREHENSIONTLV_FLAG_CR); + GsmPDUHelper.writeHexOctet(1); + GsmPDUHelper.writeHexOctet(options.locationStatus); + } + + // Location Info + if (options.locationInfo) { + ComprehensionTlvHelper.writeLocationInfoTlv(options.locationInfo); + } + Buf.writeUint32(0); Buf.sendParcel(); }, @@ -6669,7 +6737,68 @@ let ComprehensionTlvHelper = { index += tlv.hlen; } return chunks; - } + }, + + /** + * Write Location Info Comprehension TLV. + * + * @param loc location Information. + */ + writeLocationInfoTlv: function writeLocationInfoTlv(loc) { + GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_LOCATION_INFO | + COMPREHENSIONTLV_FLAG_CR); + GsmPDUHelper.writeHexOctet(loc.gsmCellId > 0xffff ? 9 : 7); + // From TS 11.14, clause 12.19 + // "The mobile country code (MCC), the mobile network code (MNC), + // the location area code (LAC) and the cell ID are + // coded as in TS 04.08." + // And from TS 04.08 and TS 24.008, + // the format is as follows: + // + // MCC = MCC_digit_1 + MCC_digit_2 + MCC_digit_3 + // + // 8 7 6 5 4 3 2 1 + // +-------------+-------------+ + // | MCC digit 2 | MCC digit 1 | octet 2 + // | MNC digit 3 | MCC digit 3 | octet 3 + // | MNC digit 2 | MNC digit 1 | octet 4 + // +-------------+-------------+ + // + // Also in TS 24.008 + // "However a network operator may decide to + // use only two digits in the MNC in the LAI over the + // radio interface. In this case, bits 5 to 8 of octet 3 + // shall be coded as '1111'". + + // MCC & MNC, 3 octets + let mcc = loc.mcc.toString(); + let mnc = loc.mnc.toString(); + if (mnc.length == 1) { + mnc = "F0" + mnc; + } else if (mnc.length == 2) { + mnc = "F" + mnc; + } else { + mnc = mnc[2] + mnc[0] + mnc[1]; + } + GsmPDUHelper.writeSwappedNibbleBCD(mcc + mnc); + + // LAC, 2 octets + GsmPDUHelper.writeHexOctet((loc.gsmLocationAreaCode >> 8) & 0xff); + GsmPDUHelper.writeHexOctet(loc.gsmLocationAreaCode & 0xff); + + // Cell Id + if (loc.gsmCellId > 0xffff) { + // UMTS/WCDMA, gsmCellId is 28 bits. + GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 24) & 0xff); + GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 16) & 0xff); + GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff); + GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff); + } else { + // GSM, gsmCellId is 16 bits. + GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff); + GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff); + } + }, }; let BerTlvHelper = { From 68e9a49d58814c3db68aef4400a834e4d9e21376 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 25 Sep 2012 10:25:08 +0800 Subject: [PATCH 12/21] Bug 790547 - Part 4: xpcshell test case for writing Location Info --- dom/system/gonk/tests/test_ril_worker_icc.js | 90 ++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/dom/system/gonk/tests/test_ril_worker_icc.js b/dom/system/gonk/tests/test_ril_worker_icc.js index 411d548bf39..ff3bedcbcf2 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc.js +++ b/dom/system/gonk/tests/test_ril_worker_icc.js @@ -142,3 +142,93 @@ add_test(function test_write_dialling_number() { run_next_test(); }); +/** + * Verify ComprehensionTlvHelper.writeLocationInfoTlv + */ +add_test(function test_write_location_info_tlv() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let tlvHelper = worker.ComprehensionTlvHelper; + + // Test with 2-digit mnc, and gsmCellId obtained from UMTS network. + let loc = { + mcc: 466, + mnc: 92, + gsmLocationAreaCode : 10291, + gsmCellId: 19072823 + }; + tlvHelper.writeLocationInfoTlv(loc); + + let tag = pduHelper.readHexOctet(); + do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO | + COMPREHENSIONTLV_FLAG_CR); + + let length = pduHelper.readHexOctet(); + do_check_eq(length, 9); + + let mcc_mnc = pduHelper.readSwappedNibbleBcdString(3); + do_check_eq(mcc_mnc, "46692"); + + let lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet(); + do_check_eq(lac, 10291); + + let cellId = (pduHelper.readHexOctet() << 24) | + (pduHelper.readHexOctet() << 16) | + (pduHelper.readHexOctet() << 8) | + (pduHelper.readHexOctet()); + do_check_eq(cellId, 19072823); + + // Test with 1-digit mnc, and gsmCellId obtained from GSM network. + loc = { + mcc: 466, + mnc: 2, + gsmLocationAreaCode : 10291, + gsmCellId: 65534 + }; + tlvHelper.writeLocationInfoTlv(loc); + + tag = pduHelper.readHexOctet(); + do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO | + COMPREHENSIONTLV_FLAG_CR); + + length = pduHelper.readHexOctet(); + do_check_eq(length, 7); + + mcc_mnc = pduHelper.readSwappedNibbleBcdString(3); + do_check_eq(mcc_mnc, "46602"); + + lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet(); + do_check_eq(lac, 10291); + + cellId = (pduHelper.readHexOctet() << 8) | + (pduHelper.readHexOctet()); + do_check_eq(cellId, 65534); + + // Test with 3-digit mnc, and gsmCellId obtained from GSM network. + loc = { + mcc: 466, + mnc: 222, + gsmLocationAreaCode : 10291, + gsmCellId: 65534 + }; + tlvHelper.writeLocationInfoTlv(loc); + + tag = pduHelper.readHexOctet(); + do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO | + COMPREHENSIONTLV_FLAG_CR); + + length = pduHelper.readHexOctet(); + do_check_eq(length, 7); + + mcc_mnc = pduHelper.readSwappedNibbleBcdString(3); + do_check_eq(mcc_mnc, "466222"); + + lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet(); + do_check_eq(lac, 10291); + + cellId = (pduHelper.readHexOctet() << 8) | + (pduHelper.readHexOctet()); + do_check_eq(cellId, 65534); + + run_next_test(); +}); From 19cc16c9aa1bdea5978b7f3bac0f053112979064 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Sat, 29 Sep 2012 00:02:41 -0400 Subject: [PATCH 13/21] Bug 787445: B2G: Rip out async paint throttling code, use compression instead r=cjones --- dom/ipc/PBrowser.ipdl | 4 ++-- gfx/layers/ipc/AsyncPanZoomController.cpp | 28 ++++++++--------------- gfx/layers/ipc/AsyncPanZoomController.h | 21 +---------------- 3 files changed, 13 insertions(+), 40 deletions(-) diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index bc8562201aa..0bd173bafb3 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -288,9 +288,9 @@ child: LoadURL(nsCString uri); - UpdateDimensions(nsRect rect, nsIntSize size); + UpdateDimensions(nsRect rect, nsIntSize size) compress; - UpdateFrame(FrameMetrics frame); + UpdateFrame(FrameMetrics frame) compress; /** * Requests handling of a double tap. |point| is in CSS pixels, relative to diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 2420d96ab27..e6f6784c127 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -92,7 +92,7 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon mLastSampleTime(TimeStamp::Now()), mState(NOTHING), mDPI(72), - mContentPainterStatus(CONTENT_IDLE), + mWaitingForContentToPaint(false), mDisableNextTouchBatch(false), mHandlingTouchQueue(false) { @@ -819,13 +819,12 @@ void AsyncPanZoomController::RequestContentRepaint() { return; } - if (mContentPainterStatus == CONTENT_IDLE) { - mContentPainterStatus = CONTENT_PAINTING; - mLastPaintRequestMetrics = mFrameMetrics; - mGeckoContentController->RequestContentRepaint(mFrameMetrics); - } else { - mContentPainterStatus = CONTENT_PAINTING_AND_PAINT_PENDING; - } + // This message is compressed, so fire whether or not we already have a paint + // queued up. We need to know whether or not a paint was requested anyways, + // ofr the purposes of content calling window.scrollTo(). + mGeckoContentController->RequestContentRepaint(mFrameMetrics); + mLastPaintRequestMetrics = mFrameMetrics; + mWaitingForContentToPaint = true; } bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime, @@ -932,14 +931,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mLastContentPaintMetrics = aViewportFrame; - if (mContentPainterStatus != CONTENT_IDLE) { - if (mContentPainterStatus == CONTENT_PAINTING_AND_PAINT_PENDING) { - mContentPainterStatus = CONTENT_IDLE; - RequestContentRepaint(); - } else { - mContentPainterStatus = CONTENT_IDLE; - } - } else { + if (!mWaitingForContentToPaint) { // No paint was requested, but we got one anyways. One possible cause of this // is that content could have fired a scrollTo(). In this case, we should take // the new scroll offset. Document/viewport changes are handled elsewhere. @@ -959,9 +951,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr } } - if (aIsFirstPaint || mFrameMetrics.IsDefault()) { - mContentPainterStatus = CONTENT_IDLE; + mWaitingForContentToPaint = false; + if (aIsFirstPaint || mFrameMetrics.IsDefault()) { mX.CancelTouch(); mY.CancelTouch(); diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index c9b9b337ec9..29573434bc6 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -424,25 +424,6 @@ private: prevented the default actions yet. we still need to abort animations. */ }; - enum ContentPainterStatus { - // A paint may be happening, but it is not due to any action taken by this - // thread. For example, content could be invalidating itself, but - // AsyncPanZoomController has nothing to do with that. - CONTENT_IDLE, - // Set every time we dispatch a request for a repaint. When a - // ShadowLayersUpdate arrives and the metrics of this frame have changed, we - // toggle this off and assume that the paint has completed. - CONTENT_PAINTING, - // Set when we have a new displayport in the pipeline that we want to paint. - // When a ShadowLayersUpdate comes in, we dispatch a new repaint using - // mFrameMetrics.mDisplayPort (the most recent request) if this is toggled. - // This is distinct from CONTENT_PAINTING in that it signals that a repaint - // is happening, whereas this signals that we want to repaint as soon as the - // previous paint finishes. When the request is eventually made, it will use - // the most up-to-date metrics. - CONTENT_PAINTING_AND_PAINT_PENDING - }; - /** * Helper to set the current state. Holds the monitor before actually setting * it. If the monitor is already held by the current thread, it is safe to @@ -521,7 +502,7 @@ private: // may be triggered by other things (like content doing things), in which case // this status will not be updated. It is only changed when this class // requests a repaint. - ContentPainterStatus mContentPainterStatus; + bool mWaitingForContentToPaint; // Flag used to determine whether or not we should disable handling of the // next batch of touch events. This is used for sync scrolling of subframes. From 4a17f281031256ac41273b91d13ec0785e407147 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Sat, 29 Sep 2012 00:02:45 -0400 Subject: [PATCH 14/21] Bug 786267: B2G: Lower resolution while doing accelerated panning r=cjones --- dom/ipc/TabChild.cpp | 6 +- gfx/layers/ipc/AsyncPanZoomController.cpp | 165 +++++++++++++++++++--- gfx/layers/ipc/AsyncPanZoomController.h | 21 ++- gfx/layers/ipc/Axis.cpp | 16 ++- gfx/layers/ipc/Axis.h | 6 + 5 files changed, 185 insertions(+), 29 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 1d53c6eb815..527693617fb 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -459,8 +459,10 @@ TabChild::HandlePossibleMetaViewportChange() metrics.mZoom.width = metrics.mZoom.height = metrics.mResolution.width = metrics.mResolution.height = zoom; metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort( - metrics, - gfx::Point(0.0f, 0.0f)); + // The page must have been refreshed in some way such as a new document or + // new CSS viewport, so we know that there's no velocity, acceleration, and + // we have no idea how long painting will take. + metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0); // Force a repaint with these metrics. This, among other things, sets the // displayport, so we start with async painting. RecvUpdateFrame(metrics); diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index e6f6784c127..ef461066207 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -44,7 +44,7 @@ static const int32_t FLING_REPAINT_INTERVAL = 75; * Minimum amount of speed along an axis before we begin painting far ahead by * adjusting the displayport. */ -static const float MIN_SKATE_SPEED = 0.5f; +static const float MIN_SKATE_SPEED = 0.7f; /** * Angle from axis within which we stay axis-locked. @@ -79,6 +79,12 @@ static const double MIN_ZOOM = 0.125; */ static const int TOUCH_LISTENER_TIMEOUT = 300; +/** + * Number of samples to store of how long it took to paint after the previous + * requests. + */ +static const int NUM_PAINT_DURATION_SAMPLES = 3; + AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController, GestureBehavior aGestures) : mGeckoContentController(aGeckoContentController), @@ -587,6 +593,10 @@ const gfx::Point AsyncPanZoomController::GetVelocityVector() { return gfx::Point(mX.GetVelocity(), mY.GetVelocity()); } +const gfx::Point AsyncPanZoomController::GetAccelerationVector() { + return gfx::Point(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()); +} + void AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) { float dx = mX.PanDistance(), dy = mY.PanDistance(); @@ -661,6 +671,9 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) { shouldContinueFlingY = mY.FlingApplyFrictionOrCancel(aDelta); // If we shouldn't continue the fling, let's just stop and repaint. if (!shouldContinueFlingX && !shouldContinueFlingY) { + // Bring the resolution back in sync with the zoom, in case we scaled down + // the zoom while accelerating. + SetZoomAndResolution(mFrameMetrics.mZoom.width); RequestContentRepaint(); mState = NOTHING; return false; @@ -712,8 +725,7 @@ void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) { } void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) { - float scaleFactor = aScale / mFrameMetrics.mZoom.width, - oldScale = mFrameMetrics.mZoom.width; + float scaleFactor = aScale / mFrameMetrics.mZoom.width; SetZoomAndResolution(aScale); @@ -721,22 +733,43 @@ void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFoc // current CSS page rect (which is unchanged since it's not affected by zoom). SetPageRect(mFrameMetrics.mScrollableRect); - mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale; - mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale; + // If the new scale is very small, we risk multiplying in huge rounding + // errors, so don't bother adjusting the scroll offset. + if (aScale >= 0.01f) { + mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / aScale; + mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / aScale; + } } -bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBounds, +bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aSkateSizeMultiplier, + double aEstimatedPaintDuration, + float aCompositionBounds, float aVelocity, + float aAcceleration, float* aDisplayPortOffset, float* aDisplayPortLength) { - const float MIN_SKATE_SIZE_MULTIPLIER = 2.0f; - const float MAX_SKATE_SIZE_MULTIPLIER = 4.0f; - if (fabsf(aVelocity) > MIN_SKATE_SPEED) { - *aDisplayPortLength = aCompositionBounds * clamped(fabsf(aVelocity), - MIN_SKATE_SIZE_MULTIPLIER, MAX_SKATE_SIZE_MULTIPLIER); + // Enlarge the area we paint. + *aDisplayPortLength = aCompositionBounds * aSkateSizeMultiplier; + // Position the area we paint such that all of the excess that extends past + // the screen is on the side towards the velocity. *aDisplayPortOffset = aVelocity > 0 ? 0 : aCompositionBounds - *aDisplayPortLength; + + // Only compensate for acceleration when we actually have any. Otherwise + // we'll overcompensate when a user is just panning around without flinging. + if (aAcceleration > 1.01f) { + // Compensate for acceleration and how long we expect a paint to take. We + // try to predict where the viewport will be when painting has finished. + *aDisplayPortOffset += + fabsf(aAcceleration) * aVelocity * aCompositionBounds * aEstimatedPaintDuration; + // If our velocity is in the negative direction of the axis, we have to + // compensate for the fact that our scroll offset is the top-left position + // of the viewport. In this case, let's make it relative to the + // bottom-right. That way, we'll always be growing the displayport upwards + // and to the left when skating negatively. + *aDisplayPortOffset -= aVelocity < 0 ? aCompositionBounds : 0; + } return true; } return false; @@ -744,36 +777,85 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBound const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort( const FrameMetrics& aFrameMetrics, - const gfx::Point& aVelocity) + const gfx::Point& aVelocity, + const gfx::Point& aAcceleration, + double aEstimatedPaintDuration) { + // The multiplier we apply to a dimension's length if it is skating. That is, + // if it's going above MIN_SKATE_SPEED. We prefer to increase the size of the + // Y axis because it is more natural in the case that a user is reading a page + // that scrolls up/down. Note that one, both or neither of these may be used + // at any instant. + const float X_SKATE_SIZE_MULTIPLIER = 3.0f; + const float Y_SKATE_SIZE_MULTIPLIER = 3.5f; + + // The multiplier we apply to a dimension's length if it is stationary. We + // prefer to increase the size of the Y axis because it is more natural in the + // case that a user is reading a page that scrolls up/down. Note that one, + // both or neither of these may be used at any instant. + const float X_STATIONARY_SIZE_MULTIPLIER = 1.5f; + const float Y_STATIONARY_SIZE_MULTIPLIER = 2.5f; + + // If we don't get an estimated paint duration, we probably don't have any + // data. In this case, we're dealing with either a stationary frame or a first + // paint. In either of these cases, we can just assume it'll take 1 second to + // paint. Getting this correct is not important anyways since it's only really + // useful when accelerating, which can't be happening at this point. + double estimatedPaintDuration = + aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0; + float scale = aFrameMetrics.mZoom.width; nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds; compositionBounds.ScaleInverseRoundIn(scale); + const gfx::Rect& scrollableRect = aFrameMetrics.mScrollableRect; gfx::Point scrollOffset = aFrameMetrics.mScrollOffset; - const float STATIONARY_SIZE_MULTIPLIER = 2.0f; gfx::Rect displayPort(0, 0, - compositionBounds.width * STATIONARY_SIZE_MULTIPLIER, - compositionBounds.height * STATIONARY_SIZE_MULTIPLIER); + compositionBounds.width * X_STATIONARY_SIZE_MULTIPLIER, + compositionBounds.height * Y_STATIONARY_SIZE_MULTIPLIER); // If there's motion along an axis of movement, and it's above a threshold, // then we want to paint a larger area in the direction of that motion so that // it's less likely to checkerboard. bool enlargedX = EnlargeDisplayPortAlongAxis( - compositionBounds.width, aVelocity.x, &displayPort.x, &displayPort.width); + X_SKATE_SIZE_MULTIPLIER, estimatedPaintDuration, + compositionBounds.width, aVelocity.x, aAcceleration.x, + &displayPort.x, &displayPort.width); bool enlargedY = EnlargeDisplayPortAlongAxis( - compositionBounds.height, aVelocity.y, &displayPort.y, &displayPort.height); + Y_SKATE_SIZE_MULTIPLIER, estimatedPaintDuration, + compositionBounds.height, aVelocity.y, aAcceleration.y, + &displayPort.y, &displayPort.height); if (!enlargedX && !enlargedY) { - displayPort.x = -displayPort.width / 4; - displayPort.y = -displayPort.height / 4; + // Position the x and y such that the screen falls in the middle of the displayport. + displayPort.x = -(displayPort.width - compositionBounds.width) / 2; + displayPort.y = -(displayPort.height - compositionBounds.height) / 2; } else if (!enlargedX) { displayPort.width = compositionBounds.width; } else if (!enlargedY) { displayPort.height = compositionBounds.height; } + // If we go over the bounds when trying to predict where we will be when this + // paint finishes, move it back into the range of the CSS content rect. + // FIXME/bug 780395: Generalize this. This code is pretty hacky as it will + // probably not work at all for RTL content. This is not intended to be + // incredibly accurate; it'll just prevent the entire displayport from being + // outside the content rect (which causes bad things to happen). + if (enlargedX || enlargedY) { + if (scrollOffset.x + compositionBounds.width > scrollableRect.width) { + scrollOffset.x -= compositionBounds.width + scrollOffset.x - scrollableRect.width; + } else if (scrollOffset.x < scrollableRect.x) { + scrollOffset.x = scrollableRect.x; + } + if (scrollOffset.y + compositionBounds.height > scrollableRect.height) { + scrollOffset.y -= compositionBounds.height + scrollOffset.y - scrollableRect.height; + } else if (scrollOffset.y < scrollableRect.y) { + scrollOffset.y = scrollableRect.y; + } + } + gfx::Rect shiftedDisplayPort = displayPort; shiftedDisplayPort.MoveBy(scrollOffset.x, scrollOffset.y); displayPort = shiftedDisplayPort.Intersect(aFrameMetrics.mScrollableRect); @@ -797,8 +879,23 @@ void AsyncPanZoomController::ScheduleComposite() { } void AsyncPanZoomController::RequestContentRepaint() { + mPreviousPaintStartTime = TimeStamp::Now(); + + double estimatedPaintSum = 0.0; + for (uint32_t i = 0; i < mPreviousPaintDurations.Length(); i++) { + estimatedPaintSum += mPreviousPaintDurations[i].ToSeconds(); + } + + double estimatedPaintDuration = 0.0; + if (estimatedPaintSum > EPSILON) { + estimatedPaintDuration = estimatedPaintSum / mPreviousPaintDurations.Length(); + } + mFrameMetrics.mDisplayPort = - CalculatePendingDisplayPort(mFrameMetrics, GetVelocityVector()); + CalculatePendingDisplayPort(mFrameMetrics, + GetVelocityVector(), + GetAccelerationVector(), + estimatedPaintDuration); gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mScrollOffset, newScrollOffset = mFrameMetrics.mScrollOffset; @@ -819,12 +916,27 @@ void AsyncPanZoomController::RequestContentRepaint() { return; } + // Cache the resolution since we're temporarily changing it to accomodate + // mixed resolution/zoom (normally we make them the same thing). + float actualResolution = mFrameMetrics.mResolution.width; + // Calculate the factor of acceleration based on the faster of the two axes. + float accelerationFactor = + clamped(NS_MAX(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()), + float(MIN_ZOOM) / 2.0f, float(MAX_ZOOM)); + // Scale down the resolution a bit based on acceleration. + mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height = + actualResolution / accelerationFactor; + // This message is compressed, so fire whether or not we already have a paint // queued up. We need to know whether or not a paint was requested anyways, // ofr the purposes of content calling window.scrollTo(). mGeckoContentController->RequestContentRepaint(mFrameMetrics); mLastPaintRequestMetrics = mFrameMetrics; mWaitingForContentToPaint = true; + + // Set the resolution back to what it was for the purpose of logic control. + mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height = + actualResolution; } bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime, @@ -931,7 +1043,16 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mLastContentPaintMetrics = aViewportFrame; - if (!mWaitingForContentToPaint) { + if (mWaitingForContentToPaint) { + // Remove the oldest sample we have if adding a new sample takes us over our + // desired number of samples. + if (mPreviousPaintDurations.Length() >= NUM_PAINT_DURATION_SAMPLES) { + mPreviousPaintDurations.RemoveElementAt(0); + } + + mPreviousPaintDurations.AppendElement( + TimeStamp::Now() - mPreviousPaintStartTime); + } else { // No paint was requested, but we got one anyways. One possible cause of this // is that content could have fired a scrollTo(). In this case, we should take // the new scroll offset. Document/viewport changes are handled elsewhere. @@ -954,6 +1075,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mWaitingForContentToPaint = false; if (aIsFirstPaint || mFrameMetrics.IsDefault()) { + mPreviousPaintDurations.Clear(); + mX.CancelTouch(); mY.CancelTouch(); diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index 29573434bc6..8c817da483d 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -205,7 +205,9 @@ public: */ static const gfx::Rect CalculatePendingDisplayPort( const FrameMetrics& aFrameMetrics, - const gfx::Point& aVelocity); + const gfx::Point& aVelocity, + const gfx::Point& aAcceleration, + double aEstimatedPaintDuration); protected: /** @@ -333,6 +335,11 @@ protected: */ const gfx::Point GetVelocityVector(); + /** + * Gets a vector of the acceleration factors of each axis. + */ + const gfx::Point GetAccelerationVector(); + /** * Gets a reference to the first SingleTouchData from a MultiTouchInput. This * gets only the first one and assumes the rest are either missing or not @@ -365,8 +372,11 @@ protected: * |aDisplayPortLength|. If enlarged, these will be updated with the new * metrics. */ - static bool EnlargeDisplayPortAlongAxis(float aCompositionBounds, + static bool EnlargeDisplayPortAlongAxis(float aSkateSizeMultiplier, + double aEstimatedPaintDuration, + float aCompositionBounds, float aVelocity, + float aAcceleration, float* aDisplayPortOffset, float* aDisplayPortLength); @@ -496,6 +506,13 @@ private: // |mMonitor|; that is, it should be held whenever this is updated. PanZoomState mState; + // How long it took in the past to paint after a series of previous requests. + nsTArray mPreviousPaintDurations; + + // When the last paint request started. Used to determine the duration of + // previous paints. + TimeStamp mPreviousPaintStartTime; + int mDPI; // Stores the current paint status of the frame that we're managing. Repaints diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/ipc/Axis.cpp index 4fef1cc9302..94f4b35efa8 100644 --- a/gfx/layers/ipc/Axis.cpp +++ b/gfx/layers/ipc/Axis.cpp @@ -18,7 +18,7 @@ static const float EPSILON = 0.0001f; * or we get a touch point very far away from the previous position for some * reason. */ -static const float MAX_EVENT_ACCELERATION = 0.5f; +static const float MAX_EVENT_ACCELERATION = 999.0f; /** * Amount of friction applied during flings. @@ -95,15 +95,19 @@ void Axis::StartTouch(int32_t aPos) { } float Axis::GetDisplacementForDuration(float aScale, const TimeDuration& aDelta) { - float velocityFactor = powf(ACCELERATION_MULTIPLIER, - NS_MAX(0, (mAcceleration - 4) * 3)); - float displacement = mVelocity * aScale * aDelta.ToMilliseconds() * velocityFactor; + if (fabsf(mVelocity) < VELOCITY_THRESHOLD) { + mAcceleration = 0; + } + + float accelerationFactor = GetAccelerationFactor(); + float displacement = mVelocity * aScale * aDelta.ToMilliseconds() * accelerationFactor; // If this displacement will cause an overscroll, throttle it. Can potentially // bring it to 0 even if the velocity is high. if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) { // No need to have a velocity along this axis anymore; it won't take us // anywhere, so we're just spinning needlessly. mVelocity = 0.0f; + mAcceleration = 0; displacement -= DisplacementWillOverscrollAmount(displacement); } return displacement; @@ -231,6 +235,10 @@ float Axis::GetVelocity() { return mVelocity; } +float Axis::GetAccelerationFactor() { + return powf(ACCELERATION_MULTIPLIER, NS_MAX(0, (mAcceleration - 4) * 3)); +} + float Axis::GetCompositionEnd() { return GetOrigin() + GetCompositionLength(); } diff --git a/gfx/layers/ipc/Axis.h b/gfx/layers/ipc/Axis.h index 9d953492c8c..59bd313b19c 100644 --- a/gfx/layers/ipc/Axis.h +++ b/gfx/layers/ipc/Axis.h @@ -116,6 +116,12 @@ public: */ float GetExcess(); + /** + * Gets the factor of acceleration applied to the velocity, based on the + * amount of flings that have been done successively. + */ + float GetAccelerationFactor(); + /** * Gets the raw velocity of this axis at this moment. */ From cb01db7f46ae209976345c2fd9abe266e2b89b7f Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 21 Sep 2012 17:35:51 +0800 Subject: [PATCH 15/21] Bug 793137 - Part 1: IDL for PLAY_TONE, POLL_INTERVAL and REFRESH. r=philikon. sr=sicking --- dom/icc/interfaces/SimToolKit.idl | 56 +++++++++++++++++++++++++ dom/icc/interfaces/nsIDOMIccManager.idl | 27 +++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/dom/icc/interfaces/SimToolKit.idl b/dom/icc/interfaces/SimToolKit.idl index cbbd4926395..dc826207078 100644 --- a/dom/icc/interfaces/SimToolKit.idl +++ b/dom/icc/interfaces/SimToolKit.idl @@ -278,6 +278,46 @@ dictionary MozStkLocationInfo unsigned long gsmCellId; }; +dictionary MozStkDuration +{ + /** + * Time unit used, should be one of STK_TIME_UNIT_*. + */ + unsigned short timeUnit; + + /** + * The length of time required, expressed in timeUnit. + */ + octet timeInterval; +}; + +dictionary MozStkPlayTone +{ + /** + * Text String. + */ + DOMString text; + + /** + * One of STK_TONE_TYPE_*. + */ + unsigned short tone; + + /** + * The length of time for which the ME shall generate the tone. + * + * @see MozStkDuration + */ + jsval duration; + + /** + * Need to vibrate or not. + * true: vibrate alert, if available, with the tone. + * false: use of vibrate alert is up to the ME. + */ + boolean isVibrate; +}; + dictionary MozStkLocationEvent { /** @@ -354,6 +394,22 @@ dictionary MozStkCommand * When typeOfCommand is * - STK_CMD_SET_UP_EVENT_LIST * options is MozStkSetUpEventList. + * + * When typeOfCommand is + * - STK_CMD_PLAY_TONE + * options is MozStkPlayTone. + * + * When typeOfCommand is + * - STK_CMD_POLL_INTERVAL + * options is MozStkDuration. + * + * When typeOfCommand is + * - STK_CMD_POLL_OFF + * options is null. + * + * When typeOfCommand is + * - STK_CMD_REFRESH + * options is null. */ jsval options; }; diff --git a/dom/icc/interfaces/nsIDOMIccManager.idl b/dom/icc/interfaces/nsIDOMIccManager.idl index c5686799977..f6333830b2f 100644 --- a/dom/icc/interfaces/nsIDOMIccManager.idl +++ b/dom/icc/interfaces/nsIDOMIccManager.idl @@ -7,7 +7,7 @@ interface nsIDOMEventListener; -[scriptable, builtinclass, uuid(b45c3873-88bd-4ae9-9c9b-10ee58ca5389)] +[scriptable, builtinclass, uuid(9d898c66-3485-4cd5-ab8d-92ef2988887b)] interface nsIDOMMozIccManager : nsIDOMEventTarget { /** @@ -30,6 +30,8 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget * @see TS 11.14, clause 13.4 */ const unsigned short STK_CMD_REFRESH = 0x01; + const unsigned short STK_CMD_POLL_INTERVAL = 0x03; + const unsigned short STK_CMD_POLL_OFF = 0x04; const unsigned short STK_CMD_SET_UP_EVENT_LIST = 0x05; const unsigned short STK_CMD_SET_UP_CALL = 0x10; const unsigned short STK_CMD_SEND_SS = 0x11; @@ -37,6 +39,7 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget const unsigned short STK_CMD_SEND_SMS = 0x13; const unsigned short STK_CMD_SEND_DTMF = 0x14; const unsigned short STK_CMD_LAUNCH_BROWSER = 0x15; + const unsigned short STK_CMD_PLAY_TONE = 0x20; const unsigned short STK_CMD_DISPLAY_TEXT = 0x21; const unsigned short STK_CMD_GET_INKEY = 0x22; const unsigned short STK_CMD_GET_INPUT = 0x23; @@ -169,6 +172,28 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget const unsigned short STK_SERVICE_STATE_LIMITED = 0x01; const unsigned short STK_SERVICE_STATE_UNAVAILABLE = 0x02; + /** + * Tone type. + */ + const unsigned short STK_TONE_TYPE_DIAL_TONE = 0x01; + const unsigned short STK_TONE_TYPE_CALLED_SUBSCRIBER_BUSY = 0x02; + const unsigned short STK_TONE_TYPE_CONGESTION = 0x03; + const unsigned short STK_TONE_TYPE_RADIO_PATH_ACK = 0x04; + const unsigned short STK_TONE_TYPE_RADIO_PATH_NOT_AVAILABLE = 0x05; + const unsigned short STK_TONE_TYPE_ERROR = 0x06; + const unsigned short STK_TONE_TYPE_CALL_WAITING_TONE = 0x07; + const unsigned short STK_TONE_TYPE_RINGING_TONE = 0x08; + const unsigned short STK_TONE_TYPE_GENERAL_BEEP = 0x10; + const unsigned short STK_TONE_TYPE_POSITIVE_ACK_TONE = 0x11; + const unsigned short STK_TONE_TYPE_NEGATIVE_ACK_TONE = 0x12; + + /** + * Time unit + */ + const unsigned short STK_TIME_UNIT_MINUTE = 0x00; + const unsigned short STK_TIME_UNIT_SECOND = 0x01; + const unsigned short STK_TIME_UNIT_TENTH_SECOND = 0x02; + /** * Send the response back to ICC after an attempt to execute STK Proactive * Command. From 87d395b1322d77c558e36447bfcebe3433ddbeb0 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 21 Sep 2012 15:01:58 +0800 Subject: [PATCH 16/21] Bug 793137 - Part 2: Support PLAY_TONE, POLL_INTERVAL and REFRESH in RIL. r=philikon --- dom/system/gonk/ril_consts.js | 30 +++++++ dom/system/gonk/ril_worker.js | 162 +++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 9c2767c3fde..194af1e1b0a 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -537,9 +537,11 @@ const COMPREHENSIONTLV_TAG_ALPHA_ID = 0x05; const COMPREHENSIONTLV_TAG_ADDRESS = 0x06; const COMPREHENSIONTLV_TAG_SMS_TPDU = 0x0b; const COMPREHENSIONTLV_TAG_TEXT_STRING = 0x0d; +const COMPREHENSIONTLV_TAG_TONE = 0x0e; const COMPREHENSIONTLV_TAG_ITEM = 0x0f; const COMPREHENSIONTLV_TAG_ITEM_ID = 0x10; const COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11; +const COMPREHENSIONTLV_TAG_FILE_LIST = 0x12; const COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13; const COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15; const COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17; @@ -560,6 +562,8 @@ const STK_DEVICE_ID_NETWORK = 0x83; // STK Proactive commands. const STK_CMD_REFRESH = 0x01; +const STK_CMD_POLL_INTERVAL = 0x03; +const STK_CMD_POLL_OFF = 0x04; const STK_CMD_SET_UP_EVENT_LIST = 0x05; const STK_CMD_SET_UP_CALL = 0x10; const STK_CMD_SEND_SS = 0x11; @@ -567,6 +571,7 @@ const STK_CMD_SEND_USSD = 0x12; const STK_CMD_SEND_SMS = 0x13; const STK_CMD_SEND_DTMF = 0x14; const STK_CMD_LAUNCH_BROWSER = 0x15; +const STK_CMD_PLAY_TONE = 0x20; const STK_CMD_DISPLAY_TEXT = 0x21; const STK_CMD_GET_INKEY = 0x22; const STK_CMD_GET_INPUT = 0x23; @@ -732,6 +737,31 @@ const STK_SERVICE_STATE_NORMAL = 0x00; const STK_SERVICE_STATE_LIMITED = 0x01; const STK_SERVICE_STATE_UNAVAILABLE = 0x02; +// Refresh mode. +const STK_REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00; +const STK_REFRESH_FILE_CHANGE = 0x01; +const STK_REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02; +const STK_REFRESH_NAA_INIT = 0x03; +const STK_REFRESH_UICC_RESET = 0x04; + +// Tone type. +const STK_TONE_TYPE_DIAL_TONE = 0x01; +const STK_TONE_TYPE_CALLED_SUBSCRIBER_BUSY = 0x02; +const STK_TONE_TYPE_CONGESTION = 0x03; +const STK_TONE_TYPE_RADIO_PATH_ACK = 0x04; +const STK_TONE_TYPE_RADIO_PATH_NOT_AVAILABLE = 0x05; +const STK_TONE_TYPE_ERROR = 0x06; +const STK_TONE_TYPE_CALL_WAITING_TONE = 0x07; +const STK_TONE_TYPE_RINGING_TONE = 0x08; +const STK_TONE_TYPE_GENERAL_BEEP = 0x10; +const STK_TONE_TYPE_POSITIVE_ACK_TONE = 0x11; +const STK_TONE_TYPE_NEGATIVE_ACK_TONE = 0x12; + +// Time unit. +const STK_TIME_UNIT_MINUTE = 0x00; +const STK_TIME_UNIT_SECOND = 0x01; +const STK_TIME_UNIT_TENTH_SECOND = 0x02; + /** * (U)SIM Services. * diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index d3a38e01623..88122b547ed 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -6074,6 +6074,15 @@ let StkCommandParamsFactory = { createParam: function createParam(cmdDetails, ctlvs) { let param; switch (cmdDetails.typeOfCommand) { + case STK_CMD_REFRESH: + param = this.processRefresh(cmdDetails, ctlvs); + break; + case STK_CMD_POLL_INTERVAL: + param = this.processPollInterval(cmdDetails, ctlvs); + break; + case STK_CMD_POLL_OFF: + param = this.processPollOff(cmdDetails, ctlvs); + break; case STK_CMD_SET_UP_EVENT_LIST: param = this.processSetUpEventList(cmdDetails, ctlvs); break; @@ -6102,9 +6111,12 @@ let StkCommandParamsFactory = { case STK_CMD_SET_UP_CALL: param = this.processSetupCall(cmdDetails, ctlvs); break; - case STK_LAUNCH_BROWSER: + case STK_CMD_LAUNCH_BROWSER: param = this.processLaunchBrowser(cmdDetails, ctlvs); break; + case STK_CMD_PLAY_TONE: + param = this.processPlayTone(cmdDetails, ctlvs); + break; default: debug("unknown proactive command"); break; @@ -6112,6 +6124,66 @@ let StkCommandParamsFactory = { return param; }, + /** + * Construct a param for Refresh. + * + * @param cmdDetails + * The value object of CommandDetails TLV. + * @param ctlvs + * The all TLVs in this proactive command. + */ + processRefresh: function processRefresh(cmdDetails, ctlvs) { + let refreshType = cmdDetails.commandQualifier; + switch (refreshType) { + case STK_REFRESH_FILE_CHANGE: + case STK_REFRESH_NAA_INIT_AND_FILE_CHANGE: + let ctlv = StkProactiveCmdHelper.searchForTag( + COMPREHENSIONTLV_FILE_LIST, ctlvs); + if (ctlv) { + let list = ctlv.value.fileList; + if (DEBUG) { + debug("Refresh, list = " + list); + } + RIL.fetchICCRecords(); + } + break; + } + return {}; + }, + + /** + * Construct a param for Poll Interval. + * + * @param cmdDetails + * The value object of CommandDetails TLV. + * @param ctlvs + * The all TLVs in this proactive command. + */ + processPollInterval: function processPollInterval(cmdDetails, ctlvs) { + let ctlv = StkProactiveCmdHelper.searchForTag( + COMPREHENSIONTLV_TAG_DURATION, ctlvs); + if (!ctlv) { + RIL.sendStkTerminalResponse({ + command: cmdDetails, + resultCode: STK_RESULT_REQUIRED_VALUES_MISSING}); + throw new Error("Stk Poll Interval: Required value missing : Duration"); + } + + return ctlv.value; + }, + + /** + * Construct a param for Poll Off. + * + * @param cmdDetails + * The value object of CommandDetails TLV. + * @param ctlvs + * The all TLVs in this proactive command. + */ + processPollOff: function processPollOff(cmdDetails, ctlvs) { + return {}; + }, + /** * Construct a param for Set Up Event list. * @@ -6377,6 +6449,32 @@ let StkCommandParamsFactory = { browser.mode = cmdDetails.commandQualifier & 0x03; return browser; + }, + + processPlayTone: function processPlayTone(cmdDetails, ctlvs) { + let playTone = {}; + + let ctlv = StkProactiveCmdHelper.searchForTag( + COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs); + if (ctlv) { + playTone.text = ctlv.value.identifier; + } + + ctlv = StkProactiveCmdHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs); + if (ctlv) { + playTone.tone = ctlv.value.tone; + } + + ctlv = StkProactiveCmdHelper.searchForTag( + COMPREHENSIONTLV_TAG_DURATION, ctlvs); + if (ctlv) { + playTone.duration = ctlv.value; + } + + // vibrate is only defined in TS 102.223 + playTone.isVibrate = (cmdDetails.commandQualifier & 0x01) != 0x00; + + return playTone; } }; @@ -6389,16 +6487,22 @@ let StkProactiveCmdHelper = { return this.retrieveDeviceId(length); case COMPREHENSIONTLV_TAG_ALPHA_ID: return this.retrieveAlphaId(length); + case COMPREHENSIONTLV_TAG_DURATION: + return this.retrieveDuration(length); case COMPREHENSIONTLV_TAG_ADDRESS: return this.retrieveAddress(length); case COMPREHENSIONTLV_TAG_TEXT_STRING: return this.retrieveTextString(length); + case COMPREHENSIONTLV_TAG_TONE: + return this.retrieveTone(length); case COMPREHENSIONTLV_TAG_ITEM: return this.retrieveItem(length); case COMPREHENSIONTLV_TAG_ITEM_ID: return this.retrieveItemId(length); case COMPREHENSIONTLV_TAG_RESPONSE_LENGTH: return this.retrieveResponseLength(length); + case COMPREHENSIONTLV_TAG_FILE_LIST: + return this.retrieveFileList(length); case COMPREHENSIONTLV_TAG_DEFAULT_TEXT: return this.retrieveDefaultText(length); case COMPREHENSIONTLV_TAG_EVENT_LIST: @@ -6466,6 +6570,23 @@ let StkProactiveCmdHelper = { return alphaId; }, + /** + * Duration. + * + * | Byte | Description | Length | + * | 1 | Response Length Tag | 1 | + * | 2 | Lenth = 02 | 1 | + * | 3 | Time unit | 1 | + * | 4 | Time interval | 1 | + */ + retrieveDuration: function retrieveDuration(length) { + let duration = { + timeUnit: GsmPDUHelper.readHexOctet(), + timeInterval: GsmPDUHelper.readHexOctet(), + }; + return duration; + }, + /** * Address. * @@ -6518,6 +6639,21 @@ let StkProactiveCmdHelper = { return text; }, + /** + * Tone. + * + * | Byte | Description | Length | + * | 1 | Tone Tag | 1 | + * | 2 | Lenth = 01 | 1 | + * | 3 | Tone | 1 | + */ + retrieveTone: function retrieveTone(length) { + let tone = { + tone: GsmPDUHelper.readHexOctet(), + }; + return tone; + }, + /** * Item. * @@ -6568,6 +6704,30 @@ let StkProactiveCmdHelper = { return rspLength; }, + /** + * File List. + * + * | Byte | Description | Length | + * | 1 | File List Tag | 1 | + * | 2 ~ (Y-1)+2 | Length (X) | Y | + * | (Y-1)+3 | Number of files | 1 | + * | (Y-1)+4 ~ | Files | X | + * | (Y-1)+X+2 | | | + */ + retrieveFileList: function retrieveFileList(length) { + let num = GsmPDUHelper.readHexOctet(); + let fileList = ""; + length--; // -1 for the num octet. + for (let i = 0; i < 2 * length; i++) { + // Didn't use readHexOctet here, + // otherwise 0x00 will be "0", not "00" + fileList += String.fromCharCode(Buf.readUint16()); + } + return { + fileList: fileList + }; + }, + /** * Default Text. * From 1bc15f4b8e974be73933c1d8938fed2bfd54b583 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Thu, 27 Sep 2012 19:12:45 +0800 Subject: [PATCH 17/21] Bug 793137 - Part 3: xpcshell tests for Play Tone, Refresh and Poll Interval. r=philikon --- dom/system/gonk/tests/test_ril_worker_icc.js | 109 +++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/dom/system/gonk/tests/test_ril_worker_icc.js b/dom/system/gonk/tests/test_ril_worker_icc.js index ff3bedcbcf2..1dc135525c4 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc.js +++ b/dom/system/gonk/tests/test_ril_worker_icc.js @@ -232,3 +232,112 @@ add_test(function test_write_location_info_tlv() { run_next_test(); }); + +/** + * Verify Proactive Command : Refresh + */ +add_test(function test_stk_proactive_command_refresh() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let berHelper = worker.BerTlvHelper; + let stkHelper = worker.StkProactiveCmdHelper; + + let refresh_1 = [ + 0xD0, + 0x10, + 0x81, 0x03, 0x01, 0x01, 0x01, + 0x82, 0x02, 0x81, 0x82, + 0x92, 0x05, 0x01, 0x3F, 0x00, 0x2F, 0xE2]; + + for (let i = 0; i < refresh_1.length; i++) { + pduHelper.writeHexOctet(refresh_1[i]); + } + + let berTlv = berHelper.decode(refresh_1.length); + let ctlvs = berTlv.value; + let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, 0x01); + do_check_eq(tlv.value.commandQualifier, 0x01); + + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_FILE_LIST, ctlvs); + do_check_eq(tlv.value.fileList, "3F002FE2"); + + run_next_test(); +}); + +/** + * Verify Proactive Command : Play Tone + */ +add_test(function test_stk_proactive_command_play_tone() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let berHelper = worker.BerTlvHelper; + let stkHelper = worker.StkProactiveCmdHelper; + + let tone_1 = [ + 0xD0, + 0x1B, + 0x81, 0x03, 0x01, 0x20, 0x00, + 0x82, 0x02, 0x81, 0x03, + 0x85, 0x09, 0x44, 0x69, 0x61, 0x6C, 0x20, 0x54, 0x6F, 0x6E, 0x65, + 0x8E, 0x01, 0x01, + 0x84, 0x02, 0x01, 0x05]; + + for (let i = 0; i < tone_1.length; i++) { + pduHelper.writeHexOctet(tone_1[i]); + } + + let berTlv = berHelper.decode(tone_1.length); + let ctlvs = berTlv.value; + let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, 0x20); + do_check_eq(tlv.value.commandQualifier, 0x00); + + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs); + do_check_eq(tlv.value.identifier, "Dial Tone"); + + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs); + do_check_eq(tlv.value.tone, STK_TONE_TYPE_DIAL_TONE); + + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs); + do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND); + do_check_eq(tlv.value.timeInterval, 5); + + run_next_test(); +}); + +/** + * Verify Proactive Command : Poll Interval + */ +add_test(function test_stk_proactive_command_poll_interval() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let berHelper = worker.BerTlvHelper; + let stkHelper = worker.StkProactiveCmdHelper; + + let poll_1 = [ + 0xD0, + 0x0D, + 0x81, 0x03, 0x01, 0x03, 0x00, + 0x82, 0x02, 0x81, 0x82, + 0x84, 0x02, 0x01, 0x14]; + + for (let i = 0; i < poll_1.length; i++) { + pduHelper.writeHexOctet(poll_1[i]); + } + + let berTlv = berHelper.decode(poll_1.length); + let ctlvs = berTlv.value; + let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, 0x03); + do_check_eq(tlv.value.commandQualifier, 0x00); + + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs); + do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND); + do_check_eq(tlv.value.timeInterval, 0x14); + + run_next_test(); +}); From 34a62cb9cc5349e0659daf7144a4f8595f3af5dc Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Sat, 29 Sep 2012 00:50:59 -0400 Subject: [PATCH 18/21] Bug 766973: Don't allow synchronous DNS queries from the main thread. r=sworkman --- netwerk/dns/nsDNSService2.cpp | 9 +++++++++ netwerk/dns/nsIDNSService.idl | 5 ++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 3bc5b5e38c9..e87094db13d 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -687,6 +687,15 @@ nsDNSService::Resolve(const nsACString &hostname, uint32_t flags, nsIDNSRecord **result) { + NS_WARNING("Do not use synchronous DNS resolution! This API may be removed soon."); + + // We will not allow this to be called on the main thread. This is transitional + // and a bit of a test for removing the synchronous API entirely. + if (NS_IsMainThread()) { + NS_ERROR("Synchronous DNS resolve failing - not allowed on the main thread!"); + return NS_ERROR_FAILURE; + } + // grab reference to global host resolver and IDN service. beware // simultaneous shutdown!! nsRefPtr res; diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index 159565de011..c24afb9a075 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -59,9 +59,8 @@ interface nsIDNSService : nsISupports in nsresult aReason); /** - * called to synchronously resolve a hostname. warning this method may - * block the calling thread for a long period of time. it is extremely - * unwise to call this function on the UI thread of an application. + * Called to synchronously resolve a hostname. This method will fail + * if called from the main thread. * * @param aHostName * the hostname or IP-address-literal to resolve. From d8f8957970f7780dbbc9d7f4a0225a1cc1f8de86 Mon Sep 17 00:00:00 2001 From: "Inder Kumar ext:(%20and%20Mike%20Habicher%20%3Cmhabicher%40mozilla.com%3E)" Date: Fri, 28 Sep 2012 22:30:52 -0700 Subject: [PATCH 19/21] Bug 776062: Add support for recording video on gonk. r=cjones,double,ikumar,mikeh sr=sicking --- configure.in | 3 +- dom/camera/AudioParameter.cpp | 179 ++ dom/camera/CameraControlImpl.cpp | 11 +- dom/camera/CameraControlImpl.h | 95 +- dom/camera/DOMCameraControl.cpp | 24 +- dom/camera/GonkCameraControl.cpp | 230 +- dom/camera/GonkCameraControl.h | 27 + dom/camera/GonkCameraHwMgr.cpp | 108 +- dom/camera/GonkCameraHwMgr.h | 9 + dom/camera/GonkCameraListener.h | 37 + dom/camera/GonkCameraSource.cpp | 733 ++++++ dom/camera/GonkCameraSource.h | 161 ++ dom/camera/GonkRecorder.cpp | 1629 ++++++++++++ dom/camera/GonkRecorder.h | 174 ++ dom/camera/ICameraControl.h | 4 +- dom/camera/Makefile.in | 3 + dom/camera/README | 28 + dom/camera/nsIDOMCameraManager.idl | 35 +- dom/camera/update.patch | 2296 +++++++++++++++++ dom/camera/update.sh | 14 + dom/camera/update2.patch | 163 ++ .../DeviceStorageRequestChild.cpp | 3 +- dom/devicestorage/nsDeviceStorage.cpp | 11 + .../devicestorage/nsIDOMDeviceStorage.idl | 5 +- js/xpconnect/src/dictionary_helper_gen.conf | 3 +- toolkit/library/Makefile.in | 4 + 26 files changed, 5944 insertions(+), 45 deletions(-) create mode 100644 dom/camera/AudioParameter.cpp create mode 100644 dom/camera/GonkCameraListener.h create mode 100644 dom/camera/GonkCameraSource.cpp create mode 100644 dom/camera/GonkCameraSource.h create mode 100644 dom/camera/GonkRecorder.cpp create mode 100644 dom/camera/GonkRecorder.h create mode 100644 dom/camera/README create mode 100644 dom/camera/update.patch create mode 100644 dom/camera/update.sh create mode 100644 dom/camera/update2.patch diff --git a/configure.in b/configure.in index 66c69296b0a..9b26859bd0b 100644 --- a/configure.in +++ b/configure.in @@ -1,3 +1,4 @@ + dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*- dnl vi: set tabstop=4 shiftwidth=4 expandtab syntax=m4: dnl This Source Code Form is subject to the terms of the Mozilla Public @@ -197,7 +198,7 @@ if test -n "$gonkdir" ; then ;; esac - CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include" + CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/dalvik/libnativehelper/include/nativehelper" CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS" CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS" dnl Add -llog by default, since we use it all over the place. diff --git a/dom/camera/AudioParameter.cpp b/dom/camera/AudioParameter.cpp new file mode 100644 index 00000000000..59ccfd00f3c --- /dev/null +++ b/dom/camera/AudioParameter.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2006-2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioParameter" +//#define LOG_NDEBUG 0 + +#include + +#include + +namespace android { + +const char *AudioParameter::keyRouting = "routing"; +const char *AudioParameter::keySamplingRate = "sampling_rate"; +const char *AudioParameter::keyFormat = "format"; +const char *AudioParameter::keyChannels = "channels"; +const char *AudioParameter::keyFrameCount = "frame_count"; +const char *AudioParameter::keyInputSource = "input_source"; + +AudioParameter::AudioParameter(const String8& keyValuePairs) +{ + char *str = new char[keyValuePairs.length()+1]; + mKeyValuePairs = keyValuePairs; + + strcpy(str, keyValuePairs.string()); + char *pair = strtok(str, ";"); + while (pair != NULL) { + if (strlen(pair) != 0) { + size_t eqIdx = strcspn(pair, "="); + String8 key = String8(pair, eqIdx); + String8 value; + if (eqIdx == strlen(pair)) { + value = String8(""); + } else { + value = String8(pair + eqIdx + 1); + } + if (mParameters.indexOfKey(key) < 0) { + mParameters.add(key, value); + } else { + mParameters.replaceValueFor(key, value); + } + } else { + LOGV("AudioParameter() cstor empty key value pair"); + } + pair = strtok(NULL, ";"); + } + + delete[] str; +} + +AudioParameter::~AudioParameter() +{ + mParameters.clear(); +} + +String8 AudioParameter::toString() +{ + String8 str = String8(""); + + size_t size = mParameters.size(); + for (size_t i = 0; i < size; i++) { + str += mParameters.keyAt(i); + str += "="; + str += mParameters.valueAt(i); + if (i < (size - 1)) str += ";"; + } + return str; +} + +status_t AudioParameter::add(const String8& key, const String8& value) +{ + if (mParameters.indexOfKey(key) < 0) { + mParameters.add(key, value); + return NO_ERROR; + } else { + mParameters.replaceValueFor(key, value); + return ALREADY_EXISTS; + } +} + +status_t AudioParameter::addInt(const String8& key, const int value) +{ + char str[12]; + if (snprintf(str, 12, "%d", value) > 0) { + String8 str8 = String8(str); + return add(key, str8); + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::addFloat(const String8& key, const float value) +{ + char str[23]; + if (snprintf(str, 23, "%.10f", value) > 0) { + String8 str8 = String8(str); + return add(key, str8); + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::remove(const String8& key) +{ + if (mParameters.indexOfKey(key) >= 0) { + mParameters.removeItem(key); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::get(const String8& key, String8& value) +{ + if (mParameters.indexOfKey(key) >= 0) { + value = mParameters.valueFor(key); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t AudioParameter::getInt(const String8& key, int& value) +{ + String8 str8; + status_t result = get(key, str8); + value = 0; + if (result == NO_ERROR) { + int val; + if (sscanf(str8.string(), "%d", &val) == 1) { + value = val; + } else { + result = INVALID_OPERATION; + } + } + return result; +} + +status_t AudioParameter::getFloat(const String8& key, float& value) +{ + String8 str8; + status_t result = get(key, str8); + value = 0; + if (result == NO_ERROR) { + float val; + if (sscanf(str8.string(), "%f", &val) == 1) { + value = val; + } else { + result = INVALID_OPERATION; + } + } + return result; +} + +status_t AudioParameter::getAt(size_t index, String8& key, String8& value) +{ + if (mParameters.size() > index) { + key = mParameters.keyAt(index); + value = mParameters.valueAt(index); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +}; // namespace android diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index 8d974b1aaf0..df4a8843fd8 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -196,9 +196,9 @@ CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAStr } nsresult -CameraControlImpl::StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) +CameraControlImpl::StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) { - nsCOMPtr startRecordingTask = new StartRecordingTask(this, aSize, onSuccess, onError); + nsCOMPtr startRecordingTask = new StartRecordingTask(this, aStorageArea, aFilename, onSuccess, onError); return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL); } @@ -223,6 +223,13 @@ CameraControlImpl::StopPreview() mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL); } +nsresult +CameraControlImpl::GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) +{ + nsCOMPtr getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError); + return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL); +} + bool CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder) { diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index c59b221e99e..b74845a9e53 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -8,6 +8,7 @@ #include "nsCOMPtr.h" #include "nsDOMFile.h" #include "DictionaryHelpers.h" +#include "nsIDOMDeviceStorage.h" #include "nsIDOMCameraManager.h" #include "ICameraControl.h" #include "CameraCommon.h" @@ -25,6 +26,7 @@ class StartRecordingTask; class StopRecordingTask; class SetParameterTask; class GetParameterTask; +class GetPreviewStreamVideoModeTask; class DOMCameraPreview; @@ -39,6 +41,7 @@ class CameraControlImpl : public ICameraControl friend class StopRecordingTask; friend class SetParameterTask; friend class GetParameterTask; + friend class GetPreviewStreamVideoModeTask; public: CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread) @@ -64,8 +67,9 @@ public: void StopPreview(); nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError); nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError); + nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError); nsresult StopRecording(); + nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError); nsresult Set(uint32_t aKey, const nsAString& aValue); nsresult Get(uint32_t aKey, nsAString& aValue); @@ -111,6 +115,7 @@ protected: virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0; virtual nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; + virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0; uint32_t mCameraId; nsCOMPtr mCameraThread; @@ -347,9 +352,8 @@ public: class StartRecordingResult : public nsRunnable { public: - StartRecordingResult(nsIDOMMediaStream* aStream, nsICameraStartRecordingCallback* onSuccess) - : mStream(aStream) - , mOnSuccessCb(onSuccess) + StartRecordingResult(nsICameraStartRecordingCallback* onSuccess) + : mOnSuccessCb(onSuccess) { } virtual ~StartRecordingResult() { } @@ -359,13 +363,12 @@ public: MOZ_ASSERT(NS_IsMainThread()); if (mOnSuccessCb) { - mOnSuccessCb->HandleEvent(mStream); + mOnSuccessCb->HandleEvent(); } return NS_OK; } protected: - nsCOMPtr mStream; nsCOMPtr mOnSuccessCb; }; @@ -373,9 +376,10 @@ protected: class StartRecordingTask : public nsRunnable { public: - StartRecordingTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) - : mSize(aSize) - , mCameraControl(aCameraControl) + StartRecordingTask(CameraControlImpl* aCameraControl, nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) + : mCameraControl(aCameraControl) + , mStorageArea(aStorageArea) + , mFilename(aFilename) , mOnSuccessCb(onSuccess) , mOnErrorCb(onError) { @@ -391,17 +395,21 @@ public: { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); nsresult rv = mCameraControl->StartRecordingImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv); - if (NS_FAILED(rv) && mOnErrorCb) { + if (NS_SUCCEEDED(rv)) { + if (mOnSuccessCb) { + rv = NS_DispatchToMainThread(new StartRecordingResult(mOnSuccessCb)); + } + } else if (mOnErrorCb) { rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"))); - NS_ENSURE_SUCCESS(rv, rv); } return rv; } - CameraSize mSize; nsRefPtr mCameraControl; + nsCOMPtr mStorageArea; + nsString mFilename; nsCOMPtr mOnSuccessCb; nsCOMPtr mOnErrorCb; }; @@ -491,6 +499,67 @@ public: nsRefPtr mCameraControl; }; +// Return the resulting preview stream to JS. Runs on the main thread. +class GetPreviewStreamVideoModeResult : public nsRunnable +{ +public: + GetPreviewStreamVideoModeResult(nsIDOMMediaStream* aStream, nsICameraPreviewStreamCallback* onSuccess) + : mStream(aStream) + , mOnSuccessCb(onSuccess) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + virtual ~GetPreviewStreamVideoModeResult() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (mOnSuccessCb) { + mOnSuccessCb->HandleEvent(mStream); + } + return NS_OK; + } + +protected: + nsCOMPtr mStream; + nsCOMPtr mOnSuccessCb; +}; + +// Get the video mode preview stream. +class GetPreviewStreamVideoModeTask : public nsRunnable +{ +public: + GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, CameraRecordingOptions aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) + : mCameraControl(aCameraControl) + , mOptions(aOptions) + , mOnSuccessCb(onSuccess) + , mOnErrorCb(onError) + { } + + NS_IMETHOD Run() + { + DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__); + nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this); + DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv); + + if (NS_FAILED(rv) && mOnErrorCb) { + rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"))); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; + } + + nsRefPtr mCameraControl; + CameraRecordingOptions mOptions; + nsCOMPtr mOnSuccessCb; + nsCOMPtr mOnErrorCb; +}; + } // namespace mozilla #endif // DOM_CAMERA_CAMERACONTROLIMPL_H diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index c05f10294b9..e4be8d22b49 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -9,6 +9,7 @@ #include "nsThread.h" #include "mozilla/Services.h" #include "nsIObserverService.h" +#include "nsIDOMDeviceStorage.h" #include "DOMCameraManager.h" #include "DOMCameraCapabilities.h" #include "DOMCameraControl.h" @@ -218,16 +219,12 @@ nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter) return NS_ERROR_NOT_IMPLEMENTED; } -/* void startRecording (in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */ +/* [implicit_jscontext] void startRecording (in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */ NS_IMETHODIMP -nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx) +nsDOMCameraControl::StartRecording(nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx) { NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG); - CameraSize size; - nsresult rv = size.Init(cx, &aOptions); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) { NS_WARNING("Could not get the Observer service for CameraControl::StartRecording."); @@ -238,7 +235,7 @@ nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartReco "recording-device-events", NS_LITERAL_STRING("starting").get()); - return mCameraControl->StartRecording(size, onSuccess, onError); + return mCameraControl->StartRecording(storageArea, filename, onSuccess, onError); } /* void stopRecording (); */ @@ -316,6 +313,19 @@ nsDOMCameraControl::TakePicture(const JS::Value& aOptions, nsICameraTakePictureC return mCameraControl->TakePicture(size, options.rotation, options.fileFormat, pos, onSuccess, onError); } +/* [implicit_jscontext] void GetPreviewStreamVideoMode (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */ +NS_IMETHODIMP +nsDOMCameraControl::GetPreviewStreamVideoMode(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx) +{ + NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG); + + CameraRecordingOptions options; + nsresult rv = options.Init(cx, &aOptions); + NS_ENSURE_SUCCESS(rv, rv); + + return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError); +} + class GetCameraResult : public nsRunnable { public: diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 115bc8d5a22..6b60351ca0f 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -15,6 +15,10 @@ */ #include +#include +#include +#include +#include #include "base/basictypes.h" #include "libcameraservice/CameraHardwareInterface.h" #include "camera/CameraParameters.h" @@ -23,6 +27,8 @@ #include "nsMemory.h" #include "jsapi.h" #include "nsThread.h" +#include +#include "nsDirectoryServiceDefs.h" // for NS_GetSpecialDirectory #include "nsPrintfCString.h" #include "DOMCameraManager.h" #include "GonkCameraHwMgr.h" @@ -547,19 +553,27 @@ nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview) } nsresult -nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview) +nsGonkCameraControl::StopPreviewInternal(bool aForced) { DOM_CAMERA_LOGI("%s: stopping preview\n", __func__); // StopPreview() is a synchronous call--it doesn't return // until the camera preview thread exits. - GonkCameraHardware::StopPreview(mHwHandle); - mDOMPreview->Stopped(); - mDOMPreview = nullptr; + if (mDOMPreview) { + GonkCameraHardware::StopPreview(mHwHandle); + mDOMPreview->Stopped(aForced); + mDOMPreview = nullptr; + } return NS_OK; } +nsresult +nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview) +{ + return StopPreviewInternal(); +} + nsresult nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus) { @@ -691,13 +705,71 @@ nsGonkCameraControl::PullParametersImpl() nsresult nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) { - return NS_ERROR_NOT_IMPLEMENTED; + mStartRecordingOnSuccessCb = aStartRecording->mOnSuccessCb; + mStartRecordingOnErrorCb = aStartRecording->mOnErrorCb; + + /** + * We need to pull in the base path from aStartRecording->mStorageArea + * once that feature lands. See bug 795201. + * + * For now, we just assume /sdcard/Movies. + * + * Also, the camera app needs to provide the file extension '.3gp' for now. + * See bug 795202. + */ +#if 1 + nsCOMPtr filename; + aStartRecording->mStorageArea->GetRootDirectory(getter_AddRefs(filename)); + filename->Append(aStartRecording->mFilename); + + nsAutoCString pathname; + filename->GetNativePath(pathname); +#else + nsAutoCString pathname(NS_LITERAL_CSTRING("/sdcard/Movies/")); + nsAutoCString filename(NS_ConvertUTF16toUTF8(aStartRecording->mFilename)); + + // Make sure that the file name doesn't contain any directory components. + if (strcmp(filename.get(), basename(filename.get())) != 0) { + DOM_CAMERA_LOGE("Video filename '%s' is not valid\n", filename.get()); + return NS_ERROR_INVALID_ARG; + } + + pathname.Append(filename); +#endif + DOM_CAMERA_LOGI("Video pathname is '%s'\n", pathname.get()); + int fd = open(pathname.get(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + DOM_CAMERA_LOGE("Couldn't create file '%s' with error (%d) %s\n", pathname.get(), errno, strerror(errno)); + return NS_ERROR_FAILURE; + } + + if (SetupRecording(fd) != NS_OK) { + DOM_CAMERA_LOGE("SetupRecording() failed\n"); + close(fd); + return NS_ERROR_FAILURE; + } + if (mRecorder->start() != OK) { + DOM_CAMERA_LOGE("mRecorder->start() failed\n"); + close(fd); + return NS_ERROR_FAILURE; + } + + // dispatch the callback + nsCOMPtr startRecordingResult = new StartRecordingResult(mStartRecordingOnSuccessCb); + nsresult rv = NS_DispatchToMainThread(startRecordingResult); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv); + } + return NS_OK; } nsresult nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording) { - return NS_ERROR_NOT_IMPLEMENTED; + mRecorder->stop(); + delete mRecorder; + mRecorder = nullptr; + return NS_OK; } void @@ -809,6 +881,152 @@ nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight) PushParameters(); } +nsresult +nsGonkCameraControl::SetupVideoMode() +{ + // read preferences for camcorder + mMediaProfiles = MediaProfiles::getInstance(); + + /** + * Right now default to profile 3, which is 352x288 on Otoro. In the + * future, allow the application to select a recording quality and + * configuration. + * + * See bug 795379. + */ + int quality = 3; // cif:352x288 + camcorder_quality q = static_cast(quality); + mDuration = mMediaProfiles->getCamcorderProfileParamByName("duration", (int)mCameraId, q); + mVideoFileFormat = mMediaProfiles->getCamcorderProfileParamByName("file.format", (int)mCameraId, q); + mVideoCodec = mMediaProfiles->getCamcorderProfileParamByName("vid.codec", (int)mCameraId, q); + mVideoBitRate = mMediaProfiles->getCamcorderProfileParamByName("vid.bps", (int)mCameraId, q); + mVideoFrameRate = mMediaProfiles->getCamcorderProfileParamByName("vid.fps", (int)mCameraId, q); + mVideoFrameWidth = mMediaProfiles->getCamcorderProfileParamByName("vid.width", (int)mCameraId, q); + mVideoFrameHeight = mMediaProfiles->getCamcorderProfileParamByName("vid.height", (int)mCameraId, q); + mAudioCodec = mMediaProfiles->getCamcorderProfileParamByName("aud.codec", (int)mCameraId, q); + mAudioBitRate = mMediaProfiles->getCamcorderProfileParamByName("aud.bps", (int)mCameraId, q); + mAudioSampleRate = mMediaProfiles->getCamcorderProfileParamByName("aud.hz", (int)mCameraId, q); + mAudioChannels = mMediaProfiles->getCamcorderProfileParamByName("aud.ch", (int)mCameraId, q); + + if (mVideoFrameRate == -1) { + DOM_CAMERA_LOGE("Failed to get a valid frame rate!\n"); + DOM_CAMERA_LOGE("Also got width=%d, height=%d\n", mVideoFrameWidth, mVideoFrameHeight); + return NS_ERROR_FAILURE; + } + + PullParametersImpl(); + + // Configure camera video recording parameters. + const size_t SIZE = 256; + char buffer[SIZE]; + + /** + * Ignore the width and height settings from app, just use the one in profile. + * Eventually, will try to choose a profile which respects the settings from app. + * See bug 795330. + */ + mParams.setPreviewSize(mVideoFrameWidth, mVideoFrameHeight); + mParams.setPreviewFrameRate(mVideoFrameRate); + snprintf(buffer, SIZE, "%dx%d", mVideoFrameWidth, mVideoFrameHeight); + + /** + * "record-size" is probably deprecated in later ICS; + * might need to set "video-size" instead of "record-size". + * See bug 795332. + */ + mParams.set("record-size", buffer); + + /** + * If we want to enable picture-taking _while_ recording video, this sets the + * size of the captured picture. For now, just set it to the same dimensions + * as the video we're recording; ideally, we should probably make sure it + * matches one of the supported picture sizes. + */ + mParams.setPictureSize(mVideoFrameWidth, mVideoFrameHeight); + + PushParametersImpl(); + return NS_OK; +} + +#ifndef CHECK_SETARG +#define CHECK_SETARG(x) \ + do { \ + if (x) { \ + DOM_CAMERA_LOGE(#x " failed\n"); \ + return NS_ERROR_INVALID_ARG; \ + } \ + } while(0) +#endif + +nsresult +nsGonkCameraControl::SetupRecording(int aFd) +{ + // choosing a size big enough to hold the params + const size_t SIZE = 256; + char buffer[SIZE]; + + mRecorder = new GonkRecorder(); + CHECK_SETARG(mRecorder->init()); + + // set all the params + CHECK_SETARG(mRecorder->setCameraHandle((int32_t)mHwHandle)); + CHECK_SETARG(mRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER)); + CHECK_SETARG(mRecorder->setVideoSource(VIDEO_SOURCE_CAMERA)); + CHECK_SETARG(mRecorder->setOutputFormat((output_format)mVideoFileFormat)); + CHECK_SETARG(mRecorder->setVideoFrameRate(mVideoFrameRate)); + CHECK_SETARG(mRecorder->setVideoSize(mVideoFrameWidth, mVideoFrameHeight)); + snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideoBitRate); + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + CHECK_SETARG(mRecorder->setVideoEncoder((video_encoder)mVideoCodec)); + snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudioBitRate); + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudioChannels); + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudioSampleRate); + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + CHECK_SETARG(mRecorder->setAudioEncoder((audio_encoder)mAudioCodec)); + // TODO: For now there is no limit on recording duration (See bug 795090) + CHECK_SETARG(mRecorder->setParameters(String8("max-duration=-1"))); + // TODO: For now there is no limit on file size (See bug 795090) + CHECK_SETARG(mRecorder->setParameters(String8("max-filesize=-1"))); + snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", mVideoRotation); + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + + // recording API needs file descriptor of output file + CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0)); + CHECK_SETARG(mRecorder->prepare()); + return NS_OK; +} + +nsresult +nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) +{ + nsCOMPtr getPreviewStreamResult = nullptr; + + // stop any currently running preview + StopPreviewInternal(true /* forced */); + + // copy the recording preview options + mVideoRotation = aGetPreviewStreamVideoMode->mOptions.rotation; + mVideoWidth = aGetPreviewStreamVideoMode->mOptions.width; + mVideoHeight = aGetPreviewStreamVideoMode->mOptions.height; + DOM_CAMERA_LOGI("recording preview format: %d x %d (w x h) (rotated %d degrees)\n", mVideoWidth, mVideoHeight, mVideoRotation); + + // setup the video mode + nsresult rv = SetupVideoMode(); + NS_ENSURE_SUCCESS(rv, rv); + + // create and return new preview stream object + getPreviewStreamResult = new GetPreviewStreamResult(this, mVideoWidth, mVideoHeight, mVideoFrameRate, aGetPreviewStreamVideoMode->mOnSuccessCb); + rv = NS_DispatchToMainThread(getPreviewStreamResult); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!"); + return rv; + } + + return NS_OK; +} + // Gonk callback handlers. namespace mozilla { diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index db82b7b9a25..0e9bec15800 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -24,6 +24,7 @@ #include "DOMCameraControl.h" #include "CameraControlImpl.h" #include "CameraCommon.h" +#include "GonkRecorder.h" namespace mozilla { @@ -47,6 +48,9 @@ public: void SetParameter(uint32_t aKey, const nsTArray& aRegions); nsresult PushParameters(); + nsresult SetupRecording(int aFd); + nsresult SetupVideoMode(); + void AutoFocusComplete(bool aSuccess); void TakePictureComplete(uint8_t* aData, uint32_t aLength); @@ -56,12 +60,14 @@ protected: nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream); nsresult StartPreviewImpl(StartPreviewTask* aStartPreview); nsresult StopPreviewImpl(StopPreviewTask* aStopPreview); + nsresult StopPreviewInternal(bool aForced = false); nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus); nsresult TakePictureImpl(TakePictureTask* aTakePicture); nsresult StartRecordingImpl(StartRecordingTask* aStartRecording); nsresult StopRecordingImpl(StopRecordingTask* aStopRecording); nsresult PushParametersImpl(); nsresult PullParametersImpl(); + nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode); void SetPreviewSize(uint32_t aWidth, uint32_t aHeight); @@ -84,6 +90,27 @@ protected: uint32_t mFps; uint32_t mDiscardedFrameCount; + android::MediaProfiles* mMediaProfiles; + android::GonkRecorder* mRecorder; + + PRUint32 mVideoRotation; + PRUint32 mVideoWidth; + PRUint32 mVideoHeight; + nsString mVideoFile; + + // camcorder profile settings for the desired quality level + int mDuration; // max recording duration (ignored) + int mVideoFileFormat; // output file format + int mVideoCodec; // video encoder + int mVideoBitRate; // video bit rate + int mVideoFrameRate; // video frame rate + int mVideoFrameWidth; // video frame width + int mVideoFrameHeight;// video frame height + int mAudioCodec; // audio encoder + int mAudioBitRate; // audio bit rate + int mAudioSampleRate; // audio sample rate + int mAudioChannels; // number of audio channels + private: nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE; nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE; diff --git a/dom/camera/GonkCameraHwMgr.cpp b/dom/camera/GonkCameraHwMgr.cpp index 9b6dfd41533..d22471b803c 100644 --- a/dom/camera/GonkCameraHwMgr.cpp +++ b/dom/camera/GonkCameraHwMgr.cpp @@ -143,6 +143,34 @@ GonkCameraHardware::NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, } } +void +GonkCameraHardware::DataCallbackTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp &aDataPtr, void* aUser) +{ + DOM_CAMERA_LOGI("%s",__func__); + GonkCameraHardware* hw = GetHardware((uint32_t)aUser); + if (!hw) { + DOM_CAMERA_LOGE("%s:aUser = %d resolved to no camera hw\n", __func__, (uint32_t)aUser); + return; + } + if (hw->mClosing) { + return; + } + + sp listener; + { + //TODO + //Mutex::Autolock _l(hw->mLock); + listener = hw->mListener; + } + if (listener.get()) { + DOM_CAMERA_LOGI("Listener registered, posting recording frame!"); + listener->postDataTimestamp(aTimestamp, aMsgType, aDataPtr); + } else { + DOM_CAMERA_LOGW("No listener was set. Drop a recording frame."); + hw->mHardware->releaseRecordingFrame(aDataPtr); + } +} + void GonkCameraHardware::Init() { @@ -162,7 +190,7 @@ GonkCameraHardware::Init() if (sHwHandle == 0) { sHwHandle = 1; // don't use 0 } - mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, NULL, (void*)sHwHandle); + mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, GonkCameraHardware::DataCallbackTimestamp, (void*)sHwHandle); mInitialized = true; } @@ -314,3 +342,81 @@ GonkCameraHardware::StopPreview(uint32_t aHwHandle) hw->mHardware->stopPreview(); } } + +int +GonkCameraHardware::StartRecording(uint32_t aHwHandle) +{ + DOM_CAMERA_LOGI("%s: aHwHandle = %d\n", __func__, aHwHandle); + int rv = OK; + GonkCameraHardware* hw = GetHardware(aHwHandle); + if (!hw) { + return DEAD_OBJECT; + } + + if (hw->mHardware->recordingEnabled()) { + return OK; + } + + if (!hw->mHardware->previewEnabled()) { + DOM_CAMERA_LOGW("Preview was not enabled, enabling now!\n"); + rv = StartPreview(aHwHandle); + if (rv != OK) { + return rv; + } + } + + // start recording mode + hw->mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME); + DOM_CAMERA_LOGI("Calling hw->startRecording\n"); + rv = hw->mHardware->startRecording(); + if (rv != OK) { + DOM_CAMERA_LOGE("mHardware->startRecording() failed with status %d", rv); + } + return rv; +} + +int +GonkCameraHardware::StopRecording(uint32_t aHwHandle) +{ + DOM_CAMERA_LOGI("%s: aHwHandle = %d\n", __func__, aHwHandle); + GonkCameraHardware* hw = GetHardware(aHwHandle); + if (!hw) { + return DEAD_OBJECT; + } + + hw->mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME); + hw->mHardware->stopRecording(); + return OK; +} + +int +GonkCameraHardware::SetListener(uint32_t aHwHandle, const sp& aListener) +{ + GonkCameraHardware* hw = GetHardware(aHwHandle); + if (!hw) { + return DEAD_OBJECT; + } + + hw->mListener = aListener; + return OK; +} + +void +GonkCameraHardware::ReleaseRecordingFrame(uint32_t aHwHandle, const sp& aFrame) +{ + GonkCameraHardware* hw = GetHardware(aHwHandle); + if (hw) { + hw->mHardware->releaseRecordingFrame(aFrame); + } +} + +int +GonkCameraHardware::StoreMetaDataInBuffers(uint32_t aHwHandle, bool aEnabled) +{ + GonkCameraHardware* hw = GetHardware(aHwHandle); + if (!hw) { + return DEAD_OBJECT; + } + + return hw->mHardware->storeMetaDataInBuffers(aEnabled); +} diff --git a/dom/camera/GonkCameraHwMgr.h b/dom/camera/GonkCameraHwMgr.h index 25833b3c55a..56f28ebd46c 100644 --- a/dom/camera/GonkCameraHwMgr.h +++ b/dom/camera/GonkCameraHwMgr.h @@ -20,6 +20,8 @@ #include "libcameraservice/CameraHardwareInterface.h" #include "binder/IMemory.h" #include "mozilla/ReentrantMonitor.h" +#include "GonkCameraListener.h" +#include #include "GonkCameraControl.h" #include "CameraCommon.h" @@ -46,6 +48,7 @@ protected: static void DataCallback(int32_t aMsgType, const sp &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser); static void NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser); + static void DataCallbackTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp& aDataPtr, void* aUser); public: virtual void OnNewFrame() MOZ_OVERRIDE; @@ -60,6 +63,11 @@ public: static void StopPreview(uint32_t aHwHandle); static int PushParameters(uint32_t aHwHandle, const CameraParameters& aParams); static void PullParameters(uint32_t aHwHandle, CameraParameters& aParams); + static int StartRecording(uint32_t aHwHandle); + static int StopRecording(uint32_t aHwHandle); + static int SetListener(uint32_t aHwHandle, const sp& aListener); + static void ReleaseRecordingFrame(uint32_t aHwHandle, const sp& aFrame); + static int StoreMetaDataInBuffers(uint32_t aHwHandle, bool aEnabled); protected: static GonkCameraHardware* sHw; @@ -93,6 +101,7 @@ protected: struct timespec mStart; struct timespec mAutoFocusStart; #endif + sp mListener; bool mInitialized; bool IsInitialized() diff --git a/dom/camera/GonkCameraListener.h b/dom/camera/GonkCameraListener.h new file mode 100644 index 00000000000..243264c2e9d --- /dev/null +++ b/dom/camera/GonkCameraListener.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 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 GONK_CAMERA_LISTENER_H +#define GONK_CAMERA_LISTENER_H + +#include +#include "libcameraservice/CameraHardwareInterface.h" + +namespace android { + +// ref-counted object for callbacks +class GonkCameraListener: virtual public RefBase +{ +public: + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; + virtual void postData(int32_t msgType, const sp& dataPtr, + camera_frame_metadata_t *metadata) = 0; + virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr) = 0; +}; + +}; // namespace android + +#endif diff --git a/dom/camera/GonkCameraSource.cpp b/dom/camera/GonkCameraSource.cpp new file mode 100644 index 00000000000..73331eda9b8 --- /dev/null +++ b/dom/camera/GonkCameraSource.cpp @@ -0,0 +1,733 @@ +/* + * 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 +#include "nsDebug.h" +#define DOM_CAMERA_LOG_LEVEL 3 +#include "CameraCommon.h" +#define LOGD DOM_CAMERA_LOGA +#define LOGV DOM_CAMERA_LOGI +#define LOGI DOM_CAMERA_LOGI +#define LOGW DOM_CAMERA_LOGW +#define LOGE DOM_CAMERA_LOGE + +#include +#include "GonkCameraSource.h" +#include "GonkCameraListener.h" +#include "GonkCameraHwMgr.h" +#include +#include +#include +#include +#include +#include + +using namespace mozilla; +namespace android { + +static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL; + +struct GonkCameraSourceListener : public GonkCameraListener { + GonkCameraSourceListener(const sp &source); + + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void postData(int32_t msgType, const sp &dataPtr, + camera_frame_metadata_t *metadata); + + virtual void postDataTimestamp( + nsecs_t timestamp, int32_t msgType, const sp& dataPtr); + +protected: + virtual ~GonkCameraSourceListener(); + +private: + wp mSource; + + GonkCameraSourceListener(const GonkCameraSourceListener &); + GonkCameraSourceListener &operator=(const GonkCameraSourceListener &); +}; + +GonkCameraSourceListener::GonkCameraSourceListener(const sp &source) + : mSource(source) { +} + +GonkCameraSourceListener::~GonkCameraSourceListener() { +} + +void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { + LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); +} + +void GonkCameraSourceListener::postData(int32_t msgType, const sp &dataPtr, + camera_frame_metadata_t *metadata) { + LOGV("postData(%d, ptr:%p, size:%d)", + msgType, dataPtr->pointer(), dataPtr->size()); + + sp source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallback(msgType, dataPtr); + } +} + +void GonkCameraSourceListener::postDataTimestamp( + nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { + + sp source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); + } +} + +static int32_t getColorFormat(const char* colorFormat) { + return OMX_COLOR_FormatYUV420SemiPlanar; + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) { + return OMX_COLOR_FormatYUV420Planar; + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) { + return OMX_COLOR_FormatYUV422SemiPlanar; + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) { + return OMX_COLOR_FormatYUV420SemiPlanar; + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) { + return OMX_COLOR_FormatYCbYCr; + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) { + return OMX_COLOR_Format16bitRGB565; + } + + if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) { + return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar; + } + + LOGE("Uknown color format (%s), please add it to " + "GonkCameraSource::getColorFormat", colorFormat); + + CHECK_EQ(0, "Unknown color format"); +} + +GonkCameraSource *GonkCameraSource::Create( + int32_t cameraHandle, + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers) { + + GonkCameraSource *source = new GonkCameraSource(cameraHandle, + videoSize, frameRate, + storeMetaDataInVideoBuffers); + return source; +} + +GonkCameraSource::GonkCameraSource( + int32_t cameraHandle, + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers) + : mCameraFlags(0), + mVideoFrameRate(-1), + mNumFramesReceived(0), + mLastFrameTimestampUs(0), + mStarted(false), + mNumFramesEncoded(0), + mTimeBetweenFrameCaptureUs(0), + mFirstFrameTimeUs(0), + mNumFramesDropped(0), + mNumGlitches(0), + mGlitchDurationThresholdUs(200000), + mCollectStats(false) { + mVideoSize.width = -1; + mVideoSize.height = -1; + + mCameraHandle = cameraHandle; + + mInitCheck = init( + videoSize, frameRate, + storeMetaDataInVideoBuffers); + if (mInitCheck != OK) releaseCamera(); +} + +status_t GonkCameraSource::initCheck() const { + return mInitCheck; +} + +//TODO: Do we need to reimplement isCameraAvailable? + +/* + * Check to see whether the requested video width and height is one + * of the supported sizes. + * @param width the video frame width in pixels + * @param height the video frame height in pixels + * @param suppportedSizes the vector of sizes that we check against + * @return true if the dimension (width and height) is supported. + */ +static bool isVideoSizeSupported( + int32_t width, int32_t height, + const Vector& supportedSizes) { + + LOGV("isVideoSizeSupported"); + for (size_t i = 0; i < supportedSizes.size(); ++i) { + if (width == supportedSizes[i].width && + height == supportedSizes[i].height) { + return true; + } + } + return false; +} + +/* + * If the preview and video output is separate, we only set the + * the video size, and applications should set the preview size + * to some proper value, and the recording framework will not + * change the preview size; otherwise, if the video and preview + * output is the same, we need to set the preview to be the same + * as the requested video size. + * + */ +/* + * Query the camera to retrieve the supported video frame sizes + * and also to see whether CameraParameters::setVideoSize() + * is supported or not. + * @param params CameraParameters to retrieve the information + * @@param isSetVideoSizeSupported retunrs whether method + * CameraParameters::setVideoSize() is supported or not. + * @param sizes returns the vector of Size objects for the + * supported video frame sizes advertised by the camera. + */ +static void getSupportedVideoSizes( + const CameraParameters& params, + bool *isSetVideoSizeSupported, + Vector& sizes) { + + *isSetVideoSizeSupported = true; + params.getSupportedVideoSizes(sizes); + if (sizes.size() == 0) { + LOGD("Camera does not support setVideoSize()"); + params.getSupportedPreviewSizes(sizes); + *isSetVideoSizeSupported = false; + } +} + +/* + * Check whether the camera has the supported color format + * @param params CameraParameters to retrieve the information + * @return OK if no error. + */ +status_t GonkCameraSource::isCameraColorFormatSupported( + const CameraParameters& params) { + mColorFormat = getColorFormat(params.get( + CameraParameters::KEY_VIDEO_FRAME_FORMAT)); + if (mColorFormat == -1) { + return BAD_VALUE; + } + return OK; +} + +/* + * Configure the camera to use the requested video size + * (width and height) and/or frame rate. If both width and + * height are -1, configuration on the video size is skipped. + * if frameRate is -1, configuration on the frame rate + * is skipped. Skipping the configuration allows one to + * use the current camera setting without the need to + * actually know the specific values (see Create() method). + * + * @param params the CameraParameters to be configured + * @param width the target video frame width in pixels + * @param height the target video frame height in pixels + * @param frameRate the target frame rate in frames per second. + * @return OK if no error. + */ +status_t GonkCameraSource::configureCamera( + CameraParameters* params, + int32_t width, int32_t height, + int32_t frameRate) { + LOGV("configureCamera"); + Vector sizes; + bool isSetVideoSizeSupportedByCamera = true; + getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes); + bool isCameraParamChanged = false; + if (width != -1 && height != -1) { + if (!isVideoSizeSupported(width, height, sizes)) { + LOGE("Video dimension (%dx%d) is unsupported", width, height); + return BAD_VALUE; + } + if (isSetVideoSizeSupportedByCamera) { + params->setVideoSize(width, height); + } else { + params->setPreviewSize(width, height); + } + isCameraParamChanged = true; + } else if ((width == -1 && height != -1) || + (width != -1 && height == -1)) { + // If one and only one of the width and height is -1 + // we reject such a request. + LOGE("Requested video size (%dx%d) is not supported", width, height); + return BAD_VALUE; + } else { // width == -1 && height == -1 + // Do not configure the camera. + // Use the current width and height value setting from the camera. + } + + if (frameRate != -1) { + CHECK(frameRate > 0 && frameRate <= 120); + const char* supportedFrameRates = + params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES); + CHECK(supportedFrameRates != NULL); + LOGV("Supported frame rates: %s", supportedFrameRates); + char buf[4]; + snprintf(buf, 4, "%d", frameRate); + if (strstr(supportedFrameRates, buf) == NULL) { + LOGE("Requested frame rate (%d) is not supported: %s", + frameRate, supportedFrameRates); + return BAD_VALUE; + } + + // The frame rate is supported, set the camera to the requested value. + params->setPreviewFrameRate(frameRate); + isCameraParamChanged = true; + } else { // frameRate == -1 + // Do not configure the camera. + // Use the current frame rate value setting from the camera + } + + if (isCameraParamChanged) { + // Either frame rate or frame size needs to be changed. + if (OK != GonkCameraHardware::PushParameters(mCameraHandle,*params)) { + LOGE("Could not change settings." + " Someone else is using camera ?"); + return -EBUSY; + } + } + return OK; +} + +/* + * Check whether the requested video frame size + * has been successfully configured or not. If both width and height + * are -1, check on the current width and height value setting + * is performed. + * + * @param params CameraParameters to retrieve the information + * @param the target video frame width in pixels to check against + * @param the target video frame height in pixels to check against + * @return OK if no error + */ +status_t GonkCameraSource::checkVideoSize( + const CameraParameters& params, + int32_t width, int32_t height) { + + LOGV("checkVideoSize"); + // The actual video size is the same as the preview size + // if the camera hal does not support separate video and + // preview output. In this case, we retrieve the video + // size from preview. + int32_t frameWidthActual = -1; + int32_t frameHeightActual = -1; + Vector sizes; + params.getSupportedVideoSizes(sizes); + if (sizes.size() == 0) { + // video size is the same as preview size + params.getPreviewSize(&frameWidthActual, &frameHeightActual); + } else { + // video size may not be the same as preview + params.getVideoSize(&frameWidthActual, &frameHeightActual); + } + if (frameWidthActual < 0 || frameHeightActual < 0) { + LOGE("Failed to retrieve video frame size (%dx%d)", + frameWidthActual, frameHeightActual); + return UNKNOWN_ERROR; + } + + // Check the actual video frame size against the target/requested + // video frame size. + if (width != -1 && height != -1) { + if (frameWidthActual != width || frameHeightActual != height) { + LOGE("Failed to set video frame size to %dx%d. " + "The actual video size is %dx%d ", width, height, + frameWidthActual, frameHeightActual); + return UNKNOWN_ERROR; + } + } + + // Good now. + mVideoSize.width = frameWidthActual; + mVideoSize.height = frameHeightActual; + return OK; +} + +/* + * Check the requested frame rate has been successfully configured or not. + * If the target frameRate is -1, check on the current frame rate value + * setting is performed. + * + * @param params CameraParameters to retrieve the information + * @param the target video frame rate to check against + * @return OK if no error. + */ +status_t GonkCameraSource::checkFrameRate( + const CameraParameters& params, + int32_t frameRate) { + + LOGV("checkFrameRate"); + int32_t frameRateActual = params.getPreviewFrameRate(); + if (frameRateActual < 0) { + LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual); + return UNKNOWN_ERROR; + } + + // Check the actual video frame rate against the target/requested + // video frame rate. + if (frameRate != -1 && (frameRateActual - frameRate) != 0) { + LOGE("Failed to set preview frame rate to %d fps. The actual " + "frame rate is %d", frameRate, frameRateActual); + return UNKNOWN_ERROR; + } + + // Good now. + mVideoFrameRate = frameRateActual; + return OK; +} + +/* + * Initialize the CameraSource to so that it becomes + * ready for providing the video input streams as requested. + * @param camera the camera object used for the video source + * @param cameraId if camera == 0, use camera with this id + * as the video source + * @param videoSize the target video frame size. If both + * width and height in videoSize is -1, use the current + * width and heigth settings by the camera + * @param frameRate the target frame rate in frames per second. + * if it is -1, use the current camera frame rate setting. + * @param storeMetaDataInVideoBuffers request to store meta + * data or real YUV data in video buffers. Request to + * store meta data in video buffers may not be honored + * if the source does not support this feature. + * + * @return OK if no error. + */ +status_t GonkCameraSource::init( + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers) { + + LOGV("init"); + status_t err = OK; + //TODO: need to do something here to check the sanity of camera + + CameraParameters params; + GonkCameraHardware::PullParameters(mCameraHandle, params); + if ((err = isCameraColorFormatSupported(params)) != OK) { + return err; + } + + // Set the camera to use the requested video frame size + // and/or frame rate. + if ((err = configureCamera(¶ms, + videoSize.width, videoSize.height, + frameRate))) { + return err; + } + + // Check on video frame size and frame rate. + CameraParameters newCameraParams; + GonkCameraHardware::PullParameters(mCameraHandle, newCameraParams); + if ((err = checkVideoSize(newCameraParams, + videoSize.width, videoSize.height)) != OK) { + return err; + } + if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) { + return err; + } + + // By default, do not store metadata in video buffers + mIsMetaDataStoredInVideoBuffers = false; + GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, false); + if (storeMetaDataInVideoBuffers) { + if (OK == GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, true)) { + mIsMetaDataStoredInVideoBuffers = true; + } + } + + const char *hfr_str = params.get("video-hfr"); + int32_t hfr = -1; + if ( hfr_str != NULL ) { + hfr = atoi(hfr_str); + } + if(hfr < 0) { + LOGW("Invalid hfr value(%d) set from app. Disabling HFR.", hfr); + hfr = 0; + } + + int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); + if (glitchDurationUs > mGlitchDurationThresholdUs) { + mGlitchDurationThresholdUs = glitchDurationUs; + } + + const char * k3dFrameArrangement = "3d-frame-format"; + const char * arrangement = params.get(k3dFrameArrangement); + // XXX: just assume left/right for now since that's all the camera supports + bool want3D = (arrangement != NULL && !strcmp("left-right", arrangement)); + + // XXX: query camera for the stride and slice height + // when the capability becomes available. + mMeta = new MetaData; + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + mMeta->setInt32(kKeyColorFormat, mColorFormat); + mMeta->setInt32(kKeyWidth, mVideoSize.width); + mMeta->setInt32(kKeyHeight, mVideoSize.height); + mMeta->setInt32(kKeyStride, mVideoSize.width); + mMeta->setInt32(kKeySliceHeight, mVideoSize.height); + mMeta->setInt32(kKeyFrameRate, mVideoFrameRate); + + return OK; +} + +GonkCameraSource::~GonkCameraSource() { + if (mStarted) { + stop(); + } else if (mInitCheck == OK) { + // Camera is initialized but because start() is never called, + // the lock on Camera is never released(). This makes sure + // Camera's lock is released in this case. + // TODO: Don't think I need to do this + releaseCamera(); + } +} + +void GonkCameraSource::startCameraRecording() { + LOGV("startCameraRecording"); + CHECK_EQ(OK, GonkCameraHardware::StartRecording(mCameraHandle)); +} + +status_t GonkCameraSource::start(MetaData *meta) { + LOGV("start"); + CHECK(!mStarted); + if (mInitCheck != OK) { + LOGE("GonkCameraSource is not initialized yet"); + return mInitCheck; + } + + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.stagefright.record-stats", value, NULL) + && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { + mCollectStats = true; + } + + mStartTimeUs = 0; + int64_t startTimeUs; + if (meta && meta->findInt64(kKeyTime, &startTimeUs)) { + LOGV("Metadata enabled, startime: %lld us", startTimeUs); + mStartTimeUs = startTimeUs; + } + + // Register a listener with GonkCameraHardware so that we can get callbacks + GonkCameraHardware::SetListener(mCameraHandle, new GonkCameraSourceListener(this)); + + startCameraRecording(); + + mStarted = true; + return OK; +} + +void GonkCameraSource::stopCameraRecording() { + LOGV("stopCameraRecording"); + GonkCameraHardware::StopRecording(mCameraHandle); +} + +void GonkCameraSource::releaseCamera() { + LOGV("releaseCamera"); +} + +status_t GonkCameraSource::stop() { + LOGV("stop: E"); + Mutex::Autolock autoLock(mLock); + mStarted = false; + mFrameAvailableCondition.signal(); + + releaseQueuedFrames(); + while (!mFramesBeingEncoded.empty()) { + if (NO_ERROR != + mFrameCompleteCondition.waitRelative(mLock, + mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { + LOGW("Timed out waiting for outstanding frames being encoded: %d", + mFramesBeingEncoded.size()); + } + } + LOGV("Calling stopCameraRecording"); + stopCameraRecording(); + releaseCamera(); + + if (mCollectStats) { + LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us", + mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, + mLastFrameTimestampUs - mFirstFrameTimeUs); + } + + if (mNumGlitches > 0) { + LOGW("%d long delays between neighboring video frames", mNumGlitches); + } + + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); + LOGV("stop: X"); + return OK; +} + +void GonkCameraSource::releaseRecordingFrame(const sp& frame) { + LOGV("releaseRecordingFrame"); + GonkCameraHardware::ReleaseRecordingFrame(mCameraHandle, frame); +} + +void GonkCameraSource::releaseQueuedFrames() { + List >::iterator it; + while (!mFramesReceived.empty()) { + it = mFramesReceived.begin(); + releaseRecordingFrame(*it); + mFramesReceived.erase(it); + ++mNumFramesDropped; + } +} + +sp GonkCameraSource::getFormat() { + return mMeta; +} + +void GonkCameraSource::releaseOneRecordingFrame(const sp& frame) { + releaseRecordingFrame(frame); +} + +void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) { + LOGV("signalBufferReturned: %p", buffer->data()); + Mutex::Autolock autoLock(mLock); + for (List >::iterator it = mFramesBeingEncoded.begin(); + it != mFramesBeingEncoded.end(); ++it) { + if ((*it)->pointer() == buffer->data()) { + releaseOneRecordingFrame((*it)); + mFramesBeingEncoded.erase(it); + ++mNumFramesEncoded; + buffer->setObserver(0); + buffer->release(); + mFrameCompleteCondition.signal(); + return; + } + } + CHECK_EQ(0, "signalBufferReturned: bogus buffer"); +} + +status_t GonkCameraSource::read( + MediaBuffer **buffer, const ReadOptions *options) { + LOGV("read"); + + *buffer = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + return ERROR_UNSUPPORTED; + } + + sp frame; + int64_t frameTime; + + { + Mutex::Autolock autoLock(mLock); + while (mStarted && mFramesReceived.empty()) { + if (NO_ERROR != + mFrameAvailableCondition.waitRelative(mLock, + mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { + //TODO: check sanity of camera? + LOGW("Timed out waiting for incoming camera video frames: %lld us", + mLastFrameTimestampUs); + } + } + if (!mStarted) { + return OK; + } + frame = *mFramesReceived.begin(); + mFramesReceived.erase(mFramesReceived.begin()); + + frameTime = *mFrameTimes.begin(); + mFrameTimes.erase(mFrameTimes.begin()); + mFramesBeingEncoded.push_back(frame); + *buffer = new MediaBuffer(frame->pointer(), frame->size()); + (*buffer)->setObserver(this); + (*buffer)->add_ref(); + (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); + } + return OK; +} + +void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs, + int32_t msgType, const sp &data) { + LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); + //LOGV("dataCallbackTimestamp: data %x size %d", data->pointer(), data->size()); + Mutex::Autolock autoLock(mLock); + if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) { + LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs); + releaseOneRecordingFrame(data); + return; + } + + if (mNumFramesReceived > 0) { + CHECK(timestampUs > mLastFrameTimestampUs); + if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) { + ++mNumGlitches; + } + } + + // May need to skip frame or modify timestamp. Currently implemented + // by the subclass GonkCameraSourceTimeLapse. + if (skipCurrentFrame(timestampUs)) { + releaseOneRecordingFrame(data); + return; + } + + mLastFrameTimestampUs = timestampUs; + if (mNumFramesReceived == 0) { + mFirstFrameTimeUs = timestampUs; + // Initial delay + if (mStartTimeUs > 0) { + if (timestampUs < mStartTimeUs) { + // Frame was captured before recording was started + // Drop it without updating the statistical data. + releaseOneRecordingFrame(data); + return; + } + mStartTimeUs = timestampUs - mStartTimeUs; + } + } + ++mNumFramesReceived; + + CHECK(data != NULL && data->size() > 0); + mFramesReceived.push_back(data); + int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs); + mFrameTimes.push_back(timeUs); + LOGV("initial delay: %lld, current time stamp: %lld", + mStartTimeUs, timeUs); + mFrameAvailableCondition.signal(); +} + +bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const { + LOGV("isMetaDataStoredInVideoBuffers"); + return mIsMetaDataStoredInVideoBuffers; +} + +} // namespace android diff --git a/dom/camera/GonkCameraSource.h b/dom/camera/GonkCameraSource.h new file mode 100644 index 00000000000..fe58f96770c --- /dev/null +++ b/dom/camera/GonkCameraSource.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +#ifndef GONK_CAMERA_SOURCE_H_ + +#define GONK_CAMERA_SOURCE_H_ + +#include +#include +#include +#include +#include +#include + +namespace android { + +class IMemory; +class GonkCameraSourceListener; + +class GonkCameraSource : public MediaSource, public MediaBufferObserver { +public: + + static GonkCameraSource *Create(int32_t cameraHandle, + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers = false); + + virtual ~GonkCameraSource(); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + + /** + * Check whether a GonkCameraSource object is properly initialized. + * Must call this method before stop(). + * @return OK if initialization has successfully completed. + */ + virtual status_t initCheck() const; + + /** + * Returns the MetaData associated with the GonkCameraSource, + * including: + * kKeyColorFormat: YUV color format of the video frames + * kKeyWidth, kKeyHeight: dimension (in pixels) of the video frames + * kKeySampleRate: frame rate in frames per second + * kKeyMIMEType: always fixed to be MEDIA_MIMETYPE_VIDEO_RAW + */ + virtual sp getFormat(); + + /** + * Tell whether this camera source stores meta data or real YUV + * frame data in video buffers. + * + * @return true if meta data is stored in the video + * buffers; false if real YUV data is stored in + * the video buffers. + */ + bool isMetaDataStoredInVideoBuffers() const; + + virtual void signalBufferReturned(MediaBuffer* buffer); + +protected: + + enum CameraFlags { + FLAGS_SET_CAMERA = 1L << 0, + FLAGS_HOT_CAMERA = 1L << 1, + }; + + int32_t mCameraFlags; + Size mVideoSize; + int32_t mVideoFrameRate; + int32_t mColorFormat; + status_t mInitCheck; + + sp mMeta; + + int64_t mStartTimeUs; + int32_t mNumFramesReceived; + int64_t mLastFrameTimestampUs; + bool mStarted; + int32_t mNumFramesEncoded; + + // Time between capture of two frames. + int64_t mTimeBetweenFrameCaptureUs; + + GonkCameraSource(int32_t cameraHandle, + Size videoSize, int32_t frameRate, + bool storeMetaDataInVideoBuffers = false); + + virtual void startCameraRecording(); + virtual void stopCameraRecording(); + virtual void releaseRecordingFrame(const sp& frame); + + // Returns true if need to skip the current frame. + // Called from dataCallbackTimestamp. + virtual bool skipCurrentFrame(int64_t timestampUs) {return false;} + + friend class GonkCameraSourceListener; + // Callback called when still camera raw data is available. + virtual void dataCallback(int32_t msgType, const sp &data) {} + + virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType, + const sp &data); + +private: + + Mutex mLock; + Condition mFrameAvailableCondition; + Condition mFrameCompleteCondition; + List > mFramesReceived; + List > mFramesBeingEncoded; + List mFrameTimes; + + int64_t mFirstFrameTimeUs; + int32_t mNumFramesDropped; + int32_t mNumGlitches; + int64_t mGlitchDurationThresholdUs; + bool mCollectStats; + bool mIsMetaDataStoredInVideoBuffers; + int32_t mCameraHandle; + + void releaseQueuedFrames(); + void releaseOneRecordingFrame(const sp& frame); + + status_t init(Size videoSize, int32_t frameRate, + bool storeMetaDataInVideoBuffers); + status_t isCameraColorFormatSupported(const CameraParameters& params); + status_t configureCamera(CameraParameters* params, + int32_t width, int32_t height, + int32_t frameRate); + + status_t checkVideoSize(const CameraParameters& params, + int32_t width, int32_t height); + + status_t checkFrameRate(const CameraParameters& params, + int32_t frameRate); + + void releaseCamera(); + + GonkCameraSource(const GonkCameraSource &); + GonkCameraSource &operator=(const GonkCameraSource &); +}; + +} // namespace android + +#endif // GONK_CAMERA_SOURCE_H_ diff --git a/dom/camera/GonkRecorder.cpp b/dom/camera/GonkRecorder.cpp new file mode 100644 index 00000000000..7ad4a0a46bd --- /dev/null +++ b/dom/camera/GonkRecorder.cpp @@ -0,0 +1,1629 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * 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 "GonkRecorder" + +#include +#include +#include "GonkRecorder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "ARTPWriter.h" + +#include +#include "GonkCameraSource.h" + +namespace android { + +static sp sOMX = NULL; +static sp GetOMX() { + if(sOMX.get() == NULL) { + sOMX = new OMX; + } + return sOMX; +} + +GonkRecorder::GonkRecorder() + : mWriter(NULL), + mOutputFd(-1), + mAudioSource(AUDIO_SOURCE_CNT), + mVideoSource(VIDEO_SOURCE_LIST_END), + mStarted(false), + mDisableAudio(false) { + + LOGV("Constructor"); + reset(); +} + +GonkRecorder::~GonkRecorder() { + LOGV("Destructor"); + stop(); +} + +status_t GonkRecorder::init() { + LOGV("init"); + return OK; +} + +status_t GonkRecorder::setAudioSource(audio_source_t as) { + LOGV("setAudioSource: %d", as); + if (as < AUDIO_SOURCE_DEFAULT || + as >= AUDIO_SOURCE_CNT) { + LOGE("Invalid audio source: %d", as); + return BAD_VALUE; + } + + if (mDisableAudio) { + return OK; + } + + if (as == AUDIO_SOURCE_DEFAULT) { + mAudioSource = AUDIO_SOURCE_MIC; + } else { + mAudioSource = as; + } + + return OK; +} + +status_t GonkRecorder::setVideoSource(video_source vs) { + LOGV("setVideoSource: %d", vs); + if (vs < VIDEO_SOURCE_DEFAULT || + vs >= VIDEO_SOURCE_LIST_END) { + LOGE("Invalid video source: %d", vs); + return BAD_VALUE; + } + + if (vs == VIDEO_SOURCE_DEFAULT) { + mVideoSource = VIDEO_SOURCE_CAMERA; + } else { + mVideoSource = vs; + } + + return OK; +} + +status_t GonkRecorder::setOutputFormat(output_format of) { + LOGV("setOutputFormat: %d", of); + if (of < OUTPUT_FORMAT_DEFAULT || + of >= OUTPUT_FORMAT_LIST_END) { + LOGE("Invalid output format: %d", of); + return BAD_VALUE; + } + + if (of == OUTPUT_FORMAT_DEFAULT) { + mOutputFormat = OUTPUT_FORMAT_THREE_GPP; + } else { + mOutputFormat = of; + } + + return OK; +} + +status_t GonkRecorder::setAudioEncoder(audio_encoder ae) { + LOGV("setAudioEncoder: %d", ae); + if (ae < AUDIO_ENCODER_DEFAULT || + ae >= AUDIO_ENCODER_LIST_END) { + LOGE("Invalid audio encoder: %d", ae); + return BAD_VALUE; + } + + if (mDisableAudio) { + return OK; + } + + if (ae == AUDIO_ENCODER_DEFAULT) { + mAudioEncoder = AUDIO_ENCODER_AMR_NB; + } else { + mAudioEncoder = ae; + } + + return OK; +} + +status_t GonkRecorder::setVideoEncoder(video_encoder ve) { + LOGV("setVideoEncoder: %d", ve); + if (ve < VIDEO_ENCODER_DEFAULT || + ve >= VIDEO_ENCODER_LIST_END) { + LOGE("Invalid video encoder: %d", ve); + return BAD_VALUE; + } + + if (ve == VIDEO_ENCODER_DEFAULT) { + mVideoEncoder = VIDEO_ENCODER_H263; + } else { + mVideoEncoder = ve; + } + + return OK; +} + +status_t GonkRecorder::setVideoSize(int width, int height) { + LOGV("setVideoSize: %dx%d", width, height); + if (width <= 0 || height <= 0) { + LOGE("Invalid video size: %dx%d", width, height); + return BAD_VALUE; + } + + // Additional check on the dimension will be performed later + mVideoWidth = width; + mVideoHeight = height; + + return OK; +} + +status_t GonkRecorder::setVideoFrameRate(int frames_per_second) { + LOGV("setVideoFrameRate: %d", frames_per_second); + if ((frames_per_second <= 0 && frames_per_second != -1) || + frames_per_second > 120) { + LOGE("Invalid video frame rate: %d", frames_per_second); + return BAD_VALUE; + } + + // Additional check on the frame rate will be performed later + mFrameRate = frames_per_second; + + return OK; +} + +status_t GonkRecorder::setOutputFile(const char *path) { + LOGE("setOutputFile(const char*) must not be called"); + // We don't actually support this at all, as the media_server process + // no longer has permissions to create files. + + return -EPERM; +} + +status_t GonkRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { + LOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); + // These don't make any sense, do they? + CHECK_EQ(offset, 0); + CHECK_EQ(length, 0); + + if (fd < 0) { + LOGE("Invalid file descriptor: %d", fd); + return -EBADF; + } + + if (mOutputFd >= 0) { + ::close(mOutputFd); + } + mOutputFd = dup(fd); + + return OK; +} + +// Attempt to parse an int64 literal optionally surrounded by whitespace, +// returns true on success, false otherwise. +static bool safe_strtoi64(const char *s, int64_t *val) { + char *end; + + // It is lame, but according to man page, we have to set errno to 0 + // before calling strtoll(). + errno = 0; + *val = strtoll(s, &end, 10); + + if (end == s || errno == ERANGE) { + return false; + } + + // Skip trailing whitespace + while (isspace(*end)) { + ++end; + } + + // For a successful return, the string must contain nothing but a valid + // int64 literal optionally surrounded by whitespace. + + return *end == '\0'; +} + +// Return true if the value is in [0, 0x007FFFFFFF] +static bool safe_strtoi32(const char *s, int32_t *val) { + int64_t temp; + if (safe_strtoi64(s, &temp)) { + if (temp >= 0 && temp <= 0x007FFFFFFF) { + *val = static_cast(temp); + return true; + } + } + return false; +} + +// Trim both leading and trailing whitespace from the given string. +static void TrimString(String8 *s) { + size_t num_bytes = s->bytes(); + const char *data = s->string(); + + size_t leading_space = 0; + while (leading_space < num_bytes && isspace(data[leading_space])) { + ++leading_space; + } + + size_t i = num_bytes; + while (i > leading_space && isspace(data[i - 1])) { + --i; + } + + s->setTo(String8(&data[leading_space], i - leading_space)); +} + +status_t GonkRecorder::setParamAudioSamplingRate(int32_t sampleRate) { + LOGV("setParamAudioSamplingRate: %d", sampleRate); + if (sampleRate <= 0) { + LOGE("Invalid audio sampling rate: %d", sampleRate); + return BAD_VALUE; + } + + // Additional check on the sample rate will be performed later. + mSampleRate = sampleRate; + return OK; +} + +status_t GonkRecorder::setParamAudioNumberOfChannels(int32_t channels) { + LOGV("setParamAudioNumberOfChannels: %d", channels); + if (channels <= 0 || channels >= 3) { + LOGE("Invalid number of audio channels: %d", channels); + return BAD_VALUE; + } + + // Additional check on the number of channels will be performed later. + mAudioChannels = channels; + return OK; +} + +status_t GonkRecorder::setParamAudioEncodingBitRate(int32_t bitRate) { + LOGV("setParamAudioEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid audio encoding bit rate: %d", bitRate); + return BAD_VALUE; + } + + // The target bit rate may not be exactly the same as the requested. + // It depends on many factors, such as rate control, and the bit rate + // range that a specific encoder supports. The mismatch between the + // the target and requested bit rate will NOT be treated as an error. + mAudioBitRate = bitRate; + return OK; +} + +status_t GonkRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { + LOGV("setParamVideoEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid video encoding bit rate: %d", bitRate); + return BAD_VALUE; + } + + // The target bit rate may not be exactly the same as the requested. + // It depends on many factors, such as rate control, and the bit rate + // range that a specific encoder supports. The mismatch between the + // the target and requested bit rate will NOT be treated as an error. + mVideoBitRate = bitRate; + return OK; +} + +// Always rotate clockwise, and only support 0, 90, 180 and 270 for now. +status_t GonkRecorder::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 GonkRecorder::setParamMaxFileDurationUs(int64_t timeUs) { + LOGV("setParamMaxFileDurationUs: %lld us", timeUs); + + // This is meant for backward compatibility for MediaRecorder.java + if (timeUs <= 0) { + LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs); + timeUs = 0; // Disable the duration limit for zero or negative values. + } else if (timeUs <= 100000LL) { // XXX: 100 milli-seconds + LOGE("Max file duration is too short: %lld us", timeUs); + return BAD_VALUE; + } + + if (timeUs <= 15 * 1000000LL) { + LOGW("Target duration (%lld us) too short to be respected", timeUs); + } + mMaxFileDurationUs = timeUs; + return OK; +} + +status_t GonkRecorder::setParamMaxFileSizeBytes(int64_t bytes) { + LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); + + // This is meant for backward compatibility for MediaRecorder.java + if (bytes <= 0) { + LOGW("Max file size is not positive: %lld bytes. " + "Disabling file size limit.", bytes); + bytes = 0; // Disable the file size limit for zero or negative values. + } else if (bytes <= 1024) { // XXX: 1 kB + LOGE("Max file size is too small: %lld bytes", bytes); + return BAD_VALUE; + } + + if (bytes <= 100 * 1024) { + LOGW("Target file size (%lld bytes) is too small to be respected", bytes); + } + + if (bytes >= 0xffffffffLL) { + LOGW("Target file size (%lld bytes) too larger than supported, clip to 4GB", bytes); + bytes = 0xffffffffLL; + } + + mMaxFileSizeBytes = bytes; + return OK; +} + +status_t GonkRecorder::setParamInterleaveDuration(int32_t durationUs) { + LOGV("setParamInterleaveDuration: %d", durationUs); + if (durationUs <= 500000) { // 500 ms + // If interleave duration is too small, it is very inefficient to do + // interleaving since the metadata overhead will count for a significant + // portion of the saved contents + LOGE("Audio/video interleave duration is too small: %d us", durationUs); + return BAD_VALUE; + } else if (durationUs >= 10000000) { // 10 seconds + // If interleaving duration is too large, it can cause the recording + // session to use too much memory since we have to save the output + // data before we write them out + LOGE("Audio/video interleave duration is too large: %d us", durationUs); + return BAD_VALUE; + } + mInterleaveDurationUs = durationUs; + return OK; +} + +// If seconds < 0, only the first frame is I frame, and rest are all P frames +// If seconds == 0, all frames are encoded as I frames. No P frames +// If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames +status_t GonkRecorder::setParamVideoIFramesInterval(int32_t seconds) { + LOGV("setParamVideoIFramesInterval: %d seconds", seconds); + mIFramesIntervalSec = seconds; + return OK; +} + +status_t GonkRecorder::setParam64BitFileOffset(bool use64Bit) { + LOGV("setParam64BitFileOffset: %s", + use64Bit? "use 64 bit file offset": "use 32 bit file offset"); + mUse64BitFileOffset = use64Bit; + return OK; +} + +status_t GonkRecorder::setParamVideoCameraId(int32_t cameraId) { + LOGV("setParamVideoCameraId: %d", cameraId); + if (cameraId < 0) { + return BAD_VALUE; + } + mCameraId = cameraId; + return OK; +} + +status_t GonkRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { + LOGV("setParamTrackTimeStatus: %lld", timeDurationUs); + if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? + LOGE("Tracking time duration too short: %lld us", timeDurationUs); + return BAD_VALUE; + } + mTrackEveryTimeDurationUs = timeDurationUs; + return OK; +} + +status_t GonkRecorder::setParamVideoEncoderProfile(int32_t profile) { + LOGV("setParamVideoEncoderProfile: %d", profile); + + // Additional check will be done later when we load the encoder. + // For now, we are accepting values defined in OpenMAX IL. + mVideoEncoderProfile = profile; + return OK; +} + +status_t GonkRecorder::setParamVideoEncoderLevel(int32_t level) { + LOGV("setParamVideoEncoderLevel: %d", level); + + // Additional check will be done later when we load the encoder. + // For now, we are accepting values defined in OpenMAX IL. + mVideoEncoderLevel = level; + return OK; +} + +status_t GonkRecorder::setParamMovieTimeScale(int32_t timeScale) { + LOGV("setParamMovieTimeScale: %d", timeScale); + + // The range is set to be the same as the audio's time scale range + // since audio's time scale has a wider range. + if (timeScale < 600 || timeScale > 96000) { + LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale); + return BAD_VALUE; + } + mMovieTimeScale = timeScale; + return OK; +} + +status_t GonkRecorder::setParamVideoTimeScale(int32_t timeScale) { + LOGV("setParamVideoTimeScale: %d", timeScale); + + // 60000 is chosen to make sure that each video frame from a 60-fps + // video has 1000 ticks. + if (timeScale < 600 || timeScale > 60000) { + LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale); + return BAD_VALUE; + } + mVideoTimeScale = timeScale; + return OK; +} + +status_t GonkRecorder::setParamAudioTimeScale(int32_t timeScale) { + LOGV("setParamAudioTimeScale: %d", timeScale); + + // 96000 Hz is the highest sampling rate support in AAC. + if (timeScale < 600 || timeScale > 96000) { + LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale); + return BAD_VALUE; + } + mAudioTimeScale = timeScale; + return OK; +} + +status_t GonkRecorder::setParamGeoDataLongitude( + int64_t longitudex10000) { + + if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { + return BAD_VALUE; + } + mLongitudex10000 = longitudex10000; + return OK; +} + +status_t GonkRecorder::setParamGeoDataLatitude( + int64_t latitudex10000) { + + if (latitudex10000 > 900000 || latitudex10000 < -900000) { + return BAD_VALUE; + } + mLatitudex10000 = latitudex10000; + return OK; +} + +status_t GonkRecorder::setParameter( + const String8 &key, const String8 &value) { + LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); + if (key == "max-duration") { + int64_t max_duration_ms; + if (safe_strtoi64(value.string(), &max_duration_ms)) { + return setParamMaxFileDurationUs(1000LL * max_duration_ms); + } + } else if (key == "max-filesize") { + int64_t max_filesize_bytes; + if (safe_strtoi64(value.string(), &max_filesize_bytes)) { + return setParamMaxFileSizeBytes(max_filesize_bytes); + } + } else if (key == "interleave-duration-us") { + int32_t durationUs; + if (safe_strtoi32(value.string(), &durationUs)) { + return setParamInterleaveDuration(durationUs); + } + } else if (key == "param-movie-time-scale") { + int32_t timeScale; + if (safe_strtoi32(value.string(), &timeScale)) { + return setParamMovieTimeScale(timeScale); + } + } else if (key == "param-use-64bit-offset") { + int32_t use64BitOffset; + if (safe_strtoi32(value.string(), &use64BitOffset)) { + return setParam64BitFileOffset(use64BitOffset != 0); + } + } else if (key == "param-geotag-longitude") { + int64_t longitudex10000; + if (safe_strtoi64(value.string(), &longitudex10000)) { + return setParamGeoDataLongitude(longitudex10000); + } + } else if (key == "param-geotag-latitude") { + int64_t latitudex10000; + if (safe_strtoi64(value.string(), &latitudex10000)) { + return setParamGeoDataLatitude(latitudex10000); + } + } else if (key == "param-track-time-status") { + int64_t timeDurationUs; + if (safe_strtoi64(value.string(), &timeDurationUs)) { + return setParamTrackTimeStatus(timeDurationUs); + } + } else if (key == "audio-param-sampling-rate") { + int32_t sampling_rate; + if (safe_strtoi32(value.string(), &sampling_rate)) { + return setParamAudioSamplingRate(sampling_rate); + } + } else if (key == "audio-param-number-of-channels") { + int32_t number_of_channels; + if (safe_strtoi32(value.string(), &number_of_channels)) { + return setParamAudioNumberOfChannels(number_of_channels); + } + } else if (key == "audio-param-encoding-bitrate") { + int32_t audio_bitrate; + if (safe_strtoi32(value.string(), &audio_bitrate)) { + return setParamAudioEncodingBitRate(audio_bitrate); + } + } else if (key == "audio-param-time-scale") { + int32_t timeScale; + if (safe_strtoi32(value.string(), &timeScale)) { + return setParamAudioTimeScale(timeScale); + } + } else if (key == "video-param-encoding-bitrate") { + int32_t video_bitrate; + 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)) { + return setParamVideoIFramesInterval(seconds); + } + } else if (key == "video-param-encoder-profile") { + int32_t profile; + if (safe_strtoi32(value.string(), &profile)) { + return setParamVideoEncoderProfile(profile); + } + } else if (key == "video-param-encoder-level") { + int32_t level; + if (safe_strtoi32(value.string(), &level)) { + return setParamVideoEncoderLevel(level); + } + } else if (key == "video-param-camera-id") { + int32_t cameraId; + if (safe_strtoi32(value.string(), &cameraId)) { + return setParamVideoCameraId(cameraId); + } + } else if (key == "video-param-time-scale") { + int32_t timeScale; + if (safe_strtoi32(value.string(), &timeScale)) { + return setParamVideoTimeScale(timeScale); + } + } else { + LOGE("setParameter: failed to find key %s", key.string()); + } + return BAD_VALUE; +} + +status_t GonkRecorder::setParameters(const String8 ¶ms) { + LOGV("setParameters: %s", params.string()); + const char *cparams = params.string(); + const char *key_start = cparams; + for (;;) { + const char *equal_pos = strchr(key_start, '='); + if (equal_pos == NULL) { + LOGE("Parameters %s miss a value", cparams); + return BAD_VALUE; + } + String8 key(key_start, equal_pos - key_start); + TrimString(&key); + if (key.length() == 0) { + LOGE("Parameters %s contains an empty key", cparams); + return BAD_VALUE; + } + const char *value_start = equal_pos + 1; + const char *semicolon_pos = strchr(value_start, ';'); + String8 value; + if (semicolon_pos == NULL) { + value.setTo(value_start); + } else { + value.setTo(value_start, semicolon_pos - value_start); + } + if (setParameter(key, value) != OK) { + return BAD_VALUE; + } + if (semicolon_pos == NULL) { + break; // Reaches the end + } + key_start = semicolon_pos + 1; + } + return OK; +} + +status_t GonkRecorder::setListener(const sp &listener) { + mListener = listener; + + return OK; +} + +status_t GonkRecorder::prepare() { + LOGV(" %s E", __func__ ); + + if(mVideoSource != VIDEO_SOURCE_LIST_END && mVideoEncoder != VIDEO_ENCODER_LIST_END && mVideoHeight && mVideoWidth && /*Video recording*/ + (mMaxFileDurationUs <=0 || /*Max duration is not set*/ + (mVideoHeight * mVideoWidth < 720 * 1280 && mMaxFileDurationUs > 30*60*1000*1000) || + (mVideoHeight * mVideoWidth >= 720 * 1280 && mMaxFileDurationUs > 10*60*1000*1000))) { + /*Above Check can be further optimized for lower resolutions to reduce file size*/ + LOGV("File is huge so setting 64 bit file offsets"); + setParam64BitFileOffset(true); + } + LOGV(" %s X", __func__ ); + return OK; +} + +status_t GonkRecorder::start() { + CHECK(mOutputFd >= 0); + + if (mWriter != NULL) { + LOGE("File writer is not available"); + return UNKNOWN_ERROR; + } + + status_t status = OK; + + switch (mOutputFormat) { + case OUTPUT_FORMAT_DEFAULT: + case OUTPUT_FORMAT_THREE_GPP: + case OUTPUT_FORMAT_MPEG_4: + status = startMPEG4Recording(); + break; + + case OUTPUT_FORMAT_AMR_NB: + case OUTPUT_FORMAT_AMR_WB: + status = startAMRRecording(); + break; + + case OUTPUT_FORMAT_MPEG2TS: + status = startMPEG2TSRecording(); + break; + default: + LOGE("Unsupported output file format: %d", mOutputFormat); + status = UNKNOWN_ERROR; + break; + } + + if ((status == OK) && (!mStarted)) { + mStarted = true; + } + + return status; +} + +sp GonkRecorder::createAudioSource() { + + sp audioSource = + new AudioSource( + mAudioSource, + mSampleRate, + mAudioChannels); + + status_t err = audioSource->initCheck(); + + if (err != OK) { + LOGE("audio source is not initialized"); + return NULL; + } + + sp encMeta = new MetaData; + const char *mime; + switch (mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_DEFAULT: + mime = MEDIA_MIMETYPE_AUDIO_AMR_NB; + break; + case AUDIO_ENCODER_AMR_WB: + mime = MEDIA_MIMETYPE_AUDIO_AMR_WB; + break; + case AUDIO_ENCODER_AAC: + mime = MEDIA_MIMETYPE_AUDIO_AAC; + break; + default: + LOGE("Unknown audio encoder: %d", mAudioEncoder); + return NULL; + } + encMeta->setCString(kKeyMIMEType, mime); + + int32_t maxInputSize; + CHECK(audioSource->getFormat()->findInt32( + kKeyMaxInputSize, &maxInputSize)); + + encMeta->setInt32(kKeyMaxInputSize, maxInputSize); + encMeta->setInt32(kKeyChannelCount, mAudioChannels); + encMeta->setInt32(kKeySampleRate, mSampleRate); + encMeta->setInt32(kKeyBitRate, mAudioBitRate); + if (mAudioTimeScale > 0) { + encMeta->setInt32(kKeyTimeScale, mAudioTimeScale); + } + + // use direct OMX interface instead of connecting to + // mediaserver over binder calls + sp audioEncoder = + OMXCodec::Create(GetOMX(), encMeta, + true /* createEncoder */, audioSource); + mAudioSourceNode = audioSource; + + return audioEncoder; +} + +status_t GonkRecorder::startAMRRecording() { + CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB || + mOutputFormat == OUTPUT_FORMAT_AMR_WB); + + if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) { + if (mAudioEncoder != AUDIO_ENCODER_DEFAULT && + mAudioEncoder != AUDIO_ENCODER_AMR_NB) { + LOGE("Invalid encoder %d used for AMRNB recording", + mAudioEncoder); + return BAD_VALUE; + } + } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB + if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { + LOGE("Invlaid encoder %d used for AMRWB recording", + mAudioEncoder); + return BAD_VALUE; + } + } + + mWriter = new AMRWriter(mOutputFd); + status_t status = startRawAudioRecording(); + if (status != OK) { + mWriter.clear(); + mWriter = NULL; + } + return status; +} + +status_t GonkRecorder::startRawAudioRecording() { + if (mAudioSource >= AUDIO_SOURCE_CNT) { + LOGE("Invalid audio source: %d", mAudioSource); + return BAD_VALUE; + } + + status_t status = BAD_VALUE; + if (OK != (status = checkAudioEncoderCapabilities())) { + return status; + } + + sp audioEncoder = createAudioSource(); + if (audioEncoder == NULL) { + return UNKNOWN_ERROR; + } + + CHECK(mWriter != 0); + mWriter->addSource(audioEncoder); + + if (mMaxFileDurationUs != 0) { + mWriter->setMaxFileDuration(mMaxFileDurationUs); + } + if (mMaxFileSizeBytes != 0) { + mWriter->setMaxFileSize(mMaxFileSizeBytes); + } + mWriter->setListener(mListener); + mWriter->start(); + + return OK; +} + +status_t GonkRecorder::startMPEG2TSRecording() { + CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS); + + sp writer = new MPEG2TSWriter(mOutputFd); + + if (mAudioSource != AUDIO_SOURCE_CNT) { + if (mAudioEncoder != AUDIO_ENCODER_AAC) { + return ERROR_UNSUPPORTED; + } + + status_t err = setupAudioEncoder(writer); + + if (err != OK) { + return err; + } + } + + if (mVideoSource < VIDEO_SOURCE_LIST_END) { + if (mVideoEncoder != VIDEO_ENCODER_H264) { + return ERROR_UNSUPPORTED; + } + + sp mediaSource; + status_t err = setupMediaSource(&mediaSource); + if (err != OK) { + return err; + } + + sp encoder; + err = setupVideoEncoder(mediaSource, mVideoBitRate, &encoder); + + if (err != OK) { + return err; + } + + writer->addSource(encoder); + } + + if (mMaxFileDurationUs != 0) { + writer->setMaxFileDuration(mMaxFileDurationUs); + } + + if (mMaxFileSizeBytes != 0) { + writer->setMaxFileSize(mMaxFileSizeBytes); + } + + mWriter = writer; + + return mWriter->start(); +} + +void GonkRecorder::clipVideoFrameRate() { + LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder); + int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.fps.min", mVideoEncoder); + int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.fps.max", mVideoEncoder); + if (mFrameRate < minFrameRate && mFrameRate != -1) { + LOGW("Intended video encoding frame rate (%d fps) is too small" + " and will be set to (%d fps)", mFrameRate, minFrameRate); + mFrameRate = minFrameRate; + } else if (mFrameRate > maxFrameRate) { + LOGW("Intended video encoding frame rate (%d fps) is too large" + " and will be set to (%d fps)", mFrameRate, maxFrameRate); + mFrameRate = maxFrameRate; + } +} + +void GonkRecorder::clipVideoBitRate() { + LOGV("clipVideoBitRate: encoder %d", mVideoEncoder); + int minBitRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.bps.min", mVideoEncoder); + int maxBitRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.bps.max", mVideoEncoder); + if (mVideoBitRate < minBitRate) { + LOGW("Intended video encoding bit rate (%d bps) is too small" + " and will be set to (%d bps)", mVideoBitRate, minBitRate); + mVideoBitRate = minBitRate; + } else if (mVideoBitRate > maxBitRate) { + LOGW("Intended video encoding bit rate (%d bps) is too large" + " and will be set to (%d bps)", mVideoBitRate, maxBitRate); + mVideoBitRate = maxBitRate; + } +} + +void GonkRecorder::clipVideoFrameWidth() { + LOGV("clipVideoFrameWidth: encoder %d", mVideoEncoder); + int minFrameWidth = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.width.min", mVideoEncoder); + int maxFrameWidth = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.width.max", mVideoEncoder); + if (mVideoWidth < minFrameWidth) { + LOGW("Intended video encoding frame width (%d) is too small" + " and will be set to (%d)", mVideoWidth, minFrameWidth); + mVideoWidth = minFrameWidth; + } else if (mVideoWidth > maxFrameWidth) { + LOGW("Intended video encoding frame width (%d) is too large" + " and will be set to (%d)", mVideoWidth, maxFrameWidth); + mVideoWidth = maxFrameWidth; + } +} + +status_t GonkRecorder::checkVideoEncoderCapabilities() { + // Dont clip for time lapse capture as encoder will have enough + // time to encode because of slow capture rate of time lapse. + clipVideoBitRate(); + clipVideoFrameRate(); + clipVideoFrameWidth(); + clipVideoFrameHeight(); + setDefaultProfileIfNecessary(); + return OK; +} + +// Set to use AVC baseline profile if the encoding parameters matches +// CAMCORDER_QUALITY_LOW profile; this is for the sake of MMS service. +void GonkRecorder::setDefaultProfileIfNecessary() { + LOGV("setDefaultProfileIfNecessary"); + + camcorder_quality quality = CAMCORDER_QUALITY_LOW; + + int64_t durationUs = mEncoderProfiles->getCamcorderProfileParamByName( + "duration", mCameraId, quality) * 1000000LL; + + int fileFormat = mEncoderProfiles->getCamcorderProfileParamByName( + "file.format", mCameraId, quality); + + int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.codec", mCameraId, quality); + + int videoBitRate = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.bps", mCameraId, quality); + + int videoFrameRate = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.fps", mCameraId, quality); + + int videoFrameWidth = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.width", mCameraId, quality); + + int videoFrameHeight = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.height", mCameraId, quality); + + int audioCodec = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.codec", mCameraId, quality); + + int audioBitRate = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.bps", mCameraId, quality); + + int audioSampleRate = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.hz", mCameraId, quality); + + int audioChannels = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.ch", mCameraId, quality); + + if (durationUs == mMaxFileDurationUs && + fileFormat == mOutputFormat && + videoCodec == mVideoEncoder && + videoBitRate == mVideoBitRate && + videoFrameRate == mFrameRate && + videoFrameWidth == mVideoWidth && + videoFrameHeight == mVideoHeight && + audioCodec == mAudioEncoder && + audioBitRate == mAudioBitRate && + audioSampleRate == mSampleRate && + audioChannels == mAudioChannels) { + if (videoCodec == VIDEO_ENCODER_H264) { + LOGI("Force to use AVC baseline profile"); + setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline); + } + } +} + +status_t GonkRecorder::checkAudioEncoderCapabilities() { + clipAudioBitRate(); + clipAudioSampleRate(); + clipNumberOfAudioChannels(); + return OK; +} + +void GonkRecorder::clipAudioBitRate() { + LOGV("clipAudioBitRate: encoder %d", mAudioEncoder); + + int minAudioBitRate = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.bps.min", mAudioEncoder); + if (mAudioBitRate < minAudioBitRate) { + LOGW("Intended audio encoding bit rate (%d) is too small" + " and will be set to (%d)", mAudioBitRate, minAudioBitRate); + mAudioBitRate = minAudioBitRate; + } + + int maxAudioBitRate = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.bps.max", mAudioEncoder); + if (mAudioBitRate > maxAudioBitRate) { + LOGW("Intended audio encoding bit rate (%d) is too large" + " and will be set to (%d)", mAudioBitRate, maxAudioBitRate); + mAudioBitRate = maxAudioBitRate; + } +} + +void GonkRecorder::clipAudioSampleRate() { + LOGV("clipAudioSampleRate: encoder %d", mAudioEncoder); + + int minSampleRate = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.hz.min", mAudioEncoder); + if (mSampleRate < minSampleRate) { + LOGW("Intended audio sample rate (%d) is too small" + " and will be set to (%d)", mSampleRate, minSampleRate); + mSampleRate = minSampleRate; + } + + int maxSampleRate = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.hz.max", mAudioEncoder); + if (mSampleRate > maxSampleRate) { + LOGW("Intended audio sample rate (%d) is too large" + " and will be set to (%d)", mSampleRate, maxSampleRate); + mSampleRate = maxSampleRate; + } +} + +void GonkRecorder::clipNumberOfAudioChannels() { + LOGV("clipNumberOfAudioChannels: encoder %d", mAudioEncoder); + + int minChannels = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.ch.min", mAudioEncoder); + if (mAudioChannels < minChannels) { + LOGW("Intended number of audio channels (%d) is too small" + " and will be set to (%d)", mAudioChannels, minChannels); + mAudioChannels = minChannels; + } + + int maxChannels = + mEncoderProfiles->getAudioEncoderParamByName( + "enc.aud.ch.max", mAudioEncoder); + if (mAudioChannels > maxChannels) { + LOGW("Intended number of audio channels (%d) is too large" + " and will be set to (%d)", mAudioChannels, maxChannels); + mAudioChannels = maxChannels; + } +} + +void GonkRecorder::clipVideoFrameHeight() { + LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder); + int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.height.min", mVideoEncoder); + int maxFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.height.max", mVideoEncoder); + if (mVideoHeight < minFrameHeight) { + LOGW("Intended video encoding frame height (%d) is too small" + " and will be set to (%d)", mVideoHeight, minFrameHeight); + mVideoHeight = minFrameHeight; + } else if (mVideoHeight > maxFrameHeight) { + LOGW("Intended video encoding frame height (%d) is too large" + " and will be set to (%d)", mVideoHeight, maxFrameHeight); + mVideoHeight = maxFrameHeight; + } +} + +// Set up the appropriate MediaSource depending on the chosen option +status_t GonkRecorder::setupMediaSource( + sp *mediaSource) { + if (mVideoSource == VIDEO_SOURCE_DEFAULT + || mVideoSource == VIDEO_SOURCE_CAMERA) { + sp cameraSource; + status_t err = setupCameraSource(&cameraSource); + if (err != OK) { + return err; + } + *mediaSource = cameraSource; + } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) { + return BAD_VALUE; + } else { + return INVALID_OPERATION; + } + return OK; +} + +status_t GonkRecorder::setupCameraSource( + sp *cameraSource) { + status_t err = OK; + if ((err = checkVideoEncoderCapabilities()) != OK) { + return err; + } + Size videoSize; + videoSize.width = mVideoWidth; + videoSize.height = mVideoHeight; + bool useMeta = true; + char value[PROPERTY_VALUE_MAX]; + if (property_get("debug.camcorder.disablemeta", value, NULL) && + atoi(value)) { + useMeta = false; + } + + *cameraSource = GonkCameraSource::Create( + mCameraHandle, videoSize, mFrameRate, useMeta); + if (*cameraSource == NULL) { + return UNKNOWN_ERROR; + } + + if ((*cameraSource)->initCheck() != OK) { + (*cameraSource).clear(); + *cameraSource = NULL; + return NO_INIT; + } + + // When frame rate is not set, the actual frame rate will be set to + // the current frame rate being used. + if (mFrameRate == -1) { + int32_t frameRate = 0; + CHECK ((*cameraSource)->getFormat()->findInt32( + kKeyFrameRate, &frameRate)); + LOGI("Frame rate is not explicitly set. Use the current frame " + "rate (%d fps)", frameRate); + mFrameRate = frameRate; + } + + CHECK(mFrameRate != -1); + + mIsMetaDataStoredInVideoBuffers = + (*cameraSource)->isMetaDataStoredInVideoBuffers(); + + return OK; +} + +status_t GonkRecorder::setupVideoEncoder( + sp cameraSource, + int32_t videoBitRate, + sp *source) { + source->clear(); + + sp enc_meta = new MetaData; + enc_meta->setInt32(kKeyBitRate, videoBitRate); + enc_meta->setInt32(kKeyFrameRate, mFrameRate); + + switch (mVideoEncoder) { + case VIDEO_ENCODER_H263: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); + break; + + case VIDEO_ENCODER_MPEG_4_SP: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + break; + + case VIDEO_ENCODER_H264: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + break; + + default: + CHECK(!"Should not be here, unsupported video encoding."); + break; + } + + sp meta = cameraSource->getFormat(); + + int32_t width, height, stride, sliceHeight, colorFormat; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); + + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec); + enc_meta->setInt32(kKeyStride, stride); + enc_meta->setInt32(kKeySliceHeight, sliceHeight); + enc_meta->setInt32(kKeyColorFormat, colorFormat); + if (mVideoTimeScale > 0) { + enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale); + } + + /* + * can set profile from the app as a parameter. + * For the mean time, set from shell + */ + + char value[PROPERTY_VALUE_MAX]; + bool customProfile = false; + + if (property_get("encoder.video.profile", value, NULL) > 0) { + customProfile = true; + } + + if (customProfile) { + switch ( mVideoEncoder ) { + case VIDEO_ENCODER_H264: + if (strncmp("base", value, 4) == 0) { + mVideoEncoderProfile = OMX_VIDEO_AVCProfileBaseline; + LOGI("H264 Baseline Profile"); + } + else if (strncmp("main", value, 4) == 0) { + mVideoEncoderProfile = OMX_VIDEO_AVCProfileMain; + LOGI("H264 Main Profile"); + } + else if (strncmp("high", value, 4) == 0) { + mVideoEncoderProfile = OMX_VIDEO_AVCProfileHigh; + LOGI("H264 High Profile"); + } + else { + LOGW("Unsupported H264 Profile"); + } + break; + case VIDEO_ENCODER_MPEG_4_SP: + if (strncmp("simple", value, 5) == 0 ) { + mVideoEncoderProfile = OMX_VIDEO_MPEG4ProfileSimple; + LOGI("MPEG4 Simple profile"); + } + else if (strncmp("asp", value, 3) == 0 ) { + mVideoEncoderProfile = OMX_VIDEO_MPEG4ProfileAdvancedSimple; + LOGI("MPEG4 Advanced Simple Profile"); + } + else { + LOGW("Unsupported MPEG4 Profile"); + } + break; + default: + LOGW("No custom profile support for other codecs"); + break; + } + } + + if (mVideoEncoderProfile != -1) { + enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile); + } + if (mVideoEncoderLevel != -1) { + enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel); + } + + uint32_t encoder_flags = 0; + if (mIsMetaDataStoredInVideoBuffers) { + LOGW("Camera source supports metadata mode, create OMXCodec for metadata"); + encoder_flags |= OMXCodec::kHardwareCodecsOnly; + encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers; + encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime; + } + + sp encoder = OMXCodec::Create( + GetOMX(), + enc_meta, + true /* createEncoder */, cameraSource, + NULL, encoder_flags); + if (encoder == NULL) { + LOGW("Failed to create the encoder"); + // When the encoder fails to be created, we need + // release the camera source due to the camera's lock + // and unlock mechanism. + cameraSource->stop(); + return UNKNOWN_ERROR; + } + + *source = encoder; + + return OK; +} + +status_t GonkRecorder::setupAudioEncoder(const sp& writer) { + status_t status = BAD_VALUE; + if (OK != (status = checkAudioEncoderCapabilities())) { + return status; + } + + switch(mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_AMR_WB: + case AUDIO_ENCODER_AAC: + break; + + default: + LOGE("Unsupported audio encoder: %d", mAudioEncoder); + return UNKNOWN_ERROR; + } + + sp audioEncoder = createAudioSource(); + if (audioEncoder == NULL) { + return UNKNOWN_ERROR; + } + + writer->addSource(audioEncoder); + return OK; +} + +status_t GonkRecorder::setupMPEG4Recording( + int outputFd, + int32_t videoWidth, int32_t videoHeight, + int32_t videoBitRate, + int32_t *totalBitRate, + sp *mediaWriter) { + mediaWriter->clear(); + *totalBitRate = 0; + status_t err = OK; + sp writer = new MPEG4Writer(outputFd); + + if (mVideoSource < VIDEO_SOURCE_LIST_END) { + + sp mediaSource; + err = setupMediaSource(&mediaSource); + if (err != OK) { + return err; + } + + sp encoder; + err = setupVideoEncoder(mediaSource, videoBitRate, &encoder); + if (err != OK) { + return err; + } + + writer->addSource(encoder); + *totalBitRate += videoBitRate; + } + + // Audio source is added at the end if it exists. + // This help make sure that the "recoding" sound is suppressed for + // camcorder applications in the recorded files. + if (mAudioSource != AUDIO_SOURCE_CNT) { + err = setupAudioEncoder(writer); + if (err != OK) return err; + *totalBitRate += mAudioBitRate; + } + + if (mInterleaveDurationUs > 0) { + reinterpret_cast(writer.get())-> + setInterleaveDuration(mInterleaveDurationUs); + } + if (mLongitudex10000 > -3600000 && mLatitudex10000 > -3600000) { + reinterpret_cast(writer.get())-> + setGeoData(mLatitudex10000, mLongitudex10000); + } + if (mMaxFileDurationUs != 0) { + writer->setMaxFileDuration(mMaxFileDurationUs); + } + if (mMaxFileSizeBytes != 0) { + writer->setMaxFileSize(mMaxFileSizeBytes); + } + + mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId); + if (mStartTimeOffsetMs > 0) { + reinterpret_cast(writer.get())-> + setStartTimeOffsetMs(mStartTimeOffsetMs); + } + + writer->setListener(mListener); + *mediaWriter = writer; + return OK; +} + +void GonkRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate, + sp *meta) { + (*meta)->setInt64(kKeyTime, startTimeUs); + (*meta)->setInt32(kKeyFileType, mOutputFormat); + (*meta)->setInt32(kKeyBitRate, totalBitRate); + (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset); + if (mMovieTimeScale > 0) { + (*meta)->setInt32(kKeyTimeScale, mMovieTimeScale); + } + if (mTrackEveryTimeDurationUs > 0) { + (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs); + } + + char value[PROPERTY_VALUE_MAX]; + if (property_get("debug.camcorder.rotation", value, 0) > 0 && atoi(value) >= 0) { + mRotationDegrees = atoi(value); + LOGI("Setting rotation to %d", mRotationDegrees ); + } + + if (mRotationDegrees != 0) { + (*meta)->setInt32(kKeyRotation, mRotationDegrees); + } +} + +status_t GonkRecorder::startMPEG4Recording() { + int32_t totalBitRate; + status_t err = setupMPEG4Recording( + mOutputFd, mVideoWidth, mVideoHeight, + mVideoBitRate, &totalBitRate, &mWriter); + if (err != OK) { + return err; + } + + //systemTime() doesn't give correct time because + //HAVE_POSIX_CLOCKS is not defined for utils/Timers.cpp + //so, using clock_gettime directly +#include + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + int64_t startTimeUs = int64_t(t.tv_sec)*1000000000LL + t.tv_nsec; + startTimeUs = startTimeUs / 1000; + sp meta = new MetaData; + setupMPEG4MetaData(startTimeUs, totalBitRate, &meta); + + err = mWriter->start(meta.get()); + if (err != OK) { + return err; + } + + return OK; +} + +status_t GonkRecorder::pause() { + LOGV("pause"); + if (mWriter == NULL) { + return UNKNOWN_ERROR; + } + mWriter->pause(); + + if (mStarted) { + mStarted = false; + } + + + return OK; +} + +status_t GonkRecorder::stop() { + LOGV("stop"); + status_t err = OK; + + if (mWriter != NULL) { + err = mWriter->stop(); + mWriter.clear(); + } + + if (mOutputFd >= 0) { + ::close(mOutputFd); + mOutputFd = -1; + } + + if (mStarted) { + mStarted = false; + } + + + return err; +} + +status_t GonkRecorder::close() { + LOGV("close"); + stop(); + + return OK; +} + +status_t GonkRecorder::reset() { + LOGV("reset"); + stop(); + + // No audio or video source by default + mAudioSource = AUDIO_SOURCE_CNT; + mVideoSource = VIDEO_SOURCE_LIST_END; + + // Default parameters + mOutputFormat = OUTPUT_FORMAT_THREE_GPP; + mAudioEncoder = AUDIO_ENCODER_AMR_NB; + mVideoEncoder = VIDEO_ENCODER_H263; + mVideoWidth = 176; + mVideoHeight = 144; + mFrameRate = -1; + mVideoBitRate = 192000; + mSampleRate = 8000; + mAudioChannels = 1; + mAudioBitRate = 12200; + mInterleaveDurationUs = 0; + mIFramesIntervalSec = 2; + mAudioSourceNode = 0; + mUse64BitFileOffset = false; + mMovieTimeScale = -1; + mAudioTimeScale = -1; + mVideoTimeScale = -1; + mCameraId = 0; + mStartTimeOffsetMs = -1; + mVideoEncoderProfile = -1; + mVideoEncoderLevel = -1; + mMaxFileDurationUs = 0; + mMaxFileSizeBytes = 0; + mTrackEveryTimeDurationUs = 0; + mIsMetaDataStoredInVideoBuffers = false; + mEncoderProfiles = MediaProfiles::getInstance(); + mRotationDegrees = 0; + mLatitudex10000 = -3600000; + mLongitudex10000 = -3600000; + + mOutputFd = -1; + mCameraHandle = -1; + //TODO: May need to register a listener eventually + //if someone is interested in recorder events for now + //default to no listener registered + mListener = NULL; + + // Disable Audio Encoding + char value[PROPERTY_VALUE_MAX]; + property_get("camcorder.debug.disableaudio", value, "0"); + if(atoi(value)) mDisableAudio = true; + + return OK; +} + +status_t GonkRecorder::getMaxAmplitude(int *max) { + LOGV("getMaxAmplitude"); + + if (max == NULL) { + LOGE("Null pointer argument"); + return BAD_VALUE; + } + + if (mAudioSourceNode != 0) { + *max = mAudioSourceNode->getMaxAmplitude(); + } else { + *max = 0; + } + + return OK; +} + +status_t GonkRecorder::dump( + int fd, const Vector& args) const { + LOGV("dump"); + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + if (mWriter != 0) { + mWriter->dump(fd, args); + } else { + snprintf(buffer, SIZE, " No file writer\n"); + result.append(buffer); + } + snprintf(buffer, SIZE, " Recorder: %p\n", this); + snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd); + result.append(buffer); + snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Max file size (bytes): %lld\n", mMaxFileSizeBytes); + result.append(buffer); + snprintf(buffer, SIZE, " Max file duration (us): %lld\n", mMaxFileDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32); + result.append(buffer); + snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Progress notification: %lld us\n", mTrackEveryTimeDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Audio\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mAudioSource); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mAudioEncoder); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mAudioBitRate); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate (hz): %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, " Number of channels: %d\n", mAudioChannels); + result.append(buffer); + snprintf(buffer, SIZE, " Max amplitude: %d\n", mAudioSourceNode == 0? 0: mAudioSourceNode->getMaxAmplitude()); + result.append(buffer); + snprintf(buffer, SIZE, " Video\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mVideoSource); + result.append(buffer); + snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); + result.append(buffer); + snprintf(buffer, SIZE, " Camera Handle: %d\n", mCameraHandle); + 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); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); + result.append(buffer); + snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesIntervalSec); + result.append(buffer); + snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); + result.append(buffer); + snprintf(buffer, SIZE, " Frame rate (fps): %d\n", mFrameRate); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mVideoBitRate); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return OK; +} + +status_t GonkRecorder::setCameraHandle(int32_t handle) { + if (handle < 0) { + return BAD_VALUE; + } + mCameraHandle = handle; + return OK; +} + +} // namespace android diff --git a/dom/camera/GonkRecorder.h b/dom/camera/GonkRecorder.h new file mode 100644 index 00000000000..be05ef74585 --- /dev/null +++ b/dom/camera/GonkRecorder.h @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#ifndef GONK_RECORDER_H_ + +#define GONK_RECORDER_H_ + +#include +#include +#include + +#include + +namespace android { + +class GonkCameraSource; +struct MediaSource; +struct MediaWriter; +class MetaData; +struct AudioSource; +class MediaProfiles; + +struct GonkRecorder { + GonkRecorder(); + virtual ~GonkRecorder(); + + virtual status_t init(); + virtual status_t setAudioSource(audio_source_t as); + virtual status_t setVideoSource(video_source vs); + virtual status_t setOutputFormat(output_format of); + virtual status_t setAudioEncoder(audio_encoder ae); + virtual status_t setVideoEncoder(video_encoder ve); + virtual status_t setVideoSize(int width, int height); + virtual status_t setVideoFrameRate(int frames_per_second); + virtual status_t setOutputFile(const char *path); + virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); + virtual status_t setParameters(const String8& params); + virtual status_t setCameraHandle(int32_t handle); + virtual status_t setListener(const sp& listener); + virtual status_t prepare(); + virtual status_t start(); + virtual status_t pause(); + virtual status_t stop(); + virtual status_t close(); + virtual status_t reset(); + virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector& args) const; + // Querying a SurfaceMediaSourcer + +private: + sp mListener; + sp mWriter; + int mOutputFd; + sp mAudioSourceNode; + + audio_source_t mAudioSource; + video_source mVideoSource; + output_format mOutputFormat; + audio_encoder mAudioEncoder; + video_encoder mVideoEncoder; + bool mUse64BitFileOffset; + int32_t mVideoWidth, mVideoHeight; + int32_t mFrameRate; + int32_t mVideoBitRate; + int32_t mAudioBitRate; + int32_t mAudioChannels; + int32_t mSampleRate; + int32_t mInterleaveDurationUs; + int32_t mIFramesIntervalSec; + int32_t mCameraId; + int32_t mVideoEncoderProfile; + int32_t mVideoEncoderLevel; + int32_t mMovieTimeScale; + int32_t mVideoTimeScale; + int32_t mAudioTimeScale; + int64_t mMaxFileSizeBytes; + int64_t mMaxFileDurationUs; + int64_t mTrackEveryTimeDurationUs; + int32_t mRotationDegrees; // Clockwise + int32_t mLatitudex10000; + int32_t mLongitudex10000; + int32_t mStartTimeOffsetMs; + + String8 mParams; + + bool mIsMetaDataStoredInVideoBuffers; + MediaProfiles *mEncoderProfiles; + + bool mStarted; + // Needed when GLFrames are encoded. + // An pointer + // will be sent to the client side using which the + // frame buffers will be queued and dequeued + bool mDisableAudio; + int32_t mCameraHandle; + + status_t setupMPEG4Recording( + int outputFd, + int32_t videoWidth, int32_t videoHeight, + int32_t videoBitRate, + int32_t *totalBitRate, + sp *mediaWriter); + void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate, + sp *meta); + status_t startMPEG4Recording(); + status_t startAMRRecording(); + status_t startRawAudioRecording(); + status_t startMPEG2TSRecording(); + sp createAudioSource(); + status_t checkVideoEncoderCapabilities(); + status_t checkAudioEncoderCapabilities(); + // Generic MediaSource set-up. Returns the appropriate + // source (CameraSource or SurfaceMediaSource) + // depending on the videosource type + status_t setupMediaSource(sp *mediaSource); + status_t setupCameraSource(sp *cameraSource); + // setup the surfacemediasource for the encoder + + status_t setupAudioEncoder(const sp& writer); + status_t setupVideoEncoder( + sp cameraSource, + int32_t videoBitRate, + sp *source); + + // Encoding parameter handling utilities + status_t setParameter(const String8 &key, const String8 &value); + status_t setParamAudioEncodingBitRate(int32_t bitRate); + status_t setParamAudioNumberOfChannels(int32_t channles); + status_t setParamAudioSamplingRate(int32_t sampleRate); + status_t setParamAudioTimeScale(int32_t timeScale); + status_t setParamVideoEncodingBitRate(int32_t bitRate); + status_t setParamVideoIFramesInterval(int32_t seconds); + status_t setParamVideoEncoderProfile(int32_t profile); + 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); + status_t setParamMaxFileDurationUs(int64_t timeUs); + status_t setParamMaxFileSizeBytes(int64_t bytes); + status_t setParamMovieTimeScale(int32_t timeScale); + status_t setParamGeoDataLongitude(int64_t longitudex10000); + status_t setParamGeoDataLatitude(int64_t latitudex10000); + void clipVideoBitRate(); + void clipVideoFrameRate(); + void clipVideoFrameWidth(); + void clipVideoFrameHeight(); + void clipAudioBitRate(); + void clipAudioSampleRate(); + void clipNumberOfAudioChannels(); + void setDefaultProfileIfNecessary(); + + GonkRecorder(const GonkRecorder &); + GonkRecorder &operator=(const GonkRecorder &); +}; + +} // namespace android + +#endif // GONK_RECORDER_H_ diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 6fb565e3dcc..2f5a88b74f3 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -6,6 +6,7 @@ #define DOM_CAMERA_ICAMERACONTROL_H #include "jsapi.h" +#include "nsIDOMDeviceStorage.h" #include "nsIDOMCameraManager.h" #include "DictionaryHelpers.h" #include "CameraCommon.h" @@ -26,8 +27,9 @@ public: virtual void StopPreview() = 0; virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0; virtual nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0; - virtual nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0; + virtual nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0; virtual nsresult StopRecording() = 0; + virtual nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0; virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0; virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0; diff --git a/dom/camera/Makefile.in b/dom/camera/Makefile.in index ff2fdbcfbcb..d7d6f24e4af 100644 --- a/dom/camera/Makefile.in +++ b/dom/camera/Makefile.in @@ -32,6 +32,9 @@ CPPSRCS += \ GonkCameraControl.cpp \ GonkCameraHwMgr.cpp \ GonkNativeWindow.cpp \ + GonkRecorder.cpp \ + GonkCameraSource.cpp \ + AudioParameter.cpp \ $(NULL) else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) CPPSRCS += \ diff --git a/dom/camera/README b/dom/camera/README new file mode 100644 index 00000000000..cd7a49205ae --- /dev/null +++ b/dom/camera/README @@ -0,0 +1,28 @@ +This README file details from where some of the camcorder source files were derived from and how to apply the provided patch file to get the updated files for B2G. +--------------------------------- + +Following is the list of B2G files which were derived from an android ics_chocolate build. It also shows the corresponding locations where the original source files can be found: + +GonkRecoder.cpp: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.cpp;hb=ef1672482a9c2b88d8017927df68144fee42626c + +GonkRecorder.h: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.h;hb=e3682213bcd3fe43b059e00f0fe4dbebc3f3c35d + +GonkCameraSource.cpp: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/CameraSource.cpp;hb=7fa677babfee9c241a131b22c9c1c5ab512ef2d2 + +GonkCameraSource.h: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/media/stagefright/CameraSource.h;hb=96af14d9b013496accf40a85a66fefcba3ac0111 + +AudioParameter.cpp: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmedia/AudioParameter.cpp;hb=4dc22e77cfd2a1c3671e5646ee87c5e4c15596a0 + +GonkCameraListener.h: +https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/camera/Camera.h;hb=796f35e408d9dca386f90d8fbde80471ac011fa6 + +There were quite a few changes done to the above listed sources to support camcorder on B2G platform. +update.patch lists the changes on top of the original files. +update.sh shell script copies the files from an android tree and applies the patch to get updated files for B2G. + + diff --git a/dom/camera/nsIDOMCameraManager.idl b/dom/camera/nsIDOMCameraManager.idl index 95787f46189..aaf0fe8f999 100644 --- a/dom/camera/nsIDOMCameraManager.idl +++ b/dom/camera/nsIDOMCameraManager.idl @@ -5,6 +5,7 @@ interface nsIDOMBlob; +interface nsIDOMDeviceStorage; /* Used to set the dimensions of a captured picture, a preview stream, a video capture stream, etc. */ @@ -108,10 +109,8 @@ interface nsICameraCapabilities : nsISupports readonly attribute jsval videoSizes; }; -/* - These properties only affect the captured image; - invalid property settings are ignored. -*/ +/* These properties only affect the captured image; + invalid property settings are ignored. */ dictionary CameraPictureOptions { /* an object with a combination of 'height' and 'width' properties @@ -147,6 +146,14 @@ dictionary CameraPictureOptions jsval position; }; +/* These properties affect video recording. */ +dictionary CameraRecordingOptions +{ + long width; + long height; + long rotation; +}; + [scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)] interface nsICameraPreviewStreamCallback : nsISupports { @@ -165,10 +172,10 @@ interface nsICameraTakePictureCallback : nsISupports void handleEvent(in nsIDOMBlob picture); }; -[scriptable, function, uuid(ac43f123-529c-48d3-84dd-ad206b7aca9b)] +[scriptable, function, uuid(89a762f8-581b-410a-ad86-e2bd2113ad82)] interface nsICameraStartRecordingCallback : nsISupports { - void handleEvent(in nsIDOMMediaStream stream); + void handleEvent(); }; [scriptable, function, uuid(fb80db71-e315-42f0-9ea9-dd3dd312ed70)] @@ -187,7 +194,7 @@ interface nsICameraErrorCallback : nsISupports attributes here affect the preview, any pictures taken, and/or any video recorded by the camera. */ -[scriptable, uuid(b8949e5c-55b0-49dd-99a9-68d11342915a)] +[scriptable, uuid(469e0462-59e4-4ed5-afa9-aecd1256ee30)] interface nsICameraControl : nsISupports { readonly attribute nsICameraCapabilities capabilities; @@ -290,15 +297,21 @@ interface nsICameraControl : nsISupports [implicit_jscontext] void takePicture(in jsval aOptions, in nsICameraTakePictureCallback onSuccess, [optional] in nsICameraErrorCallback onError); - /* start recording video; 'aOptions' define the frame size of to - capture, chosen from capabilities.videoSizes, e.g.: + /* get a media stream to be used as a camera viewfinder in video mode; 'aOptions' + define the frame size of the video capture, chosen from capabilities.videoSizes, e.g.: { width: 640, - height: 480 + height: 480, + rotation: 90 } */ [implicit_jscontext] - void startRecording(in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); + void getPreviewStreamVideoMode(in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); + + /* start recording video; + */ + [implicit_jscontext] + void startRecording(in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); /* stop precording video. */ void stopRecording(); diff --git a/dom/camera/update.patch b/dom/camera/update.patch new file mode 100644 index 00000000000..96f73452ffa --- /dev/null +++ b/dom/camera/update.patch @@ -0,0 +1,2296 @@ +diff --git a/GonkCameraListener.h b/GonkCameraListener.h +index 67eeef3..243264c 100644 +--- a/GonkCameraListener.h ++++ b/GonkCameraListener.h +@@ -14,49 +14,16 @@ + * limitations under the License. + */ + +-#ifndef ANDROID_HARDWARE_CAMERA_H +-#define ANDROID_HARDWARE_CAMERA_H ++#ifndef GONK_CAMERA_LISTENER_H ++#define GONK_CAMERA_LISTENER_H + + #include +-#include +-#include +-#include +-#include +-#include ++#include "libcameraservice/CameraHardwareInterface.h" + + namespace android { + +-struct CameraInfo { +- /** +- * The direction that the camera faces to. It should be CAMERA_FACING_BACK +- * or CAMERA_FACING_FRONT. +- */ +- int facing; +- +- /** +- * The orientation of the camera image. The value is the angle that the +- * camera image needs to be rotated clockwise so it shows correctly on the +- * display in its natural orientation. It should be 0, 90, 180, or 270. +- * +- * For example, suppose a device has a naturally tall screen. The +- * back-facing camera sensor is mounted in landscape. You are looking at +- * the screen. If the top side of the camera sensor is aligned with the +- * right edge of the screen in natural orientation, the value should be +- * 90. If the top side of a front-facing camera sensor is aligned with the +- * right of the screen, the value should be 270. +- */ +- int orientation; +- int mode; +-}; +- +-class ICameraService; +-class ICamera; +-class Surface; +-class Mutex; +-class String8; +- + // ref-counted object for callbacks +-class CameraListener: virtual public RefBase ++class GonkCameraListener: virtual public RefBase + { + public: + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; +@@ -65,133 +32,6 @@ public: + virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr) = 0; + }; + +-class Camera : public BnCameraClient, public IBinder::DeathRecipient +-{ +-public: +- // construct a camera client from an existing remote +- static sp create(const sp& camera); +- static int32_t getNumberOfCameras(); +- static status_t getCameraInfo(int cameraId, +- struct CameraInfo* cameraInfo); +- static sp connect(int cameraId); +- virtual ~Camera(); +- void init(); +- +- status_t reconnect(); +- void disconnect(); +- status_t lock(); +- status_t unlock(); +- +- status_t getStatus() { return mStatus; } +- +- // pass the buffered Surface to the camera service +- status_t setPreviewDisplay(const sp& surface); +- +- // pass the buffered ISurfaceTexture to the camera service +- status_t setPreviewTexture(const sp& surfaceTexture); +- +- // start preview mode, must call setPreviewDisplay first +- status_t startPreview(); +- +- // stop preview mode +- void stopPreview(); +- +- // get preview state +- bool previewEnabled(); +- +- // start recording mode, must call setPreviewDisplay first +- status_t startRecording(); +- +- // stop recording mode +- void stopRecording(); +- +- // get recording state +- bool recordingEnabled(); +- +- // release a recording frame +- void releaseRecordingFrame(const sp& mem); +- +- // autoFocus - status returned from callback +- status_t autoFocus(); +- +- // cancel auto focus +- status_t cancelAutoFocus(); +- +- // take a picture - picture returned from callback +- status_t takePicture(int msgType); +- +- // set preview/capture parameters - key/value pairs +- status_t setParameters(const String8& params); +- +- // get preview/capture parameters - key/value pairs +- String8 getParameters() const; +- +- // send command to camera driver +- status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); +- +- // tell camera hal to store meta data or real YUV in video buffers. +- status_t storeMetaDataInBuffers(bool enabled); +- +- void setListener(const sp& listener); +- void setRecordingProxyListener(const sp& listener); +- void setPreviewCallbackFlags(int preview_callback_flag); +- +- sp getRecordingProxy(); +- +- // ICameraClient interface +- virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2); +- virtual void dataCallback(int32_t msgType, const sp& dataPtr, +- camera_frame_metadata_t *metadata); +- virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr); +- +- sp remote(); +- +- class RecordingProxy : public BnCameraRecordingProxy +- { +- public: +- RecordingProxy(const sp& camera); +- +- // ICameraRecordingProxy interface +- virtual status_t startRecording(const sp& listener); +- virtual void stopRecording(); +- virtual void releaseRecordingFrame(const sp& mem); +- +- private: +- sp mCamera; +- }; +- +-private: +- Camera(); +- Camera(const Camera&); +- Camera& operator=(const Camera); +- virtual void binderDied(const wp& who); +- +- class DeathNotifier: public IBinder::DeathRecipient +- { +- public: +- DeathNotifier() { +- } +- +- virtual void binderDied(const wp& who); +- }; +- +- static sp mDeathNotifier; +- +- // helper function to obtain camera service handle +- static const sp& getCameraService(); +- +- sp mCamera; +- status_t mStatus; +- +- sp mListener; +- sp mRecordingProxyListener; +- +- friend class DeathNotifier; +- +- static Mutex mLock; +- static sp mCameraService; +-}; +- + }; // namespace android + + #endif +diff --git a/GonkCameraSource.cpp b/GonkCameraSource.cpp +index af6b340..9dba596 100644 +--- a/GonkCameraSource.cpp ++++ b/GonkCameraSource.cpp +@@ -14,29 +14,34 @@ + * limitations under the License. + */ + +-//#define LOG_NDEBUG 0 +-#define LOG_TAG "CameraSource" +-#include ++#include ++#include "nsDebug.h" ++#define DOM_CAMERA_LOG_LEVEL 3 ++#include "CameraCommon.h" ++#define LOGD DOM_CAMERA_LOGA ++#define LOGV DOM_CAMERA_LOGI ++#define LOGI DOM_CAMERA_LOGI ++#define LOGW DOM_CAMERA_LOGW ++#define LOGE DOM_CAMERA_LOGE + + #include +-#include +-#include ++#include "GonkCameraSource.h" ++#include "GonkCameraListener.h" ++#include "GonkCameraHwMgr.h" + #include + #include + #include + #include +-#include +-#include +-#include + #include + #include + ++using namespace mozilla; + namespace android { + + static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL; + +-struct CameraSourceListener : public CameraListener { +- CameraSourceListener(const sp &source); ++struct GonkCameraSourceListener : public GonkCameraListener { ++ GonkCameraSourceListener(const sp &source); + + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void postData(int32_t msgType, const sp &dataPtr, +@@ -46,41 +51,41 @@ struct CameraSourceListener : public CameraListener { + nsecs_t timestamp, int32_t msgType, const sp& dataPtr); + + protected: +- virtual ~CameraSourceListener(); ++ virtual ~GonkCameraSourceListener(); + + private: +- wp mSource; ++ wp mSource; + +- CameraSourceListener(const CameraSourceListener &); +- CameraSourceListener &operator=(const CameraSourceListener &); ++ GonkCameraSourceListener(const GonkCameraSourceListener &); ++ GonkCameraSourceListener &operator=(const GonkCameraSourceListener &); + }; + +-CameraSourceListener::CameraSourceListener(const sp &source) ++GonkCameraSourceListener::GonkCameraSourceListener(const sp &source) + : mSource(source) { + } + +-CameraSourceListener::~CameraSourceListener() { ++GonkCameraSourceListener::~GonkCameraSourceListener() { + } + +-void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { ++void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { + LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); + } + +-void CameraSourceListener::postData(int32_t msgType, const sp &dataPtr, ++void GonkCameraSourceListener::postData(int32_t msgType, const sp &dataPtr, + camera_frame_metadata_t *metadata) { + LOGV("postData(%d, ptr:%p, size:%d)", + msgType, dataPtr->pointer(), dataPtr->size()); + +- sp source = mSource.promote(); ++ sp source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallback(msgType, dataPtr); + } + } + +-void CameraSourceListener::postDataTimestamp( ++void GonkCameraSourceListener::postDataTimestamp( + nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { + +- sp source = mSource.promote(); ++ sp source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); + } +@@ -114,48 +119,30 @@ static int32_t getColorFormat(const char* colorFormat) { + } + + LOGE("Uknown color format (%s), please add it to " +- "CameraSource::getColorFormat", colorFormat); ++ "GonkCameraSource::getColorFormat", colorFormat); + + CHECK_EQ(0, "Unknown color format"); + } + +-CameraSource *CameraSource::Create() { +- Size size; +- size.width = -1; +- size.height = -1; +- +- sp camera; +- return new CameraSource(camera, NULL, 0, size, -1, NULL, false); +-} +- +-// static +-CameraSource *CameraSource::CreateFromCamera( +- const sp& camera, +- const sp& proxy, +- int32_t cameraId, ++GonkCameraSource *GonkCameraSource::Create( ++ int32_t cameraHandle, + Size videoSize, + int32_t frameRate, +- const sp& surface, + bool storeMetaDataInVideoBuffers) { + +- CameraSource *source = new CameraSource(camera, proxy, cameraId, +- videoSize, frameRate, surface, ++ GonkCameraSource *source = new GonkCameraSource(cameraHandle, ++ videoSize, frameRate, + storeMetaDataInVideoBuffers); + return source; + } + +-CameraSource::CameraSource( +- const sp& camera, +- const sp& proxy, +- int32_t cameraId, ++GonkCameraSource::GonkCameraSource( ++ int32_t cameraHandle, + Size videoSize, + int32_t frameRate, +- const sp& surface, + bool storeMetaDataInVideoBuffers) + : mCameraFlags(0), + mVideoFrameRate(-1), +- mCamera(0), +- mSurface(surface), + mNumFramesReceived(0), + mLastFrameTimestampUs(0), + mStarted(false), +@@ -169,43 +156,19 @@ CameraSource::CameraSource( + mVideoSize.width = -1; + mVideoSize.height = -1; + +- mInitCheck = init(camera, proxy, cameraId, ++ mCameraHandle = cameraHandle; ++ ++ mInitCheck = init( + videoSize, frameRate, + storeMetaDataInVideoBuffers); + if (mInitCheck != OK) releaseCamera(); + } + +-status_t CameraSource::initCheck() const { ++status_t GonkCameraSource::initCheck() const { + return mInitCheck; + } + +-status_t CameraSource::isCameraAvailable( +- const sp& camera, const sp& proxy, +- int32_t cameraId) { +- +- if (camera == 0) { +- mCamera = Camera::connect(cameraId); +- if (mCamera == 0) return -EBUSY; +- mCameraFlags &= ~FLAGS_HOT_CAMERA; +- } else { +- // We get the proxy from Camera, not ICamera. We need to get the proxy +- // to the remote Camera owned by the application. Here mCamera is a +- // local Camera object created by us. We cannot use the proxy from +- // mCamera here. +- mCamera = Camera::create(camera); +- if (mCamera == 0) return -EBUSY; +- mCameraRecordingProxy = proxy; +- mCameraFlags |= FLAGS_HOT_CAMERA; +- mDeathNotifier = new DeathNotifier(); +- // isBinderAlive needs linkToDeath to work. +- mCameraRecordingProxy->asBinder()->linkToDeath(mDeathNotifier); +- } +- +- mCamera->lock(); +- +- return OK; +-} +- ++//TODO: Do we need to reimplement isCameraAvailable? + + /* + * Check to see whether the requested video width and height is one +@@ -267,7 +230,7 @@ static void getSupportedVideoSizes( + * @param params CameraParameters to retrieve the information + * @return OK if no error. + */ +-status_t CameraSource::isCameraColorFormatSupported( ++status_t GonkCameraSource::isCameraColorFormatSupported( + const CameraParameters& params) { + mColorFormat = getColorFormat(params.get( + CameraParameters::KEY_VIDEO_FRAME_FORMAT)); +@@ -292,7 +255,7 @@ status_t CameraSource::isCameraColorFormatSupported( + * @param frameRate the target frame rate in frames per second. + * @return OK if no error. + */ +-status_t CameraSource::configureCamera( ++status_t GonkCameraSource::configureCamera( + CameraParameters* params, + int32_t width, int32_t height, + int32_t frameRate) { +@@ -347,10 +310,9 @@ status_t CameraSource::configureCamera( + + if (isCameraParamChanged) { + // Either frame rate or frame size needs to be changed. +- String8 s = params->flatten(); +- if (OK != mCamera->setParameters(s)) { ++ if (OK != GonkCameraHardware::PushParameters(mCameraHandle,*params)) { + LOGE("Could not change settings." +- " Someone else is using camera %p?", mCamera.get()); ++ " Someone else is using camera ?"); + return -EBUSY; + } + } +@@ -368,7 +330,7 @@ status_t CameraSource::configureCamera( + * @param the target video frame height in pixels to check against + * @return OK if no error + */ +-status_t CameraSource::checkVideoSize( ++status_t GonkCameraSource::checkVideoSize( + const CameraParameters& params, + int32_t width, int32_t height) { + +@@ -420,7 +382,7 @@ status_t CameraSource::checkVideoSize( + * @param the target video frame rate to check against + * @return OK if no error. + */ +-status_t CameraSource::checkFrameRate( ++status_t GonkCameraSource::checkFrameRate( + const CameraParameters& params, + int32_t frameRate) { + +@@ -462,39 +424,17 @@ status_t CameraSource::checkFrameRate( + * + * @return OK if no error. + */ +-status_t CameraSource::init( +- const sp& camera, +- const sp& proxy, +- int32_t cameraId, ++status_t GonkCameraSource::init( + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers) { + + LOGV("init"); + status_t err = OK; +- int64_t token = IPCThreadState::self()->clearCallingIdentity(); +- err = initWithCameraAccess(camera, proxy, cameraId, +- videoSize, frameRate, +- storeMetaDataInVideoBuffers); +- IPCThreadState::self()->restoreCallingIdentity(token); +- return err; +-} +- +-status_t CameraSource::initWithCameraAccess( +- const sp& camera, +- const sp& proxy, +- int32_t cameraId, +- Size videoSize, +- int32_t frameRate, +- bool storeMetaDataInVideoBuffers) { +- LOGV("initWithCameraAccess"); +- status_t err = OK; ++ //TODO: need to do something here to check the sanity of camera + +- if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) { +- LOGE("Camera connection could not be established."); +- return err; +- } +- CameraParameters params(mCamera->getParameters()); ++ CameraParameters params; ++ GonkCameraHardware::PullParameters(mCameraHandle, params); + if ((err = isCameraColorFormatSupported(params)) != OK) { + return err; + } +@@ -508,7 +448,8 @@ status_t CameraSource::initWithCameraAccess( + } + + // Check on video frame size and frame rate. +- CameraParameters newCameraParams(mCamera->getParameters()); ++ CameraParameters newCameraParams; ++ GonkCameraHardware::PullParameters(mCameraHandle, newCameraParams); + if ((err = checkVideoSize(newCameraParams, + videoSize.width, videoSize.height)) != OK) { + return err; +@@ -517,15 +458,11 @@ status_t CameraSource::initWithCameraAccess( + return err; + } + +- // This CHECK is good, since we just passed the lock/unlock +- // check earlier by calling mCamera->setParameters(). +- CHECK_EQ(OK, mCamera->setPreviewDisplay(mSurface)); +- + // By default, do not store metadata in video buffers + mIsMetaDataStoredInVideoBuffers = false; +- mCamera->storeMetaDataInBuffers(false); ++ GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, false); + if (storeMetaDataInVideoBuffers) { +- if (OK == mCamera->storeMetaDataInBuffers(true)) { ++ if (OK == GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, true)) { + mIsMetaDataStoredInVideoBuffers = true; + } + } +@@ -568,40 +505,28 @@ status_t CameraSource::initWithCameraAccess( + return OK; + } + +-CameraSource::~CameraSource() { ++GonkCameraSource::~GonkCameraSource() { + if (mStarted) { + stop(); + } else if (mInitCheck == OK) { + // Camera is initialized but because start() is never called, + // the lock on Camera is never released(). This makes sure + // Camera's lock is released in this case. ++ // TODO: Don't think I need to do this + releaseCamera(); + } + } + +-void CameraSource::startCameraRecording() { ++void GonkCameraSource::startCameraRecording() { + LOGV("startCameraRecording"); +- // Reset the identity to the current thread because media server owns the +- // camera and recording is started by the applications. The applications +- // will connect to the camera in ICameraRecordingProxy::startRecording. +- int64_t token = IPCThreadState::self()->clearCallingIdentity(); +- if (mCameraFlags & FLAGS_HOT_CAMERA) { +- mCamera->unlock(); +- mCamera.clear(); +- CHECK_EQ(OK, mCameraRecordingProxy->startRecording(new ProxyListener(this))); +- } else { +- mCamera->setListener(new CameraSourceListener(this)); +- mCamera->startRecording(); +- CHECK(mCamera->recordingEnabled()); +- } +- IPCThreadState::self()->restoreCallingIdentity(token); ++ CHECK_EQ(OK, GonkCameraHardware::StartRecording(mCameraHandle)); + } + +-status_t CameraSource::start(MetaData *meta) { ++status_t GonkCameraSource::start(MetaData *meta) { + LOGV("start"); + CHECK(!mStarted); + if (mInitCheck != OK) { +- LOGE("CameraSource is not initialized yet"); ++ LOGE("GonkCameraSource is not initialized yet"); + return mInitCheck; + } + +@@ -614,58 +539,34 @@ status_t CameraSource::start(MetaData *meta) { + mStartTimeUs = 0; + int64_t startTimeUs; + if (meta && meta->findInt64(kKeyTime, &startTimeUs)) { ++ LOGV("Metadata enabled, startime: %lld us", startTimeUs); + mStartTimeUs = startTimeUs; + } + ++ // Register a listener with GonkCameraHardware so that we can get callbacks ++ GonkCameraHardware::SetListener(mCameraHandle, new GonkCameraSourceListener(this)); ++ + startCameraRecording(); + + mStarted = true; + return OK; + } + +-void CameraSource::stopCameraRecording() { ++void GonkCameraSource::stopCameraRecording() { + LOGV("stopCameraRecording"); +- if (mCameraFlags & FLAGS_HOT_CAMERA) { +- mCameraRecordingProxy->stopRecording(); +- } else { +- mCamera->setListener(NULL); +- mCamera->stopRecording(); +- } ++ GonkCameraHardware::StopRecording(mCameraHandle); + } + +-void CameraSource::releaseCamera() { ++void GonkCameraSource::releaseCamera() { + LOGV("releaseCamera"); +- if (mCamera != 0) { +- int64_t token = IPCThreadState::self()->clearCallingIdentity(); +- if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { +- LOGV("Camera was cold when we started, stopping preview"); +- mCamera->stopPreview(); +- mCamera->disconnect(); +- } +- mCamera->unlock(); +- mCamera.clear(); +- mCamera = 0; +- IPCThreadState::self()->restoreCallingIdentity(token); +- } +- if (mCameraRecordingProxy != 0) { +- mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); +- mCameraRecordingProxy.clear(); +- } +- mCameraFlags = 0; + } + +-status_t CameraSource::stop() { +- LOGD("stop: E"); ++status_t GonkCameraSource::stop() { ++ LOGV("stop: E"); + Mutex::Autolock autoLock(mLock); + mStarted = false; + mFrameAvailableCondition.signal(); + +- int64_t token; +- bool isTokenValid = false; +- if (mCamera != 0) { +- token = IPCThreadState::self()->clearCallingIdentity(); +- isTokenValid = true; +- } + releaseQueuedFrames(); + while (!mFramesBeingEncoded.empty()) { + if (NO_ERROR != +@@ -675,11 +576,9 @@ status_t CameraSource::stop() { + mFramesBeingEncoded.size()); + } + } ++ LOGV("Calling stopCameraRecording"); + stopCameraRecording(); + releaseCamera(); +- if (isTokenValid) { +- IPCThreadState::self()->restoreCallingIdentity(token); +- } + + if (mCollectStats) { + LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us", +@@ -692,22 +591,16 @@ status_t CameraSource::stop() { + } + + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); +- LOGD("stop: X"); ++ LOGV("stop: X"); + return OK; + } + +-void CameraSource::releaseRecordingFrame(const sp& frame) { ++void GonkCameraSource::releaseRecordingFrame(const sp& frame) { + LOGV("releaseRecordingFrame"); +- if (mCameraRecordingProxy != NULL) { +- mCameraRecordingProxy->releaseRecordingFrame(frame); +- } else if (mCamera != NULL) { +- int64_t token = IPCThreadState::self()->clearCallingIdentity(); +- mCamera->releaseRecordingFrame(frame); +- IPCThreadState::self()->restoreCallingIdentity(token); +- } ++ GonkCameraHardware::ReleaseRecordingFrame(mCameraHandle, frame); + } + +-void CameraSource::releaseQueuedFrames() { ++void GonkCameraSource::releaseQueuedFrames() { + List >::iterator it; + while (!mFramesReceived.empty()) { + it = mFramesReceived.begin(); +@@ -717,15 +610,15 @@ void CameraSource::releaseQueuedFrames() { + } + } + +-sp CameraSource::getFormat() { ++sp GonkCameraSource::getFormat() { + return mMeta; + } + +-void CameraSource::releaseOneRecordingFrame(const sp& frame) { ++void GonkCameraSource::releaseOneRecordingFrame(const sp& frame) { + releaseRecordingFrame(frame); + } + +-void CameraSource::signalBufferReturned(MediaBuffer *buffer) { ++void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) { + LOGV("signalBufferReturned: %p", buffer->data()); + Mutex::Autolock autoLock(mLock); + for (List >::iterator it = mFramesBeingEncoded.begin(); +@@ -743,7 +636,7 @@ void CameraSource::signalBufferReturned(MediaBuffer *buffer) { + CHECK_EQ(0, "signalBufferReturned: bogus buffer"); + } + +-status_t CameraSource::read( ++status_t GonkCameraSource::read( + MediaBuffer **buffer, const ReadOptions *options) { + LOGV("read"); + +@@ -764,11 +657,7 @@ status_t CameraSource::read( + if (NO_ERROR != + mFrameAvailableCondition.waitRelative(mLock, + mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { +- if (mCameraRecordingProxy != 0 && +- !mCameraRecordingProxy->asBinder()->isBinderAlive()) { +- LOGW("camera recording proxy is gone"); +- return ERROR_END_OF_STREAM; +- } ++ //TODO: check sanity of camera? + LOGW("Timed out waiting for incoming camera video frames: %lld us", + mLastFrameTimestampUs); + } +@@ -790,9 +679,10 @@ status_t CameraSource::read( + return OK; + } + +-void CameraSource::dataCallbackTimestamp(int64_t timestampUs, ++void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs, + int32_t msgType, const sp &data) { + LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); ++ //LOGV("dataCallbackTimestamp: data %x size %d", data->pointer(), data->size()); + Mutex::Autolock autoLock(mLock); + if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) { + LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs); +@@ -808,7 +698,7 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, + } + + // May need to skip frame or modify timestamp. Currently implemented +- // by the subclass CameraSourceTimeLapse. ++ // by the subclass GonkCameraSourceTimeLapse. + if (skipCurrentFrame(timestampUs)) { + releaseOneRecordingFrame(data); + return; +@@ -839,22 +729,9 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, + mFrameAvailableCondition.signal(); + } + +-bool CameraSource::isMetaDataStoredInVideoBuffers() const { ++bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const { + LOGV("isMetaDataStoredInVideoBuffers"); + return mIsMetaDataStoredInVideoBuffers; + } + +-CameraSource::ProxyListener::ProxyListener(const sp& source) { +- mSource = source; +-} +- +-void CameraSource::ProxyListener::dataCallbackTimestamp( +- nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { +- mSource->dataCallbackTimestamp(timestamp / 1000, msgType, dataPtr); +-} +- +-void CameraSource::DeathNotifier::binderDied(const wp& who) { +- LOGI("Camera recording proxy died"); +-} +- +-} // namespace android ++} // namespace android +diff --git a/GonkCameraSource.h b/GonkCameraSource.h +index 446720b..fe58f96 100644 +--- a/GonkCameraSource.h ++++ b/GonkCameraSource.h +@@ -14,69 +14,31 @@ + * limitations under the License. + */ + +-#ifndef CAMERA_SOURCE_H_ ++#ifndef GONK_CAMERA_SOURCE_H_ + +-#define CAMERA_SOURCE_H_ ++#define GONK_CAMERA_SOURCE_H_ + + #include + #include +-#include +-#include + #include + #include + #include ++#include + + namespace android { + + class IMemory; +-class Camera; +-class Surface; ++class GonkCameraSourceListener; + +-class CameraSource : public MediaSource, public MediaBufferObserver { ++class GonkCameraSource : public MediaSource, public MediaBufferObserver { + public: +- /** +- * Factory method to create a new CameraSource using the current +- * settings (such as video size, frame rate, color format, etc) +- * from the default camera. +- * +- * @return NULL on error. +- */ +- static CameraSource *Create(); + +- /** +- * Factory method to create a new CameraSource. +- * +- * @param camera the video input frame data source. If it is NULL, +- * we will try to connect to the camera with the given +- * cameraId. +- * +- * @param cameraId the id of the camera that the source will connect +- * to if camera is NULL; otherwise ignored. +- * +- * @param videoSize the dimension (in pixels) of the video frame +- * @param frameRate the target frames per second +- * @param surface the preview surface for display where preview +- * frames are sent to +- * @param storeMetaDataInVideoBuffers true to request the camera +- * source to store meta data in video buffers; false to +- * request the camera source to store real YUV frame data +- * in the video buffers. The camera source may not support +- * storing meta data in video buffers, if so, a request +- * to do that will NOT be honored. To find out whether +- * meta data is actually being stored in video buffers +- * during recording, call isMetaDataStoredInVideoBuffers(). +- * +- * @return NULL on error. +- */ +- static CameraSource *CreateFromCamera(const sp &camera, +- const sp &proxy, +- int32_t cameraId, +- Size videoSize, +- int32_t frameRate, +- const sp& surface, +- bool storeMetaDataInVideoBuffers = false); ++ static GonkCameraSource *Create(int32_t cameraHandle, ++ Size videoSize, ++ int32_t frameRate, ++ bool storeMetaDataInVideoBuffers = false); + +- virtual ~CameraSource(); ++ virtual ~GonkCameraSource(); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); +@@ -84,14 +46,14 @@ public: + MediaBuffer **buffer, const ReadOptions *options = NULL); + + /** +- * Check whether a CameraSource object is properly initialized. ++ * Check whether a GonkCameraSource object is properly initialized. + * Must call this method before stop(). + * @return OK if initialization has successfully completed. + */ + virtual status_t initCheck() const; + + /** +- * Returns the MetaData associated with the CameraSource, ++ * Returns the MetaData associated with the GonkCameraSource, + * including: + * kKeyColorFormat: YUV color format of the video frames + * kKeyWidth, kKeyHeight: dimension (in pixels) of the video frames +@@ -113,22 +75,6 @@ public: + virtual void signalBufferReturned(MediaBuffer* buffer); + + protected: +- class ProxyListener: public BnCameraRecordingProxyListener { +- public: +- ProxyListener(const sp& source); +- virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType, +- const sp &data); +- +- private: +- sp mSource; +- }; +- +- // isBinderAlive needs linkToDeath to work. +- class DeathNotifier: public IBinder::DeathRecipient { +- public: +- DeathNotifier() {} +- virtual void binderDied(const wp& who); +- }; + + enum CameraFlags { + FLAGS_SET_CAMERA = 1L << 0, +@@ -141,10 +87,6 @@ protected: + int32_t mColorFormat; + status_t mInitCheck; + +- sp mCamera; +- sp mCameraRecordingProxy; +- sp mDeathNotifier; +- sp mSurface; + sp mMeta; + + int64_t mStartTimeUs; +@@ -156,11 +98,9 @@ protected: + // Time between capture of two frames. + int64_t mTimeBetweenFrameCaptureUs; + +- CameraSource(const sp& camera, const sp& proxy, +- int32_t cameraId, ++ GonkCameraSource(int32_t cameraHandle, + Size videoSize, int32_t frameRate, +- const sp& surface, +- bool storeMetaDataInVideoBuffers); ++ bool storeMetaDataInVideoBuffers = false); + + virtual void startCameraRecording(); + virtual void stopCameraRecording(); +@@ -170,6 +110,7 @@ protected: + // Called from dataCallbackTimestamp. + virtual bool skipCurrentFrame(int64_t timestampUs) {return false;} + ++ friend class GonkCameraSourceListener; + // Callback called when still camera raw data is available. + virtual void dataCallback(int32_t msgType, const sp &data) {} + +@@ -177,7 +118,6 @@ protected: + const sp &data); + + private: +- friend class CameraSourceListener; + + Mutex mLock; + Condition mFrameAvailableCondition; +@@ -192,23 +132,13 @@ private: + int64_t mGlitchDurationThresholdUs; + bool mCollectStats; + bool mIsMetaDataStoredInVideoBuffers; ++ int32_t mCameraHandle; + + void releaseQueuedFrames(); + void releaseOneRecordingFrame(const sp& frame); + +- +- status_t init(const sp& camera, const sp& proxy, +- int32_t cameraId, Size videoSize, int32_t frameRate, +- bool storeMetaDataInVideoBuffers); +- +- status_t initWithCameraAccess( +- const sp& camera, const sp& proxy, +- int32_t cameraId, Size videoSize, int32_t frameRate, ++ status_t init(Size videoSize, int32_t frameRate, + bool storeMetaDataInVideoBuffers); +- +- status_t isCameraAvailable(const sp& camera, +- const sp& proxy, +- int32_t cameraId); + status_t isCameraColorFormatSupported(const CameraParameters& params); + status_t configureCamera(CameraParameters* params, + int32_t width, int32_t height, +@@ -222,10 +152,10 @@ private: + + void releaseCamera(); + +- CameraSource(const CameraSource &); +- CameraSource &operator=(const CameraSource &); ++ GonkCameraSource(const GonkCameraSource &); ++ GonkCameraSource &operator=(const GonkCameraSource &); + }; + + } // namespace android + +-#endif // CAMERA_SOURCE_H_ ++#endif // GONK_CAMERA_SOURCE_H_ +diff --git a/GonkRecorder.cpp b/GonkRecorder.cpp +index b20ca9d..2dc625c 100644 +--- a/GonkRecorder.cpp ++++ b/GonkRecorder.cpp +@@ -16,35 +16,23 @@ + */ + + //#define LOG_NDEBUG 0 +-#define LOG_TAG "StagefrightRecorder" ++#define LOG_TAG "GonkRecorder" ++ + #include + #include +-#include "StagefrightRecorder.h" +- +-#include +-#include ++#include "GonkRecorder.h" + +-#include + #include + #include +-#include +-#include +-#include +-#include +-#include + #include + #include + #include + #include + #include + #include +-#include ++#include + #include +-#include + #include +-#include +-#include +-#include + #include + + #include +@@ -57,51 +45,41 @@ + #include "ARTPWriter.h" + + #include ++#include "GonkCameraSource.h" + + namespace android { + +-// To collect the encoder usage for the battery app +-static void addBatteryData(uint32_t params) { +- sp binder = +- defaultServiceManager()->getService(String16("media.player")); +- sp service = interface_cast(binder); +- CHECK(service.get() != NULL); +- +- service->addBatteryData(params); ++static sp sOMX = NULL; ++static sp GetOMX() { ++ if(sOMX.get() == NULL) { ++ sOMX = new OMX; ++ } ++ return sOMX; + } + +- +-StagefrightRecorder::StagefrightRecorder() ++GonkRecorder::GonkRecorder() + : mWriter(NULL), + mOutputFd(-1), + mAudioSource(AUDIO_SOURCE_CNT), + mVideoSource(VIDEO_SOURCE_LIST_END), +- mStarted(false), mSurfaceMediaSource(NULL), ++ mStarted(false), + mDisableAudio(false) { + + LOGV("Constructor"); + reset(); + } + +-StagefrightRecorder::~StagefrightRecorder() { ++GonkRecorder::~GonkRecorder() { + LOGV("Destructor"); + stop(); + } + +-status_t StagefrightRecorder::init() { ++status_t GonkRecorder::init() { + LOGV("init"); + return OK; + } + +-// The client side of mediaserver asks it to creat a SurfaceMediaSource +-// and return a interface reference. The client side will use that +-// while encoding GL Frames +-sp StagefrightRecorder::querySurfaceMediaSource() const { +- LOGV("Get SurfaceMediaSource"); +- return mSurfaceMediaSource; +-} +- +-status_t StagefrightRecorder::setAudioSource(audio_source_t as) { ++status_t GonkRecorder::setAudioSource(audio_source_t as) { + LOGV("setAudioSource: %d", as); + if (as < AUDIO_SOURCE_DEFAULT || + as >= AUDIO_SOURCE_CNT) { +@@ -122,7 +100,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setVideoSource(video_source vs) { ++status_t GonkRecorder::setVideoSource(video_source vs) { + LOGV("setVideoSource: %d", vs); + if (vs < VIDEO_SOURCE_DEFAULT || + vs >= VIDEO_SOURCE_LIST_END) { +@@ -139,7 +117,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setOutputFormat(output_format of) { ++status_t GonkRecorder::setOutputFormat(output_format of) { + LOGV("setOutputFormat: %d", of); + if (of < OUTPUT_FORMAT_DEFAULT || + of >= OUTPUT_FORMAT_LIST_END) { +@@ -156,7 +134,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) { ++status_t GonkRecorder::setAudioEncoder(audio_encoder ae) { + LOGV("setAudioEncoder: %d", ae); + if (ae < AUDIO_ENCODER_DEFAULT || + ae >= AUDIO_ENCODER_LIST_END) { +@@ -174,21 +152,10 @@ + mAudioEncoder = ae; + } + +- // Use default values if appropriate setparam's weren't called. +- if(mAudioEncoder == AUDIO_ENCODER_AAC) { +- mSampleRate = mSampleRate ? mSampleRate : 48000; +- mAudioChannels = mAudioChannels ? mAudioChannels : 2; +- mAudioBitRate = mAudioBitRate ? mAudioBitRate : 156000; +- } +- else{ +- mSampleRate = mSampleRate ? mSampleRate : 8000; +- mAudioChannels = mAudioChannels ? mAudioChannels : 1; +- mAudioBitRate = mAudioBitRate ? mAudioBitRate : 12200; +- } + return OK; + } + +-status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { ++status_t GonkRecorder::setVideoEncoder(video_encoder ve) { + LOGV("setVideoEncoder: %d", ve); + if (ve < VIDEO_ENCODER_DEFAULT || + ve >= VIDEO_ENCODER_LIST_END) { +@@ -205,7 +172,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setVideoSize(int width, int height) { ++status_t GonkRecorder::setVideoSize(int width, int height) { + LOGV("setVideoSize: %dx%d", width, height); + if (width <= 0 || height <= 0) { + LOGE("Invalid video size: %dx%d", width, height); +@@ -219,7 +186,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { ++status_t GonkRecorder::setVideoFrameRate(int frames_per_second) { + LOGV("setVideoFrameRate: %d", frames_per_second); + if ((frames_per_second <= 0 && frames_per_second != -1) || + frames_per_second > 120) { +@@ -233,31 +200,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setCamera(const sp &camera, +- const sp &proxy) { +- LOGV("setCamera"); +- if (camera == 0) { +- LOGE("camera is NULL"); +- return BAD_VALUE; +- } +- if (proxy == 0) { +- LOGE("camera proxy is NULL"); +- return BAD_VALUE; +- } +- +- mCamera = camera; +- mCameraProxy = proxy; +- return OK; +-} +- +-status_t StagefrightRecorder::setPreviewSurface(const sp &surface) { +- LOGV("setPreviewSurface: %p", surface.get()); +- mPreviewSurface = surface; +- +- return OK; +-} +- +-status_t StagefrightRecorder::setOutputFile(const char *path) { ++status_t GonkRecorder::setOutputFile(const char *path) { + LOGE("setOutputFile(const char*) must not be called"); + // We don't actually support this at all, as the media_server process + // no longer has permissions to create files. +@@ -265,7 +208,7 @@ + return -EPERM; + } + +-status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { ++status_t GonkRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { + LOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); + // These don't make any sense, do they? + CHECK_EQ(offset, 0); +@@ -339,7 +282,7 @@ + s->setTo(String8(&data[leading_space], i - leading_space)); + } + +-status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) { ++status_t GonkRecorder::setParamAudioSamplingRate(int32_t sampleRate) { + LOGV("setParamAudioSamplingRate: %d", sampleRate); + if (sampleRate <= 0) { + LOGE("Invalid audio sampling rate: %d", sampleRate); +@@ -351,7 +294,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { ++status_t GonkRecorder::setParamAudioNumberOfChannels(int32_t channels) { + LOGV("setParamAudioNumberOfChannels: %d", channels); + if (channels <= 0 || channels >= 3) { + LOGE("Invalid number of audio channels: %d", channels); +@@ -363,7 +306,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) { ++status_t GonkRecorder::setParamAudioEncodingBitRate(int32_t bitRate) { + LOGV("setParamAudioEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid audio encoding bit rate: %d", bitRate); +@@ -378,7 +321,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { ++status_t GonkRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { + LOGV("setParamVideoEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid video encoding bit rate: %d", bitRate); +@@ -394,7 +337,7 @@ + } + + // Always rotate clockwise, and only support 0, 90, 180 and 270 for now. +-status_t StagefrightRecorder::setParamVideoRotation(int32_t degrees) { ++status_t GonkRecorder::setParamVideoRotation(int32_t degrees) { + LOGV("setParamVideoRotation: %d", degrees); + if (degrees < 0 || degrees % 90 != 0) { + LOGE("Unsupported video rotation angle: %d", degrees); +@@ -404,7 +347,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { ++status_t GonkRecorder::setParamMaxFileDurationUs(int64_t timeUs) { + LOGV("setParamMaxFileDurationUs: %lld us", timeUs); + + // This is meant for backward compatibility for MediaRecorder.java +@@ -423,7 +366,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { ++status_t GonkRecorder::setParamMaxFileSizeBytes(int64_t bytes) { + LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); + + // This is meant for backward compatibility for MediaRecorder.java +@@ -449,7 +392,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { ++status_t GonkRecorder::setParamInterleaveDuration(int32_t durationUs) { + LOGV("setParamInterleaveDuration: %d", durationUs); + if (durationUs <= 500000) { // 500 ms + // If interleave duration is too small, it is very inefficient to do +@@ -471,20 +414,20 @@ + // If seconds < 0, only the first frame is I frame, and rest are all P frames + // If seconds == 0, all frames are encoded as I frames. No P frames + // If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames +-status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t seconds) { ++status_t GonkRecorder::setParamVideoIFramesInterval(int32_t seconds) { + LOGV("setParamVideoIFramesInterval: %d seconds", seconds); + mIFramesIntervalSec = seconds; + return OK; + } + +-status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) { ++status_t GonkRecorder::setParam64BitFileOffset(bool use64Bit) { + LOGV("setParam64BitFileOffset: %s", + use64Bit? "use 64 bit file offset": "use 32 bit file offset"); + mUse64BitFileOffset = use64Bit; + return OK; + } + +-status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) { ++status_t GonkRecorder::setParamVideoCameraId(int32_t cameraId) { + LOGV("setParamVideoCameraId: %d", cameraId); + if (cameraId < 0) { + return BAD_VALUE; +@@ -493,7 +436,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { ++status_t GonkRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { + LOGV("setParamTrackTimeStatus: %lld", timeDurationUs); + if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? + LOGE("Tracking time duration too short: %lld us", timeDurationUs); +@@ -503,7 +446,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamVideoEncoderProfile(int32_t profile) { ++status_t GonkRecorder::setParamVideoEncoderProfile(int32_t profile) { + LOGV("setParamVideoEncoderProfile: %d", profile); + + // Additional check will be done later when we load the encoder. +@@ -512,7 +455,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamVideoEncoderLevel(int32_t level) { ++status_t GonkRecorder::setParamVideoEncoderLevel(int32_t level) { + LOGV("setParamVideoEncoderLevel: %d", level); + + // Additional check will be done later when we load the encoder. +@@ -521,7 +464,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamMovieTimeScale(int32_t timeScale) { ++status_t GonkRecorder::setParamMovieTimeScale(int32_t timeScale) { + LOGV("setParamMovieTimeScale: %d", timeScale); + + // The range is set to be the same as the audio's time scale range +@@ -534,7 +477,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamVideoTimeScale(int32_t timeScale) { ++status_t GonkRecorder::setParamVideoTimeScale(int32_t timeScale) { + LOGV("setParamVideoTimeScale: %d", timeScale); + + // 60000 is chosen to make sure that each video frame from a 60-fps +@@ -547,7 +490,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) { ++status_t GonkRecorder::setParamAudioTimeScale(int32_t timeScale) { + LOGV("setParamAudioTimeScale: %d", timeScale); + + // 96000 Hz is the highest sampling rate support in AAC. +@@ -559,33 +502,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamTimeLapseEnable(int32_t timeLapseEnable) { +- LOGV("setParamTimeLapseEnable: %d", timeLapseEnable); +- +- if(timeLapseEnable == 0) { +- mCaptureTimeLapse = false; +- } else if (timeLapseEnable == 1) { +- mCaptureTimeLapse = true; +- } else { +- return BAD_VALUE; +- } +- return OK; +-} +- +-status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) { +- LOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs); +- +- // Not allowing time more than a day +- if (timeUs <= 0 || timeUs > 86400*1E6) { +- LOGE("Time between time lapse frame capture (%lld) is out of range [0, 1 Day]", timeUs); +- return BAD_VALUE; +- } +- +- mTimeBetweenTimeLapseFrameCaptureUs = timeUs; +- return OK; +-} +- +-status_t StagefrightRecorder::setParamGeoDataLongitude( ++status_t GonkRecorder::setParamGeoDataLongitude( + int64_t longitudex10000) { + + if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { +@@ -595,7 +512,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParamGeoDataLatitude( ++status_t GonkRecorder::setParamGeoDataLatitude( + int64_t latitudex10000) { + + if (latitudex10000 > 900000 || latitudex10000 < -900000) { +@@ -605,7 +522,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setParameter( ++status_t GonkRecorder::setParameter( + const String8 &key, const String8 &value) { + LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); + if (key == "max-duration") { +@@ -703,24 +620,13 @@ + if (safe_strtoi32(value.string(), &timeScale)) { + return setParamVideoTimeScale(timeScale); + } +- } else if (key == "time-lapse-enable") { +- int32_t timeLapseEnable; +- if (safe_strtoi32(value.string(), &timeLapseEnable)) { +- return setParamTimeLapseEnable(timeLapseEnable); +- } +- } else if (key == "time-between-time-lapse-frame-capture") { +- int64_t timeBetweenTimeLapseFrameCaptureMs; +- if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) { +- return setParamTimeBetweenTimeLapseFrameCapture( +- 1000LL * timeBetweenTimeLapseFrameCaptureMs); +- } + } else { + LOGE("setParameter: failed to find key %s", key.string()); + } + return BAD_VALUE; + } + +-status_t StagefrightRecorder::setParameters(const String8 ¶ms) { ++status_t GonkRecorder::setParameters(const String8 ¶ms) { + LOGV("setParameters: %s", params.string()); + const char *cparams = params.string(); + const char *key_start = cparams; +@@ -755,13 +661,13 @@ + return OK; + } + +-status_t StagefrightRecorder::setListener(const sp &listener) { ++status_t GonkRecorder::setListener(const sp &listener) { + mListener = listener; + + return OK; + } + +-status_t StagefrightRecorder::prepare() { ++status_t GonkRecorder::prepare() { + LOGV(" %s E", __func__ ); + + if(mVideoSource != VIDEO_SOURCE_LIST_END && mVideoEncoder != VIDEO_ENCODER_LIST_END && mVideoHeight && mVideoWidth && /*Video recording*/ +@@ -776,17 +682,15 @@ + return OK; + } + +-status_t StagefrightRecorder::start() { ++status_t GonkRecorder::start() { + CHECK(mOutputFd >= 0); + + if (mWriter != NULL) { +- LOGE("File writer is not avaialble"); ++ LOGE("File writer is not available"); + return UNKNOWN_ERROR; + } + + status_t status = OK; +- if(AUDIO_SOURCE_FM_RX_A2DP == mAudioSource) +- return startFMA2DPWriter(); + + switch (mOutputFormat) { + case OUTPUT_FORMAT_DEFAULT: +@@ -800,22 +704,9 @@ + status = startAMRRecording(); + break; + +- case OUTPUT_FORMAT_AAC_ADIF: +- case OUTPUT_FORMAT_AAC_ADTS: +- status = startAACRecording(); +- break; +- +- case OUTPUT_FORMAT_RTP_AVP: +- status = startRTPRecording(); +- break; +- + case OUTPUT_FORMAT_MPEG2TS: + status = startMPEG2TSRecording(); + break; +- +- case OUTPUT_FORMAT_QCP: +- status = startExtendedRecording( ); +- break; + default: + LOGE("Unsupported output file format: %d", mOutputFormat); + status = UNKNOWN_ERROR; +@@ -824,22 +715,12 @@ + + if ((status == OK) && (!mStarted)) { + mStarted = true; +- +- uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted; +- if (mAudioSource != AUDIO_SOURCE_CNT) { +- params |= IMediaPlayerService::kBatteryDataTrackAudio; +- } +- if (mVideoSource != VIDEO_SOURCE_LIST_END) { +- params |= IMediaPlayerService::kBatteryDataTrackVideo; +- } +- +- addBatteryData(params); + } + + return status; + } + +-sp StagefrightRecorder::createAudioSource() { ++sp GonkRecorder::createAudioSource() { + + bool tunneledSource = false; + const char *tunnelMime; +@@ -907,12 +788,6 @@ + case AUDIO_ENCODER_AAC: + mime = MEDIA_MIMETYPE_AUDIO_AAC; + break; +- case AUDIO_ENCODER_EVRC: +- mime = MEDIA_MIMETYPE_AUDIO_EVRC; +- break; +- case AUDIO_ENCODER_QCELP: +- mime = MEDIA_MIMETYPE_AUDIO_QCELP; +- break; + default: + LOGE("Unknown audio encoder: %d", mAudioEncoder); + return NULL; +@@ -931,36 +806,17 @@ + encMeta->setInt32(kKeyTimeScale, mAudioTimeScale); + } + +- OMXClient client; +- CHECK_EQ(client.connect(), OK); +- ++ // use direct OMX interface instead of connecting to ++ // mediaserver over binder calls + sp audioEncoder = +- OMXCodec::Create(client.interface(), encMeta, ++ OMXCodec::Create(GetOMX(), encMeta, + true /* createEncoder */, audioSource); + mAudioSourceNode = audioSource; + + return audioEncoder; + } + +-status_t StagefrightRecorder::startAACRecording() { +- // FIXME: +- // Add support for OUTPUT_FORMAT_AAC_ADIF +- CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADTS); +- +- CHECK(mAudioEncoder == AUDIO_ENCODER_AAC); +- CHECK(mAudioSource != AUDIO_SOURCE_CNT); +- +- mWriter = new AACWriter(mOutputFd); +- status_t status = startRawAudioRecording(); +- if (status != OK) { +- mWriter.clear(); +- mWriter = NULL; +- } +- +- return status; +-} +- +-status_t StagefrightRecorder::startAMRRecording() { ++status_t GonkRecorder::startAMRRecording() { + CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB || + mOutputFormat == OUTPUT_FORMAT_AMR_WB); + +@@ -971,28 +827,12 @@ + mAudioEncoder); + return BAD_VALUE; + } +- if (mSampleRate != 8000) { +- LOGE("Invalid sampling rate %d used for AMRNB recording", +- mSampleRate); +- return BAD_VALUE; +- } + } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB + if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { + LOGE("Invlaid encoder %d used for AMRWB recording", + mAudioEncoder); + return BAD_VALUE; + } +- if (mSampleRate != 16000) { +- LOGE("Invalid sample rate %d used for AMRWB recording", +- mSampleRate); +- return BAD_VALUE; +- } +- } +- +- if (mAudioChannels != 1) { +- LOGE("Invalid number of audio channels %d used for amr recording", +- mAudioChannels); +- return BAD_VALUE; + } + + mWriter = new AMRWriter(mOutputFd); +@@ -1004,7 +844,7 @@ + return status; + } + +-status_t StagefrightRecorder::startRawAudioRecording() { ++status_t GonkRecorder::startRawAudioRecording() { + if (mAudioSource >= AUDIO_SOURCE_CNT) { + LOGE("Invalid audio source: %d", mAudioSource); + return BAD_VALUE; +@@ -1035,62 +875,7 @@ + return OK; + } + +-status_t StagefrightRecorder::startFMA2DPWriter() { +- /* FM soc outputs at 48k */ +- mSampleRate = 48000; +- mAudioChannels = 2; +- +- sp meta = new MetaData; +- meta->setInt32(kKeyChannelCount, mAudioChannels); +- meta->setInt32(kKeySampleRate, mSampleRate); +- +- mWriter = new FMA2DPWriter(); +- mWriter->setListener(mListener); +- mWriter->start(meta.get()); +- return OK; +-} +- +-status_t StagefrightRecorder::startRTPRecording() { +- CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP); +- +- if ((mAudioSource != AUDIO_SOURCE_CNT +- && mVideoSource != VIDEO_SOURCE_LIST_END) +- || (mAudioSource == AUDIO_SOURCE_CNT +- && mVideoSource == VIDEO_SOURCE_LIST_END)) { +- // Must have exactly one source. +- return BAD_VALUE; +- } +- +- if (mOutputFd < 0) { +- return BAD_VALUE; +- } +- +- sp source; +- +- if (mAudioSource != AUDIO_SOURCE_CNT) { +- source = createAudioSource(); +- } else { +- +- sp mediaSource; +- status_t err = setupMediaSource(&mediaSource); +- if (err != OK) { +- return err; +- } +- +- err = setupVideoEncoder(mediaSource, mVideoBitRate, &source); +- if (err != OK) { +- return err; +- } +- } +- +- mWriter = new ARTPWriter(mOutputFd); +- mWriter->addSource(source); +- mWriter->setListener(mListener); +- +- return mWriter->start(); +-} +- +-status_t StagefrightRecorder::startMPEG2TSRecording() { ++status_t GonkRecorder::startMPEG2TSRecording() { + CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS); + + sp writer = new MPEG2TSWriter(mOutputFd); +@@ -1141,7 +926,7 @@ + return mWriter->start(); + } + +-void StagefrightRecorder::clipVideoFrameRate() { ++void GonkRecorder::clipVideoFrameRate() { + LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder); + int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.fps.min", mVideoEncoder); +@@ -1158,7 +943,7 @@ + } + } + +-void StagefrightRecorder::clipVideoBitRate() { ++void GonkRecorder::clipVideoBitRate() { + LOGV("clipVideoBitRate: encoder %d", mVideoEncoder); + int minBitRate = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.bps.min", mVideoEncoder); +@@ -1175,7 +960,7 @@ + } + } + +-void StagefrightRecorder::clipVideoFrameWidth() { ++void GonkRecorder::clipVideoFrameWidth() { + LOGV("clipVideoFrameWidth: encoder %d", mVideoEncoder); + int minFrameWidth = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.width.min", mVideoEncoder); +@@ -1192,8 +977,7 @@ + } + } + +-status_t StagefrightRecorder::checkVideoEncoderCapabilities() { +- if (!mCaptureTimeLapse) { ++status_t GonkRecorder::checkVideoEncoderCapabilities() { + // Dont clip for time lapse capture as encoder will have enough + // time to encode because of slow capture rate of time lapse. + clipVideoBitRate(); +@@ -1201,13 +985,12 @@ + clipVideoFrameWidth(); + clipVideoFrameHeight(); + setDefaultProfileIfNecessary(); +- } + return OK; + } + + // Set to use AVC baseline profile if the encoding parameters matches + // CAMCORDER_QUALITY_LOW profile; this is for the sake of MMS service. +-void StagefrightRecorder::setDefaultProfileIfNecessary() { ++void GonkRecorder::setDefaultProfileIfNecessary() { + LOGV("setDefaultProfileIfNecessary"); + + camcorder_quality quality = CAMCORDER_QUALITY_LOW; +@@ -1263,14 +1046,14 @@ + } + } + +-status_t StagefrightRecorder::checkAudioEncoderCapabilities() { ++status_t GonkRecorder::checkAudioEncoderCapabilities() { + clipAudioBitRate(); + clipAudioSampleRate(); + clipNumberOfAudioChannels(); + return OK; + } + +-void StagefrightRecorder::clipAudioBitRate() { ++void GonkRecorder::clipAudioBitRate() { + LOGV("clipAudioBitRate: encoder %d", mAudioEncoder); + + int minAudioBitRate = +@@ -1292,7 +1075,7 @@ + } + } + +-void StagefrightRecorder::clipAudioSampleRate() { ++void GonkRecorder::clipAudioSampleRate() { + LOGV("clipAudioSampleRate: encoder %d", mAudioEncoder); + + int minSampleRate = +@@ -1314,7 +1097,7 @@ + } + } + +-void StagefrightRecorder::clipNumberOfAudioChannels() { ++void GonkRecorder::clipNumberOfAudioChannels() { + LOGV("clipNumberOfAudioChannels: encoder %d", mAudioEncoder); + + int minChannels = +@@ -1336,7 +1119,7 @@ + } + } + +-void StagefrightRecorder::clipVideoFrameHeight() { ++void GonkRecorder::clipVideoFrameHeight() { + LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder); + int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.height.min", mVideoEncoder); +@@ -1354,61 +1137,26 @@ + } + + // Set up the appropriate MediaSource depending on the chosen option +-status_t StagefrightRecorder::setupMediaSource( ++status_t GonkRecorder::setupMediaSource( + sp *mediaSource) { + if (mVideoSource == VIDEO_SOURCE_DEFAULT + || mVideoSource == VIDEO_SOURCE_CAMERA) { +- sp cameraSource; ++ sp cameraSource; + status_t err = setupCameraSource(&cameraSource); + if (err != OK) { + return err; + } + *mediaSource = cameraSource; + } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) { +- // If using GRAlloc buffers, setup surfacemediasource. +- // Later a handle to that will be passed +- // to the client side when queried +- status_t err = setupSurfaceMediaSource(); +- if (err != OK) { +- return err; +- } +- *mediaSource = mSurfaceMediaSource; ++ return BAD_VALUE; + } else { + return INVALID_OPERATION; + } + return OK; + } + +-// setupSurfaceMediaSource creates a source with the given +-// width and height and framerate. +-// TODO: This could go in a static function inside SurfaceMediaSource +-// similar to that in CameraSource +-status_t StagefrightRecorder::setupSurfaceMediaSource() { +- status_t err = OK; +- mSurfaceMediaSource = new SurfaceMediaSource(mVideoWidth, mVideoHeight); +- if (mSurfaceMediaSource == NULL) { +- return NO_INIT; +- } +- +- if (mFrameRate == -1) { +- int32_t frameRate = 0; +- CHECK (mSurfaceMediaSource->getFormat()->findInt32( +- kKeyFrameRate, &frameRate)); +- LOGI("Frame rate is not explicitly set. Use the current frame " +- "rate (%d fps)", frameRate); +- mFrameRate = frameRate; +- } else { +- err = mSurfaceMediaSource->setFrameRate(mFrameRate); +- } +- CHECK(mFrameRate != -1); +- +- mIsMetaDataStoredInVideoBuffers = +- mSurfaceMediaSource->isMetaDataStoredInVideoBuffers(); +- return err; +-} +- +-status_t StagefrightRecorder::setupCameraSource( +- sp *cameraSource) { ++status_t GonkRecorder::setupCameraSource( ++ sp *cameraSource) { + status_t err = OK; + if ((err = checkVideoEncoderCapabilities()) != OK) { + return err; +@@ -1416,26 +1164,15 @@ + Size videoSize; + videoSize.width = mVideoWidth; + videoSize.height = mVideoHeight; +- if (mCaptureTimeLapse) { +- mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( +- mCamera, mCameraProxy, mCameraId, +- videoSize, mFrameRate, mPreviewSurface, +- mTimeBetweenTimeLapseFrameCaptureUs); +- *cameraSource = mCameraSourceTimeLapse; +- } else { +- +- bool useMeta = true; +- char value[PROPERTY_VALUE_MAX]; +- if (property_get("debug.camcorder.disablemeta", value, NULL) && ++ bool useMeta = true; ++ char value[PROPERTY_VALUE_MAX]; ++ if (property_get("debug.camcorder.disablemeta", value, NULL) && + atoi(value)) { +- useMeta = false; +- } +- *cameraSource = CameraSource::CreateFromCamera( +- mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate, +- mPreviewSurface, useMeta); ++ useMeta = false; + } +- mCamera.clear(); +- mCameraProxy.clear(); ++ ++ *cameraSource = GonkCameraSource::Create( ++ mCameraHandle, videoSize, mFrameRate, useMeta); + if (*cameraSource == NULL) { + return UNKNOWN_ERROR; + } +@@ -1465,7 +1202,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setupVideoEncoder( ++status_t GonkRecorder::setupVideoEncoder( + sp cameraSource, + int32_t videoBitRate, + sp *source) { +@@ -1501,10 +1238,7 @@ + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); +- hfr = 0; +- if (!meta->findInt32(kKeyHFR, &hfr)) { +- LOGW("hfr not found, default to 0"); +- } ++ CHECK(meta->findInt32(kKeyHFR, &hfr)); + + if(hfr) { + mMaxFileDurationUs = mMaxFileDurationUs * (hfr/mFrameRate); +@@ -1598,30 +1332,17 @@ + enc_meta->setInt32(kKey3D, is3D); + } + +- OMXClient client; +- CHECK_EQ(client.connect(), OK); +- + uint32_t encoder_flags = 0; + if (mIsMetaDataStoredInVideoBuffers) { + LOGW("Camera source supports metadata mode, create OMXCodec for metadata"); + encoder_flags |= OMXCodec::kHardwareCodecsOnly; + encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers; +- if (property_get("ro.board.platform", value, "0") +- && (!strncmp(value, "msm7627", sizeof("msm7627") - 1))) { +- LOGW("msm7627 family of chipsets supports, only one buffer at a time"); +- encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime; +- } +- } +- +- // Do not wait for all the input buffers to become available. +- // This give timelapse video recording faster response in +- // receiving output from video encoder component. +- if (mCaptureTimeLapse) { + encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime; + } + + sp encoder = OMXCodec::Create( +- client.interface(), enc_meta, ++ GetOMX(), ++ enc_meta, + true /* createEncoder */, cameraSource, + NULL, encoder_flags); + if (encoder == NULL) { +@@ -1638,7 +1359,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setupAudioEncoder(const sp& writer) { ++status_t GonkRecorder::setupAudioEncoder(const sp& writer) { + status_t status = BAD_VALUE; + if (OK != (status = checkAudioEncoderCapabilities())) { + return status; +@@ -1664,7 +1385,7 @@ + return OK; + } + +-status_t StagefrightRecorder::setupMPEG4Recording( ++status_t GonkRecorder::setupMPEG4Recording( + int outputFd, + int32_t videoWidth, int32_t videoHeight, + int32_t videoBitRate, +@@ -1696,7 +1417,7 @@ + // Audio source is added at the end if it exists. + // This help make sure that the "recoding" sound is suppressed for + // camcorder applications in the recorded files. +- if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) { ++ if (mAudioSource != AUDIO_SOURCE_CNT) { + err = setupAudioEncoder(writer); + if (err != OK) return err; + *totalBitRate += mAudioBitRate; +@@ -1728,7 +1449,7 @@ + return OK; + } + +-void StagefrightRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate, ++void GonkRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate, + sp *meta) { + (*meta)->setInt64(kKeyTime, startTimeUs); + (*meta)->setInt32(kKeyFileType, mOutputFormat); +@@ -1752,7 +1473,7 @@ + } + } + +-status_t StagefrightRecorder::startMPEG4Recording() { ++status_t GonkRecorder::startMPEG4Recording() { + int32_t totalBitRate; + status_t err = setupMPEG4Recording( + mOutputFd, mVideoWidth, mVideoHeight, +@@ -1761,7 +1482,14 @@ + return err; + } + +- int64_t startTimeUs = systemTime() / 1000; ++ //systemTime() doesn't give correct time because ++ //HAVE_POSIX_CLOCKS is not defined for utils/Timers.cpp ++ //so, using clock_gettime directly ++#include ++ struct timespec t; ++ clock_gettime(CLOCK_MONOTONIC, &t); ++ int64_t startTimeUs = int64_t(t.tv_sec)*1000000000LL + t.tv_nsec; ++ startTimeUs = startTimeUs / 1000; + sp meta = new MetaData; + setupMPEG4MetaData(startTimeUs, totalBitRate, &meta); + +@@ -1773,7 +1501,7 @@ + return OK; + } + +-status_t StagefrightRecorder::pause() { ++status_t GonkRecorder::pause() { + LOGV("pause"); + if (mWriter == NULL) { + return UNKNOWN_ERROR; +@@ -1782,31 +1510,16 @@ + + if (mStarted) { + mStarted = false; +- +- uint32_t params = 0; +- if (mAudioSource != AUDIO_SOURCE_CNT) { +- params |= IMediaPlayerService::kBatteryDataTrackAudio; +- } +- if (mVideoSource != VIDEO_SOURCE_LIST_END) { +- params |= IMediaPlayerService::kBatteryDataTrackVideo; +- } +- +- addBatteryData(params); + } + + + return OK; + } + +-status_t StagefrightRecorder::stop() { ++status_t GonkRecorder::stop() { + LOGV("stop"); + status_t err = OK; + +- if (mCaptureTimeLapse && mCameraSourceTimeLapse != NULL) { +- mCameraSourceTimeLapse->startQuickReadReturns(); +- mCameraSourceTimeLapse = NULL; +- } +- + if (mWriter != NULL) { + err = mWriter->stop(); + mWriter.clear(); +@@ -1819,30 +1532,20 @@ + + if (mStarted) { + mStarted = false; +- +- uint32_t params = 0; +- if (mAudioSource != AUDIO_SOURCE_CNT) { +- params |= IMediaPlayerService::kBatteryDataTrackAudio; +- } +- if (mVideoSource != VIDEO_SOURCE_LIST_END) { +- params |= IMediaPlayerService::kBatteryDataTrackVideo; +- } +- +- addBatteryData(params); + } + + + return err; + } + +-status_t StagefrightRecorder::close() { ++status_t GonkRecorder::close() { + LOGV("close"); + stop(); + + return OK; + } + +-status_t StagefrightRecorder::reset() { ++status_t GonkRecorder::reset() { + LOGV("reset"); + stop(); + +@@ -1858,9 +1561,9 @@ + mVideoHeight = 144; + mFrameRate = -1; + mVideoBitRate = 192000; +- mSampleRate = 0; +- mAudioChannels = 0; +- mAudioBitRate = 0; ++ mSampleRate = 8000; ++ mAudioChannels = 1; ++ mAudioBitRate = 12200; + mInterleaveDurationUs = 0; + mIFramesIntervalSec = 2; + mAudioSourceNode = 0; +@@ -1875,9 +1578,6 @@ + mMaxFileDurationUs = 0; + mMaxFileSizeBytes = 0; + mTrackEveryTimeDurationUs = 0; +- mCaptureTimeLapse = false; +- mTimeBetweenTimeLapseFrameCaptureUs = -1; +- mCameraSourceTimeLapse = NULL; + mIsMetaDataStoredInVideoBuffers = false; + mEncoderProfiles = MediaProfiles::getInstance(); + mRotationDegrees = 0; +@@ -1885,6 +1585,11 @@ + mLongitudex10000 = -3600000; + + mOutputFd = -1; ++ mCameraHandle = -1; ++ //TODO: May need to register a listener eventually ++ //if someone is interested in recorder events for now ++ //default to no listener registered ++ mListener = NULL; + + // Disable Audio Encoding + char value[PROPERTY_VALUE_MAX]; +@@ -1894,7 +1599,7 @@ + return OK; + } + +-status_t StagefrightRecorder::getMaxAmplitude(int *max) { ++status_t GonkRecorder::getMaxAmplitude(int *max) { + LOGV("getMaxAmplitude"); + + if (max == NULL) { +@@ -1911,7 +1616,7 @@ + return OK; + } + +-status_t StagefrightRecorder::dump( ++status_t GonkRecorder::dump( + int fd, const Vector& args) const { + LOGV("dump"); + const size_t SIZE = 256; +@@ -1958,6 +1663,8 @@ + result.append(buffer); + snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); + result.append(buffer); ++ snprintf(buffer, SIZE, " Camera Handle: %d\n", mCameraHandle); ++ result.append(buffer); + snprintf(buffer, SIZE, " Start time offset (ms): %d\n", mStartTimeOffsetMs); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder); +@@ -1978,45 +1685,12 @@ + return OK; + } + +-status_t StagefrightRecorder::startExtendedRecording() { +- CHECK(mOutputFormat == OUTPUT_FORMAT_QCP); +- +- if (mSampleRate != 8000) { +- LOGE("Invalid sampling rate %d used for recording", +- mSampleRate); +- return BAD_VALUE; +- } +- if (mAudioChannels != 1) { +- LOGE("Invalid number of audio channels %d used for recording", +- mAudioChannels); +- return BAD_VALUE; +- } +- +- if (mAudioSource >= AUDIO_SOURCE_CNT) { +- LOGE("Invalid audio source: %d", mAudioSource); +- return BAD_VALUE; +- } +- +- sp audioEncoder = createAudioSource(); +- +- if (audioEncoder == NULL) { +- LOGE("AudioEncoder NULL"); +- return UNKNOWN_ERROR; +- } +- +- mWriter = new ExtendedWriter(dup(mOutputFd)); +- mWriter->addSource(audioEncoder); +- +- if (mMaxFileDurationUs != 0) { +- mWriter->setMaxFileDuration(mMaxFileDurationUs); +- } +- if (mMaxFileSizeBytes != 0) { +- mWriter->setMaxFileSize(mMaxFileSizeBytes); +- } +- mWriter->setListener(mListener); +- mWriter->start(); +- +- return OK; ++status_t GonkRecorder::setCameraHandle(int32_t handle) { ++ if (handle < 0) { ++ return BAD_VALUE; ++ } ++ mCameraHandle = handle; ++ return OK; + } + + } // namespace android +diff --git a/GonkRecorder.h b/GonkRecorder.h +index dba6110..fa948af 100644 +--- a/GonkRecorder.h ++++ b/GonkRecorder.h +@@ -14,11 +14,11 @@ + * limitations under the License. + */ + +-#ifndef STAGEFRIGHT_RECORDER_H_ ++#ifndef GONK_RECORDER_H_ + +-#define STAGEFRIGHT_RECORDER_H_ ++#define GONK_RECORDER_H_ + +-#include ++#include + #include + #include + +@@ -26,21 +26,16 @@ + + namespace android { + +-class Camera; +-class ICameraRecordingProxy; +-class CameraSource; +-class CameraSourceTimeLapse; ++class GonkCameraSource; + struct MediaSource; + struct MediaWriter; + class MetaData; + struct AudioSource; + class MediaProfiles; +-class ISurfaceTexture; +-class SurfaceMediaSource; + +-struct StagefrightRecorder : public MediaRecorderBase { +- StagefrightRecorder(); +- virtual ~StagefrightRecorder(); ++struct GonkRecorder { ++ GonkRecorder(); ++ virtual ~GonkRecorder(); + + virtual status_t init(); + virtual status_t setAudioSource(audio_source_t as); +@@ -50,11 +45,10 @@ + virtual status_t setVideoEncoder(video_encoder ve); + virtual status_t setVideoSize(int width, int height); + virtual status_t setVideoFrameRate(int frames_per_second); +- virtual status_t setCamera(const sp& camera, const sp& proxy); +- virtual status_t setPreviewSurface(const sp& surface); + virtual status_t setOutputFile(const char *path); + virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); + virtual status_t setParameters(const String8& params); ++ virtual status_t setCameraHandle(int32_t handle); + virtual status_t setListener(const sp& listener); + virtual status_t prepare(); + virtual status_t start(); +@@ -65,12 +59,8 @@ + virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector& args) const; + // Querying a SurfaceMediaSourcer +- virtual sp querySurfaceMediaSource() const; + + private: +- sp mCamera; +- sp mCameraProxy; +- sp mPreviewSurface; + sp mListener; + sp mWriter; + int mOutputFd; +@@ -104,11 +94,6 @@ + int32_t mLongitudex10000; + int32_t mStartTimeOffsetMs; + +- bool mCaptureTimeLapse; +- int64_t mTimeBetweenTimeLapseFrameCaptureUs; +- sp mCameraSourceTimeLapse; +- +- + String8 mParams; + + bool mIsMetaDataStoredInVideoBuffers; +@@ -119,8 +104,8 @@ + // An pointer + // will be sent to the client side using which the + // frame buffers will be queued and dequeued +- sp mSurfaceMediaSource; + bool mDisableAudio; ++ int32_t mCameraHandle; + + status_t setupMPEG4Recording( + int outputFd, +@@ -132,10 +117,7 @@ + sp *meta); + status_t startMPEG4Recording(); + status_t startAMRRecording(); +- status_t startFMA2DPWriter(); +- status_t startAACRecording(); + status_t startRawAudioRecording(); +- status_t startRTPRecording(); + status_t startMPEG2TSRecording(); + sp createAudioSource(); + status_t checkVideoEncoderCapabilities(); +@@ -144,9 +126,8 @@ + // source (CameraSource or SurfaceMediaSource) + // depending on the videosource type + status_t setupMediaSource(sp *mediaSource); +- status_t setupCameraSource(sp *cameraSource); ++ status_t setupCameraSource(sp *cameraSource); + // setup the surfacemediasource for the encoder +- status_t setupSurfaceMediaSource(); + + status_t setupAudioEncoder(const sp& writer); + status_t setupVideoEncoder( +@@ -160,8 +141,6 @@ + status_t setParamAudioNumberOfChannels(int32_t channles); + status_t setParamAudioSamplingRate(int32_t sampleRate); + status_t setParamAudioTimeScale(int32_t timeScale); +- status_t setParamTimeLapseEnable(int32_t timeLapseEnable); +- status_t setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs); + status_t setParamVideoEncodingBitRate(int32_t bitRate); + status_t setParamVideoIFramesInterval(int32_t seconds); + status_t setParamVideoEncoderProfile(int32_t profile); +@@ -186,14 +165,10 @@ + void clipNumberOfAudioChannels(); + void setDefaultProfileIfNecessary(); + +- +- StagefrightRecorder(const StagefrightRecorder &); +- StagefrightRecorder &operator=(const StagefrightRecorder &); +- +- /* extension */ +- status_t startExtendedRecording(); ++ GonkRecorder(const GonkRecorder &); ++ GonkRecorder &operator=(const GonkRecorder &); + }; + + } // namespace android + +-#endif // STAGEFRIGHT_RECORDER_H_ ++#endif // GONK_RECORDER_H_ diff --git a/dom/camera/update.sh b/dom/camera/update.sh new file mode 100644 index 00000000000..9b5a968cea2 --- /dev/null +++ b/dom/camera/update.sh @@ -0,0 +1,14 @@ +# Usage: ./update.sh +# +# Copies the needed files from the directory containing the original +# Android ICS OS source and applies the B2G specific changes for the +# camcorder functionality in B2G. +cp $1/frameworks/base/media/libmediaplayerservice/StagefrightRecorder.cpp ./GonkRecorder.cpp +cp $1/frameworks/base/media/libmediaplayerservice/StagefrightRecorder.h ./GonkRecorder.h +cp $1/frameworks/base/media/libstagefright/CameraSource.cpp ./GonkCameraSource.cpp +cp $1/frameworks/base/include/media/stagefright/CameraSource.h ./GonkCameraSource.h +cp $1/frameworks/base/media/libmedia/AudioParameter.cpp ./AudioParameter.cpp +cp $1/frameworks/base/include/camera/Camera.h ./GonkCameraListener.h +patch -p1 setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + mMeta->setInt32(kKeyColorFormat, mColorFormat); + mMeta->setInt32(kKeyWidth, mVideoSize.width); + mMeta->setInt32(kKeyHeight, mVideoSize.height); + mMeta->setInt32(kKeyStride, mVideoSize.width); + mMeta->setInt32(kKeySliceHeight, mVideoSize.height); + mMeta->setInt32(kKeyFrameRate, mVideoFrameRate); +- mMeta->setInt32(kKeyHFR, hfr); + +- if (want3D) { +- mMeta->setInt32(kKey3D, !0); +- } + return OK; + } + + GonkCameraSource::~GonkCameraSource() { + if (mStarted) { + stop(); + } else if (mInitCheck == OK) { + // Camera is initialized but because start() is never called, +diff --git a/dom/camera/GonkRecorder.cpp b/dom/camera/GonkRecorder.cpp +--- a/dom/camera/GonkRecorder.cpp ++++ b/dom/camera/GonkRecorder.cpp +@@ -716,56 +716,16 @@ status_t GonkRecorder::start() { + mStarted = true; + } + + return status; + } + + sp GonkRecorder::createAudioSource() { + +- bool tunneledSource = false; +- const char *tunnelMime; +- { +- AudioParameter param; +- String8 key("tunneled-input-formats"); +- param.add( key, String8("get") ); +- String8 valueStr = AudioSystem::getParameters( 0, param.toString()); +- AudioParameter result(valueStr); +- int value; +- if ( mAudioEncoder == AUDIO_ENCODER_AMR_NB && +- result.getInt(String8("AMR"),value) == NO_ERROR ) { +- tunneledSource = true; +- tunnelMime = MEDIA_MIMETYPE_AUDIO_AMR_NB; +- } +- else if ( mAudioEncoder == AUDIO_ENCODER_QCELP && +- result.getInt(String8("QCELP"),value) == NO_ERROR ) { +- tunneledSource = true; +- tunnelMime = MEDIA_MIMETYPE_AUDIO_QCELP; +- } +- else if ( mAudioEncoder == AUDIO_ENCODER_EVRC && +- result.getInt(String8("EVRC"),value) == NO_ERROR ) { +- tunneledSource = true; +- tunnelMime = MEDIA_MIMETYPE_AUDIO_EVRC; +- } +- } +- +- if ( tunneledSource ) { +- sp audioSource = NULL; +- sp meta = new MetaData; +- meta->setInt32(kKeyChannelCount, mAudioChannels); +- meta->setInt32(kKeySampleRate, mSampleRate); +- meta->setInt32(kKeyBitRate, mAudioBitRate); +- if (mAudioTimeScale > 0) { +- meta->setInt32(kKeyTimeScale, mAudioTimeScale); +- } +- meta->setCString( kKeyMIMEType, tunnelMime ); +- audioSource = new AudioSource( mAudioSource, meta); +- return audioSource->initCheck( ) == OK ? audioSource : NULL; +- } +- + sp audioSource = + new AudioSource( + mAudioSource, + mSampleRate, + mAudioChannels); + + status_t err = audioSource->initCheck(); + +@@ -1226,56 +1186,33 @@ status_t GonkRecorder::setupVideoEncoder + + default: + CHECK(!"Should not be here, unsupported video encoding."); + break; + } + + sp meta = cameraSource->getFormat(); + +- int32_t width, height, stride, sliceHeight, colorFormat, hfr, is3D; ++ int32_t width, height, stride, sliceHeight, colorFormat; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); +- CHECK(meta->findInt32(kKeyHFR, &hfr)); +- +- if(hfr) { +- mMaxFileDurationUs = mMaxFileDurationUs * (hfr/mFrameRate); +- } +- + + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec); + enc_meta->setInt32(kKeyStride, stride); + enc_meta->setInt32(kKeySliceHeight, sliceHeight); + enc_meta->setInt32(kKeyColorFormat, colorFormat); +- enc_meta->setInt32(kKeyHFR, hfr); + if (mVideoTimeScale > 0) { + enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale); + } + +- char mDeviceName[100]; +- property_get("ro.board.platform",mDeviceName,"0"); +- if(!strncmp(mDeviceName, "msm7627a", 8)) { +- if(hfr && (width * height > 432*240)) { +- LOGE("HFR mode is supported only upto WQVGA resolution"); +- return INVALID_OPERATION; +- } +- } +- else { +- if(hfr && ((mVideoEncoder != VIDEO_ENCODER_H264) || (width * height > 800*480))) { +- LOGE("HFR mode is supported only upto WVGA and H264 codec."); +- return INVALID_OPERATION; +- } +- } +- +- + /* + * can set profile from the app as a parameter. + * For the mean time, set from shell + */ + + char value[PROPERTY_VALUE_MAX]; + bool customProfile = false; + +@@ -1322,19 +1259,16 @@ status_t GonkRecorder::setupVideoEncoder + } + + if (mVideoEncoderProfile != -1) { + enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile); + } + if (mVideoEncoderLevel != -1) { + enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel); + } +- if (meta->findInt32(kKey3D, &is3D)) { +- enc_meta->setInt32(kKey3D, is3D); +- } + + uint32_t encoder_flags = 0; + if (mIsMetaDataStoredInVideoBuffers) { + LOGW("Camera source supports metadata mode, create OMXCodec for metadata"); + encoder_flags |= OMXCodec::kHardwareCodecsOnly; + encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers; + encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime; + } diff --git a/dom/devicestorage/DeviceStorageRequestChild.cpp b/dom/devicestorage/DeviceStorageRequestChild.cpp index 09241060357..d4b7854bfd6 100644 --- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -55,7 +55,8 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal BlobChild* actor = static_cast(r.blobChild()); nsCOMPtr blob = actor->GetBlob(); - jsval result = InterfaceToJsval(mRequest->GetOwner(), blob, &NS_GET_IID(nsIDOMBlob)); + nsCOMPtr file = do_QueryInterface(blob); + jsval result = InterfaceToJsval(mRequest->GetOwner(), file, &NS_GET_IID(nsIDOMFile)); mRequest->FireSuccess(result); break; } diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index d62af32a40a..d1f6aeb4c0c 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -1913,6 +1913,17 @@ nsDOMDeviceStorage::Stat(nsIDOMDOMRequest** aRetval) return NS_OK; } +NS_IMETHODIMP +nsDOMDeviceStorage::GetRootDirectory(nsIFile** aRootDirectory) +{ + if (!mRootDirectory) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr file; + return mRootDirectory->Clone(aRootDirectory); +} + NS_IMETHODIMP nsDOMDeviceStorage::Enumerate(const JS::Value & aName, const JS::Value & aOptions, diff --git a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl index c3820d85230..aad7b8436d7 100644 --- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl +++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl @@ -9,13 +9,14 @@ interface nsIDOMDOMRequest; interface nsIDOMDeviceStorageCursor; interface nsIDOMDeviceStorageChangeEvent; interface nsIDOMEventListener; +interface nsIFile; dictionary DeviceStorageEnumerationParameters { jsval since; }; -[scriptable, uuid(7efbe025-3a8a-4151-9257-3e8c941dc099), builtinclass] +[scriptable, uuid(7f69936f-2948-4733-ba41-c7e1d657a88b), builtinclass] interface nsIDOMDeviceStorage : nsIDOMEventTarget { [implicit_jscontext] attribute jsval onchange; @@ -38,4 +39,6 @@ interface nsIDOMDeviceStorage : nsIDOMEventTarget nsIDOMDeviceStorageCursor enumerateEditable([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options); nsIDOMDOMRequest stat(); + + [noscript] readonly attribute nsIFile rootDirectory; }; diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index e3fdfa1df19..46b87df4e5d 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -21,7 +21,8 @@ dictionaries = [ [ 'CameraRegion', 'nsIDOMCameraManager.idl' ], [ 'CameraPosition', 'nsIDOMCameraManager.idl' ], [ 'CameraSelector', 'nsIDOMCameraManager.idl' ], - [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ] + [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ], + [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ] ] # include file names diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index 09453a7721d..d2554041fed 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -118,6 +118,10 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) STATIC_LIBS += moznetd_s endif +ifdef MOZ_B2G_CAMERA #{ +OS_LIBS += -lstagefright -lstagefright_omx +endif #} + ifdef MOZ_IPDL_TESTS STATIC_LIBS += ipdlunittest_s endif From bb4f0d8eacfba0c7717dff1b09fc487308400db1 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 28 Sep 2012 22:58:10 -0700 Subject: [PATCH 20/21] Backout 44465ef545e3 (Bug 786126) - Are we fast yet regression. --- js/src/ion/IonBuilder.cpp | 19 +------------------ js/src/jsinfer.h | 13 ------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index a87b89788da..fd7f226386f 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -5914,24 +5914,7 @@ IonBuilder::jsop_getprop(HandlePropertyName name) return makeCallBarrier(getter, 0, false, types, barrier); } - // If the input is guaranteed to be an object, then we want - // to specialize it via an slot load or an IC. If it's - // guaranteed to be an object or NULL, then we do the same - // thing except prefixed by a fallible unbox. - bool targetIsObject = (unary.ival == MIRType_Object); - - if (!targetIsObject) { - if (unaryTypes.inTypes->objectOrSentinel()) { - // Fallibly unwrap the object before getprop. Getprop - // on null or undefined will cause exception anyway. - MUnbox *unbox = MUnbox::New(obj, MIRType_Object, MUnbox::Fallible); - current->add(unbox); - obj = unbox; - targetIsObject = true; - } - } - - if (targetIsObject) { + if (unary.ival == MIRType_Object) { MIRType rvalType = MIRType_Value; if (!barrier && !IsNullOrUndefined(unary.rval)) rvalType = unary.rval; diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 11a2f8ff572..f9acb63ca8c 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -571,19 +571,6 @@ class StackTypeSet : public TypeSet /* Whether this value may be an object. */ bool maybeObject() { return unknownObject() || baseObjectCount() > 0; } - /* - * Whether this typeset represents a potentially sentineled object value: - * where the value may be an object, but maybe potentially null or undefined. - * This returns false in situations where the value cannot ever be an object. - */ - bool objectOrSentinel() { - TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT; - if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK)) - return false; - - return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0; - } - /* Whether the type set contains objects with any of a set of flags. */ bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags); From a24fec4838251f34063e53157d49265d24b51e78 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 28 Sep 2012 22:58:53 -0700 Subject: [PATCH 21/21] Backout a2843362ce9b (Bug 786126) - Are we fast yet regression. --- js/src/ion/TypeOracle.cpp | 10 ---------- js/src/jsanalyze.h | 9 ++++----- js/src/jsinterpinlines.h | 25 ++++++++++--------------- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/js/src/ion/TypeOracle.cpp b/js/src/ion/TypeOracle.cpp index 3c285dc11a5..0f102ab96e5 100644 --- a/js/src/ion/TypeOracle.cpp +++ b/js/src/ion/TypeOracle.cpp @@ -373,16 +373,6 @@ TypeInferenceOracle::elementReadGeneric(JSScript *script, jsbytecode *pc, bool * *cacheable = (obj == MIRType_Object && (id == MIRType_Value || id == MIRType_Int32 || id == MIRType_String)); - - // Turn off cacheing if the element is int32 and we've seen non-native objects as the target - // of this getelem. - if (*cacheable) { - if (id == MIRType_Int32) { - if (script->analysis()->getCode(pc).nonNativeGetElement) - *cacheable = false; - } - } - if (*cacheable) *monitorResult = (id == MIRType_String || script->analysis()->getCode(pc).getStringElement); else diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 8456aebe217..1d37f696853 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -111,11 +111,10 @@ class Bytecode * Dynamically observed state about the execution of this opcode. These are * hints about the script for use during compilation. */ - bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */ - bool getStringElement:1; /* GETELEM which has accessed string properties. */ - bool nonNativeGetElement:1; /* GETELEM on a non-native object. */ - bool accessGetter: 1; /* Property read on a shape with a getter hook. */ - bool notIdempotent: 1; /* Don't use an idempotent cache for this property read. */ + bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */ + bool getStringElement:1; /* GETELEM which has accessed string properties. */ + bool accessGetter: 1; /* Property read on a shape with a getter hook. */ + bool notIdempotent: 1; /* Don't use an idempotent cache for this property read. */ /* Stack depth before this opcode. */ uint32_t stackDepth; diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index bb47f97d909..dc84c572f7a 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -686,19 +686,6 @@ GetObjectElementOperation(JSContext *cx, JSOp op, HandleObject obj, const Value } #endif - bool updateAnalysis = false; - RootedScript script(cx, NULL); - jsbytecode *pc = NULL; - if (!cx->fp()->beginsIonActivation()) { - // Don't call GetPcScript from inside Ion since it's expensive. - types::TypeScript::GetPcScript(cx, &script, &pc); - if (script->hasAnalysis()) - updateAnalysis = true; - } - - if (updateAnalysis && !obj->isNative()) - script->analysis()->getCode(pc).nonNativeGetElement = true; - uint32_t index; if (IsDefinitelyIndex(rref, &index)) { do { @@ -716,8 +703,16 @@ GetObjectElementOperation(JSContext *cx, JSOp op, HandleObject obj, const Value return false; } while(0); } else { - if (updateAnalysis) - script->analysis()->getCode(pc).getStringElement = true; + if (!cx->fp()->beginsIonActivation()) { + // Don't update getStringElement if called from Ion code, since + // ion::GetPcScript is expensive. + RootedScript script(cx); + jsbytecode *pc; + types::TypeScript::GetPcScript(cx, &script, &pc); + + if (script->hasAnalysis()) + script->analysis()->getCode(pc).getStringElement = true; + } SpecialId special; res.set(rref);