dom/camera/GonkRecorderProfiles.cpp
author Nathan Froyd <froydnj@mozilla.com>
Wed, 07 Oct 2015 16:50:25 -0400
changeset 266617 91d4539e00cecb658604e021675a923c60ef3235
parent 233147 1a02c68523da83ac02855e0c7e6fb669aabb3330
child 266639 41dea9df27ed995f8315ab4318c187a617937664
permissions -rw-r--r--
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi

/*
 * Copyright (C) 2012-2014 Mozilla Foundation
 *
 * 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 "GonkRecorderProfiles.h"
#include "nsMimeTypes.h"
#include "CameraControlImpl.h"
#include "CameraCommon.h"

#ifdef MOZ_WIDGET_GONK
#include "GonkRecorder.h"
#endif

using namespace mozilla;
using namespace android;

namespace mozilla {

struct ProfileConfig {
  const char* name;
  int quality;
};

#define DEF_GONK_RECORDER_PROFILE(e, n) { n, e },
static const ProfileConfig ProfileList[] = {
  #include "GonkRecorderProfiles.def"
};

static const size_t ProfileListSize = MOZ_ARRAY_LENGTH(ProfileList);

struct ProfileConfigDetect {
  const char* name;
  uint32_t width;
  uint32_t height;
};

#define DEF_GONK_RECORDER_PROFILE_DETECT(n, w, h) { n, w, h },
static const ProfileConfigDetect ProfileListDetect[] = {
  #include "GonkRecorderProfiles.def"
};

static const size_t ProfileListDetectSize = MOZ_ARRAY_LENGTH(ProfileListDetect);

};

/* static */ nsClassHashtable<nsUint32HashKey, ProfileHashtable> GonkRecorderProfile::sProfiles;
/* static */ android::MediaProfiles* sMediaProfiles = nullptr;

static MediaProfiles*
GetMediaProfiles()
{
  if (!sMediaProfiles) {
    sMediaProfiles = MediaProfiles::getInstance();
  }
  MOZ_ASSERT(sMediaProfiles);
  return sMediaProfiles;
}

static bool
IsProfileSupported(uint32_t aCameraId, int aQuality)
{
  MediaProfiles* profiles = GetMediaProfiles();
  return profiles->hasCamcorderProfile(static_cast<int>(aCameraId),
                                       static_cast<camcorder_quality>(aQuality));
}

static int
GetProfileParameter(uint32_t aCameraId, int aQuality, const char* aParameter)
{
  MediaProfiles* profiles = GetMediaProfiles();
  return profiles->getCamcorderProfileParamByName(aParameter, static_cast<int>(aCameraId),
                                                  static_cast<camcorder_quality>(aQuality));
}

/* static */ bool
GonkRecorderVideo::Translate(video_encoder aCodec, nsAString& aCodecName)
{
  switch (aCodec) {
    case VIDEO_ENCODER_H263:
      aCodecName.AssignASCII("h263");
      break;

    case VIDEO_ENCODER_H264:
      aCodecName.AssignASCII("h264");
      break;

    case VIDEO_ENCODER_MPEG_4_SP:
      aCodecName.AssignASCII("mpeg4sp");
      break;

    default:
      return false;
  }

  return true;
}

int
GonkRecorderVideo::GetProfileParameter(const char* aParameter)
{
  return ::GetProfileParameter(mCameraId, mQuality, aParameter);
}

GonkRecorderVideo::GonkRecorderVideo(uint32_t aCameraId, int aQuality)
  : mCameraId(aCameraId)
  , mQuality(aQuality)
  , mIsValid(false)
{
  mPlatformEncoder = static_cast<video_encoder>(GetProfileParameter("vid.codec"));
  bool isValid = Translate(mPlatformEncoder, mCodec);

  int v = GetProfileParameter("vid.width");
  if (v >= 0) {
    mSize.width = v;
  } else {
    isValid = false;
  }
  v = GetProfileParameter("vid.height");
  if (v >= 0) {
    mSize.height = v;
  } else {
    isValid = false;
  }
  v = GetProfileParameter("vid.bps");
  if (v >= 0) {
    mBitsPerSecond = v;
  } else {
    isValid = false;
  }
  v = GetProfileParameter("vid.fps");
  if (v >= 0) {
    mFramesPerSecond = v;
  } else {
    isValid = false;
  }

  mIsValid = isValid;
}

