diff options
29 files changed, 1266 insertions, 342 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index bbdb47d..da5ac59 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES:= \ CameraMetadata.cpp \ CameraParameters.cpp \ CaptureResult.cpp \ + CameraParameters2.cpp \ ICamera.cpp \ ICameraClient.cpp \ ICameraService.cpp \ diff --git a/camera/CameraParameters2.cpp b/camera/CameraParameters2.cpp new file mode 100644 index 0000000..378afeb --- /dev/null +++ b/camera/CameraParameters2.cpp @@ -0,0 +1,382 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** +** 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. +*/ + +#define LOG_TAG "CameraParams2" +// #define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <string.h> +#include <stdlib.h> +#include <camera/CameraParameters2.h> + +namespace android { + +CameraParameters2::CameraParameters2() + : mMap() +{ +} + +CameraParameters2::~CameraParameters2() +{ +} + +String8 CameraParameters2::flatten() const +{ + String8 flattened(""); + size_t size = mMap.size(); + + for (size_t i = 0; i < size; i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + + flattened += k; + flattened += "="; + flattened += v; + if (i != size-1) + flattened += ";"; + } + + ALOGV("%s: Flattened params = %s", __FUNCTION__, flattened.string()); + + return flattened; +} + +void CameraParameters2::unflatten(const String8 ¶ms) +{ + const char *a = params.string(); + const char *b; + + mMap.clear(); + + for (;;) { + // Find the bounds of the key name. + b = strchr(a, '='); + if (b == 0) + break; + + // Create the key string. + String8 k(a, (size_t)(b-a)); + + // Find the value. + a = b+1; + b = strchr(a, ';'); + if (b == 0) { + // If there's no semicolon, this is the last item. + String8 v(a); + mMap.add(k, v); + break; + } + + String8 v(a, (size_t)(b-a)); + mMap.add(k, v); + a = b+1; + } +} + + +void CameraParameters2::set(const char *key, const char *value) +{ + // XXX i think i can do this with strspn() + if (strchr(key, '=') || strchr(key, ';')) { + //XXX ALOGE("Key \"%s\"contains invalid character (= or ;)", key); + return; + } + + if (strchr(value, '=') || strchr(value, ';')) { + //XXX ALOGE("Value \"%s\"contains invalid character (= or ;)", value); + return; + } + + // Replacing a value updates the key's order to be the new largest order + ssize_t res = mMap.replaceValueFor(String8(key), String8(value)); + LOG_ALWAYS_FATAL_IF(res < 0, "replaceValueFor(%s,%s) failed", key, value); +} + +void CameraParameters2::set(const char *key, int value) +{ + char str[16]; + sprintf(str, "%d", value); + set(key, str); +} + +void CameraParameters2::setFloat(const char *key, float value) +{ + char str[16]; // 14 should be enough. We overestimate to be safe. + snprintf(str, sizeof(str), "%g", value); + set(key, str); +} + +const char *CameraParameters2::get(const char *key) const +{ + ssize_t idx = mMap.indexOfKey(String8(key)); + if (idx < 0) { + return NULL; + } else { + return mMap.valueAt(idx).string(); + } +} + +int CameraParameters2::getInt(const char *key) const +{ + const char *v = get(key); + if (v == 0) + return -1; + return strtol(v, 0, 0); +} + +float CameraParameters2::getFloat(const char *key) const +{ + const char *v = get(key); + if (v == 0) return -1; + return strtof(v, 0); +} + +status_t CameraParameters2::compareSetOrder(const char *key1, const char *key2, + int *order) const { + if (key1 == NULL) { + ALOGE("%s: key1 must not be NULL", __FUNCTION__); + return BAD_VALUE; + } else if (key2 == NULL) { + ALOGE("%s: key2 must not be NULL", __FUNCTION__); + return BAD_VALUE; + } else if (order == NULL) { + ALOGE("%s: order must not be NULL", __FUNCTION__); + return BAD_VALUE; + } + + ssize_t index1 = mMap.indexOfKey(String8(key1)); + ssize_t index2 = mMap.indexOfKey(String8(key2)); + if (index1 < 0) { + ALOGW("%s: Key1 (%s) was not set", __FUNCTION__, key1); + return NAME_NOT_FOUND; + } else if (index2 < 0) { + ALOGW("%s: Key2 (%s) was not set", __FUNCTION__, key2); + return NAME_NOT_FOUND; + } + + *order = (index1 == index2) ? 0 : + (index1 < index2) ? -1 : + 1; + + return OK; +} + +void CameraParameters2::remove(const char *key) +{ + mMap.removeItem(String8(key)); +} + +// Parse string like "640x480" or "10000,20000" +static int parse_pair(const char *str, int *first, int *second, char delim, + char **endptr = NULL) +{ + // Find the first integer. + char *end; + int w = (int)strtol(str, &end, 10); + // If a delimeter does not immediately follow, give up. + if (*end != delim) { + ALOGE("Cannot find delimeter (%c) in str=%s", delim, str); + return -1; + } + + // Find the second integer, immediately after the delimeter. + int h = (int)strtol(end+1, &end, 10); + + *first = w; + *second = h; + + if (endptr) { + *endptr = end; + } + + return 0; +} + +static void parseSizesList(const char *sizesStr, Vector<Size> &sizes) +{ + if (sizesStr == 0) { + return; + } + + char *sizeStartPtr = (char *)sizesStr; + + while (true) { + int width, height; + int success = parse_pair(sizeStartPtr, &width, &height, 'x', + &sizeStartPtr); + if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) { + ALOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr); + return; + } + sizes.push(Size(width, height)); + + if (*sizeStartPtr == '\0') { + return; + } + sizeStartPtr++; + } +} + +void CameraParameters2::setPreviewSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set(CameraParameters::KEY_PREVIEW_SIZE, str); +} + +void CameraParameters2::getPreviewSize(int *width, int *height) const +{ + *width = *height = -1; + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get(CameraParameters::KEY_PREVIEW_SIZE); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + +void CameraParameters2::getPreferredPreviewSizeForVideo(int *width, int *height) const +{ + *width = *height = -1; + const char *p = get(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + +void CameraParameters2::getSupportedPreviewSizes(Vector<Size> &sizes) const +{ + const char *previewSizesStr = get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); + parseSizesList(previewSizesStr, sizes); +} + +void CameraParameters2::setVideoSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set(CameraParameters::KEY_VIDEO_SIZE, str); +} + +void CameraParameters2::getVideoSize(int *width, int *height) const +{ + *width = *height = -1; + const char *p = get(CameraParameters::KEY_VIDEO_SIZE); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + +void CameraParameters2::getSupportedVideoSizes(Vector<Size> &sizes) const +{ + const char *videoSizesStr = get(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES); + parseSizesList(videoSizesStr, sizes); +} + +void CameraParameters2::setPreviewFrameRate(int fps) +{ + set(CameraParameters::KEY_PREVIEW_FRAME_RATE, fps); +} + +int CameraParameters2::getPreviewFrameRate() const +{ + return getInt(CameraParameters::KEY_PREVIEW_FRAME_RATE); +} + +void CameraParameters2::getPreviewFpsRange(int *min_fps, int *max_fps) const +{ + *min_fps = *max_fps = -1; + const char *p = get(CameraParameters::KEY_PREVIEW_FPS_RANGE); + if (p == 0) return; + parse_pair(p, min_fps, max_fps, ','); +} + +void CameraParameters2::setPreviewFpsRange(int min_fps, int max_fps) +{ + String8 str = String8::format("%d,%d", min_fps, max_fps); + set(CameraParameters::KEY_PREVIEW_FPS_RANGE, str.string()); +} + +void CameraParameters2::setPreviewFormat(const char *format) +{ + set(CameraParameters::KEY_PREVIEW_FORMAT, format); +} + +const char *CameraParameters2::getPreviewFormat() const +{ + return get(CameraParameters::KEY_PREVIEW_FORMAT); +} + +void CameraParameters2::setPictureSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set(CameraParameters::KEY_PICTURE_SIZE, str); +} + +void CameraParameters2::getPictureSize(int *width, int *height) const +{ + *width = *height = -1; + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get(CameraParameters::KEY_PICTURE_SIZE); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + +void CameraParameters2::getSupportedPictureSizes(Vector<Size> &sizes) const +{ + const char *pictureSizesStr = get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); + parseSizesList(pictureSizesStr, sizes); +} + +void CameraParameters2::setPictureFormat(const char *format) +{ + set(CameraParameters::KEY_PICTURE_FORMAT, format); +} + +const char *CameraParameters2::getPictureFormat() const +{ + return get(CameraParameters::KEY_PICTURE_FORMAT); +} + +void CameraParameters2::dump() const +{ + ALOGD("dump: mMap.size = %d", mMap.size()); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + ALOGD("%s: %s\n", k.string(), v.string()); + } +} + +status_t CameraParameters2::dump(int fd, const Vector<String16>& args) const +{ + (void)args; + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, 255, "CameraParameters2::dump: mMap.size = %zu\n", mMap.size()); + result.append(buffer); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + snprintf(buffer, 255, "\t%s: %s\n", k.string(), v.string()); + result.append(buffer); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +}; // namespace android diff --git a/include/camera/CameraParameters2.h b/include/camera/CameraParameters2.h new file mode 100644 index 0000000..88ad812 --- /dev/null +++ b/include/camera/CameraParameters2.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_PARAMETERS2_H +#define ANDROID_HARDWARE_CAMERA_PARAMETERS2_H + +#include <utils/Vector.h> +#include <utils/String8.h> +#include "CameraParameters.h" + +namespace android { + +/** + * A copy of CameraParameters plus ABI-breaking changes. Needed + * because some camera HALs directly link to CameraParameters and cannot + * tolerate an ABI change. + */ +class CameraParameters2 +{ +public: + CameraParameters2(); + CameraParameters2(const String8 ¶ms) { unflatten(params); } + ~CameraParameters2(); + + String8 flatten() const; + void unflatten(const String8 ¶ms); + + void set(const char *key, const char *value); + void set(const char *key, int value); + void setFloat(const char *key, float value); + // Look up string value by key. + // -- The string remains valid until the next set/remove of the same key, + // or until the map gets cleared. + const char *get(const char *key) const; + int getInt(const char *key) const; + float getFloat(const char *key) const; + + // Compare the order that key1 was set vs the order that key2 was set. + // + // Sets the order parameter to an integer less than, equal to, or greater + // than zero if key1's set order was respectively, to be less than, to + // match, or to be greater than key2's set order. + // + // Error codes: + // * NAME_NOT_FOUND - if either key has not been set previously + // * BAD_VALUE - if any of the parameters are NULL + status_t compareSetOrder(const char *key1, const char *key2, + /*out*/ + int *order) const; + + void remove(const char *key); + + void setPreviewSize(int width, int height); + void getPreviewSize(int *width, int *height) const; + void getSupportedPreviewSizes(Vector<Size> &sizes) const; + + // Set the dimensions in pixels to the given width and height + // for video frames. The given width and height must be one + // of the supported dimensions returned from + // getSupportedVideoSizes(). Must not be called if + // getSupportedVideoSizes() returns an empty Vector of Size. + void setVideoSize(int width, int height); + // Retrieve the current dimensions (width and height) + // in pixels for video frames, which must be one of the + // supported dimensions returned from getSupportedVideoSizes(). + // Must not be called if getSupportedVideoSizes() returns an + // empty Vector of Size. + void getVideoSize(int *width, int *height) const; + // Retrieve a Vector of supported dimensions (width and height) + // in pixels for video frames. If sizes returned from the method + // is empty, the camera does not support calls to setVideoSize() + // or getVideoSize(). In adddition, it also indicates that + // the camera only has a single output, and does not have + // separate output for video frames and preview frame. + void getSupportedVideoSizes(Vector<Size> &sizes) const; + // Retrieve the preferred preview size (width and height) in pixels + // for video recording. The given width and height must be one of + // supported preview sizes returned from getSupportedPreviewSizes(). + // Must not be called if getSupportedVideoSizes() returns an empty + // Vector of Size. If getSupportedVideoSizes() returns an empty + // Vector of Size, the width and height returned from this method + // is invalid, and is "-1x-1". + void getPreferredPreviewSizeForVideo(int *width, int *height) const; + + void setPreviewFrameRate(int fps); + int getPreviewFrameRate() const; + void getPreviewFpsRange(int *min_fps, int *max_fps) const; + void setPreviewFpsRange(int min_fps, int max_fps); + void setPreviewFormat(const char *format); + const char *getPreviewFormat() const; + void setPictureSize(int width, int height); + void getPictureSize(int *width, int *height) const; + void getSupportedPictureSizes(Vector<Size> &sizes) const; + void setPictureFormat(const char *format); + const char *getPictureFormat() const; + + void dump() const; + status_t dump(int fd, const Vector<String16>& args) const; + +private: + + // Quick and dirty map that maintains insertion order + template <typename KeyT, typename ValueT> + struct OrderedKeyedVector { + + ssize_t add(const KeyT& key, const ValueT& value) { + return mList.add(Pair(key, value)); + } + + size_t size() const { + return mList.size(); + } + + const KeyT& keyAt(size_t idx) const { + return mList[idx].mKey; + } + + const ValueT& valueAt(size_t idx) const { + return mList[idx].mValue; + } + + const ValueT& valueFor(const KeyT& key) const { + ssize_t i = indexOfKey(key); + LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); + + return valueAt(i); + } + + ssize_t indexOfKey(const KeyT& key) const { + size_t vectorIdx = 0; + for (; vectorIdx < mList.size(); ++vectorIdx) { + if (mList[vectorIdx].mKey == key) { + return (ssize_t) vectorIdx; + } + } + + return NAME_NOT_FOUND; + } + + ssize_t removeItem(const KeyT& key) { + size_t vectorIdx = (size_t) indexOfKey(key); + + if (vectorIdx < 0) { + return vectorIdx; + } + + return mList.removeAt(vectorIdx); + } + + void clear() { + mList.clear(); + } + + // Same as removing and re-adding. The key's index changes to max. + ssize_t replaceValueFor(const KeyT& key, const ValueT& value) { + removeItem(key); + return add(key, value); + } + + private: + + struct Pair { + Pair() : mKey(), mValue() {} + Pair(const KeyT& key, const ValueT& value) : + mKey(key), + mValue(value) {} + KeyT mKey; + ValueT mValue; + }; + + Vector<Pair> mList; + }; + + /** + * Order matters: Keys that are set() later are stored later in the map. + * + * If two keys have meaning that conflict, then the later-set key + * wins. + * + * For example, preview FPS and preview FPS range conflict since only + * we only want to use the FPS range if that's the last thing that was set. + * So in that case, only use preview FPS range if it was set later than + * the preview FPS. + */ + OrderedKeyedVector<String8,String8> mMap; +}; + +}; // namespace android + +#endif diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h index ab7a4b8..cd56adb 100644 --- a/include/media/MediaCodecInfo.h +++ b/include/media/MediaCodecInfo.h @@ -107,6 +107,7 @@ private: status_t initializeCapabilities(const CodecCapabilities &caps); void addDetail(const AString &key, const AString &value); void addFeature(const AString &key, int32_t value); + void addFeature(const AString &key, const char *value); void removeMime(const char *mime); void complete(); diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp index 446c582..7b4c4e2 100644 --- a/media/libmedia/MediaCodecInfo.cpp +++ b/media/libmedia/MediaCodecInfo.cpp @@ -257,4 +257,10 @@ void MediaCodecInfo::addFeature(const AString &key, int32_t value) { mCurrentCaps->mDetails->setInt32(tag.c_str(), value); } +void MediaCodecInfo::addFeature(const AString &key, const char *value) { + AString tag = "feature-"; + tag.append(key); + mCurrentCaps->mDetails->setString(tag.c_str(), value); +} + } // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index a706987..2c48306 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1898,7 +1898,8 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( me, buffer->raw, buffer->size, me->mCallbackCookie, CB_EVENT_FILL_BUFFER); - if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { + if ((me->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0 && + actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { // We've reached EOS but the audio track is not stopped yet, // keep playing silence. diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 6676461..76e1d54 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -192,9 +192,26 @@ status_t NuPlayer::GenericSource::setBuffers( } NuPlayer::GenericSource::~GenericSource() { + if (mLooper != NULL) { + mLooper->unregisterHandler(id()); + mLooper->stop(); + } } void NuPlayer::GenericSource::prepareAsync() { + if (mLooper == NULL) { + mLooper = new ALooper; + mLooper->setName("generic"); + mLooper->start(); + + mLooper->registerHandler(this); + } + + sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id()); + msg->post(); +} + +void NuPlayer::GenericSource::onPrepareAsync() { // delayed data source creation AString sniffedMIME; sp<DataSource> dataSource; @@ -267,6 +284,11 @@ status_t NuPlayer::GenericSource::feedMoreTSData() { void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { + case kWhatPrepareAsync: + { + onPrepareAsync(); + break; + } case kWhatFetchSubtitleData: { fetchTextData(kWhatSendSubtitleData, MEDIA_TRACK_TYPE_SUBTITLE, diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index 44d690e..d3081de 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -70,6 +70,7 @@ protected: private: enum { + kWhatPrepareAsync, kWhatFetchSubtitleData, kWhatFetchTimedTextData, kWhatSendSubtitleData, @@ -104,12 +105,17 @@ private: int64_t mOffset; int64_t mLength; + sp<ALooper> mLooper; + + void resetDataSource(); status_t initFromDataSource( const sp<DataSource> &dataSource, const char *mime); + void onPrepareAsync(); + void fetchTextData( uint32_t what, media_track_type type, int32_t curGen, sp<AnotherPacketSource> packets, sp<AMessage> msg); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index c713d39..a003c81 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -62,17 +62,24 @@ NuPlayer::HTTPLiveSource::HTTPLiveSource( NuPlayer::HTTPLiveSource::~HTTPLiveSource() { if (mLiveSession != NULL) { mLiveSession->disconnect(); - mLiveSession.clear(); + mLiveLooper->unregisterHandler(mLiveSession->id()); + mLiveLooper->unregisterHandler(id()); mLiveLooper->stop(); + + mLiveSession.clear(); mLiveLooper.clear(); } } void NuPlayer::HTTPLiveSource::prepareAsync() { - mLiveLooper = new ALooper; - mLiveLooper->setName("http live"); - mLiveLooper->start(); + if (mLiveLooper == NULL) { + mLiveLooper = new ALooper; + mLiveLooper->setName("http live"); + mLiveLooper->start(); + + mLiveLooper->registerHandler(this); + } sp<AMessage> notify = new AMessage(kWhatSessionNotify, id()); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 9a4e811..7ed65de 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -147,6 +147,7 @@ NuPlayer::NuPlayer() mSourceFlags(0), mVideoIsAVC(false), mOffloadAudio(false), + mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), mAudioEOS(false), mVideoEOS(false), mScanSourcesPending(false), @@ -372,7 +373,6 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findObject("source", &obj)); if (obj != NULL) { mSource = static_cast<Source *>(obj.get()); - looper()->registerHandler(mSource); } else { err = UNKNOWN_ERROR; } @@ -639,11 +639,18 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { bool mHadAnySourcesBefore = (mAudioDecoder != NULL) || (mVideoDecoder != NULL); + // initialize video before audio because successful initialization of + // video may change deep buffer mode of audio. if (mNativeWindow != NULL) { instantiateDecoder(false, &mVideoDecoder); } if (mAudioSink != NULL) { + if (mOffloadAudio) { + // open audio sink early under offload mode. + sp<AMessage> format = mSource->getFormat(true /*audio*/); + openAudioSink(format, true /*offloadOnly*/); + } instantiateDecoder(true, &mAudioDecoder); } @@ -743,138 +750,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findMessage("format", &format)); if (audio) { - int32_t numChannels; - CHECK(format->findInt32( - "channel-count", &numChannels)); - - int32_t sampleRate; - CHECK(format->findInt32("sample-rate", &sampleRate)); - - ALOGV("Audio output format changed to %d Hz, %d channels", - sampleRate, numChannels); - - mAudioSink->close(); - - uint32_t flags; - int64_t durationUs; - // FIXME: we should handle the case where the video decoder - // is created after we receive the format change indication. - // Current code will just make that we select deep buffer - // with video which should not be a problem as it should - // not prevent from keeping A/V sync. - if (mVideoDecoder == NULL && - mSource->getDuration(&durationUs) == OK && - durationUs - > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } else { - flags = AUDIO_OUTPUT_FLAG_NONE; - } - - int32_t channelMask; - if (!format->findInt32("channel-mask", &channelMask)) { - channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; - } - - if (mOffloadAudio) { - audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; - audio_offload_info_t offloadInfo = - AUDIO_INFO_INITIALIZER; - - AString mime; - CHECK(format->findString("mime", &mime)); - - status_t err = - mapMimeToAudioFormat(audioFormat, mime.c_str()); - if (err != OK) { - ALOGE("Couldn't map mime \"%s\" to a valid " - "audio_format", mime.c_str()); - mOffloadAudio = false; - } else { - ALOGV("Mime \"%s\" mapped to audio_format 0x%x", - mime.c_str(), audioFormat); - - int32_t aacProfile = -1; - if (audioFormat == AUDIO_FORMAT_AAC - && format->findInt32("aac-profile", &aacProfile)) { - // Redefine AAC format as per aac profile - mapAACProfileToAudioFormat( - audioFormat, - aacProfile); - } - - flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; - - offloadInfo.duration_us = -1; - format->findInt64( - "durationUs", &offloadInfo.duration_us); - - int avgBitRate = -1; - format->findInt32("bit-rate", &avgBitRate); - - offloadInfo.sample_rate = sampleRate; - offloadInfo.channel_mask = channelMask; - offloadInfo.format = audioFormat; - offloadInfo.stream_type = AUDIO_STREAM_MUSIC; - offloadInfo.bit_rate = avgBitRate; - offloadInfo.has_video = (mVideoDecoder != NULL); - offloadInfo.is_streaming = true; - - ALOGV("try to open AudioSink in offload mode"); - err = mAudioSink->open( - sampleRate, - numChannels, - (audio_channel_mask_t)channelMask, - audioFormat, - 8 /* bufferCount */, - &NuPlayer::Renderer::AudioSinkCallback, - mRenderer.get(), - (audio_output_flags_t)flags, - &offloadInfo); - - if (err == OK) { - // If the playback is offloaded to h/w, we pass - // the HAL some metadata information. - // We don't want to do this for PCM because it - // will be going through the AudioFlinger mixer - // before reaching the hardware. - sp<MetaData> audioMeta = - mSource->getFormatMeta(true /* audio */); - sendMetaDataToHal(mAudioSink, audioMeta); - - err = mAudioSink->start(); - } - } - - if (err != OK) { - // Clean up, fall back to non offload mode. - mAudioSink->close(); - mAudioDecoder.clear(); - mRenderer->signalDisableOffloadAudio(); - mOffloadAudio = false; - - instantiateDecoder( - true /* audio */, &mAudioDecoder); - } - } - - if (!mOffloadAudio) { - flags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; - ALOGV("open AudioSink in NON-offload mode"); - CHECK_EQ(mAudioSink->open( - sampleRate, - numChannels, - (audio_channel_mask_t)channelMask, - AUDIO_FORMAT_PCM_16_BIT, - 8 /* bufferCount */, - NULL, - NULL, - (audio_output_flags_t)flags), - (status_t)OK); - mAudioSink->start(); - } - - mRenderer->signalAudioSinkChanged(); + openAudioSink(format, false /*offloadOnly*/); } else { // video sp<AMessage> inputFormat = @@ -981,7 +857,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("Tear down audio offload, fall back to s/w path"); int64_t positionUs; CHECK(msg->findInt64("positionUs", &positionUs)); - mAudioSink->close(); + closeAudioSink(); mAudioDecoder.clear(); mRenderer->flush(true /* audio */); if (mVideoDecoder != NULL) { @@ -1108,6 +984,149 @@ void NuPlayer::postScanSources() { mScanSourcesPending = true; } +void NuPlayer::openAudioSink(const sp<AMessage> &format, bool offloadOnly) { + ALOGV("openAudioSink: offloadOnly(%d) mOffloadAudio(%d)", + offloadOnly, mOffloadAudio); + bool audioSinkChanged = false; + + int32_t numChannels; + CHECK(format->findInt32("channel-count", &numChannels)); + + int32_t channelMask; + if (!format->findInt32("channel-mask", &channelMask)) { + // signal to the AudioSink to derive the mask from count. + channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; + } + + int32_t sampleRate; + CHECK(format->findInt32("sample-rate", &sampleRate)); + + uint32_t flags; + int64_t durationUs; + // FIXME: we should handle the case where the video decoder + // is created after we receive the format change indication. + // Current code will just make that we select deep buffer + // with video which should not be a problem as it should + // not prevent from keeping A/V sync. + if (mVideoDecoder == NULL && + mSource->getDuration(&durationUs) == OK && + durationUs + > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } else { + flags = AUDIO_OUTPUT_FLAG_NONE; + } + + if (mOffloadAudio) { + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; + AString mime; + CHECK(format->findString("mime", &mime)); + status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str()); + + if (err != OK) { + ALOGE("Couldn't map mime \"%s\" to a valid " + "audio_format", mime.c_str()); + mOffloadAudio = false; + } else { + ALOGV("Mime \"%s\" mapped to audio_format 0x%x", + mime.c_str(), audioFormat); + + int avgBitRate = -1; + format->findInt32("bit-rate", &avgBitRate); + + int32_t aacProfile = -1; + if (audioFormat == AUDIO_FORMAT_AAC + && format->findInt32("aac-profile", &aacProfile)) { + // Redefine AAC format as per aac profile + mapAACProfileToAudioFormat( + audioFormat, + aacProfile); + } + + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.duration_us = -1; + format->findInt64( + "durationUs", &offloadInfo.duration_us); + offloadInfo.sample_rate = sampleRate; + offloadInfo.channel_mask = channelMask; + offloadInfo.format = audioFormat; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.bit_rate = avgBitRate; + offloadInfo.has_video = (mVideoDecoder != NULL); + offloadInfo.is_streaming = true; + + if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) { + ALOGV("openAudioSink: no change in offload mode"); + return; // no change from previous configuration, everything ok. + } + ALOGV("openAudioSink: try to open AudioSink in offload mode"); + flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + flags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + audioSinkChanged = true; + mAudioSink->close(); + err = mAudioSink->open( + sampleRate, + numChannels, + (audio_channel_mask_t)channelMask, + audioFormat, + 8 /* bufferCount */, + &NuPlayer::Renderer::AudioSinkCallback, + mRenderer.get(), + (audio_output_flags_t)flags, + &offloadInfo); + + if (err == OK) { + // If the playback is offloaded to h/w, we pass + // the HAL some metadata information. + // We don't want to do this for PCM because it + // will be going through the AudioFlinger mixer + // before reaching the hardware. + sp<MetaData> audioMeta = + mSource->getFormatMeta(true /* audio */); + sendMetaDataToHal(mAudioSink, audioMeta); + mCurrentOffloadInfo = offloadInfo; + err = mAudioSink->start(); + ALOGV_IF(err == OK, "openAudioSink: offload succeeded"); + } + if (err != OK) { + // Clean up, fall back to non offload mode. + mAudioSink->close(); + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + ALOGV("openAudioSink: offload failed"); + } + } + } + if (!offloadOnly && !mOffloadAudio) { + flags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + ALOGV("openAudioSink: open AudioSink in NON-offload mode"); + + audioSinkChanged = true; + mAudioSink->close(); + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + CHECK_EQ(mAudioSink->open( + sampleRate, + numChannels, + (audio_channel_mask_t)channelMask, + AUDIO_FORMAT_PCM_16_BIT, + 8 /* bufferCount */, + NULL, + NULL, + (audio_output_flags_t)flags), + (status_t)OK); + mAudioSink->start(); + } + if (audioSinkChanged) { + mRenderer->signalAudioSinkChanged(); + } +} + +void NuPlayer::closeAudioSink() { + mAudioSink->close(); + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; +} + status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { if (*decoder != NULL) { return OK; @@ -1688,8 +1707,6 @@ void NuPlayer::performReset() { if (mSource != NULL) { mSource->stop(); - looper()->unregisterHandler(mSource->id()); - mSource.clear(); } @@ -1722,13 +1739,6 @@ void NuPlayer::performSetSurface(const sp<NativeWindowWrapper> &wrapper) { // XXX - ignore error from setVideoScalingMode for now setVideoScalingMode(mVideoScalingMode); - - if (mDriver != NULL) { - sp<NuPlayerDriver> driver = mDriver.promote(); - if (driver != NULL) { - driver->notifySetSurfaceComplete(); - } - } } void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index fc456a4..48882c5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -124,6 +124,7 @@ private: sp<Decoder> mVideoDecoder; bool mVideoIsAVC; bool mOffloadAudio; + audio_offload_info_t mCurrentOffloadInfo; sp<Decoder> mAudioDecoder; sp<CCDecoder> mCCDecoder; sp<Renderer> mRenderer; @@ -167,6 +168,9 @@ private: bool mStarted; + void openAudioSink(const sp<AMessage> &format, bool offloadOnly); + void closeAudioSink(); + status_t instantiateDecoder(bool audio, sp<Decoder> *decoder); void updateVideoSize( diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index e33e647..140e1ae 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -34,7 +34,6 @@ NuPlayerDriver::NuPlayerDriver() : mState(STATE_IDLE), mIsAsyncPrepare(false), mAsyncResult(UNKNOWN_ERROR), - mSetSurfaceInProgress(false), mDurationUs(-1), mPositionUs(-1), mNumFramesTotal(0), @@ -135,10 +134,6 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( const sp<IGraphicBufferProducer> &bufferProducer) { Mutex::Autolock autoLock(mLock); - if (mSetSurfaceInProgress) { - return INVALID_OPERATION; - } - switch (mState) { case STATE_SET_DATASOURCE_PENDING: case STATE_RESET_IN_PROGRESS: @@ -148,14 +143,8 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( break; } - mSetSurfaceInProgress = true; - mPlayer->setVideoSurfaceTextureAsync(bufferProducer); - while (mSetSurfaceInProgress) { - mCondition.wait(mLock); - } - return OK; } @@ -178,6 +167,16 @@ status_t NuPlayerDriver::prepare_l() { mCondition.wait(mLock); } return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR; + case STATE_STOPPED: + // this is really just paused. handle as seek to start + mAtEOS = false; + mState = STATE_STOPPED_AND_PREPARING; + mIsAsyncPrepare = false; + mPlayer->seekToAsync(0); + while (mState == STATE_STOPPED_AND_PREPARING) { + mCondition.wait(mLock); + } + return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR; default: return INVALID_OPERATION; }; @@ -192,6 +191,13 @@ status_t NuPlayerDriver::prepareAsync() { mIsAsyncPrepare = true; mPlayer->prepareAsync(); return OK; + case STATE_STOPPED: + // this is really just paused. handle as seek to start + mAtEOS = false; + mState = STATE_STOPPED_AND_PREPARING; + mIsAsyncPrepare = true; + mPlayer->seekToAsync(0); + return OK; default: return INVALID_OPERATION; }; @@ -235,6 +241,7 @@ status_t NuPlayerDriver::start() { break; case STATE_PAUSED: + case STATE_STOPPED_AND_PREPARED: { mPlayer->resume(); break; @@ -250,7 +257,29 @@ status_t NuPlayerDriver::start() { } status_t NuPlayerDriver::stop() { - return pause(); + Mutex::Autolock autoLock(mLock); + + switch (mState) { + case STATE_RUNNING: + mPlayer->pause(); + // fall through + + case STATE_PAUSED: + notifyListener(MEDIA_STOPPED); + // fall through + + case STATE_PREPARED: + case STATE_STOPPED: + case STATE_STOPPED_AND_PREPARING: + case STATE_STOPPED_AND_PREPARED: + mState = STATE_STOPPED; + break; + + default: + return INVALID_OPERATION; + } + + return OK; } status_t NuPlayerDriver::pause() { @@ -359,7 +388,9 @@ status_t NuPlayerDriver::reset() { break; } - notifyListener(MEDIA_STOPPED); + if (mState != STATE_STOPPED) { + notifyListener(MEDIA_STOPPED); + } mState = STATE_RESET_IN_PROGRESS; mPlayer->resetAsync(); @@ -483,15 +514,6 @@ void NuPlayerDriver::notifyResetComplete() { mCondition.broadcast(); } -void NuPlayerDriver::notifySetSurfaceComplete() { - Mutex::Autolock autoLock(mLock); - - CHECK(mSetSurfaceInProgress); - mSetSurfaceInProgress = false; - - mCondition.broadcast(); -} - void NuPlayerDriver::notifyDuration(int64_t durationUs) { Mutex::Autolock autoLock(mLock); mDurationUs = durationUs; @@ -503,7 +525,23 @@ void NuPlayerDriver::notifyPosition(int64_t positionUs) { } void NuPlayerDriver::notifySeekComplete() { - notifyListener(MEDIA_SEEK_COMPLETE); + bool wasSeeking = true; + { + Mutex::Autolock autoLock(mLock); + if (mState == STATE_STOPPED_AND_PREPARING) { + wasSeeking = false; + mState = STATE_STOPPED_AND_PREPARED; + mCondition.broadcast(); + if (!mIsAsyncPrepare) { + // if we are preparing synchronously, no need to notify listener + return; + } + } else if (mState == STATE_STOPPED) { + // no need to notify listener + return; + } + } + notifyListener(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED); } void NuPlayerDriver::notifyFrameStats( diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 9424aae..f520395 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -66,7 +66,6 @@ struct NuPlayerDriver : public MediaPlayerInterface { void notifySetDataSourceCompleted(status_t err); void notifyPrepareCompleted(status_t err); void notifyResetComplete(); - void notifySetSurfaceComplete(); void notifyDuration(int64_t durationUs); void notifyPosition(int64_t positionUs); void notifySeekComplete(); @@ -87,6 +86,9 @@ private: STATE_RUNNING, STATE_PAUSED, STATE_RESET_IN_PROGRESS, + STATE_STOPPED, // equivalent to PAUSED + STATE_STOPPED_AND_PREPARING, // equivalent to PAUSED, but seeking + STATE_STOPPED_AND_PREPARED, // equivalent to PAUSED, but seek complete }; mutable Mutex mLock; @@ -99,7 +101,6 @@ private: // The following are protected through "mLock" // >>> - bool mSetSurfaceInProgress; int64_t mDurationUs; int64_t mPositionUs; int64_t mNumFramesTotal; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 3640038..3777f64 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -318,6 +318,7 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { bool hasEOS = false; size_t sizeCopied = 0; + bool firstEntry = true; while (sizeCopied < size && !mAudioQueue.empty()) { QueueEntry *entry = &*mAudioQueue.begin(); @@ -328,14 +329,14 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { break; } - if (entry->mOffset == 0) { + if (firstEntry && entry->mOffset == 0) { + firstEntry = false; int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); if (mFirstAudioTimeUs == -1) { mFirstAudioTimeUs = mediaTimeUs; } - mAnchorTimeMediaUs = mediaTimeUs; uint32_t numFramesPlayed; CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index a911f6c..ffacb8f 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -70,6 +70,7 @@ NuPlayer::RTSPSource::RTSPSource( NuPlayer::RTSPSource::~RTSPSource() { if (mLooper != NULL) { + mLooper->unregisterHandler(id()); mLooper->stop(); } } @@ -80,14 +81,13 @@ void NuPlayer::RTSPSource::prepareAsync() { mLooper->setName("rtsp"); mLooper->start(); - mReflector = new AHandlerReflector<RTSPSource>(this); - mLooper->registerHandler(mReflector); + mLooper->registerHandler(this); } CHECK(mHandler == NULL); CHECK(mSDPLoader == NULL); - sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id()); + sp<AMessage> notify = new AMessage(kWhatNotify, id()); CHECK_EQ(mState, (int)DISCONNECTED); mState = CONNECTING; @@ -118,7 +118,7 @@ void NuPlayer::RTSPSource::stop() { if (mLooper == NULL) { return; } - sp<AMessage> msg = new AMessage(kWhatDisconnect, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatDisconnect, id()); sp<AMessage> dummy; msg->postAndAwaitResponse(&dummy); @@ -305,7 +305,7 @@ status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { } status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { - sp<AMessage> msg = new AMessage(kWhatPerformSeek, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatPerformSeek, id()); msg->setInt32("generation", ++mSeekGeneration); msg->setInt64("timeUs", seekTimeUs); msg->post(200000ll); @@ -613,7 +613,7 @@ void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { ALOGE("Unable to find url in SDP"); err = UNKNOWN_ERROR; } else { - sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id()); + sp<AMessage> notify = new AMessage(kWhatNotify, id()); mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); mLooper->registerHandler(mHandler); diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 3718bf9..f1cae53 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -22,8 +22,6 @@ #include "ATSParser.h" -#include <media/stagefright/foundation/AHandlerReflector.h> - namespace android { struct ALooper; @@ -102,7 +100,6 @@ private: bool mBuffering; sp<ALooper> mLooper; - sp<AHandlerReflector<RTSPSource> > mReflector; sp<MyHandler> mHandler; sp<SDPLoader> mSDPLoader; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index b77e1cd..b44d5cc 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1419,8 +1419,10 @@ status_t ACodec::configureCodec( } else { if (encoder) { if (!msg->findInt32( + "complexity", &compressionLevel) && + !msg->findInt32( "flac-compression-level", &compressionLevel)) { - compressionLevel = 5;// default FLAC compression level + compressionLevel = 5; // default FLAC compression level } else if (compressionLevel < 0) { ALOGW("compression level %d outside [0..8] range, " "using 0", diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index cd05c54..ab8ac79 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -103,8 +103,9 @@ private: struct AwesomeLocalRenderer : public AwesomeRenderer { AwesomeLocalRenderer( - const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta) - : mTarget(new SoftwareRenderer(nativeWindow, meta)) { + const sp<ANativeWindow> &nativeWindow, const sp<AMessage> &format) + : mFormat(format), + mTarget(new SoftwareRenderer(nativeWindow)) { } virtual void render(MediaBuffer *buffer) { @@ -116,7 +117,7 @@ struct AwesomeLocalRenderer : public AwesomeRenderer { } void render(const void *data, size_t size, int64_t timestampNs) { - mTarget->render(data, size, timestampNs, NULL); + mTarget->render(data, size, timestampNs, NULL, mFormat); } protected: @@ -126,6 +127,7 @@ protected: } private: + sp<AMessage> mFormat; SoftwareRenderer *mTarget; AwesomeLocalRenderer(const AwesomeLocalRenderer &); @@ -1236,7 +1238,9 @@ void AwesomePlayer::initRenderer_l() { // allocate their buffers in local address space. This renderer // then performs a color conversion and copy to get the data // into the ANativeBuffer. - mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, meta); + sp<AMessage> format; + convertMetaDataToMessage(meta, &format); + mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, format); } } diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 908cdca..008da5a 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -209,25 +209,29 @@ sp<DataSource> DataSource::CreateFromURI( uri = tmp.string(); } - if (httpSource->connect(uri, headers) != OK) { + String8 cacheConfig; + bool disconnectAtHighwatermark; + KeyedVector<String8, String8> nonCacheSpecificHeaders; + if (headers != NULL) { + nonCacheSpecificHeaders = *headers; + NuCachedSource2::RemoveCacheSpecificHeaders( + &nonCacheSpecificHeaders, + &cacheConfig, + &disconnectAtHighwatermark); + } + + if (httpSource->connect(uri, &nonCacheSpecificHeaders) != OK) { ALOGE("Failed to connect http source!"); return NULL; } if (!isWidevine) { - String8 cacheConfig; - bool disconnectAtHighwatermark; - if (headers != NULL) { - KeyedVector<String8, String8> copy = *headers; - NuCachedSource2::RemoveCacheSpecificHeaders( - ©, &cacheConfig, &disconnectAtHighwatermark); - } + String8 contentType = httpSource->getMIMEType(); sp<NuCachedSource2> cachedSource = new NuCachedSource2( httpSource, - cacheConfig.isEmpty() ? NULL : cacheConfig.string()); - - String8 contentType = httpSource->getMIMEType(); + cacheConfig.isEmpty() ? NULL : cacheConfig.string(), + disconnectAtHighwatermark); if (strncasecmp(contentType.string(), "audio/", 6)) { // We're not doing this for streams that appear to be audio-only diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 42691b9..7bb7ed9 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -958,36 +958,14 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { { ALOGV("codec output format changed"); - if ((mFlags & kFlagIsSoftwareCodec) - && mNativeWindow != NULL) { + if (mSoftRenderer == NULL && + mNativeWindow != NULL && + (mFlags & kFlagIsSoftwareCodec)) { AString mime; CHECK(msg->findString("mime", &mime)); - if (!strncasecmp("video/", mime.c_str(), 6)) { - delete mSoftRenderer; - mSoftRenderer = NULL; - - int32_t width, height; - CHECK(msg->findInt32("width", &width)); - CHECK(msg->findInt32("height", &height)); - - int32_t cropLeft, cropTop, cropRight, cropBottom; - CHECK(msg->findRect("crop", - &cropLeft, &cropTop, &cropRight, &cropBottom)); - - int32_t colorFormat; - CHECK(msg->findInt32( - "color-format", &colorFormat)); - - sp<MetaData> meta = new MetaData; - meta->setInt32(kKeyWidth, width); - meta->setInt32(kKeyHeight, height); - meta->setRect(kKeyCropRect, - cropLeft, cropTop, cropRight, cropBottom); - meta->setInt32(kKeyColorFormat, colorFormat); - - mSoftRenderer = - new SoftwareRenderer(mNativeWindow, meta); + if (mime.startsWithIgnoreCase("video/")) { + mSoftRenderer = new SoftwareRenderer(mNativeWindow); } } @@ -1799,6 +1777,8 @@ size_t MediaCodec::updateBuffers( CHECK(info->mNotify == NULL); CHECK(msg->findMessage("reply", &info->mNotify)); + info->mFormat = + (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat; mAvailPortBuffers[portIndex].push_back(i); return i; @@ -1978,7 +1958,8 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) { if (mSoftRenderer != NULL) { mSoftRenderer->render( - info->mData->data(), info->mData->size(), timestampNs, NULL); + info->mData->data(), info->mData->size(), + timestampNs, NULL, info->mFormat); } } @@ -2004,7 +1985,6 @@ ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) { CHECK(!info->mOwnedByClient); { Mutex::Autolock al(mBufferLock); - info->mFormat = portIndex == kPortIndexInput ? mInputFormat : mOutputFormat; info->mOwnedByClient = true; // set image-data diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 2f2a0b3..5b8be46 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -783,6 +783,7 @@ status_t MediaCodecList::addFeature(const char **attrs) { const char *name = NULL; int32_t optional = -1; int32_t required = -1; + const char *value = NULL; while (attrs[i] != NULL) { if (attrs[i + 1] == NULL) { @@ -801,6 +802,9 @@ status_t MediaCodecList::addFeature(const char **attrs) { required = value; } ++i; + } else if (!strcmp(attrs[i], "value")) { + value = attrs[i + 1]; + ++i; } else { return -EINVAL; } @@ -816,7 +820,16 @@ status_t MediaCodecList::addFeature(const char **attrs) { return -EINVAL; } - mCurrentInfo->addFeature(name, (required == 1) || (optional == 0)); + if ((optional != -1 || required != -1) && (value != NULL)) { + ALOGE("feature '%s' has both a value and optional/required attribute", name); + return -EINVAL; + } + + if (value != NULL) { + mCurrentInfo->addFeature(name, value); + } else { + mCurrentInfo->addFeature(name, (required == 1) || (optional == 0)); + } return OK; } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 5f1d1c6..25afc5b 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -109,6 +109,25 @@ status_t convertMetaDataToMessage( msg->setInt32("sar-width", sarWidth); msg->setInt32("sar-height", sarHeight); } + + int32_t colorFormat; + if (meta->findInt32(kKeyColorFormat, &colorFormat)) { + msg->setInt32("color-format", colorFormat); + } + + int32_t cropLeft, cropTop, cropRight, cropBottom; + if (meta->findRect(kKeyCropRect, + &cropLeft, + &cropTop, + &cropRight, + &cropBottom)) { + msg->setRect("crop", cropLeft, cropTop, cropRight, cropBottom); + } + + int32_t rotationDegrees; + if (meta->findInt32(kKeyRotation, &rotationDegrees)) { + msg->setInt32("rotation-degrees", rotationDegrees); + } } else if (!strncasecmp("audio/", mime, 6)) { int32_t numChannels, sampleRate; CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); @@ -475,6 +494,25 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { meta->setInt32(kKeySARWidth, sarWidth); meta->setInt32(kKeySARHeight, sarHeight); } + + int32_t colorFormat; + if (msg->findInt32("color-format", &colorFormat)) { + meta->setInt32(kKeyColorFormat, colorFormat); + } + + int32_t cropLeft, cropTop, cropRight, cropBottom; + if (msg->findRect("crop", + &cropLeft, + &cropTop, + &cropRight, + &cropBottom)) { + meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom); + } + + int32_t rotationDegrees; + if (msg->findInt32("rotation-degrees", &rotationDegrees)) { + meta->setInt32(kKeyRotation, rotationDegrees); + } } else if (mime.startsWith("audio/")) { int32_t numChannels; if (msg->findInt32("channel-count", &numChannels)) { diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 0c5527a..cc98da0 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -21,7 +21,7 @@ #include <cutils/properties.h> // for property_get #include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/MetaData.h> +#include <media/stagefright/foundation/AMessage.h> #include <system/window.h> #include <ui/GraphicBufferMapper.h> #include <gui/IGraphicBufferProducer.h> @@ -33,34 +33,71 @@ static bool runningInEmulator() { return (property_get("ro.kernel.qemu", prop, NULL) > 0); } -SoftwareRenderer::SoftwareRenderer( - const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta) - : mConverter(NULL), +static int ALIGN(int x, int y) { + // y must be a power of 2. + return (x + y - 1) & ~(y - 1); +} + +SoftwareRenderer::SoftwareRenderer(const sp<ANativeWindow> &nativeWindow) + : mColorFormat(OMX_COLOR_FormatUnused), + mConverter(NULL), mYUVMode(None), - mNativeWindow(nativeWindow) { - int32_t tmp; - CHECK(meta->findInt32(kKeyColorFormat, &tmp)); - mColorFormat = (OMX_COLOR_FORMATTYPE)tmp; - - CHECK(meta->findInt32(kKeyWidth, &mWidth)); - CHECK(meta->findInt32(kKeyHeight, &mHeight)); - - if (!meta->findRect( - kKeyCropRect, - &mCropLeft, &mCropTop, &mCropRight, &mCropBottom)) { - mCropLeft = mCropTop = 0; - mCropRight = mWidth - 1; - mCropBottom = mHeight - 1; + mNativeWindow(nativeWindow), + mWidth(0), + mHeight(0), + mCropLeft(0), + mCropTop(0), + mCropRight(0), + mCropBottom(0), + mCropWidth(0), + mCropHeight(0) { +} + +SoftwareRenderer::~SoftwareRenderer() { + delete mConverter; + mConverter = NULL; +} + +void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) { + CHECK(format != NULL); + + int32_t colorFormatNew; + CHECK(format->findInt32("color-format", &colorFormatNew)); + + int32_t widthNew, heightNew; + CHECK(format->findInt32("width", &widthNew)); + CHECK(format->findInt32("height", &heightNew)); + + int32_t cropLeftNew, cropTopNew, cropRightNew, cropBottomNew; + if (!format->findRect( + "crop", &cropLeftNew, &cropTopNew, &cropRightNew, &cropBottomNew)) { + cropLeftNew = cropTopNew = 0; + cropRightNew = widthNew - 1; + cropBottomNew = heightNew - 1; } + if (static_cast<int32_t>(mColorFormat) == colorFormatNew && + mWidth == widthNew && + mHeight == heightNew && + mCropLeft == cropLeftNew && + mCropTop == cropTopNew && + mCropRight == cropRightNew && + mCropBottom == cropBottomNew) { + // Nothing changed, no need to reset renderer. + return; + } + + mColorFormat = static_cast<OMX_COLOR_FORMATTYPE>(colorFormatNew); + mWidth = widthNew; + mHeight = heightNew; + mCropLeft = cropLeftNew; + mCropTop = cropTopNew; + mCropRight = cropRightNew; + mCropBottom = cropBottomNew; + mCropWidth = mCropRight - mCropLeft + 1; mCropHeight = mCropBottom - mCropTop + 1; - int32_t rotationDegrees; - if (!meta->findInt32(kKeyRotation, &rotationDegrees)) { - rotationDegrees = 0; - } - int halFormat; size_t bufWidth, bufHeight; @@ -106,10 +143,12 @@ SoftwareRenderer::SoftwareRenderer( NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); // Width must be multiple of 32??? - CHECK_EQ(0, native_window_set_buffers_geometry( + CHECK_EQ(0, native_window_set_buffers_dimensions( mNativeWindow.get(), bufWidth, - bufHeight, + bufHeight)); + CHECK_EQ(0, native_window_set_buffers_format( + mNativeWindow.get(), halFormat)); // NOTE: native window uses extended right-bottom coordinate @@ -123,6 +162,10 @@ SoftwareRenderer::SoftwareRenderer( CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop)); + int32_t rotationDegrees; + if (!format->findInt32("rotation-degrees", &rotationDegrees)) { + rotationDegrees = 0; + } uint32_t transform; switch (rotationDegrees) { case 0: transform = 0; break; @@ -132,24 +175,15 @@ SoftwareRenderer::SoftwareRenderer( default: transform = 0; break; } - if (transform) { - CHECK_EQ(0, native_window_set_buffers_transform( - mNativeWindow.get(), transform)); - } -} - -SoftwareRenderer::~SoftwareRenderer() { - delete mConverter; - mConverter = NULL; -} - -static int ALIGN(int x, int y) { - // y must be a power of 2. - return (x + y - 1) & ~(y - 1); + CHECK_EQ(0, native_window_set_buffers_transform( + mNativeWindow.get(), transform)); } void SoftwareRenderer::render( - const void *data, size_t size, int64_t timestampNs, void *platformPrivate) { + const void *data, size_t /*size*/, int64_t timestampNs, + void* /*platformPrivate*/, const sp<AMessage>& format) { + resetFormatIfChanged(format); + ANativeWindowBuffer *buf; int err; if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml index f6db0cc..f599004 100644 --- a/media/libstagefright/data/media_codecs_google_audio.xml +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -16,21 +16,76 @@ <Included> <Decoders> - <MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg" /> - <MediaCodec name="OMX.google.amrnb.decoder" type="audio/3gpp" /> - <MediaCodec name="OMX.google.amrwb.decoder" type="audio/amr-wb" /> - <MediaCodec name="OMX.google.aac.decoder" type="audio/mp4a-latm" /> - <MediaCodec name="OMX.google.g711.alaw.decoder" type="audio/g711-alaw" /> - <MediaCodec name="OMX.google.g711.mlaw.decoder" type="audio/g711-mlaw" /> - <MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis" /> - <MediaCodec name="OMX.google.opus.decoder" type="audio/opus" /> - <MediaCodec name="OMX.google.raw.decoder" type="audio/raw" /> + <MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg"> + <Limit name="channel-count" max="2" /> + <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" /> + <Limit name="bitrate" range="8000-320000" /> + </MediaCodec> + <MediaCodec name="OMX.google.amrnb.decoder" type="audio/3gpp"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="8000" /> + <Limit name="bitrate" range="4750-12200" /> + </MediaCodec> + <MediaCodec name="OMX.google.amrwb.decoder" type="audio/amr-wb"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="16000" /> + <Limit name="bitrate" range="6600-23850" /> + </MediaCodec> + <MediaCodec name="OMX.google.aac.decoder" type="audio/mp4a-latm"> + <Limit name="channel-count" max="8" /> + <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" /> + <Limit name="bitrate" range="8000-960000" /> + </MediaCodec> + <MediaCodec name="OMX.google.g711.alaw.decoder" type="audio/g711-alaw"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="8000" /> + <Limit name="bitrate" range="64000" /> + </MediaCodec> + <MediaCodec name="OMX.google.g711.mlaw.decoder" type="audio/g711-mlaw"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="8000" /> + <Limit name="bitrate" range="64000" /> + </MediaCodec> + <MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis"> + <Limit name="channel-count" max="8" /> + <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000,96000" /> + <Limit name="bitrate" range="32000-500000" /> + </MediaCodec> + <MediaCodec name="OMX.google.opus.decoder" type="audio/opus"> + <Limit name="channel-count" max="8" /> + <Limit name="sample-rate" ranges="48000" /> + <Limit name="bitrate" range="6000-510000" /> + </MediaCodec> + <MediaCodec name="OMX.google.raw.decoder" type="audio/raw"> + <Limit name="channel-count" max="8" /> + <Limit name="sample-rate" ranges="8000-96000" /> + <Limit name="bitrate" range="1-10000000" /> + </MediaCodec> </Decoders> - <Encoders> - <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm" /> - <MediaCodec name="OMX.google.amrnb.encoder" type="audio/3gpp" /> - <MediaCodec name="OMX.google.amrwb.encoder" type="audio/amr-wb" /> - <MediaCodec name="OMX.google.flac.encoder" type="audio/flac" /> + <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm"> + <Limit name="channel-count" max="6" /> + <Limit name="sample-rate" ranges="11025,12000,16000,22050,24000,32000,44100,48000" /> + <Limit name="bitrate" range="8000-960000" /> + </MediaCodec> + <MediaCodec name="OMX.google.amrnb.encoder" type="audio/3gpp"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="8000" /> + <Limit name="bitrate" range="4750-12200" /> + <Feature name="bitrate-modes" value="CBR" /> + </MediaCodec> + <MediaCodec name="OMX.google.amrwb.encoder" type="audio/amr-wb"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="16000" /> + <Limit name="bitrate" range="6600-23850" /> + <Feature name="bitrate-modes" value="CBR" /> + </MediaCodec> + <MediaCodec name="OMX.google.flac.encoder" type="audio/flac"> + <Limit name="channel-count" max="2" /> + <Limit name="sample-rate" ranges="1-655350" /> + <Limit name="bitrate" range="1-21000000" /> + <Limit name="complexity" range="0-8" default="5" /> + <Feature name="bitrate-modes" value="CQ" /> + </MediaCodec> </Encoders> </Included> diff --git a/media/libstagefright/data/media_codecs_google_telephony.xml b/media/libstagefright/data/media_codecs_google_telephony.xml index 28f5ffc..5ad90d9 100644 --- a/media/libstagefright/data/media_codecs_google_telephony.xml +++ b/media/libstagefright/data/media_codecs_google_telephony.xml @@ -16,6 +16,10 @@ <Included> <Decoders> - <MediaCodec name="OMX.google.gsm.decoder" type="audio/gsm" /> + <MediaCodec name="OMX.google.gsm.decoder" type="audio/gsm"> + <Limit name="channel-count" max="1" /> + <Limit name="sample-rate" ranges="8000" /> + <Limit name="bitrate" range="13000" /> + </MediaCodec> </Decoders> </Included> diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index 0ba670c..fa3ea89 100644 --- a/media/libstagefright/include/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -24,17 +24,17 @@ namespace android { -struct MetaData; +struct AMessage; class SoftwareRenderer { public: - SoftwareRenderer( - const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta); + explicit SoftwareRenderer(const sp<ANativeWindow> &nativeWindow); ~SoftwareRenderer(); void render( - const void *data, size_t size, int64_t timestampNs, void *platformPrivate); + const void *data, size_t size, int64_t timestampNs, + void *platformPrivate, const sp<AMessage> &format); private: enum YUVMode { @@ -51,6 +51,8 @@ private: SoftwareRenderer(const SoftwareRenderer &); SoftwareRenderer &operator=(const SoftwareRenderer &); + + void resetFormatIfChanged(const sp<AMessage> &format); }; } // namespace android diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 193f8e4..f721d5c 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5017,9 +5017,12 @@ reacquire_wakelock: // activeTracks accumulates a copy of a subset of mActiveTracks Vector< sp<RecordTrack> > activeTracks; - // reference to the (first and only) fast track + // reference to the (first and only) active fast track sp<RecordTrack> fastTrack; + // reference to a fast track which is about to be removed + sp<RecordTrack> fastTrackToRemove; + { // scope for mLock Mutex::Autolock _l(mLock); @@ -5058,6 +5061,10 @@ reacquire_wakelock: activeTrack = mActiveTracks[i]; if (activeTrack->isTerminated()) { + if (activeTrack->isFastTrack()) { + ALOG_ASSERT(fastTrackToRemove == 0); + fastTrackToRemove = activeTrack; + } removeTrack_l(activeTrack); mActiveTracks.remove(activeTrack); mActiveTracksGen++; @@ -5130,10 +5137,12 @@ reacquire_wakelock: effectChains[i]->process_l(); } - // Start the fast capture if it's not already running + // Push a new fast capture state if fast capture is not already running, or cblk change if (mFastCapture != 0) { FastCaptureStateQueue *sq = mFastCapture->sq(); FastCaptureState *state = sq->begin(); + bool didModify = false; + FastCaptureStateQueue::block_t block = FastCaptureStateQueue::BLOCK_UNTIL_PUSHED; if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME && (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) { if (state->mCommand == FastCaptureState::COLD_IDLE) { @@ -5147,19 +5156,32 @@ reacquire_wakelock: mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); #endif - state->mCblk = fastTrack != 0 ? fastTrack->cblk() : NULL; - sq->end(); - sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); + didModify = true; + } + audio_track_cblk_t *cblkOld = state->mCblk; + audio_track_cblk_t *cblkNew = fastTrack != 0 ? fastTrack->cblk() : NULL; + if (cblkNew != cblkOld) { + state->mCblk = cblkNew; + // block until acked if removing a fast track + if (cblkOld != NULL) { + block = FastCaptureStateQueue::BLOCK_UNTIL_ACKED; + } + didModify = true; + } + sq->end(didModify); + if (didModify) { + sq->push(block); #if 0 if (kUseFastCapture == FastCapture_Dynamic) { mNormalSource = mPipeSource; } #endif - } else { - sq->end(false /*didModify*/); } } + // now run the fast track destructor with thread mutex unlocked + fastTrackToRemove.clear(); + // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one. // Only the client(s) that are too slow will overrun. But if even the fastest client is too // slow, then this RecordThread will overrun by not calling HAL read often enough. diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 30a6c7b..e7f9a78 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "Camera2-Parameters" #define ATRACE_TAG ATRACE_TAG_CAMERA -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #include <utils/Log.h> #include <utils/Trace.h> @@ -115,26 +115,6 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); if (!availableFpsRanges.count) return NO_INIT; - previewFpsRange[0] = availableFpsRanges.data.i32[0]; - previewFpsRange[1] = availableFpsRanges.data.i32[1]; - - params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, - String8::format("%d,%d", - previewFpsRange[0] * kFpsToApiScale, - previewFpsRange[1] * kFpsToApiScale)); - - { - String8 supportedPreviewFpsRange; - for (size_t i=0; i < availableFpsRanges.count; i += 2) { - if (i != 0) supportedPreviewFpsRange += ","; - supportedPreviewFpsRange += String8::format("(%d,%d)", - availableFpsRanges.data.i32[i] * kFpsToApiScale, - availableFpsRanges.data.i32[i+1] * kFpsToApiScale); - } - params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, - supportedPreviewFpsRange); - } - previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; params.set(CameraParameters::KEY_PREVIEW_FORMAT, formatEnumToString(previewFormat)); // NV21 @@ -200,6 +180,9 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { supportedPreviewFormats); } + previewFpsRange[0] = availableFpsRanges.data.i32[0]; + previewFpsRange[1] = availableFpsRanges.data.i32[1]; + // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but // still have to do something sane for them @@ -208,6 +191,27 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE, previewFps); + // PREVIEW_FPS_RANGE + // -- Order matters. Set range after single value to so that a roundtrip + // of setParameters(getParameters()) would keep the FPS range in higher + // order. + params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + previewFpsRange[0] * kFpsToApiScale, + previewFpsRange[1] * kFpsToApiScale)); + + { + String8 supportedPreviewFpsRange; + for (size_t i=0; i < availableFpsRanges.count; i += 2) { + if (i != 0) supportedPreviewFpsRange += ","; + supportedPreviewFpsRange += String8::format("(%d,%d)", + availableFpsRanges.data.i32[i] * kFpsToApiScale, + availableFpsRanges.data.i32[i+1] * kFpsToApiScale); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, + supportedPreviewFpsRange); + } + { SortedVector<int32_t> sortedPreviewFrameRates; @@ -640,8 +644,17 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { focusMode = Parameters::FOCUS_MODE_AUTO; params.set(CameraParameters::KEY_FOCUS_MODE, CameraParameters::FOCUS_MODE_AUTO); - String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY); - bool addComma = true; + String8 supportedFocusModes; + bool addComma = false; + camera_metadata_ro_entry_t focusDistanceCalibration = + staticInfo(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, 0, 0, false); + + if (focusDistanceCalibration.count && + focusDistanceCalibration.data.u8[0] != + ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED) { + supportedFocusModes += CameraParameters::FOCUS_MODE_INFINITY; + addComma = true; + } for (size_t i=0; i < availableAfModes.count; i++) { if (addComma) supportedFocusModes += ","; @@ -1103,7 +1116,7 @@ camera_metadata_ro_entry_t Parameters::staticInfo(uint32_t tag, status_t Parameters::set(const String8& paramString) { status_t res; - CameraParameters newParams(paramString); + CameraParameters2 newParams(paramString); // TODO: Currently ignoring any changes to supposedly read-only parameters // such as supported preview sizes, etc. Should probably produce an error if @@ -1146,29 +1159,73 @@ status_t Parameters::set(const String8& paramString) { // RECORDING_HINT (always supported) validatedParams.recordingHint = boolFromString( newParams.get(CameraParameters::KEY_RECORDING_HINT) ); - bool recordingHintChanged = validatedParams.recordingHint != recordingHint; - ALOGV_IF(recordingHintChanged, "%s: Recording hint changed to %d", - __FUNCTION__, recordingHintChanged); + IF_ALOGV() { // Avoid unused variable warning + bool recordingHintChanged = + validatedParams.recordingHint != recordingHint; + if (recordingHintChanged) { + ALOGV("%s: Recording hint changed to %d", + __FUNCTION__, validatedParams.recordingHint); + } + } // PREVIEW_FPS_RANGE - bool fpsRangeChanged = false; - int32_t lastSetFpsRange[2]; - params.getPreviewFpsRange(&lastSetFpsRange[0], &lastSetFpsRange[1]); - lastSetFpsRange[0] /= kFpsToApiScale; - lastSetFpsRange[1] /= kFpsToApiScale; + /** + * Use the single FPS value if it was set later than the range. + * Otherwise, use the range value. + */ + bool fpsUseSingleValue; + { + const char *fpsRange, *fpsSingle; + fpsRange = newParams.get(CameraParameters::KEY_PREVIEW_FRAME_RATE); + fpsSingle = newParams.get(CameraParameters::KEY_PREVIEW_FPS_RANGE); + + /** + * Pick either the range or the single key if only one was set. + * + * If both are set, pick the one that has greater set order. + */ + if (fpsRange == NULL && fpsSingle == NULL) { + ALOGE("%s: FPS was not set. One of %s or %s must be set.", + __FUNCTION__, CameraParameters::KEY_PREVIEW_FRAME_RATE, + CameraParameters::KEY_PREVIEW_FPS_RANGE); + return BAD_VALUE; + } else if (fpsRange == NULL) { + fpsUseSingleValue = true; + ALOGV("%s: FPS range not set, using FPS single value", + __FUNCTION__); + } else if (fpsSingle == NULL) { + fpsUseSingleValue = false; + ALOGV("%s: FPS single not set, using FPS range value", + __FUNCTION__); + } else { + int fpsKeyOrder; + res = newParams.compareSetOrder( + CameraParameters::KEY_PREVIEW_FRAME_RATE, + CameraParameters::KEY_PREVIEW_FPS_RANGE, + &fpsKeyOrder); + LOG_ALWAYS_FATAL_IF(res != OK, "Impossibly bad FPS keys"); + + fpsUseSingleValue = (fpsKeyOrder > 0); + + } + + ALOGV("%s: Preview FPS value is used from '%s'", + __FUNCTION__, fpsUseSingleValue ? "single" : "range"); + } newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0], &validatedParams.previewFpsRange[1]); + validatedParams.previewFpsRange[0] /= kFpsToApiScale; validatedParams.previewFpsRange[1] /= kFpsToApiScale; - // Compare the FPS range value from the last set() to the current set() - // to determine if the client has changed it - if (validatedParams.previewFpsRange[0] != lastSetFpsRange[0] || - validatedParams.previewFpsRange[1] != lastSetFpsRange[1]) { + // Ignore the FPS range if the FPS single has higher precedence + if (!fpsUseSingleValue) { + ALOGV("%s: Preview FPS range (%d, %d)", __FUNCTION__, + validatedParams.previewFpsRange[0], + validatedParams.previewFpsRange[1]); - fpsRangeChanged = true; camera_metadata_ro_entry_t availablePreviewFpsRanges = staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); for (i = 0; i < availablePreviewFpsRanges.count; i += 2) { @@ -1217,14 +1274,13 @@ status_t Parameters::set(const String8& paramString) { } } - // PREVIEW_FRAME_RATE Deprecated, only use if the preview fps range is - // unchanged this time. The single-value FPS is the same as the minimum of - // the range. To detect whether the application has changed the value of - // previewFps, compare against their last-set preview FPS. - if (!fpsRangeChanged) { + // PREVIEW_FRAME_RATE Deprecated + // - Use only if the single FPS value was set later than the FPS range + if (fpsUseSingleValue) { int previewFps = newParams.getPreviewFrameRate(); - int lastSetPreviewFps = params.getPreviewFrameRate(); - if (previewFps != lastSetPreviewFps || recordingHintChanged) { + ALOGV("%s: Preview FPS single value requested: %d", + __FUNCTION__, previewFps); + { camera_metadata_ro_entry_t availableFrameRates = staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); /** @@ -1293,6 +1349,35 @@ status_t Parameters::set(const String8& paramString) { } } + /** + * Update Preview FPS and Preview FPS ranges based on + * what we actually set. + * + * This updates the API-visible (Camera.Parameters#getParameters) values of + * the FPS fields, not only the internal versions. + * + * Order matters: The value that was set last takes precedence. + * - If the client does a setParameters(getParameters()) we retain + * the same order for preview FPS. + */ + if (!fpsUseSingleValue) { + // Set fps single, then fps range (range wins) + newParams.setPreviewFrameRate( + fpsFromRange(/*min*/validatedParams.previewFpsRange[0], + /*max*/validatedParams.previewFpsRange[1])); + newParams.setPreviewFpsRange( + validatedParams.previewFpsRange[0] * kFpsToApiScale, + validatedParams.previewFpsRange[1] * kFpsToApiScale); + } else { + // Set fps range, then fps single (single wins) + newParams.setPreviewFpsRange( + validatedParams.previewFpsRange[0] * kFpsToApiScale, + validatedParams.previewFpsRange[1] * kFpsToApiScale); + // Set this to the same value, but with higher priority + newParams.setPreviewFrameRate( + newParams.getPreviewFrameRate()); + } + // PICTURE_SIZE newParams.getPictureSize(&validatedParams.pictureWidth, &validatedParams.pictureHeight); diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h index f95c69a..d9d33c4 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.h +++ b/services/camera/libcameraservice/api1/client2/Parameters.h @@ -25,6 +25,7 @@ #include <utils/Vector.h> #include <utils/KeyedVector.h> #include <camera/CameraParameters.h> +#include <camera/CameraParameters2.h> #include <camera/CameraMetadata.h> namespace android { @@ -32,7 +33,7 @@ namespace camera2 { /** * Current camera state; this is the full state of the Camera under the old - * camera API (contents of the CameraParameters object in a more-efficient + * camera API (contents of the CameraParameters2 object in a more-efficient * format, plus other state). The enum values are mostly based off the * corresponding camera2 enums, not the camera1 strings. A few are defined here * if they don't cleanly map to camera2 values. @@ -136,7 +137,7 @@ struct Parameters { LIGHTFX_HDR } lightFx; - CameraParameters params; + CameraParameters2 params; String8 paramsFlattened; // These parameters are also part of the camera API-visible state, but not |