From 2a7e0a1eb29306982fd77bdc64d324464a48a2b9 Mon Sep 17 00:00:00 2001 From: James Dong Date: Mon, 28 Feb 2011 21:07:39 -0800 Subject: Get rid of redundant media profiles bug - 3330679 Change-Id: Idc55aea32746c0c57552c5e15a289681421aa859 --- include/media/MediaProfiles.h | 71 +++++++++++++++- media/libmedia/MediaProfiles.cpp | 178 +++++++++++++++++++++++++++++++++++++-- 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 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& 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& 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 mVideoDecoders; Vector mEncoderOutputFileFormats; Vector 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 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(format); } +static bool isCameraIdFound(int cameraId, const Vector& 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& 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(fileFormat); profile->mQuality = static_cast(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& 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; } -- cgit v1.1