netwerk/protocol/device/AndroidCaptureProvider.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/device/AndroidCaptureProvider.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,301 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "base/basictypes.h"
    1.10 +#include "AndroidCaptureProvider.h"
    1.11 +#include "nsXULAppAPI.h"
    1.12 +#include "AndroidBridge.h"
    1.13 +#include "nsStreamUtils.h"
    1.14 +#include "nsThreadUtils.h"
    1.15 +#include "nsMemory.h"
    1.16 +#include "RawStructs.h"
    1.17 +
    1.18 +// The maximum number of frames we keep in our queue. Don't live in the past.
    1.19 +#define MAX_FRAMES_QUEUED 10
    1.20 +
    1.21 +using namespace mozilla::net;
    1.22 +
    1.23 +NS_IMPL_ISUPPORTS(AndroidCameraInputStream, nsIInputStream, nsIAsyncInputStream)
    1.24 +
    1.25 +AndroidCameraInputStream::AndroidCameraInputStream() :
    1.26 +  mWidth(0), mHeight(0), mCamera(0), mHeaderSent(false), mClosed(true), mFrameSize(0),
    1.27 +  mMonitor("AndroidCamera.Monitor")
    1.28 +{
    1.29 +  mAvailable = sizeof(RawVideoHeader);
    1.30 +  mFrameQueue = new nsDeque();
    1.31 +}
    1.32 +
    1.33 +AndroidCameraInputStream::~AndroidCameraInputStream() {
    1.34 +  // clear the frame queue
    1.35 +  while (mFrameQueue->GetSize() > 0) {
    1.36 +    nsMemory::Free(mFrameQueue->PopFront());
    1.37 +  }
    1.38 +  delete mFrameQueue;
    1.39 +}
    1.40 +
    1.41 +NS_IMETHODIMP
    1.42 +AndroidCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams)
    1.43 +{
    1.44 +  if (XRE_GetProcessType() != GeckoProcessType_Default)
    1.45 +    return NS_ERROR_NOT_IMPLEMENTED;
    1.46 +
    1.47 +  mContentType = aContentType;
    1.48 +  mWidth = aParams->width;
    1.49 +  mHeight = aParams->height;
    1.50 +  mCamera = aParams->camera;
    1.51 +  
    1.52 +  CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
    1.53 +  if (!impl)
    1.54 +    return NS_ERROR_OUT_OF_MEMORY;
    1.55 +  if (impl->Init(mContentType, mCamera, mWidth, mHeight, this)) {
    1.56 +    mWidth = impl->GetWidth();
    1.57 +    mHeight = impl->GetHeight();
    1.58 +    mClosed = false;
    1.59 +  }
    1.60 +  return NS_OK;
    1.61 +}
    1.62 +
    1.63 +void AndroidCameraInputStream::ReceiveFrame(char* frame, uint32_t length) {
    1.64 +  {
    1.65 +    mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
    1.66 +    if (mFrameQueue->GetSize() > MAX_FRAMES_QUEUED) {
    1.67 +      nsMemory::Free(mFrameQueue->PopFront());
    1.68 +      mAvailable -= mFrameSize;
    1.69 +    }
    1.70 +  }
    1.71 +  
    1.72 +  mFrameSize = sizeof(RawPacketHeader) + length;
    1.73 +  
    1.74 +  char* fullFrame = (char*)nsMemory::Alloc(mFrameSize);
    1.75 +
    1.76 +  if (!fullFrame)
    1.77 +    return;
    1.78 +  
    1.79 +  RawPacketHeader* header = (RawPacketHeader*) fullFrame;
    1.80 +  header->packetID = 0xFF;
    1.81 +  header->codecID = 0x595556; // "YUV"
    1.82 +  
    1.83 +  // we copy the Y plane, and de-interlace the CrCb
    1.84 +  
    1.85 +  uint32_t yFrameSize = mWidth * mHeight;
    1.86 +  uint32_t uvFrameSize = yFrameSize / 4;
    1.87 +
    1.88 +  memcpy(fullFrame + sizeof(RawPacketHeader), frame, yFrameSize);
    1.89 +  
    1.90 +  char* uFrame = fullFrame + yFrameSize;
    1.91 +  char* vFrame = fullFrame + yFrameSize + uvFrameSize;
    1.92 +  char* yFrame = frame + yFrameSize;
    1.93 +  for (uint32_t i = 0; i < uvFrameSize; i++) {
    1.94 +    uFrame[i] = yFrame[2 * i + 1];
    1.95 +    vFrame[i] = yFrame[2 * i];
    1.96 +  }
    1.97 +  
    1.98 +  {
    1.99 +    mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
   1.100 +    mAvailable += mFrameSize;
   1.101 +    mFrameQueue->Push((void*)fullFrame);
   1.102 +  }
   1.103 +
   1.104 +  NotifyListeners();
   1.105 +}
   1.106 +
   1.107 +NS_IMETHODIMP
   1.108 +AndroidCameraInputStream::Available(uint64_t *aAvailable)
   1.109 +{
   1.110 +  mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
   1.111 +
   1.112 +  *aAvailable = mAvailable;
   1.113 +
   1.114 +  return NS_OK;
   1.115 +}
   1.116 +
   1.117 +NS_IMETHODIMP AndroidCameraInputStream::IsNonBlocking(bool *aNonBlock) {
   1.118 +  *aNonBlock = true;
   1.119 +  return NS_OK;
   1.120 +}
   1.121 +
   1.122 +NS_IMETHODIMP AndroidCameraInputStream::Read(char *aBuffer, uint32_t aCount, uint32_t *aRead) {
   1.123 +  return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead);
   1.124 +}
   1.125 +
   1.126 +NS_IMETHODIMP AndroidCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *aRead) {
   1.127 +  *aRead = 0;
   1.128 +  
   1.129 +  nsresult rv;
   1.130 +
   1.131 +  if (mAvailable == 0)
   1.132 +    return NS_BASE_STREAM_WOULD_BLOCK;
   1.133 +  
   1.134 +  if (aCount > mAvailable)
   1.135 +    aCount = mAvailable;
   1.136 +
   1.137 +  if (!mHeaderSent) {
   1.138 +    CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
   1.139 +    RawVideoHeader header;
   1.140 +    header.headerPacketID = 0;
   1.141 +    header.codecID = 0x595556; // "YUV"
   1.142 +    header.majorVersion = 0;
   1.143 +    header.minorVersion = 1;
   1.144 +    header.options = 1 | 1 << 1; // color, 4:2:2
   1.145 +
   1.146 +    header.alphaChannelBpp = 0;
   1.147 +    header.lumaChannelBpp = 8;
   1.148 +    header.chromaChannelBpp = 4;
   1.149 +    header.colorspace = 1;
   1.150 +
   1.151 +    header.frameWidth = mWidth;
   1.152 +    header.frameHeight = mHeight;
   1.153 +    header.aspectNumerator = 1;
   1.154 +    header.aspectDenominator = 1;
   1.155 +
   1.156 +    header.framerateNumerator = impl->GetFps();
   1.157 +    header.framerateDenominator = 1;
   1.158 +
   1.159 +    rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(RawVideoHeader), aRead);
   1.160 +   
   1.161 +    if (NS_FAILED(rv))
   1.162 +      return NS_OK;
   1.163 +    
   1.164 +    mHeaderSent = true;
   1.165 +    aCount -= sizeof(RawVideoHeader);
   1.166 +    mAvailable -= sizeof(RawVideoHeader);
   1.167 +  }
   1.168 +  
   1.169 +  {
   1.170 +    mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
   1.171 +    while ((mAvailable > 0) && (aCount >= mFrameSize)) {
   1.172 +      uint32_t readThisTime = 0;
   1.173 +
   1.174 +      char* frame = (char*)mFrameQueue->PopFront();
   1.175 +      rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime);
   1.176 +
   1.177 +      if (readThisTime != mFrameSize) {
   1.178 +        mFrameQueue->PushFront((void*)frame);
   1.179 +        return NS_OK;
   1.180 +      }
   1.181 +  
   1.182 +      // RawReader does a copy when calling VideoData::Create()
   1.183 +      nsMemory::Free(frame);
   1.184 +  
   1.185 +      if (NS_FAILED(rv))
   1.186 +        return NS_OK;
   1.187 +
   1.188 +      aCount -= readThisTime;
   1.189 +      mAvailable -= readThisTime;
   1.190 +      *aRead += readThisTime;
   1.191 +    }
   1.192 +  }
   1.193 +  return NS_OK;
   1.194 +}
   1.195 +
   1.196 +NS_IMETHODIMP AndroidCameraInputStream::Close() {
   1.197 +  return CloseWithStatus(NS_OK);
   1.198 +}
   1.199 +
   1.200 +
   1.201 +/**
   1.202 + * must be called on the main (java) thread
   1.203 + */
   1.204 +void AndroidCameraInputStream::doClose() {
   1.205 +  NS_ASSERTION(!mClosed, "Camera is already closed");
   1.206 +
   1.207 +  CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
   1.208 +  impl->Close();
   1.209 +  mClosed = true;
   1.210 +}
   1.211 +
   1.212 +
   1.213 +void AndroidCameraInputStream::NotifyListeners() {
   1.214 +  mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
   1.215 +  
   1.216 +  if (mCallback && (mAvailable > sizeof(RawVideoHeader))) {
   1.217 +    nsCOMPtr<nsIInputStreamCallback> callback;
   1.218 +    if (mCallbackTarget) {
   1.219 +      callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
   1.220 +    } else {
   1.221 +      callback = mCallback;
   1.222 +    }
   1.223 +
   1.224 +    NS_ASSERTION(callback, "Shouldn't fail to make the callback!");
   1.225 +
   1.226 +    // Null the callback first because OnInputStreamReady may reenter AsyncWait
   1.227 +    mCallback = nullptr;
   1.228 +    mCallbackTarget = nullptr;
   1.229 +
   1.230 +    callback->OnInputStreamReady(this);
   1.231 +  }
   1.232 +}
   1.233 +
   1.234 +NS_IMETHODIMP AndroidCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget *aTarget)
   1.235 +{
   1.236 +  if (aFlags != 0)
   1.237 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.238 +
   1.239 +  if (mCallback || mCallbackTarget)
   1.240 +    return NS_ERROR_UNEXPECTED;
   1.241 +
   1.242 +  mCallbackTarget = aTarget;
   1.243 +  mCallback = aCallback;
   1.244 +
   1.245 +  // What we are being asked for may be present already
   1.246 +  NotifyListeners();
   1.247 +  return NS_OK;
   1.248 +}
   1.249 +
   1.250 +
   1.251 +NS_IMETHODIMP AndroidCameraInputStream::CloseWithStatus(nsresult status)
   1.252 +{
   1.253 +  AndroidCameraInputStream::doClose();
   1.254 +  return NS_OK;
   1.255 +}
   1.256 +
   1.257 +/**
   1.258 + * AndroidCaptureProvider implementation
   1.259 + */
   1.260 +
   1.261 +NS_IMPL_ISUPPORTS0(AndroidCaptureProvider)
   1.262 +
   1.263 +AndroidCaptureProvider* AndroidCaptureProvider::sInstance = nullptr;
   1.264 +
   1.265 +AndroidCaptureProvider::AndroidCaptureProvider() {
   1.266 +}
   1.267 +
   1.268 +AndroidCaptureProvider::~AndroidCaptureProvider() {
   1.269 +  AndroidCaptureProvider::sInstance = nullptr;
   1.270 +}
   1.271 +
   1.272 +nsresult AndroidCaptureProvider::Init(nsACString& aContentType,
   1.273 +                        nsCaptureParams* aParams,
   1.274 +                        nsIInputStream** aStream) {
   1.275 +
   1.276 +  NS_ENSURE_ARG_POINTER(aParams);
   1.277 +
   1.278 +  NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0,
   1.279 +    "Cannot set both a frame limit and a time limit!");
   1.280 +
   1.281 +  nsRefPtr<AndroidCameraInputStream> stream;
   1.282 +
   1.283 +  if (aContentType.EqualsLiteral("video/x-raw-yuv")) {
   1.284 +    stream = new AndroidCameraInputStream();
   1.285 +    if (stream) {
   1.286 +      nsresult rv = stream->Init(aContentType, aParams);
   1.287 +      if (NS_FAILED(rv))
   1.288 +        return rv;
   1.289 +    }
   1.290 +    else
   1.291 +      return NS_ERROR_OUT_OF_MEMORY;
   1.292 +  } else {
   1.293 +    NS_NOTREACHED("Should not have asked Android for this type!");
   1.294 +  }
   1.295 +  return CallQueryInterface(stream, aStream);
   1.296 +}
   1.297 +
   1.298 +already_AddRefed<AndroidCaptureProvider> GetAndroidCaptureProvider() {
   1.299 +  if (!AndroidCaptureProvider::sInstance) {
   1.300 +    AndroidCaptureProvider::sInstance = new AndroidCaptureProvider();
   1.301 +  }
   1.302 +  nsRefPtr<AndroidCaptureProvider> ret = AndroidCaptureProvider::sInstance;
   1.303 +  return ret.forget();
   1.304 +}

mercurial