MediaProfiles.cpp revision 2a7e0a1eb29306982fd77bdc64d324464a48a2b9
1/*
2**
3** Copyright 2010, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "MediaProfiles"
21
22#include <stdlib.h>
23#include <utils/Log.h>
24#include <utils/Vector.h>
25#include <cutils/properties.h>
26#include <expat.h>
27#include <media/MediaProfiles.h>
28#include <media/stagefright/MediaDebug.h>
29
30namespace android {
31
32Mutex MediaProfiles::sLock;
33bool MediaProfiles::sIsInitialized = false;
34MediaProfiles *MediaProfiles::sInstance = NULL;
35
36const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = {
37    {"h263", VIDEO_ENCODER_H263},
38    {"h264", VIDEO_ENCODER_H264},
39    {"m4v",  VIDEO_ENCODER_MPEG_4_SP}
40};
41
42const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = {
43    {"amrnb", AUDIO_ENCODER_AMR_NB},
44    {"amrwb", AUDIO_ENCODER_AMR_WB},
45    {"aac",   AUDIO_ENCODER_AAC},
46};
47
48const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = {
49    {"3gp", OUTPUT_FORMAT_THREE_GPP},
50    {"mp4", OUTPUT_FORMAT_MPEG_4}
51};
52
53const MediaProfiles::NameToTagMap MediaProfiles::sVideoDecoderNameMap[] = {
54    {"wmv", VIDEO_DECODER_WMV}
55};
56
57const MediaProfiles::NameToTagMap MediaProfiles::sAudioDecoderNameMap[] = {
58    {"wma", AUDIO_DECODER_WMA}
59};
60
61const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = {
62    {"low", CAMCORDER_QUALITY_LOW},
63    {"high", CAMCORDER_QUALITY_HIGH},
64    {"qcif", CAMCORDER_QUALITY_QCIF},
65    {"cif", CAMCORDER_QUALITY_CIF},
66    {"480p", CAMCORDER_QUALITY_480P},
67    {"720p", CAMCORDER_QUALITY_720P},
68    {"1080p", CAMCORDER_QUALITY_1080P},
69
70    {"timelapselow",  CAMCORDER_QUALITY_TIME_LAPSE_LOW},
71    {"timelapsehigh", CAMCORDER_QUALITY_TIME_LAPSE_HIGH},
72    {"timelapseqcif", CAMCORDER_QUALITY_TIME_LAPSE_QCIF},
73    {"timelapsecif", CAMCORDER_QUALITY_TIME_LAPSE_CIF},
74    {"timelapse480p", CAMCORDER_QUALITY_TIME_LAPSE_480P},
75    {"timelapse720p", CAMCORDER_QUALITY_TIME_LAPSE_720P},
76    {"timelapse1080p", CAMCORDER_QUALITY_TIME_LAPSE_1080P}
77};
78
79/*static*/ void
80MediaProfiles::logVideoCodec(const MediaProfiles::VideoCodec& codec)
81{
82    LOGV("video codec:");
83    LOGV("codec = %d", codec.mCodec);
84    LOGV("bit rate: %d", codec.mBitRate);
85    LOGV("frame width: %d", codec.mFrameWidth);
86    LOGV("frame height: %d", codec.mFrameHeight);
87    LOGV("frame rate: %d", codec.mFrameRate);
88}
89
90/*static*/ void
91MediaProfiles::logAudioCodec(const MediaProfiles::AudioCodec& codec)
92{
93    LOGV("audio codec:");
94    LOGV("codec = %d", codec.mCodec);
95    LOGV("bit rate: %d", codec.mBitRate);
96    LOGV("sample rate: %d", codec.mSampleRate);
97    LOGV("number of channels: %d", codec.mChannels);
98}
99
100/*static*/ void
101MediaProfiles::logVideoEncoderCap(const MediaProfiles::VideoEncoderCap& cap)
102{
103    LOGV("video encoder cap:");
104    LOGV("codec = %d", cap.mCodec);
105    LOGV("bit rate: min = %d and max = %d", cap.mMinBitRate, cap.mMaxBitRate);
106    LOGV("frame width: min = %d and max = %d", cap.mMinFrameWidth, cap.mMaxFrameWidth);
107    LOGV("frame height: min = %d and max = %d", cap.mMinFrameHeight, cap.mMaxFrameHeight);
108    LOGV("frame rate: min = %d and max = %d", cap.mMinFrameRate, cap.mMaxFrameRate);
109}
110
111/*static*/ void
112MediaProfiles::logAudioEncoderCap(const MediaProfiles::AudioEncoderCap& cap)
113{
114    LOGV("audio encoder cap:");
115    LOGV("codec = %d", cap.mCodec);
116    LOGV("bit rate: min = %d and max = %d", cap.mMinBitRate, cap.mMaxBitRate);
117    LOGV("sample rate: min = %d and max = %d", cap.mMinSampleRate, cap.mMaxSampleRate);
118    LOGV("number of channels: min = %d and max = %d", cap.mMinChannels, cap.mMaxChannels);
119}
120
121/*static*/ void
122MediaProfiles::logVideoDecoderCap(const MediaProfiles::VideoDecoderCap& cap)
123{
124    LOGV("video decoder cap:");
125    LOGV("codec = %d", cap.mCodec);
126}
127
128/*static*/ void
129MediaProfiles::logAudioDecoderCap(const MediaProfiles::AudioDecoderCap& cap)
130{
131    LOGV("audio codec cap:");
132    LOGV("codec = %d", cap.mCodec);
133}
134
135/*static*/ int
136MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMappings, const char *name)
137{
138    int tag = -1;
139    for (size_t i = 0; i < nMappings; ++i) {
140        if (!strcmp(map[i].name, name)) {
141            tag = map[i].tag;
142            break;
143        }
144    }
145    return tag;
146}
147
148/*static*/ MediaProfiles::VideoCodec*
149MediaProfiles::createVideoCodec(const char **atts, MediaProfiles *profiles)
150{
151    CHECK(!strcmp("codec",     atts[0]) &&
152          !strcmp("bitRate",   atts[2]) &&
153          !strcmp("width",     atts[4]) &&
154          !strcmp("height",    atts[6]) &&
155          !strcmp("frameRate", atts[8]));
156
157    const size_t nMappings = sizeof(sVideoEncoderNameMap)/sizeof(sVideoEncoderNameMap[0]);
158    const int codec = findTagForName(sVideoEncoderNameMap, nMappings, atts[1]);
159    CHECK(codec != -1);
160
161    MediaProfiles::VideoCodec *videoCodec =
162        new MediaProfiles::VideoCodec(static_cast<video_encoder>(codec),
163            atoi(atts[3]), atoi(atts[5]), atoi(atts[7]), atoi(atts[9]));
164    logVideoCodec(*videoCodec);
165
166    size_t nCamcorderProfiles;
167    CHECK((nCamcorderProfiles = profiles->mCamcorderProfiles.size()) >= 1);
168    profiles->mCamcorderProfiles[nCamcorderProfiles - 1]->mVideoCodec = videoCodec;
169    return videoCodec;
170}
171
172/*static*/ MediaProfiles::AudioCodec*
173MediaProfiles::createAudioCodec(const char **atts, MediaProfiles *profiles)
174{
175    CHECK(!strcmp("codec",      atts[0]) &&
176          !strcmp("bitRate",    atts[2]) &&
177          !strcmp("sampleRate", atts[4]) &&
178          !strcmp("channels",   atts[6]));
179    const size_t nMappings = sizeof(sAudioEncoderNameMap)/sizeof(sAudioEncoderNameMap[0]);
180    const int codec = findTagForName(sAudioEncoderNameMap, nMappings, atts[1]);
181    CHECK(codec != -1);
182
183    MediaProfiles::AudioCodec *audioCodec =
184        new MediaProfiles::AudioCodec(static_cast<audio_encoder>(codec),
185            atoi(atts[3]), atoi(atts[5]), atoi(atts[7]));
186    logAudioCodec(*audioCodec);
187
188    size_t nCamcorderProfiles;
189    CHECK((nCamcorderProfiles = profiles->mCamcorderProfiles.size()) >= 1);
190    profiles->mCamcorderProfiles[nCamcorderProfiles - 1]->mAudioCodec = audioCodec;
191    return audioCodec;
192}
193/*static*/ MediaProfiles::AudioDecoderCap*
194MediaProfiles::createAudioDecoderCap(const char **atts)
195{
196    CHECK(!strcmp("name",    atts[0]) &&
197          !strcmp("enabled", atts[2]));
198
199    const size_t nMappings = sizeof(sAudioDecoderNameMap)/sizeof(sAudioDecoderNameMap[0]);
200    const int codec = findTagForName(sAudioDecoderNameMap, nMappings, atts[1]);
201    CHECK(codec != -1);
202
203    MediaProfiles::AudioDecoderCap *cap =
204        new MediaProfiles::AudioDecoderCap(static_cast<audio_decoder>(codec));
205    logAudioDecoderCap(*cap);
206    return cap;
207}
208
209/*static*/ MediaProfiles::VideoDecoderCap*
210MediaProfiles::createVideoDecoderCap(const char **atts)
211{
212    CHECK(!strcmp("name",    atts[0]) &&
213          !strcmp("enabled", atts[2]));
214
215    const size_t nMappings = sizeof(sVideoDecoderNameMap)/sizeof(sVideoDecoderNameMap[0]);
216    const int codec = findTagForName(sVideoDecoderNameMap, nMappings, atts[1]);
217    CHECK(codec != -1);
218
219    MediaProfiles::VideoDecoderCap *cap =
220        new MediaProfiles::VideoDecoderCap(static_cast<video_decoder>(codec));
221    logVideoDecoderCap(*cap);
222    return cap;
223}
224
225/*static*/ MediaProfiles::VideoEncoderCap*
226MediaProfiles::createVideoEncoderCap(const char **atts)
227{
228    CHECK(!strcmp("name",           atts[0])  &&
229          !strcmp("enabled",        atts[2])  &&
230          !strcmp("minBitRate",     atts[4])  &&
231          !strcmp("maxBitRate",     atts[6])  &&
232          !strcmp("minFrameWidth",  atts[8])  &&
233          !strcmp("maxFrameWidth",  atts[10]) &&
234          !strcmp("minFrameHeight", atts[12]) &&
235          !strcmp("maxFrameHeight", atts[14]) &&
236          !strcmp("minFrameRate",   atts[16]) &&
237          !strcmp("maxFrameRate",   atts[18]));
238
239    const size_t nMappings = sizeof(sVideoEncoderNameMap)/sizeof(sVideoEncoderNameMap[0]);
240    const int codec = findTagForName(sVideoEncoderNameMap, nMappings, atts[1]);
241    CHECK(codec != -1);
242
243    MediaProfiles::VideoEncoderCap *cap =
244        new MediaProfiles::VideoEncoderCap(static_cast<video_encoder>(codec),
245            atoi(atts[5]), atoi(atts[7]), atoi(atts[9]), atoi(atts[11]), atoi(atts[13]),
246            atoi(atts[15]), atoi(atts[17]), atoi(atts[19]));
247    logVideoEncoderCap(*cap);
248    return cap;
249}
250
251/*static*/ MediaProfiles::AudioEncoderCap*
252MediaProfiles::createAudioEncoderCap(const char **atts)
253{
254    CHECK(!strcmp("name",          atts[0])  &&
255          !strcmp("enabled",       atts[2])  &&
256          !strcmp("minBitRate",    atts[4])  &&
257          !strcmp("maxBitRate",    atts[6])  &&
258          !strcmp("minSampleRate", atts[8])  &&
259          !strcmp("maxSampleRate", atts[10]) &&
260          !strcmp("minChannels",   atts[12]) &&
261          !strcmp("maxChannels",   atts[14]));
262
263    const size_t nMappings = sizeof(sAudioEncoderNameMap)/sizeof(sAudioEncoderNameMap[0]);
264    const int codec = findTagForName(sAudioEncoderNameMap, nMappings, atts[1]);
265    CHECK(codec != -1);
266
267    MediaProfiles::AudioEncoderCap *cap =
268        new MediaProfiles::AudioEncoderCap(static_cast<audio_encoder>(codec), atoi(atts[5]), atoi(atts[7]),
269            atoi(atts[9]), atoi(atts[11]), atoi(atts[13]),
270            atoi(atts[15]));
271    logAudioEncoderCap(*cap);
272    return cap;
273}
274
275/*static*/ output_format
276MediaProfiles::createEncoderOutputFileFormat(const char **atts)
277{
278    CHECK(!strcmp("name", atts[0]));
279
280    const size_t nMappings =sizeof(sFileFormatMap)/sizeof(sFileFormatMap[0]);
281    const int format = findTagForName(sFileFormatMap, nMappings, atts[1]);
282    CHECK(format != -1);
283
284    return static_cast<output_format>(format);
285}
286
287static bool isCameraIdFound(int cameraId, const Vector<int>& cameraIds) {
288    for (int i = 0, n = cameraIds.size(); i < n; ++i) {
289        if (cameraId == cameraIds[i]) {
290            return true;
291        }
292    }
293    return false;
294}
295
296/*static*/ MediaProfiles::CamcorderProfile*
297MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<int>& cameraIds)
298{
299    CHECK(!strcmp("quality",    atts[0]) &&
300          !strcmp("fileFormat", atts[2]) &&
301          !strcmp("duration",   atts[4]));
302
303    const size_t nProfileMappings = sizeof(sCamcorderQualityNameMap)/sizeof(sCamcorderQualityNameMap[0]);
304    const int quality = findTagForName(sCamcorderQualityNameMap, nProfileMappings, atts[1]);
305    CHECK(quality != -1);
306
307    const size_t nFormatMappings = sizeof(sFileFormatMap)/sizeof(sFileFormatMap[0]);
308    const int fileFormat = findTagForName(sFileFormatMap, nFormatMappings, atts[3]);
309    CHECK(fileFormat != -1);
310
311    MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
312    profile->mCameraId = cameraId;
313    if (!isCameraIdFound(cameraId, cameraIds)) {
314        cameraIds.add(cameraId);
315    }
316    profile->mFileFormat = static_cast<output_format>(fileFormat);
317    profile->mQuality = static_cast<camcorder_quality>(quality);
318    profile->mDuration = atoi(atts[5]);
319    return profile;
320}
321
322MediaProfiles::ImageEncodingQualityLevels*
323MediaProfiles::findImageEncodingQualityLevels(int cameraId) const
324{
325    int n = mImageEncodingQualityLevels.size();
326    for (int i = 0; i < n; i++) {
327        ImageEncodingQualityLevels *levels = mImageEncodingQualityLevels[i];
328        if (levels->mCameraId == cameraId) {
329            return levels;
330        }
331    }
332    return NULL;
333}
334
335void MediaProfiles::addImageEncodingQualityLevel(int cameraId, const char** atts)
336{
337    CHECK(!strcmp("quality", atts[0]));
338    int quality = atoi(atts[1]);
339    LOGV("%s: cameraId=%d, quality=%d\n", __func__, cameraId, quality);
340    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
341
342    if (levels == NULL) {
343        levels = new ImageEncodingQualityLevels();
344        levels->mCameraId = cameraId;
345        mImageEncodingQualityLevels.add(levels);
346    }
347
348    levels->mLevels.add(quality);
349}
350
351/*static*/ int
352MediaProfiles::getCameraId(const char** atts)
353{
354    if (!atts[0]) return 0;  // default cameraId = 0
355    CHECK(!strcmp("cameraId", atts[0]));
356    return atoi(atts[1]);
357}
358
359/*static*/ void
360MediaProfiles::startElementHandler(void *userData, const char *name, const char **atts)
361{
362    MediaProfiles *profiles = (MediaProfiles *) userData;
363    if (strcmp("Video", name) == 0) {
364        createVideoCodec(atts, profiles);
365    } else if (strcmp("Audio", name) == 0) {
366        createAudioCodec(atts, profiles);
367    } else if (strcmp("VideoEncoderCap", name) == 0 &&
368               strcmp("true", atts[3]) == 0) {
369        profiles->mVideoEncoders.add(createVideoEncoderCap(atts));
370    } else if (strcmp("AudioEncoderCap", name) == 0 &&
371               strcmp("true", atts[3]) == 0) {
372        profiles->mAudioEncoders.add(createAudioEncoderCap(atts));
373    } else if (strcmp("VideoDecoderCap", name) == 0 &&
374               strcmp("true", atts[3]) == 0) {
375        profiles->mVideoDecoders.add(createVideoDecoderCap(atts));
376    } else if (strcmp("AudioDecoderCap", name) == 0 &&
377               strcmp("true", atts[3]) == 0) {
378        profiles->mAudioDecoders.add(createAudioDecoderCap(atts));
379    } else if (strcmp("EncoderOutputFileFormat", name) == 0) {
380        profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts));
381    } else if (strcmp("CamcorderProfiles", name) == 0) {
382        profiles->mCurrentCameraId = getCameraId(atts);
383    } else if (strcmp("EncoderProfile", name) == 0) {
384        profiles->mCamcorderProfiles.add(
385            createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds));
386    } else if (strcmp("ImageEncoding", name) == 0) {
387        profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
388    }
389}
390
391static bool isCamcorderProfile(camcorder_quality quality) {
392    return quality >= CAMCORDER_QUALITY_LIST_START &&
393           quality <= CAMCORDER_QUALITY_LIST_END;
394}
395
396static bool isTimelapseProfile(camcorder_quality quality) {
397    return quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START &&
398           quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END;
399}
400
401void MediaProfiles::initRequiredProfileRefs(const Vector<int>& cameraIds) {
402    LOGV("Number of camera ids: %d", cameraIds.size());
403    CHECK(cameraIds.size() > 0);
404    mRequiredProfileRefs = new RequiredProfiles[cameraIds.size()];
405    for (size_t i = 0, n = cameraIds.size(); i < n; ++i) {
406        mRequiredProfileRefs[i].mCameraId = cameraIds[i];
407        for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
408            mRequiredProfileRefs[i].mRefs[j].mHasRefProfile = false;
409            mRequiredProfileRefs[i].mRefs[j].mRefProfileIndex = -1;
410            if ((j & 1) == 0) {  // low resolution
411                mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0x7FFFFFFF;
412            } else {             // high resolution
413                mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0;
414            }
415        }
416    }
417}
418
419int MediaProfiles::getRequiredProfileRefIndex(int cameraId) {
420    for (size_t i = 0, n = mCameraIds.size(); i < n; ++i) {
421        if (mCameraIds[i] == cameraId) {
422            return i;
423        }
424    }
425    return -1;
426}
427
428void MediaProfiles::checkAndAddRequiredProfilesIfNecessary() {
429    if (sIsInitialized) {
430        return;
431    }
432
433    initRequiredProfileRefs(mCameraIds);
434
435    for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
436        int product = mCamcorderProfiles[i]->mVideoCodec->mFrameWidth *
437                      mCamcorderProfiles[i]->mVideoCodec->mFrameHeight;
438
439        camcorder_quality quality = mCamcorderProfiles[i]->mQuality;
440        int cameraId = mCamcorderProfiles[i]->mCameraId;
441        int index = -1;
442        int refIndex = getRequiredProfileRefIndex(cameraId);
443        CHECK(refIndex != -1);
444        RequiredProfileRefInfo *info;
445        camcorder_quality refQuality;
446        VideoCodec *codec = NULL;
447
448        // Check high and low from either camcorder profile or timelapse profile
449        // but not both. Default, check camcorder profile
450        size_t j = 0;
451        size_t n = 2;
452        if (isTimelapseProfile(quality)) {
453            // Check timelapse profile instead.
454            j = 2;
455            n = kNumRequiredProfiles;
456        } else {
457            // Must be camcorder profile.
458            CHECK(isCamcorderProfile(quality));
459        }
460        for (; j < n; ++j) {
461            info = &(mRequiredProfileRefs[refIndex].mRefs[j]);
462            if ((j % 2 == 0 && product > info->mResolutionProduct) ||  // low
463                (j % 2 != 0 && product < info->mResolutionProduct)) {  // high
464                continue;
465            }
466            switch (j) {
467                case 0:
468                   refQuality = CAMCORDER_QUALITY_LOW;
469                   break;
470                case 1:
471                   refQuality = CAMCORDER_QUALITY_HIGH;
472                   break;
473                case 2:
474                   refQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
475                   break;
476                case 3:
477                   refQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
478                   break;
479                default:
480                    CHECK(!"Should never reach here");
481            }
482
483            if (!info->mHasRefProfile) {
484                index = getCamcorderProfileIndex(cameraId, refQuality);
485            }
486            if (index == -1) {
487                // New high or low quality profile is found.
488                // Update its reference.
489                info->mHasRefProfile = true;
490                info->mRefProfileIndex = i;
491                info->mResolutionProduct = product;
492            }
493        }
494    }
495
496    for (size_t cameraId = 0; cameraId < mCameraIds.size(); ++cameraId) {
497        for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
498            int refIndex = getRequiredProfileRefIndex(cameraId);
499            CHECK(refIndex != -1);
500            RequiredProfileRefInfo *info =
501                    &mRequiredProfileRefs[refIndex].mRefs[j];
502
503            if (info->mHasRefProfile) {
504
505                CamcorderProfile *profile =
506                    new CamcorderProfile(
507                            *mCamcorderProfiles[info->mRefProfileIndex]);
508
509                // Overwrite the quality
510                switch (j % kNumRequiredProfiles) {
511                    case 0:
512                        profile->mQuality = CAMCORDER_QUALITY_LOW;
513                        break;
514                    case 1:
515                        profile->mQuality = CAMCORDER_QUALITY_HIGH;
516                        break;
517                    case 2:
518                        profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
519                        break;
520                    case 3:
521                        profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
522                        break;
523                    default:
524                        CHECK(!"Should never come here");
525                }
526
527                int index = getCamcorderProfileIndex(cameraId, profile->mQuality);
528                if (index != -1) {
529                    LOGV("Profile quality %d for camera %d already exists",
530                        profile->mQuality, cameraId);
531                    CHECK(index == refIndex);
532                    continue;
533                }
534
535                // Insert the new profile
536                LOGV("Add a profile: quality %d=>%d for camera %d",
537                        mCamcorderProfiles[info->mRefProfileIndex]->mQuality,
538                        profile->mQuality, cameraId);
539
540                mCamcorderProfiles.add(profile);
541            }
542        }
543    }
544}
545
546/*static*/ MediaProfiles*
547MediaProfiles::getInstance()
548{
549    LOGV("getInstance");
550    Mutex::Autolock lock(sLock);
551    if (!sIsInitialized) {
552        char value[PROPERTY_VALUE_MAX];
553        if (property_get("media.settings.xml", value, NULL) <= 0) {
554            const char *defaultXmlFile = "/etc/media_profiles.xml";
555            FILE *fp = fopen(defaultXmlFile, "r");
556            if (fp == NULL) {
557                LOGW("could not find media config xml file");
558                sInstance = createDefaultInstance();
559            } else {
560                fclose(fp);  // close the file first.
561                sInstance = createInstanceFromXmlFile(defaultXmlFile);
562            }
563        } else {
564            sInstance = createInstanceFromXmlFile(value);
565        }
566        CHECK(sInstance != NULL);
567        sInstance->checkAndAddRequiredProfilesIfNecessary();
568        sIsInitialized = true;
569    }
570
571    return sInstance;
572}
573
574/*static*/ MediaProfiles::VideoEncoderCap*
575MediaProfiles::createDefaultH263VideoEncoderCap()
576{
577    return new MediaProfiles::VideoEncoderCap(
578        VIDEO_ENCODER_H263, 192000, 420000, 176, 352, 144, 288, 1, 20);
579}
580
581/*static*/ MediaProfiles::VideoEncoderCap*
582MediaProfiles::createDefaultM4vVideoEncoderCap()
583{
584    return new MediaProfiles::VideoEncoderCap(
585        VIDEO_ENCODER_MPEG_4_SP, 192000, 420000, 176, 352, 144, 288, 1, 20);
586}
587
588
589/*static*/ void
590MediaProfiles::createDefaultVideoEncoders(MediaProfiles *profiles)
591{
592    profiles->mVideoEncoders.add(createDefaultH263VideoEncoderCap());
593    profiles->mVideoEncoders.add(createDefaultM4vVideoEncoderCap());
594}
595
596/*static*/ MediaProfiles::CamcorderProfile*
597MediaProfiles::createDefaultCamcorderTimeLapseQcifProfile(camcorder_quality quality)
598{
599    MediaProfiles::VideoCodec *videoCodec =
600        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 1000000, 176, 144, 20);
601
602    AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
603    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
604    profile->mCameraId = 0;
605    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
606    profile->mQuality = quality;
607    profile->mDuration = 60;
608    profile->mVideoCodec = videoCodec;
609    profile->mAudioCodec = audioCodec;
610    return profile;
611}
612
613/*static*/ MediaProfiles::CamcorderProfile*
614MediaProfiles::createDefaultCamcorderTimeLapse480pProfile(camcorder_quality quality)
615{
616    MediaProfiles::VideoCodec *videoCodec =
617        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 20000000, 720, 480, 20);
618
619    AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
620    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
621    profile->mCameraId = 0;
622    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
623    profile->mQuality = quality;
624    profile->mDuration = 60;
625    profile->mVideoCodec = videoCodec;
626    profile->mAudioCodec = audioCodec;
627    return profile;
628}
629
630/*static*/ void
631MediaProfiles::createDefaultCamcorderTimeLapseLowProfiles(
632        MediaProfiles::CamcorderProfile **lowTimeLapseProfile,
633        MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile) {
634    *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_LOW);
635    *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_QCIF);
636}
637
638/*static*/ void
639MediaProfiles::createDefaultCamcorderTimeLapseHighProfiles(
640        MediaProfiles::CamcorderProfile **highTimeLapseProfile,
641        MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile) {
642    *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_HIGH);
643    *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_480P);
644}
645
646/*static*/ MediaProfiles::CamcorderProfile*
647MediaProfiles::createDefaultCamcorderQcifProfile(camcorder_quality quality)
648{
649    MediaProfiles::VideoCodec *videoCodec =
650        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 192000, 176, 144, 20);
651
652    MediaProfiles::AudioCodec *audioCodec =
653        new MediaProfiles::AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
654
655    MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
656    profile->mCameraId = 0;
657    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
658    profile->mQuality = quality;
659    profile->mDuration = 30;
660    profile->mVideoCodec = videoCodec;
661    profile->mAudioCodec = audioCodec;
662    return profile;
663}
664
665/*static*/ MediaProfiles::CamcorderProfile*
666MediaProfiles::createDefaultCamcorderCifProfile(camcorder_quality quality)
667{
668    MediaProfiles::VideoCodec *videoCodec =
669        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
670
671    AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
672    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
673    profile->mCameraId = 0;
674    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
675    profile->mQuality = quality;
676    profile->mDuration = 60;
677    profile->mVideoCodec = videoCodec;
678    profile->mAudioCodec = audioCodec;
679    return profile;
680}
681
682/*static*/ void
683MediaProfiles::createDefaultCamcorderLowProfiles(
684        MediaProfiles::CamcorderProfile **lowProfile,
685        MediaProfiles::CamcorderProfile **lowSpecificProfile) {
686    *lowProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_LOW);
687    *lowSpecificProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_QCIF);
688}
689
690/*static*/ void
691MediaProfiles::createDefaultCamcorderHighProfiles(
692        MediaProfiles::CamcorderProfile **highProfile,
693        MediaProfiles::CamcorderProfile **highSpecificProfile) {
694    *highProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_HIGH);
695    *highSpecificProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_CIF);
696}
697
698/*static*/ void
699MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles)
700{
701    // low camcorder profiles.
702    MediaProfiles::CamcorderProfile *lowProfile, *lowSpecificProfile;
703    createDefaultCamcorderLowProfiles(&lowProfile, &lowSpecificProfile);
704    profiles->mCamcorderProfiles.add(lowProfile);
705    profiles->mCamcorderProfiles.add(lowSpecificProfile);
706
707    // high camcorder profiles.
708    MediaProfiles::CamcorderProfile* highProfile, *highSpecificProfile;
709    createDefaultCamcorderHighProfiles(&highProfile, &highSpecificProfile);
710    profiles->mCamcorderProfiles.add(highProfile);
711    profiles->mCamcorderProfiles.add(highSpecificProfile);
712
713    // low camcorder time lapse profiles.
714    MediaProfiles::CamcorderProfile *lowTimeLapseProfile, *lowSpecificTimeLapseProfile;
715    createDefaultCamcorderTimeLapseLowProfiles(&lowTimeLapseProfile, &lowSpecificTimeLapseProfile);
716    profiles->mCamcorderProfiles.add(lowTimeLapseProfile);
717    profiles->mCamcorderProfiles.add(lowSpecificTimeLapseProfile);
718
719    // high camcorder time lapse profiles.
720    MediaProfiles::CamcorderProfile *highTimeLapseProfile, *highSpecificTimeLapseProfile;
721    createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile);
722    profiles->mCamcorderProfiles.add(highTimeLapseProfile);
723    profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile);
724}
725
726/*static*/ void
727MediaProfiles::createDefaultAudioEncoders(MediaProfiles *profiles)
728{
729    profiles->mAudioEncoders.add(createDefaultAmrNBEncoderCap());
730}
731
732/*static*/ void
733MediaProfiles::createDefaultVideoDecoders(MediaProfiles *profiles)
734{
735    MediaProfiles::VideoDecoderCap *cap =
736        new MediaProfiles::VideoDecoderCap(VIDEO_DECODER_WMV);
737
738    profiles->mVideoDecoders.add(cap);
739}
740
741/*static*/ void
742MediaProfiles::createDefaultAudioDecoders(MediaProfiles *profiles)
743{
744    MediaProfiles::AudioDecoderCap *cap =
745        new MediaProfiles::AudioDecoderCap(AUDIO_DECODER_WMA);
746
747    profiles->mAudioDecoders.add(cap);
748}
749
750/*static*/ void
751MediaProfiles::createDefaultEncoderOutputFileFormats(MediaProfiles *profiles)
752{
753    profiles->mEncoderOutputFileFormats.add(OUTPUT_FORMAT_THREE_GPP);
754    profiles->mEncoderOutputFileFormats.add(OUTPUT_FORMAT_MPEG_4);
755}
756
757/*static*/ MediaProfiles::AudioEncoderCap*
758MediaProfiles::createDefaultAmrNBEncoderCap()
759{
760    return new MediaProfiles::AudioEncoderCap(
761        AUDIO_ENCODER_AMR_NB, 5525, 12200, 8000, 8000, 1, 1);
762}
763
764/*static*/ void
765MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles)
766{
767    ImageEncodingQualityLevels *levels = new ImageEncodingQualityLevels();
768    levels->mCameraId = 0;
769    levels->mLevels.add(70);
770    levels->mLevels.add(80);
771    levels->mLevels.add(90);
772    profiles->mImageEncodingQualityLevels.add(levels);
773}
774
775/*static*/ MediaProfiles*
776MediaProfiles::createDefaultInstance()
777{
778    MediaProfiles *profiles = new MediaProfiles;
779    createDefaultCamcorderProfiles(profiles);
780    createDefaultVideoEncoders(profiles);
781    createDefaultAudioEncoders(profiles);
782    createDefaultVideoDecoders(profiles);
783    createDefaultAudioDecoders(profiles);
784    createDefaultEncoderOutputFileFormats(profiles);
785    createDefaultImageEncodingQualityLevels(profiles);
786    return profiles;
787}
788
789/*static*/ MediaProfiles*
790MediaProfiles::createInstanceFromXmlFile(const char *xml)
791{
792    FILE *fp = NULL;
793    CHECK((fp = fopen(xml, "r")));
794
795    XML_Parser parser = ::XML_ParserCreate(NULL);
796    CHECK(parser != NULL);
797
798    MediaProfiles *profiles = new MediaProfiles();
799    ::XML_SetUserData(parser, profiles);
800    ::XML_SetElementHandler(parser, startElementHandler, NULL);
801
802    /*
803      FIXME:
804      expat is not compiled with -DXML_DTD. We don't have DTD parsing support.
805
806      if (!::XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS)) {
807          LOGE("failed to enable DTD support in the xml file");
808          return UNKNOWN_ERROR;
809      }
810
811    */
812
813    const int BUFF_SIZE = 512;
814    for (;;) {
815        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
816        if (buff == NULL) {
817            LOGE("failed to in call to XML_GetBuffer()");
818            delete profiles;
819            profiles = NULL;
820            goto exit;
821        }
822
823        int bytes_read = ::fread(buff, 1, BUFF_SIZE, fp);
824        if (bytes_read < 0) {
825            LOGE("failed in call to read");
826            delete profiles;
827            profiles = NULL;
828            goto exit;
829        }
830
831        CHECK(::XML_ParseBuffer(parser, bytes_read, bytes_read == 0));
832
833        if (bytes_read == 0) break;  // done parsing the xml file
834    }
835
836exit:
837    ::XML_ParserFree(parser);
838    ::fclose(fp);
839    return profiles;
840}
841
842Vector<output_format> MediaProfiles::getOutputFileFormats() const
843{
844    return mEncoderOutputFileFormats;  // copy out
845}
846
847Vector<video_encoder> MediaProfiles::getVideoEncoders() const
848{
849    Vector<video_encoder> encoders;
850    for (size_t i = 0; i < mVideoEncoders.size(); ++i) {
851        encoders.add(mVideoEncoders[i]->mCodec);
852    }
853    return encoders;  // copy out
854}
855
856int MediaProfiles::getVideoEncoderParamByName(const char *name, video_encoder codec) const
857{
858    LOGV("getVideoEncoderParamByName: %s for codec %d", name, codec);
859    int index = -1;
860    for (size_t i = 0, n = mVideoEncoders.size(); i < n; ++i) {
861        if (mVideoEncoders[i]->mCodec == codec) {
862            index = i;
863            break;
864        }
865    }
866    if (index == -1) {
867        LOGE("The given video encoder %d is not found", codec);
868        return -1;
869    }
870
871    if (!strcmp("enc.vid.width.min", name)) return mVideoEncoders[index]->mMinFrameWidth;
872    if (!strcmp("enc.vid.width.max", name)) return mVideoEncoders[index]->mMaxFrameWidth;
873    if (!strcmp("enc.vid.height.min", name)) return mVideoEncoders[index]->mMinFrameHeight;
874    if (!strcmp("enc.vid.height.max", name)) return mVideoEncoders[index]->mMaxFrameHeight;
875    if (!strcmp("enc.vid.bps.min", name)) return mVideoEncoders[index]->mMinBitRate;
876    if (!strcmp("enc.vid.bps.max", name)) return mVideoEncoders[index]->mMaxBitRate;
877    if (!strcmp("enc.vid.fps.min", name)) return mVideoEncoders[index]->mMinFrameRate;
878    if (!strcmp("enc.vid.fps.max", name)) return mVideoEncoders[index]->mMaxFrameRate;
879
880    LOGE("The given video encoder param name %s is not found", name);
881    return -1;
882}
883
884Vector<audio_encoder> MediaProfiles::getAudioEncoders() const
885{
886    Vector<audio_encoder> encoders;
887    for (size_t i = 0; i < mAudioEncoders.size(); ++i) {
888        encoders.add(mAudioEncoders[i]->mCodec);
889    }
890    return encoders;  // copy out
891}
892
893int MediaProfiles::getAudioEncoderParamByName(const char *name, audio_encoder codec) const
894{
895    LOGV("getAudioEncoderParamByName: %s for codec %d", name, codec);
896    int index = -1;
897    for (size_t i = 0, n = mAudioEncoders.size(); i < n; ++i) {
898        if (mAudioEncoders[i]->mCodec == codec) {
899            index = i;
900            break;
901        }
902    }
903    if (index == -1) {
904        LOGE("The given audio encoder %d is not found", codec);
905        return -1;
906    }
907
908    if (!strcmp("enc.aud.ch.min", name)) return mAudioEncoders[index]->mMinChannels;
909    if (!strcmp("enc.aud.ch.max", name)) return mAudioEncoders[index]->mMaxChannels;
910    if (!strcmp("enc.aud.bps.min", name)) return mAudioEncoders[index]->mMinBitRate;
911    if (!strcmp("enc.aud.bps.max", name)) return mAudioEncoders[index]->mMaxBitRate;
912    if (!strcmp("enc.aud.hz.min", name)) return mAudioEncoders[index]->mMinSampleRate;
913    if (!strcmp("enc.aud.hz.max", name)) return mAudioEncoders[index]->mMaxSampleRate;
914
915    LOGE("The given audio encoder param name %s is not found", name);
916    return -1;
917}
918
919Vector<video_decoder> MediaProfiles::getVideoDecoders() const
920{
921    Vector<video_decoder> decoders;
922    for (size_t i = 0; i < mVideoDecoders.size(); ++i) {
923        decoders.add(mVideoDecoders[i]->mCodec);
924    }
925    return decoders;  // copy out
926}
927
928Vector<audio_decoder> MediaProfiles::getAudioDecoders() const
929{
930    Vector<audio_decoder> decoders;
931    for (size_t i = 0; i < mAudioDecoders.size(); ++i) {
932        decoders.add(mAudioDecoders[i]->mCodec);
933    }
934    return decoders;  // copy out
935}
936
937int MediaProfiles::getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const
938{
939    int index = -1;
940    for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
941        if (mCamcorderProfiles[i]->mCameraId == cameraId &&
942            mCamcorderProfiles[i]->mQuality == quality) {
943            index = i;
944            break;
945        }
946    }
947    return index;
948}
949
950int MediaProfiles::getCamcorderProfileParamByName(const char *name,
951                                                  int cameraId,
952                                                  camcorder_quality quality) const
953{
954    LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d",
955         name, cameraId, quality);
956
957    int index = getCamcorderProfileIndex(cameraId, quality);
958    if (index == -1) {
959        LOGE("The given camcorder profile camera %d quality %d is not found",
960             cameraId, quality);
961        return -1;
962    }
963
964    if (!strcmp("duration", name)) return mCamcorderProfiles[index]->mDuration;
965    if (!strcmp("file.format", name)) return mCamcorderProfiles[index]->mFileFormat;
966    if (!strcmp("vid.codec", name)) return mCamcorderProfiles[index]->mVideoCodec->mCodec;
967    if (!strcmp("vid.width", name)) return mCamcorderProfiles[index]->mVideoCodec->mFrameWidth;
968    if (!strcmp("vid.height", name)) return mCamcorderProfiles[index]->mVideoCodec->mFrameHeight;
969    if (!strcmp("vid.bps", name)) return mCamcorderProfiles[index]->mVideoCodec->mBitRate;
970    if (!strcmp("vid.fps", name)) return mCamcorderProfiles[index]->mVideoCodec->mFrameRate;
971    if (!strcmp("aud.codec", name)) return mCamcorderProfiles[index]->mAudioCodec->mCodec;
972    if (!strcmp("aud.bps", name)) return mCamcorderProfiles[index]->mAudioCodec->mBitRate;
973    if (!strcmp("aud.ch", name)) return mCamcorderProfiles[index]->mAudioCodec->mChannels;
974    if (!strcmp("aud.hz", name)) return mCamcorderProfiles[index]->mAudioCodec->mSampleRate;
975
976    LOGE("The given camcorder profile param id %d name %s is not found", cameraId, name);
977    return -1;
978}
979
980bool MediaProfiles::hasCamcorderProfile(int cameraId, camcorder_quality quality) const
981{
982    return (getCamcorderProfileIndex(cameraId, quality) != -1);
983}
984
985Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const
986{
987    Vector<int> result;
988    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
989    if (levels != NULL) {
990        result = levels->mLevels;  // copy out
991    }
992    return result;
993}
994
995MediaProfiles::~MediaProfiles()
996{
997    CHECK("destructor should never be called" == 0);
998#if 0
999    for (size_t i = 0; i < mAudioEncoders.size(); ++i) {
1000        delete mAudioEncoders[i];
1001    }
1002    mAudioEncoders.clear();
1003
1004    for (size_t i = 0; i < mVideoEncoders.size(); ++i) {
1005        delete mVideoEncoders[i];
1006    }
1007    mVideoEncoders.clear();
1008
1009    for (size_t i = 0; i < mVideoDecoders.size(); ++i) {
1010        delete mVideoDecoders[i];
1011    }
1012    mVideoDecoders.clear();
1013
1014    for (size_t i = 0; i < mAudioDecoders.size(); ++i) {
1015        delete mAudioDecoders[i];
1016    }
1017    mAudioDecoders.clear();
1018
1019    for (size_t i = 0; i < mCamcorderProfiles.size(); ++i) {
1020        delete mCamcorderProfiles[i];
1021    }
1022    mCamcorderProfiles.clear();
1023#endif
1024}
1025} // namespace android
1026