summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Dong <jdong@google.com>2011-02-28 21:07:39 -0800
committerJames Dong <jdong@google.com>2011-03-01 15:58:59 -0800
commit2a7e0a1eb29306982fd77bdc64d324464a48a2b9 (patch)
tree394731f4a2cfb40c76de91decce1520c0a705dac
parentc0bff9b96152179650dc9508799a38e35ef2a321 (diff)
downloadframeworks_av-2a7e0a1eb29306982fd77bdc64d324464a48a2b9.zip
frameworks_av-2a7e0a1eb29306982fd77bdc64d324464a48a2b9.tar.gz
frameworks_av-2a7e0a1eb29306982fd77bdc64d324464a48a2b9.tar.bz2
Get rid of redundant media profiles
bug - 3330679 Change-Id: Idc55aea32746c0c57552c5e15a289681421aa859
-rw-r--r--include/media/MediaProfiles.h71
-rw-r--r--media/libmedia/MediaProfiles.cpp178
2 files changed, 241 insertions, 8 deletions
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index aa97874..f2107ec 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -24,6 +24,7 @@
namespace android {
enum camcorder_quality {
+ CAMCORDER_QUALITY_LIST_START = 0,
CAMCORDER_QUALITY_LOW = 0,
CAMCORDER_QUALITY_HIGH = 1,
CAMCORDER_QUALITY_QCIF = 2,
@@ -31,14 +32,17 @@ enum camcorder_quality {
CAMCORDER_QUALITY_480P = 4,
CAMCORDER_QUALITY_720P = 5,
CAMCORDER_QUALITY_1080P = 6,
+ CAMCORDER_QUALITY_LIST_END = 6,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_HIGH = 1001,
CAMCORDER_QUALITY_TIME_LAPSE_QCIF = 1002,
CAMCORDER_QUALITY_TIME_LAPSE_CIF = 1003,
CAMCORDER_QUALITY_TIME_LAPSE_480P = 1004,
CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
- CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006
+ CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1006,
};
enum video_decoder {
@@ -147,6 +151,11 @@ public:
Vector<int> getImageEncodingQualityLevels(int cameraId) const;
private:
+ enum {
+ // Camcorder profiles (high/low) and timelapse profiles (high/low)
+ kNumRequiredProfiles = 4,
+ };
+
MediaProfiles& operator=(const MediaProfiles&); // Don't call me
MediaProfiles(const MediaProfiles&); // Don't call me
MediaProfiles() {} // Dummy default constructor
@@ -160,6 +169,14 @@ private:
mFrameHeight(frameHeight),
mFrameRate(frameRate) {}
+ VideoCodec(const VideoCodec& copy) {
+ mCodec = copy.mCodec;
+ mBitRate = copy.mBitRate;
+ mFrameWidth = copy.mFrameWidth;
+ mFrameHeight = copy.mFrameHeight;
+ mFrameRate = copy.mFrameRate;
+ }
+
~VideoCodec() {}
video_encoder mCodec;
@@ -176,6 +193,13 @@ private:
mSampleRate(sampleRate),
mChannels(channels) {}
+ AudioCodec(const AudioCodec& copy) {
+ mCodec = copy.mCodec;
+ mBitRate = copy.mBitRate;
+ mSampleRate = copy.mSampleRate;
+ mChannels = copy.mChannels;
+ }
+
~AudioCodec() {}
audio_encoder mCodec;
@@ -193,6 +217,15 @@ private:
mVideoCodec(0),
mAudioCodec(0) {}
+ CamcorderProfile(const CamcorderProfile& copy) {
+ mCameraId = copy.mCameraId;
+ mFileFormat = copy.mFileFormat;
+ mQuality = copy.mQuality;
+ mDuration = copy.mDuration;
+ mVideoCodec = new VideoCodec(*copy.mVideoCodec);
+ mAudioCodec = new AudioCodec(*copy.mAudioCodec);
+ }
+
~CamcorderProfile() {
delete mVideoCodec;
delete mAudioCodec;
@@ -272,6 +305,8 @@ private:
};
int getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const;
+ void initRequiredProfileRefs(const Vector<int>& cameraIds);
+ int getRequiredProfileRefIndex(int cameraId);
// Debug
static void logVideoCodec(const VideoCodec& codec);
@@ -291,7 +326,10 @@ private:
static VideoDecoderCap* createVideoDecoderCap(const char **atts);
static VideoEncoderCap* createVideoEncoderCap(const char **atts);
static AudioEncoderCap* createAudioEncoderCap(const char **atts);
- static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts);
+
+ static CamcorderProfile* createCamcorderProfile(
+ int cameraId, const char **atts, Vector<int>& cameraIds);
+
static int getCameraId(const char **atts);
ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const;
@@ -335,6 +373,21 @@ private:
static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name);
+ /**
+ * Check on existing profiles with the following criteria:
+ * 1. Low quality profile must have the lowest video
+ * resolution product (width x height)
+ * 2. High quality profile must have the highest video
+ * resolution product (width x height)
+ *
+ * and add required low/high quality camcorder/timelapse
+ * profiles if they are not found. This allows to remove
+ * duplicate profile definitions in the media_profiles.xml
+ * file.
+ */
+ void checkAndAddRequiredProfilesIfNecessary();
+
+
// Mappings from name (for instance, codec name) to enum value
static const NameToTagMap sVideoEncoderNameMap[];
static const NameToTagMap sAudioEncoderNameMap[];
@@ -355,6 +408,20 @@ private:
Vector<VideoDecoderCap*> mVideoDecoders;
Vector<output_format> mEncoderOutputFileFormats;
Vector<ImageEncodingQualityLevels *> mImageEncodingQualityLevels;
+
+ typedef struct {
+ bool mHasRefProfile; // Refers to an existing profile
+ int mRefProfileIndex; // Reference profile index
+ int mResolutionProduct; // width x height
+ } RequiredProfileRefInfo; // Required low and high profiles
+
+ typedef struct {
+ RequiredProfileRefInfo mRefs[kNumRequiredProfiles];
+ int mCameraId;
+ } RequiredProfiles;
+
+ RequiredProfiles *mRequiredProfileRefs;
+ Vector<int> mCameraIds;
};
}; // namespace android
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 9ad63f0..7fb7aed 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -284,8 +284,17 @@ MediaProfiles::createEncoderOutputFileFormat(const char **atts)
return static_cast<output_format>(format);
}
+static bool isCameraIdFound(int cameraId, const Vector<int>& cameraIds) {
+ for (int i = 0, n = cameraIds.size(); i < n; ++i) {
+ if (cameraId == cameraIds[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
+MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<int>& cameraIds)
{
CHECK(!strcmp("quality", atts[0]) &&
!strcmp("fileFormat", atts[2]) &&
@@ -301,6 +310,9 @@ MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
profile->mCameraId = cameraId;
+ if (!isCameraIdFound(cameraId, cameraIds)) {
+ cameraIds.add(cameraId);
+ }
profile->mFileFormat = static_cast<output_format>(fileFormat);
profile->mQuality = static_cast<camcorder_quality>(quality);
profile->mDuration = atoi(atts[5]);
@@ -370,12 +382,167 @@ MediaProfiles::startElementHandler(void *userData, const char *name, const char
profiles->mCurrentCameraId = getCameraId(atts);
} else if (strcmp("EncoderProfile", name) == 0) {
profiles->mCamcorderProfiles.add(
- createCamcorderProfile(profiles->mCurrentCameraId, atts));
+ createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds));
} else if (strcmp("ImageEncoding", name) == 0) {
profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
}
}
+static bool isCamcorderProfile(camcorder_quality quality) {
+ return quality >= CAMCORDER_QUALITY_LIST_START &&
+ quality <= CAMCORDER_QUALITY_LIST_END;
+}
+
+static bool isTimelapseProfile(camcorder_quality quality) {
+ return quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START &&
+ quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END;
+}
+
+void MediaProfiles::initRequiredProfileRefs(const Vector<int>& cameraIds) {
+ LOGV("Number of camera ids: %d", cameraIds.size());
+ CHECK(cameraIds.size() > 0);
+ mRequiredProfileRefs = new RequiredProfiles[cameraIds.size()];
+ for (size_t i = 0, n = cameraIds.size(); i < n; ++i) {
+ mRequiredProfileRefs[i].mCameraId = cameraIds[i];
+ for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+ mRequiredProfileRefs[i].mRefs[j].mHasRefProfile = false;
+ mRequiredProfileRefs[i].mRefs[j].mRefProfileIndex = -1;
+ if ((j & 1) == 0) { // low resolution
+ mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0x7FFFFFFF;
+ } else { // high resolution
+ mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0;
+ }
+ }
+ }
+}
+
+int MediaProfiles::getRequiredProfileRefIndex(int cameraId) {
+ for (size_t i = 0, n = mCameraIds.size(); i < n; ++i) {
+ if (mCameraIds[i] == cameraId) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void MediaProfiles::checkAndAddRequiredProfilesIfNecessary() {
+ if (sIsInitialized) {
+ return;
+ }
+
+ initRequiredProfileRefs(mCameraIds);
+
+ for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
+ int product = mCamcorderProfiles[i]->mVideoCodec->mFrameWidth *
+ mCamcorderProfiles[i]->mVideoCodec->mFrameHeight;
+
+ camcorder_quality quality = mCamcorderProfiles[i]->mQuality;
+ int cameraId = mCamcorderProfiles[i]->mCameraId;
+ int index = -1;
+ int refIndex = getRequiredProfileRefIndex(cameraId);
+ CHECK(refIndex != -1);
+ RequiredProfileRefInfo *info;
+ camcorder_quality refQuality;
+ VideoCodec *codec = NULL;
+
+ // Check high and low from either camcorder profile or timelapse profile
+ // but not both. Default, check camcorder profile
+ size_t j = 0;
+ size_t n = 2;
+ if (isTimelapseProfile(quality)) {
+ // Check timelapse profile instead.
+ j = 2;
+ n = kNumRequiredProfiles;
+ } else {
+ // Must be camcorder profile.
+ CHECK(isCamcorderProfile(quality));
+ }
+ for (; j < n; ++j) {
+ info = &(mRequiredProfileRefs[refIndex].mRefs[j]);
+ if ((j % 2 == 0 && product > info->mResolutionProduct) || // low
+ (j % 2 != 0 && product < info->mResolutionProduct)) { // high
+ continue;
+ }
+ switch (j) {
+ case 0:
+ refQuality = CAMCORDER_QUALITY_LOW;
+ break;
+ case 1:
+ refQuality = CAMCORDER_QUALITY_HIGH;
+ break;
+ case 2:
+ refQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+ break;
+ case 3:
+ refQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+ break;
+ default:
+ CHECK(!"Should never reach here");
+ }
+
+ if (!info->mHasRefProfile) {
+ index = getCamcorderProfileIndex(cameraId, refQuality);
+ }
+ if (index == -1) {
+ // New high or low quality profile is found.
+ // Update its reference.
+ info->mHasRefProfile = true;
+ info->mRefProfileIndex = i;
+ info->mResolutionProduct = product;
+ }
+ }
+ }
+
+ for (size_t cameraId = 0; cameraId < mCameraIds.size(); ++cameraId) {
+ for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+ int refIndex = getRequiredProfileRefIndex(cameraId);
+ CHECK(refIndex != -1);
+ RequiredProfileRefInfo *info =
+ &mRequiredProfileRefs[refIndex].mRefs[j];
+
+ if (info->mHasRefProfile) {
+
+ CamcorderProfile *profile =
+ new CamcorderProfile(
+ *mCamcorderProfiles[info->mRefProfileIndex]);
+
+ // Overwrite the quality
+ switch (j % kNumRequiredProfiles) {
+ case 0:
+ profile->mQuality = CAMCORDER_QUALITY_LOW;
+ break;
+ case 1:
+ profile->mQuality = CAMCORDER_QUALITY_HIGH;
+ break;
+ case 2:
+ profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+ break;
+ case 3:
+ profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+ break;
+ default:
+ CHECK(!"Should never come here");
+ }
+
+ int index = getCamcorderProfileIndex(cameraId, profile->mQuality);
+ if (index != -1) {
+ LOGV("Profile quality %d for camera %d already exists",
+ profile->mQuality, cameraId);
+ CHECK(index == refIndex);
+ continue;
+ }
+
+ // Insert the new profile
+ LOGV("Add a profile: quality %d=>%d for camera %d",
+ mCamcorderProfiles[info->mRefProfileIndex]->mQuality,
+ profile->mQuality, cameraId);
+
+ mCamcorderProfiles.add(profile);
+ }
+ }
+ }
+}
+
/*static*/ MediaProfiles*
MediaProfiles::getInstance()
{
@@ -396,6 +563,9 @@ MediaProfiles::getInstance()
} else {
sInstance = createInstanceFromXmlFile(value);
}
+ CHECK(sInstance != NULL);
+ sInstance->checkAndAddRequiredProfilesIfNecessary();
+ sIsInitialized = true;
}
return sInstance;
@@ -613,7 +783,6 @@ MediaProfiles::createDefaultInstance()
createDefaultAudioDecoders(profiles);
createDefaultEncoderOutputFileFormats(profiles);
createDefaultImageEncodingQualityLevels(profiles);
- sIsInitialized = true;
return profiles;
}
@@ -667,9 +836,6 @@ MediaProfiles::createInstanceFromXmlFile(const char *xml)
exit:
::XML_ParserFree(parser);
::fclose(fp);
- if (profiles) {
- sIsInitialized = true;
- }
return profiles;
}