/* static */ bool
GonkRecorderAudio::Translate(audio_encoder aCodec, nsAString& aCodecName)
{
  switch (aCodec) {
    case AUDIO_ENCODER_AMR_NB:
      aCodecName.AssignASCII("amrnb");
      break;

    case AUDIO_ENCODER_AMR_WB:
      aCodecName.AssignASCII("amrwb");
      break;

    case AUDIO_ENCODER_AAC:
      aCodecName.AssignASCII("aac");
      break;

    default:
      return false;
  }

  return true;
}

int
GonkRecorderAudio::GetProfileParameter(const char* aParameter)
{
  return ::GetProfileParameter(mCameraId, mQuality, aParameter);
}

GonkRecorderAudio::GonkRecorderAudio(uint32_t aCameraId, int aQuality)
  : mCameraId(aCameraId)
  , mQuality(aQuality)
  , mIsValid(false)
{
  mPlatformEncoder = static_cast<audio_encoder>(GetProfileParameter("aud.codec"));
  bool isValid = Translate(mPlatformEncoder, mCodec);

  int v = GetProfileParameter("aud.ch");
  if (v >= 0) {
    mChannels = v;
  } else {
    isValid = false;
  }
  v = GetProfileParameter("aud.bps");
  if (v >= 0) {
    mBitsPerSecond = v;
  } else {
    isValid = false;
  }
  v = GetProfileParameter("aud.hz");
  if (v >= 0) {
    mSamplesPerSecond = v;
  } else {
    isValid = false;
  }

  mIsValid = isValid;
}

/* static */ bool
GonkRecorderProfile::Translate(output_format aContainer, nsAString& aContainerName)
{
  switch (aContainer) {
    case OUTPUT_FORMAT_THREE_GPP:
      aContainerName.AssignASCII("3gp");
      break;

    case OUTPUT_FORMAT_MPEG_4:
      aContainerName.AssignASCII("mp4");
      break;

    default:
      return false;
  }

  return true;
}

/* static */ bool
GonkRecorderProfile::GetMimeType(output_format aContainer, nsAString& aMimeType)
{
  switch (aContainer) {
    case OUTPUT_FORMAT_THREE_GPP:
      aMimeType.AssignASCII(VIDEO_3GPP);
      break;

    case OUTPUT_FORMAT_MPEG_4:
      aMimeType.AssignASCII(VIDEO_MP4);
      break;

    default:
      return false;
  }

  return true;
}

int
GonkRecorderProfile::GetProfileParameter(const char* aParameter)
{
  return ::GetProfileParameter(mCameraId, mQuality, aParameter);
}

GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId,
                                         int aQuality)
  : GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>(aCameraId,
                                                                  aQuality)
  , mCameraId(aCameraId)
  , mQuality(aQuality)
  , mIsValid(false)
{
  mOutputFormat = static_cast<output_format>(GetProfileParameter("file.format"));
  bool isValid = Translate(mOutputFormat, mContainer);
  isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false;

  mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid();
}

/* static */ PLDHashOperator
GonkRecorderProfile::Enumerate(const nsAString& aProfileName,
                               GonkRecorderProfile* aProfile,
                               void* aUserArg)
{
  nsTArray<RefPtr<ICameraControl::RecorderProfile>>* profiles =
    static_cast<nsTArray<RefPtr<ICameraControl::RecorderProfile>>*>(aUserArg);
  MOZ_ASSERT(profiles);
  profiles->AppendElement(aProfile);
  return PL_DHASH_NEXT;
}

/* static */
already_AddRefed<GonkRecorderProfile>
GonkRecorderProfile::CreateProfile(uint32_t aCameraId, int aQuality)
{
  if (!IsProfileSupported(aCameraId, aQuality)) {
    DOM_CAMERA_LOGI("Profile %d not supported by platform\n", aQuality);
    return nullptr;
  }

  RefPtr<GonkRecorderProfile> profile = new GonkRecorderProfile(aCameraId, aQuality);
  if (!profile->IsValid()) {
    DOM_CAMERA_LOGE("Profile %d is not valid\n", aQuality);
    return nullptr;
  }

  return profile.forget();
}

/* static */
ProfileHashtable*
GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId)
{
  ProfileHashtable* profiles = sProfiles.Get(aCameraId);
  if (!profiles) {
    profiles = new ProfileHashtable();
    sProfiles.Put(aCameraId, profiles);

    /* First handle the profiles with a known enum. We can process those
       efficently because MediaProfiles indexes their profiles that way. */
    int highestKnownQuality = CAMCORDER_QUALITY_LIST_START - 1;
    for (size_t i = 0; i < ProfileListSize; ++i) {
      const ProfileConfig& p = ProfileList[i];
      if (p.quality > highestKnownQuality) {
        highestKnownQuality = p.quality;
      }

      RefPtr<GonkRecorderProfile> profile = CreateProfile(aCameraId, p.quality);
      if (!profile) {
        continue;
      }

      DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", p.quality, p.name);
      profile->mName.AssignASCII(p.name);
      profiles->Put(profile->GetName(), profile);
    }

    /* However not all of the potentially supported profiles have a known
       enum on all of our supported platforms because some entries may
       be missing from MediaProfiles.h. As such, we can't rely upon
       having the CAMCORDER_QUALITY_* enums for those profiles. We need
       to map the profiles to a name by matching the width and height of
       the video resolution to our configured values.

       In theory there may be collisions given that there can be multiple
       resolutions sharing the same name (e.g. 800x480 and 768x480 are both
       wvga). In practice this should not happen because there should be
       only one WVGA profile given there is only one enum for it. In the
       situation there is a collision, it will merely select the last
       detected profile. */
    for (int q = highestKnownQuality + 1; q <= CAMCORDER_QUALITY_LIST_END; ++q) {
      RefPtr<GonkRecorderProfile> profile = CreateProfile(aCameraId, q);
      if (!profile) {
        continue;
      }

      const ICameraControl::Size& s = profile->GetVideo().GetSize();
      size_t match;
      for (match = 0; match < ProfileListDetectSize; ++match) {
        const ProfileConfigDetect& p = ProfileListDetect[match];
        if (s.width == p.width && s.height == p.height) {
          DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", q, p.name);
          profile->mName.AssignASCII(p.name);
          profiles->Put(profile->GetName(), profile);
          break;
        }
      }

      if (match == ProfileListDetectSize) {
        DOM_CAMERA_LOGW("Profile %d size %u x %u is not recognized\n",
                        q, s.width, s.height);
      }
    }
  }
  return profiles;
}

/* static */ nsresult
GonkRecorderProfile::GetAll(uint32_t aCameraId,
                            nsTArray<RefPtr<ICameraControl::RecorderProfile>>& aProfiles)
{
  ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
  if (!profiles) {
    return NS_ERROR_FAILURE;
  }

  aProfiles.Clear();
  profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));

  return NS_OK;
}

#ifdef MOZ_WIDGET_GONK
nsresult
GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
{
  static const size_t SIZE = 256;
  char buffer[SIZE];

  // set all the params
  CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
  CHECK_SETARG(aRecorder.setVideoSource(VIDEO_SOURCE_CAMERA));
  CHECK_SETARG(aRecorder.setOutputFormat(mOutputFormat));
  CHECK_SETARG(aRecorder.setVideoFrameRate(mVideo.GetFramesPerSecond()));
  CHECK_SETARG(aRecorder.setVideoSize(mVideo.GetSize().width, mVideo.GetSize().height));
  CHECK_SETARG(aRecorder.setVideoEncoder(mVideo.GetPlatformEncoder()));
  CHECK_SETARG(aRecorder.setAudioEncoder(mAudio.GetPlatformEncoder()));

  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitsPerSecond());
  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));

  snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitsPerSecond());
  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));

  snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels());
  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));

  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond());
  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));

  return NS_OK;
}

/* static */ nsresult
GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder,
                                       uint32_t aCameraId,
                                       const nsAString& aProfileName)
{
  ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
  if (!profiles) {
    return NS_ERROR_FAILURE;
  }

  GonkRecorderProfile* profile;
  if (!profiles->Get(aProfileName, &profile)) {
    return NS_ERROR_INVALID_ARG;
  }

  return profile->ConfigureRecorder(aRecorder);
}
#endif