diff options
48 files changed, 2992 insertions, 555 deletions
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index e862ec3..d38d976 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -53,6 +53,7 @@ enum { kKeyESDS = 'esds', // raw data kKeyAACProfile = 'aacp', // int32_t kKeyAVCC = 'avcc', // raw data + kKeyHVCC = 'hvcc', // raw data kKeyD263 = 'd263', // raw data kKeyVorbisInfo = 'vinf', // raw data kKeyVorbisBooks = 'vboo', // raw data @@ -170,6 +171,7 @@ enum { enum { kTypeESDS = 'esds', kTypeAVCC = 'avcc', + kTypeHVCC = 'hvcc', kTypeD263 = 'd263', }; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 5121c17..5590b60 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -352,6 +352,9 @@ private: int64_t getDecodingTimeUs(); + status_t parseHEVCCodecSpecificData( + const void *data, size_t size, + unsigned *profile, unsigned *level); status_t parseAVCCodecSpecificData( const void *data, size_t size, unsigned *profile, unsigned *level); diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp index 788dfc8..14b31ec 100644 --- a/media/img_utils/src/DngUtils.cpp +++ b/media/img_utils/src/DngUtils.cpp @@ -19,7 +19,7 @@ namespace android { namespace img_utils { -OpcodeListBuilder::OpcodeListBuilder() : mOpList(), mEndianOut(&mOpList, BIG) { +OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) { if(mEndianOut.open() != OK) { ALOGE("%s: Open failed.", __FUNCTION__); } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 1c808d0..db61e85 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -203,23 +203,6 @@ status_t AudioRecord::set( mFrameSize = sizeof(uint8_t); } - // validate framecount - size_t minFrameCount; - status_t status = AudioRecord::getMinFrameCount(&minFrameCount, - sampleRate, format, channelMask); - if (status != NO_ERROR) { - ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d", - sampleRate, format, channelMask, status); - return status; - } - ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount); - - if (frameCount == 0) { - frameCount = minFrameCount; - } else if (frameCount < minFrameCount) { - ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount); - return BAD_VALUE; - } // mFrameCount is initialized in openRecord_l mReqFrameCount = frameCount; @@ -242,7 +225,7 @@ status_t AudioRecord::set( } // create the IAudioRecord - status = openRecord_l(0 /*epoch*/); + status_t status = openRecord_l(0 /*epoch*/); if (status != NO_ERROR) { if (mAudioRecordThread != 0) { @@ -464,6 +447,29 @@ status_t AudioRecord::openRecord_l(size_t epoch) size_t frameCount = mReqFrameCount; if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) { + // validate framecount + // If fast track was not requested, this preserves + // the old behavior of validating on client side. + // FIXME Eventually the validation should be done on server side + // regardless of whether it's a fast or normal track. It's debatable + // whether to account for the input latency to provision buffers appropriately. + size_t minFrameCount; + status = AudioRecord::getMinFrameCount(&minFrameCount, + mSampleRate, mFormat, mChannelMask); + if (status != NO_ERROR) { + ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; " + "status %d", + mSampleRate, mFormat, mChannelMask, status); + return status; + } + + if (frameCount == 0) { + frameCount = minFrameCount; + } else if (frameCount < minFrameCount) { + ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount); + return BAD_VALUE; + } + // Make sure that application is notified with sufficient margin before overrun if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { mNotificationFramesAct = frameCount/2; diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 219dbfd..0dbfa62 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -134,10 +134,17 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques ssize_t filled = rear - front; // pipe should not be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { - ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); - mIsShutdown = true; - status = NO_INIT; - goto end; + if (mIsOut) { + ALOGE("Shared memory control block is corrupt (filled=%d, mFrameCount=%u); " + "shutting down", filled, mFrameCount); + mIsShutdown = true; + status = NO_INIT; + goto end; + } + // for input, sync up on overrun + filled = 0; + cblk->u.mStreaming.mFront = rear; + (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags); } // don't allow filling pipe beyond the nominal size size_t avail = mIsOut ? mFrameCount - filled : filled; diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index eee72c5..77d131b 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -421,7 +421,6 @@ public: status = (status_t)reply.readInt32(); *num_ports = (unsigned int)reply.readInt32(); } - ALOGI("listAudioPorts() status %d got *num_ports %d", status, *num_ports); if (status == NO_ERROR) { if (numPortsReq > *num_ports) { numPortsReq = *num_ports; @@ -840,7 +839,6 @@ status_t BnAudioPolicyService::onTransact( status_t status = listAudioPorts(role, type, &numPorts, ports, &generation); reply->writeInt32(status); reply->writeInt32(numPorts); - ALOGI("LIST_AUDIO_PORTS status %d got numPorts %d", status, numPorts); if (status == NO_ERROR) { if (numPortsReq > numPorts) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 469c9ca..cfbf282 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -37,6 +37,7 @@ NuPlayer::Decoder::Decoder( : mNotify(notify), mNativeWindow(nativeWindow), mBufferGeneration(0), + mPaused(true), mComponentName("decoder") { // Every decoder has its own looper because MediaCodec operations // are blocking, but NuPlayer needs asynchronous operations. @@ -112,6 +113,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mOutputBuffers.size()); requestCodecNotification(); + mPaused = false; } void NuPlayer::Decoder::requestCodecNotification() { @@ -352,6 +354,11 @@ void NuPlayer::Decoder::onFlush() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); + mPaused = true; +} + +void NuPlayer::Decoder::onResume() { + mPaused = false; } void NuPlayer::Decoder::onShutdown() { @@ -380,6 +387,7 @@ void NuPlayer::Decoder::onShutdown() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatShutdownCompleted); notify->post(); + mPaused = true; } void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { @@ -397,7 +405,9 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { case kWhatCodecNotify: { if (!isStaleReply(msg)) { - while (handleAnInputBuffer()) { + if (!mPaused) { + while (handleAnInputBuffer()) { + } } while (handleAnOutputBuffer()) { @@ -430,6 +440,12 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatResume: + { + onResume(); + break; + } + case kWhatShutdown: { onShutdown(); @@ -447,7 +463,7 @@ void NuPlayer::Decoder::signalFlush() { } void NuPlayer::Decoder::signalResume() { - // nothing to do + (new AMessage(kWhatResume, id()))->post(); } void NuPlayer::Decoder::initiateShutdown() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 94243fc..2892584 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -87,11 +87,13 @@ private: void onConfigure(const sp<AMessage> &format); void onFlush(); + void onResume(); void onInputBufferFilled(const sp<AMessage> &msg); void onRenderBuffer(const sp<AMessage> &msg); void onShutdown(); int32_t mBufferGeneration; + bool mPaused; AString mComponentName; bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index e07b6aa..aabe0ec 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -95,6 +95,7 @@ private: uint64_t* mCurrentSampleInfoOffsets; bool mIsAVC; + bool mIsHEVC; size_t mNALLengthSize; bool mStarted; @@ -317,6 +318,9 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC('a', 'v', 'c', '1'): return MEDIA_MIMETYPE_VIDEO_AVC; + case FOURCC('h', 'v', 'c', '1'): + case FOURCC('h', 'e', 'v', '1'): + return MEDIA_MIMETYPE_VIDEO_HEVC; default: CHECK(!"should not be here."); return NULL; @@ -478,11 +482,20 @@ status_t MPEG4Extractor::readMetaData() { off64_t offset = 0; status_t err; while (true) { + off64_t orig_offset = offset; err = parseChunk(&offset, 0); - if (err == OK) { + + if (offset <= orig_offset) { + // only continue parsing if the offset was advanced, + // otherwise we might end up in an infinite loop + ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset); + err = ERROR_MALFORMED; + break; + } else if (err == OK) { continue; + } else if (err != UNKNOWN_ERROR) { + break; } - uint32_t hdr[2]; if (mDataSource->readAt(offset, hdr, 8) < 8) { break; @@ -505,8 +518,6 @@ status_t MPEG4Extractor::readMetaData() { } else { mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); } - - mInitCheck = OK; } else { mInitCheck = err; } @@ -758,8 +769,25 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // The smallest valid chunk is 16 bytes long in this case. return ERROR_MALFORMED; } + } else if (chunk_size == 0) { + if (depth == 0) { + // atom extends to end of file + off64_t sourceSize; + if (mDataSource->getSize(&sourceSize) == OK) { + chunk_size = (sourceSize - *offset); + } else { + // XXX could we just pick a "sufficiently large" value here? + ALOGE("atom size is 0, and data source has no size"); + return ERROR_MALFORMED; + } + } else { + // not allowed for non-toplevel atoms, skip it + *offset += 4; + return OK; + } } else if (chunk_size < 8) { // The smallest valid chunk is 8 bytes long. + ALOGE("invalid chunk size: %d", int(chunk_size)); return ERROR_MALFORMED; } @@ -1288,6 +1316,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('H', '2', '6', '3'): case FOURCC('h', '2', '6', '3'): case FOURCC('a', 'v', 'c', '1'): + case FOURCC('h', 'v', 'c', '1'): + case FOURCC('h', 'e', 'v', '1'): { mHasVideo = true; @@ -1580,6 +1610,21 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('h', 'v', 'c', 'C'): + { + sp<ABuffer> buffer = new ABuffer(chunk_data_size); + + if (mDataSource->readAt( + data_offset, buffer->data(), chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + + mLastTrack->meta->setData( + kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size); + + *offset += chunk_size; + break; + } case FOURCC('d', '2', '6', '3'): { @@ -2452,6 +2497,11 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { || type != kTypeAVCC) { return ERROR_MALFORMED; } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { + if (!track->meta->findData(kKeyHVCC, &type, &data, &size) + || type != kTypeHVCC) { + return ERROR_MALFORMED; + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { if (!track->meta->findData(kKeyESDS, &type, &data, &size) @@ -2776,6 +2826,7 @@ MPEG4Source::MPEG4Source( mCurrentSampleInfoOffsetsAllocSize(0), mCurrentSampleInfoOffsets(NULL), mIsAVC(false), + mIsHEVC(false), mNALLengthSize(0), mStarted(false), mGroup(NULL), @@ -2800,6 +2851,7 @@ MPEG4Source::MPEG4Source( CHECK(success); mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); + mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC); if (mIsAVC) { uint32_t type; @@ -2814,6 +2866,18 @@ MPEG4Source::MPEG4Source( // The number of bytes used to encode the length of a NAL unit. mNALLengthSize = 1 + (ptr[4] & 3); + } else if (mIsHEVC) { + uint32_t type; + const void *data; + size_t size; + CHECK(format->findData(kKeyHVCC, &type, &data, &size)); + + const uint8_t *ptr = (const uint8_t *)data; + + CHECK(size >= 7); + CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 + + mNALLengthSize = 1 + (ptr[14 + 7] & 3); } CHECK(format->findInt32(kKeyTrackID, &mTrackId)); @@ -3562,7 +3626,7 @@ status_t MPEG4Source::read( } } - if (!mIsAVC || mWantsNALFragments) { + if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) { if (newBuffer) { ssize_t num_bytes_read = mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); @@ -3594,7 +3658,7 @@ status_t MPEG4Source::read( ++mCurrentSampleIndex; } - if (!mIsAVC) { + if (!mIsAVC && !mIsHEVC) { *out = mBuffer; mBuffer = NULL; @@ -3837,7 +3901,7 @@ status_t MPEG4Source::fragmentedRead( bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); } - if (!mIsAVC || mWantsNALFragments) { + if ((!mIsAVC && !mIsHEVC)|| mWantsNALFragments) { if (newBuffer) { ssize_t num_bytes_read = mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); @@ -3869,7 +3933,7 @@ status_t MPEG4Source::fragmentedRead( ++mCurrentSampleIndex; } - if (!mIsAVC) { + if (!mIsAVC && !mIsHEVC) { *out = mBuffer; mBuffer = NULL; @@ -4043,6 +4107,8 @@ static bool isCompatibleBrand(uint32_t fourcc) { FOURCC('i', 's', 'o', 'm'), FOURCC('i', 's', 'o', '2'), FOURCC('a', 'v', 'c', '1'), + FOURCC('h', 'v', 'c', '1'), + FOURCC('h', 'e', 'v', '1'), FOURCC('3', 'g', 'p', '4'), FOURCC('m', 'p', '4', '1'), FOURCC('m', 'p', '4', '2'), diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 8a451c8..cd51582 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -70,11 +70,6 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) { return; } - // These are currently still used by the video editing suite. - addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm"); - addMediaCodec( - false /* encoder */, "OMX.google.raw.decoder", "audio/raw"); - for (size_t i = mCodecInfos.size(); i-- > 0;) { CodecInfo *info = &mCodecInfos.editItemAt(i); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index c028dbf..354712c 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -381,6 +381,57 @@ sp<MediaSource> OMXCodec::Create( return NULL; } +status_t OMXCodec::parseHEVCCodecSpecificData( + const void *data, size_t size, + unsigned *profile, unsigned *level) { + const uint8_t *ptr = (const uint8_t *)data; + + // verify minimum size and configurationVersion == 1. + if (size < 7 || ptr[0] != 1) { + return ERROR_MALFORMED; + } + + *profile = (ptr[1] & 31); + *level = ptr[12]; + + ptr += 22; + size -= 22; + + size_t numofArrays = (char)ptr[0]; + ptr += 1; + size -= 1; + size_t j = 0, i = 0; + for (i = 0; i < numofArrays; i++) { + ptr += 1; + size -= 1; + + // Num of nals + size_t numofNals = U16_AT(ptr); + ptr += 2; + size -= 2; + + for (j = 0;j < numofNals;j++) { + if (size < 2) { + return ERROR_MALFORMED; + } + + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + if (size < length) { + return ERROR_MALFORMED; + } + addCodecSpecificData(ptr, length); + + ptr += length; + size -= length; + } + } + return OK; +} + status_t OMXCodec::parseAVCCodecSpecificData( const void *data, size_t size, unsigned *profile, unsigned *level) { @@ -493,6 +544,20 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { CODEC_LOGI( "AVC profile = %u (%s), level = %u", profile, AVCProfileToString(profile), level); + } else if (meta->findData(kKeyHVCC, &type, &data, &size)) { + // Parse the HEVCDecoderConfigurationRecord + + unsigned profile, level; + status_t err; + if ((err = parseHEVCCodecSpecificData( + data, size, &profile, &level)) != OK) { + ALOGE("Malformed HEVC codec specific data."); + return err; + } + + CODEC_LOGI( + "HEVC profile = %u , level = %u", + profile, level); } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { addCodecSpecificData(data, size); @@ -822,6 +887,8 @@ void OMXCodec::setVideoInputFormat( OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { compressionFormat = OMX_VIDEO_CodingAVC; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { + compressionFormat = OMX_VIDEO_CodingHEVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { @@ -1217,6 +1284,8 @@ status_t OMXCodec::setVideoOutputFormat( compressionFormat = OMX_VIDEO_CodingAVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG4; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { + compressionFormat = OMX_VIDEO_CodingHEVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP8, mime)) { @@ -1411,6 +1480,8 @@ void OMXCodec::setComponentRole( "audio_decoder.g711alaw", "audio_encoder.g711alaw" }, { MEDIA_MIMETYPE_VIDEO_AVC, "video_decoder.avc", "video_encoder.avc" }, + { MEDIA_MIMETYPE_VIDEO_HEVC, + "video_decoder.hevc", "video_encoder.hevc" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "video_decoder.mpeg4", "video_encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_H263, @@ -3009,7 +3080,8 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { size_t size = specific->mSize; - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME) + if ((!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME) || + !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mMIME)) && !(mQuirks & kWantsNALFragments)) { static const uint8_t kNALStartCode[4] = { 0x00, 0x00, 0x00, 0x01 }; diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 047fac7..7ff31a1 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -217,6 +217,56 @@ status_t convertMetaDataToMessage( buffer->meta()->setInt32("csd", true); buffer->meta()->setInt64("timeUs", 0); msg->setBuffer("csd-1", buffer); + } else if (meta->findData(kKeyHVCC, &type, &data, &size)) { + const uint8_t *ptr = (const uint8_t *)data; + + CHECK(size >= 7); + CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 + uint8_t profile = ptr[1] & 31; + uint8_t level = ptr[12]; + ptr += 22; + size -= 22; + + + size_t numofArrays = (char)ptr[0]; + ptr += 1; + size -= 1; + size_t j = 0, i = 0; + + sp<ABuffer> buffer = new ABuffer(1024); + buffer->setRange(0, 0); + + for (i = 0; i < numofArrays; i++) { + ptr += 1; + size -= 1; + + //Num of nals + size_t numofNals = U16_AT(ptr); + + ptr += 2; + size -= 2; + + for (j = 0; j < numofNals; j++) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + buffer->size() + 4, ptr, length); + buffer->setRange(0, buffer->size() + 4 + length); + + ptr += length; + size -= length; + } + } + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); + } else if (meta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds((const char *)data, size); CHECK_EQ(esds.InitCheck(), (status_t)OK); diff --git a/media/libstagefright/codecs/hevcdec/Android.mk b/media/libstagefright/codecs/hevcdec/Android.mk new file mode 100644 index 0000000..2fe347b --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libstagefright_soft_hevcdec +LOCAL_MODULE_TAGS := optional + +LOCAL_STATIC_LIBRARIES := libhevcdec +LOCAL_SRC_FILES := SoftHEVC.cpp + +LOCAL_C_INCLUDES := $(TOP)/external/libhevc/decoder +LOCAL_C_INCLUDES += $(TOP)/external/libhevc/common +LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include +LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax + +LOCAL_SHARED_LIBRARIES := libstagefright +LOCAL_SHARED_LIBRARIES += libstagefright_omx +LOCAL_SHARED_LIBRARIES += libstagefright_foundation +LOCAL_SHARED_LIBRARIES += libutils +LOCAL_SHARED_LIBRARIES += liblog + + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp new file mode 100644 index 0000000..0aae5ed --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp @@ -0,0 +1,710 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftHEVC" +#include <utils/Log.h> + +#include "ihevc_typedefs.h" +#include "iv.h" +#include "ivd.h" +#include "ithread.h" +#include "ihevcd_cxa.h" +#include "SoftHEVC.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <OMX_VideoExt.h> + +namespace android { + +#define componentName "video_decoder.hevc" +#define codingType OMX_VIDEO_CodingHEVC +#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_HEVC + +/** Function and structure definitions to keep code similar for each codec */ +#define ivdec_api_function ihevcd_cxa_api_function +#define ivdext_init_ip_t ihevcd_cxa_init_ip_t +#define ivdext_init_op_t ihevcd_cxa_init_op_t +#define ivdext_fill_mem_rec_ip_t ihevcd_cxa_fill_mem_rec_ip_t +#define ivdext_fill_mem_rec_op_t ihevcd_cxa_fill_mem_rec_op_t +#define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t +#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t + +#define IVDEXT_CMD_CTL_SET_NUM_CORES \ + (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel1 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel2 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel21 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel3 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel31 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel4 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel41 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel5 }, + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel51 }, +}; + +SoftHEVC::SoftHEVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent(name, componentName, codingType, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + CODEC_MAX_WIDTH /* width */, CODEC_MAX_HEIGHT /* height */, callbacks, + appData, component) { + initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, + CODEC_MIME_TYPE); + + mOmxColorFormat = OMX_COLOR_FormatYUV420Planar; + mStride = mWidth; + + if (OMX_COLOR_FormatYUV420Planar == mOmxColorFormat) { + mIvColorFormat = IV_YUV_420P; + } else if (OMX_COLOR_FormatYUV420SemiPlanar == mOmxColorFormat) { + mIvColorFormat = IV_YUV_420SP_UV; + } + + mInitWidth = mWidth; + mInitHeight = mHeight; + + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftHEVC::~SoftHEVC() { + ALOGD("In SoftHEVC::~SoftHEVC"); + CHECK_EQ(deInitDecoder(), (status_t)OK); +} + +static size_t GetCPUCoreCount() { + long cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK(cpuCoreCount >= 1); + ALOGD("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +status_t SoftHEVC::getVersion() { + ivd_ctl_getversioninfo_ip_t s_ctl_ip; + ivd_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t); + s_ctl_ip.pv_version_buffer = au1_buf; + s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version number: 0x%x", + s_ctl_op.u4_error_code); + } else { + ALOGD("Ittiam decoder version number: %s", + (char *)s_ctl_ip.pv_version_buffer); + } + return OK; +} + +status_t SoftHEVC::setParams(WORD32 stride, IVD_VIDEO_DECODE_MODE_T decMode) { + ivd_ctl_set_config_ip_t s_ctl_ip; + ivd_ctl_set_config_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + s_ctl_ip.u4_disp_wd = stride; + s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; + + s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; + s_ctl_ip.e_vid_dec_mode = decMode; + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; + s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); + + ALOGD("Set the run-time (dynamic) parameters"); + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the run-time parameters: 0x%x", + s_ctl_op.u4_error_code); + + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftHEVC::resetPlugin() { + mIsInFlush = false; + mReceivedEOS = false; + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + /* Initialize both start and end times */ + gettimeofday(&mTimeStart, NULL); + gettimeofday(&mTimeEnd, NULL); + + return OK; +} + +status_t SoftHEVC::resetDecoder() { + ivd_ctl_reset_ip_t s_ctl_ip; + ivd_ctl_reset_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; + s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + if (IV_SUCCESS != status) { + ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); + return UNKNOWN_ERROR; + } + + /* Set the run-time (dynamic) parameters */ + setParams(0, IVD_DECODE_FRAME); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + return OK; +} + +status_t SoftHEVC::setNumCores() { + ivdext_ctl_set_num_cores_ip_t s_set_cores_ip; + ivdext_ctl_set_num_cores_op_t s_set_cores_op; + IV_API_CALL_STATUS_T status; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES); + s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); + ALOGD("Set number of cores to %u", s_set_cores_ip.u4_num_cores); + status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, + (void *)&s_set_cores_op); + if (IV_SUCCESS != status) { + ALOGE("Error in setting number of cores: 0x%x", + s_set_cores_op.u4_error_code); + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftHEVC::setFlushMode() { + IV_API_CALL_STATUS_T status; + ivd_ctl_flush_ip_t s_video_flush_ip; + ivd_ctl_flush_op_t s_video_flush_op; + + s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH; + s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t); + s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t); + ALOGD("Set the decoder in flush mode "); + + /* Set the decoder in Flush mode, subsequent decode() calls will flush */ + status = ivdec_api_function(mCodecCtx, (void *)&s_video_flush_ip, + (void *)&s_video_flush_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status, + s_video_flush_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mIsInFlush = true; + return OK; +} + +status_t SoftHEVC::initDecoder() { + IV_API_CALL_STATUS_T status; + + UWORD32 u4_num_reorder_frames; + UWORD32 u4_num_ref_frames; + UWORD32 u4_share_disp_buf; + WORD32 i4_level; + + mNumCores = GetCPUCoreCount(); + + /* Initialize number of ref and reorder modes (for HEVC) */ + u4_num_reorder_frames = 16; + u4_num_ref_frames = 16; + u4_share_disp_buf = 0; + + if ((mWidth * mHeight) > (1920 * 1088)) { + i4_level = 50; + } else if ((mWidth * mHeight) > (1280 * 720)) { + i4_level = 41; + } else { + i4_level = 31; + } + + { + iv_num_mem_rec_ip_t s_num_mem_rec_ip; + iv_num_mem_rec_op_t s_num_mem_rec_op; + + s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip); + s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op); + s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC; + + ALOGV("Get number of mem records"); + status = ivdec_api_function(mCodecCtx, (void*)&s_num_mem_rec_ip, + (void*)&s_num_mem_rec_op); + if (IV_SUCCESS != status) { + ALOGE("Error in getting mem records: 0x%x", + s_num_mem_rec_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec; + } + + mMemRecords = (iv_mem_rec_t*)ivd_aligned_malloc( + 128, mNumMemRecords * sizeof(iv_mem_rec_t)); + if (mMemRecords == NULL) { + ALOGE("Allocation failure"); + return NO_MEMORY; + } + + { + size_t i; + ivdext_fill_mem_rec_ip_t s_fill_mem_ip; + ivdext_fill_mem_rec_op_t s_fill_mem_op; + iv_mem_rec_t *ps_mem_rec; + + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = + sizeof(ivdext_fill_mem_rec_ip_t); + s_fill_mem_ip.i4_level = i4_level; + s_fill_mem_ip.u4_num_reorder_frames = u4_num_reorder_frames; + s_fill_mem_ip.u4_num_ref_frames = u4_num_ref_frames; + s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf; + s_fill_mem_ip.u4_num_extra_disp_buf = 0; + s_fill_mem_ip.e_output_format = mIvColorFormat; + + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = mWidth; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = mHeight; + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = + sizeof(ivdext_fill_mem_rec_op_t); + + ps_mem_rec = mMemRecords; + for (i = 0; i < mNumMemRecords; i++) + ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_fill_mem_ip, + (void *)&s_fill_mem_op); + + if (IV_SUCCESS != status) { + ALOGE("Error in filling mem records: 0x%x", + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + mNumMemRecords = + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled; + + ps_mem_rec = mMemRecords; + + for (i = 0; i < mNumMemRecords; i++) { + ps_mem_rec->pv_base = ivd_aligned_malloc( + ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size); + if (ps_mem_rec->pv_base == NULL) { + ALOGE("Allocation failure for memory record #%zu of size %u", + i, ps_mem_rec->u4_mem_size); + status = IV_FAIL; + return NO_MEMORY; + } + + ps_mem_rec++; + } + } + + /* Initialize the decoder */ + { + ivdext_init_ip_t s_init_ip; + ivdext_init_op_t s_init_op; + + void *dec_fxns = (void *)ivdec_api_function; + + s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t); + s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT; + s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = mWidth; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight; + + s_init_ip.i4_level = i4_level; + s_init_ip.u4_num_reorder_frames = u4_num_reorder_frames; + s_init_ip.u4_num_ref_frames = u4_num_ref_frames; + s_init_ip.u4_share_disp_buf = u4_share_disp_buf; + s_init_ip.u4_num_extra_disp_buf = 0; + + s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op); + + s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords; + s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat; + + mCodecCtx = (iv_obj_t*)mMemRecords[0].pv_base; + mCodecCtx->pv_fxns = dec_fxns; + mCodecCtx->u4_size = sizeof(iv_obj_t); + + ALOGD("Initializing decoder"); + status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, + (void *)&s_init_op); + if (status != IV_SUCCESS) { + ALOGE("Error in init: 0x%x", + s_init_op.s_ivd_init_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + } + + /* Reset the plugin state */ + resetPlugin(); + + /* Set the run time (dynamic) parameters */ + setParams(0, IVD_DECODE_FRAME); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + /* Get codec version */ + getVersion(); + + /* Allocate internal picture buffer */ + mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, mStride * mHeight * 3 / 2); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %zu", mStride * mHeight * 3 / 2); + return NO_MEMORY; + } + + return OK; +} + +status_t SoftHEVC::deInitDecoder() { + size_t i; + iv_mem_rec_t *ps_mem_rec; + ps_mem_rec = mMemRecords; + ALOGD("Freeing codec memory"); + for (i = 0; i < mNumMemRecords; i++) { + ivd_aligned_free(ps_mem_rec->pv_base); + ps_mem_rec++; + } + + ivd_aligned_free(mMemRecords); + ivd_aligned_free(mFlushOutBuffer); + return OK; +} + +void SoftHEVC::onReset() { + ALOGD("onReset called"); + SoftVideoDecoderOMXComponent::onReset(); + + resetDecoder(); + resetPlugin(); +} + +void SoftHEVC::onPortFlushCompleted(OMX_U32 portIndex) { + ALOGD("onPortFlushCompleted on port %d", portIndex); + + /* Once the output buffers are flushed, ignore any buffers that are held in decoder */ + if (kOutputPortIndex == portIndex) { + setFlushMode(); + + /* Reset the time stamp arrays */ + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + while (true) { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + IV_API_CALL_STATUS_T status; + size_t sizeY, sizeUV; + + s_dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE; + + s_dec_ip.u4_ts = 0; + s_dec_ip.pv_stream_buffer = NULL; + s_dec_ip.u4_num_Bytes = 0; + + s_dec_ip.u4_size = sizeof(ivd_video_decode_ip_t); + s_dec_op.u4_size = sizeof(ivd_video_decode_op_t); + + sizeY = mStride * mHeight; + sizeUV = sizeY / 4; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[0] = sizeY; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[1] = sizeUV; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[2] = sizeUV; + + s_dec_ip.s_out_buffer.pu1_bufs[0] = mFlushOutBuffer; + s_dec_ip.s_out_buffer.pu1_bufs[1] = + s_dec_ip.s_out_buffer.pu1_bufs[0] + sizeY; + s_dec_ip.s_out_buffer.pu1_bufs[2] = + s_dec_ip.s_out_buffer.pu1_bufs[1] + sizeUV; + s_dec_ip.s_out_buffer.u4_num_bufs = 3; + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, + (void *)&s_dec_op); + if (0 == s_dec_op.u4_output_present) { + resetPlugin(); + break; + } + } + } +} + +void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { + IV_API_CALL_STATUS_T status; + + UNUSED(portIndex); + + if (mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); + List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); + + /* If input EOS is seen and decoder is not in flush mode, + * set the decoder in flush mode. + * There can be a case where EOS is sent along with last picture data + * In that case, only after decoding that input data, decoder has to be + * put in flush. This case is handled here */ + + if (mReceivedEOS && !mIsInFlush) { + setFlushMode(); + } + + while (outQueue.size() == kNumBuffers) { + BufferInfo *inInfo; + OMX_BUFFERHEADERTYPE *inHeader; + + BufferInfo *outInfo; + OMX_BUFFERHEADERTYPE *outHeader; + size_t timeStampIx; + + inInfo = NULL; + inHeader = NULL; + + if (!mIsInFlush) { + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } else { + break; + } + } + + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + outHeader->nTimeStamp = 0; + outHeader->nOffset = 0; + + if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + ALOGD("EOS seen on input"); + mReceivedEOS = true; + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + setFlushMode(); + } + } + + /* Get a free slot in timestamp array to hold input timestamp */ + { + size_t i; + timeStampIx = 0; + for (i = 0; i < MAX_TIME_STAMPS; i++) { + if (!mTimeStampsValid[i]) { + timeStampIx = i; + break; + } + } + if (inHeader != NULL) { + mTimeStampsValid[timeStampIx] = true; + mTimeStamps[timeStampIx] = inHeader->nTimeStamp; + } + } + + { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + WORD32 timeDelay, timeTaken; + size_t sizeY, sizeUV; + + s_dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE; + + /* When in flush and after EOS with zero byte input, + * inHeader is set to zero. Hence check for non-null */ + if (inHeader != NULL) { + s_dec_ip.u4_ts = timeStampIx; + s_dec_ip.pv_stream_buffer = inHeader->pBuffer + + inHeader->nOffset; + s_dec_ip.u4_num_Bytes = inHeader->nFilledLen; + } else { + s_dec_ip.u4_ts = 0; + s_dec_ip.pv_stream_buffer = NULL; + s_dec_ip.u4_num_Bytes = 0; + } + + s_dec_ip.u4_size = sizeof(ivd_video_decode_ip_t); + s_dec_op.u4_size = sizeof(ivd_video_decode_op_t); + + sizeY = mStride * mHeight; + sizeUV = sizeY / 4; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[0] = sizeY; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[1] = sizeUV; + s_dec_ip.s_out_buffer.u4_min_out_buf_size[2] = sizeUV; + + s_dec_ip.s_out_buffer.pu1_bufs[0] = outHeader->pBuffer; + s_dec_ip.s_out_buffer.pu1_bufs[1] = + s_dec_ip.s_out_buffer.pu1_bufs[0] + sizeY; + s_dec_ip.s_out_buffer.pu1_bufs[2] = + s_dec_ip.s_out_buffer.pu1_bufs[1] + sizeUV; + s_dec_ip.s_out_buffer.u4_num_bufs = 3; + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, + (void *)&s_dec_op); + + GETTIME(&mTimeEnd, NULL); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGD("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + s_dec_op.u4_num_bytes_consumed); + + if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { + /* If the input did not contain picture data, then ignore + * the associated timestamp */ + mTimeStampsValid[timeStampIx] = false; + } + + /* If valid height and width are decoded, + * then look at change in resolution */ + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { + uint32_t width = s_dec_op.u4_pic_wd; + uint32_t height = s_dec_op.u4_pic_ht; + + if ((width != mWidth || height != mHeight)) { + mWidth = width; + mHeight = height; + mStride = mWidth; + + /* If width and height are greater than the + * the dimensions used during codec create, then + * delete the current instance and recreate an instance with + * new dimensions */ + /* TODO: The following does not work currently, since the decoder + * currently returns 0 x 0 as width height when it is not supported + * Once the decoder is updated to return actual width and height, + * then this can be validated*/ + + if ((mWidth * mHeight) > (mInitWidth * mInitHeight)) { + status_t ret; + ALOGD("Trying reInit"); + ret = deInitDecoder(); + if (OK != ret) { + // TODO: Handle graceful exit + ALOGE("Create failure"); + return; + } + + mInitWidth = mWidth; + mInitHeight = mHeight; + + ret = initDecoder(); + if (OK != ret) { + // TODO: Handle graceful exit + ALOGE("Create failure"); + return; + } + } + updatePortDefinitions(); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + } + + if (s_dec_op.u4_output_present) { + outHeader->nFilledLen = (mStride * mHeight * 3) / 2; + + outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; + mTimeStampsValid[s_dec_op.u4_ts] = false; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } else { + /* If in flush mode and no output is returned by the codec, + * then come out of flush mode */ + mIsInFlush = false; + + /* If EOS was recieved on input port and there is no output + * from the codec, then signal EOS on output port */ + if (mReceivedEOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags |= OMX_BUFFERFLAG_EOS; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + resetPlugin(); + } + } + } + + // TODO: Handle more than one picture data + if (inHeader != NULL) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent(const char *name, + const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + return new android::SoftHEVC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.h b/media/libstagefright/codecs/hevcdec/SoftHEVC.h new file mode 100644 index 0000000..20db0e1 --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.h @@ -0,0 +1,117 @@ +/* + * 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 SOFT_HEVC_H_ + +#define SOFT_HEVC_H_ + +#include "SoftVideoDecoderOMXComponent.h" +#include <sys/time.h> + +namespace android { + +#define ivd_aligned_malloc(alignment, size) memalign(alignment, size) +#define ivd_aligned_free(buf) free(buf) + +/** Number of entries in the time-stamp array */ +#define MAX_TIME_STAMPS 64 + +/** Maximum number of cores supported by the codec */ +#define CODEC_MAX_NUM_CORES 4 + +#define CODEC_MAX_WIDTH 1920 + +#define CODEC_MAX_HEIGHT 1088 + +/** Input buffer size */ +#define INPUT_BUF_SIZE (1024 * 1024) + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = ((end.tv_sec - start.tv_sec) * 1000000) + \ + (end.tv_usec - start.tv_usec); + +struct SoftHEVC: public SoftVideoDecoderOMXComponent { + SoftHEVC(const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftHEVC(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); +private: + // Number of input and output buffers + enum { + kNumBuffers = 8 + }; + + iv_obj_t *mCodecCtx; // Codec context + iv_mem_rec_t *mMemRecords; // Memory records requested by the codec + size_t mNumMemRecords; // Number of memory records requested by the codec + + uint32_t mNewWidth; // New width after change in resolution + uint32_t mNewHeight; // New height after change in resolution + uint32_t mInitWidth; // Width used during codec creation + uint32_t mInitHeight; // Height used during codec creation + size_t mStride; // Stride to be used for display buffers + + size_t mNumCores; // Number of cores to be uesd by the codec + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + // Internal buffer to be used to flush out the buffers from decoder + uint8_t *mFlushOutBuffer; + + // Status of entries in the timestamp array + bool mTimeStampsValid[MAX_TIME_STAMPS]; + + // Timestamp array - Since codec does not take 64 bit timestamps, + // they are maintained in the plugin + OMX_S64 mTimeStamps[MAX_TIME_STAMPS]; + + OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format + IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format + + bool mIsInFlush; // codec is flush mode + bool mReceivedEOS; // EOS is receieved on input port + bool mIsAdapting; // plugin in middle of change in resolution + + status_t initDecoder(); + status_t deInitDecoder(); + status_t setFlushMode(); + status_t setParams(WORD32 stride, IVD_VIDEO_DECODE_MODE_T decMode); + status_t getVersion(); + status_t setNumCores(); + status_t resetDecoder(); + status_t resetPlugin(); + + DISALLOW_EVIL_CONSTRUCTORS (SoftHEVC); +}; + +} // namespace android + +#endif // SOFT_HEVC_H_ diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml index b1f93de..f6db0cc 100644 --- a/media/libstagefright/data/media_codecs_google_audio.xml +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -24,6 +24,7 @@ <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" /> </Decoders> <Encoders> diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml index 41e0efb..9b930bc 100644 --- a/media/libstagefright/data/media_codecs_google_video.xml +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -19,6 +19,7 @@ <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" /> <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" /> <MediaCodec name="OMX.google.h264.decoder" type="video/avc" /> + <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" /> <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" /> <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" /> </Decoders> diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 65f5404..9b6958a 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -42,6 +42,7 @@ static const struct { { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" }, { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" }, { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" }, + { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" }, { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index eb9fcf7..1c383f7 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -183,12 +183,12 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( return OMX_ErrorUnsupportedIndex; } - if (index >= mNumProfileLevels) { + if (profileLevel->nProfileIndex >= mNumProfileLevels) { return OMX_ErrorNoMore; } - profileLevel->eProfile = mProfileLevels[index].mProfile; - profileLevel->eLevel = mProfileLevels[index].mLevel; + profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; + profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; return OMX_ErrorNone; } diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index f7b6f64..0bdf5a3 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -64,6 +64,7 @@ LOCAL_32_BIT_ONLY := true LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp +LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 457ac3d..a4cad4e 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -169,7 +169,8 @@ AudioFlinger::AudioFlinger() mBtNrecIsOff(false), mIsLowRamDevice(true), mIsDeviceTypeKnown(false), - mGlobalEffectEnableTime(0) + mGlobalEffectEnableTime(0), + mPrimaryOutputSampleRate(0) { getpid_cached = getpid(); char value[PROPERTY_VALUE_MAX]; @@ -1265,7 +1266,7 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below - mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), + mMemoryDealer(new MemoryDealer(4 * 1024*1024, "AudioFlinger::Client")), mPid(pid), mTimedTrackCount(0) { @@ -1679,6 +1680,8 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, mHardwareStatus = AUDIO_HW_SET_MODE; hwDevHal->set_mode(hwDevHal, mMode); mHardwareStatus = AUDIO_HW_IDLE; + + mPrimaryOutputSampleRate = config.sample_rate; } return id; } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 29dc6b2..6e73a14 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -50,6 +50,8 @@ #include <media/AudioBufferProvider.h> #include <media/ExtendedAudioBufferProvider.h> + +#include "FastCapture.h" #include "FastMixer.h" #include <media/nbaio/NBAIO.h> #include "AudioWatchdog.h" @@ -691,6 +693,9 @@ private: nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled sp<PatchPanel> mPatchPanel; + + uint32_t mPrimaryOutputSampleRate; // sample rate of the primary output, or zero if none + // protected by mHardwareLock }; #undef INCLUDING_FROM_AUDIOFLINGER_H diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 805eaa4..d73292e 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -34,6 +34,7 @@ #include <system/audio.h> #include <audio_utils/primitives.h> +#include <audio_utils/format.h> #include <common_time/local_clock.h> #include <common_time/cc_helper.h> @@ -88,6 +89,103 @@ void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buf } } +template <typename T> +T min(const T& a, const T& b) +{ + return a < b ? a : b; +} + +AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat) : + mTrackBufferProvider(NULL), + mChannels(channels), + mInputFormat(inputFormat), + mOutputFormat(outputFormat), + mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)), + mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)), + mOutputData(NULL), + mOutputCount(0), + mConsumed(0) +{ + ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat); + if (requiresInternalBuffers()) { + mOutputCount = 256; + (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize); + } + mBuffer.frameCount = 0; +} + +AudioMixer::ReformatBufferProvider::~ReformatBufferProvider() +{ + ALOGV("~ReformatBufferProvider(%p)", this); + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + free(mOutputData); +} + +status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, + int64_t pts) { + //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)", + // this, pBuffer, pBuffer->frameCount, pts); + if (!requiresInternalBuffers()) { + status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts); + if (res == OK) { + memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat, + pBuffer->frameCount * mChannels); + } + return res; + } + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = pBuffer->frameCount; + status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts); + // TODO: Track down a bug in the upstream provider + // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0, + // "ReformatBufferProvider::getNextBuffer():" + // " Invalid zero framecount returned from getNextBuffer()"); + if (res != OK || mBuffer.frameCount == 0) { + pBuffer->raw = NULL; + pBuffer->frameCount = 0; + return res; + } + } + ALOG_ASSERT(mConsumed < mBuffer.frameCount); + size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed); + count = min(count, pBuffer->frameCount); + pBuffer->raw = mOutputData; + pBuffer->frameCount = count; + //ALOGV("reformatting %d frames from %#x to %#x, %d chan", + // pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels); + memcpy_by_audio_format(pBuffer->raw, mOutputFormat, + (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat, + pBuffer->frameCount * mChannels); + return OK; +} + +void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) { + //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))", + // this, pBuffer, pBuffer->frameCount); + if (!requiresInternalBuffers()) { + mTrackBufferProvider->releaseBuffer(pBuffer); + return; + } + // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount"); + mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content + if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) { + mConsumed = 0; + mTrackBufferProvider->releaseBuffer(&mBuffer); + // ALOG_ASSERT(mBuffer.frameCount == 0); + } + pBuffer->raw = NULL; + pBuffer->frameCount = 0; +} + +void AudioMixer::ReformatBufferProvider::reset() { + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + mConsumed = 0; +} // ---------------------------------------------------------------------------- bool AudioMixer::sIsMultichannelCapable = false; @@ -153,8 +251,13 @@ void AudioMixer::setLog(NBLog::Writer *log) mState.mLog = log; } -int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) +int AudioMixer::getTrackName(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) { + if (!isValidPcmTrackFormat(format)) { + ALOGE("AudioMixer::getTrackName invalid format (%#x)", format); + return -1; + } uint32_t names = (~mTrackNames) & mConfiguredNames; if (names != 0) { int n = __builtin_ctz(names); @@ -162,8 +265,8 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) // assume default parameters for the track, except where noted below track_t* t = &mState.tracks[n]; t->needs = 0; - t->volume[0] = UNITY_GAIN; - t->volume[1] = UNITY_GAIN; + t->volume[0] = UNITY_GAIN_INT; + t->volume[1] = UNITY_GAIN_INT; // no initialization needed // t->prevVolume[0] // t->prevVolume[1] @@ -176,7 +279,8 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) // t->frameCount t->channelCount = audio_channel_count_from_out_mask(channelMask); t->enabled = false; - t->format = 16; + ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO, + "Non-stereo channel mask: %d\n", channelMask); t->channelMask = channelMask; t->sessionId = sessionId; // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) @@ -191,9 +295,15 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name) t->mainBuffer = NULL; t->auxBuffer = NULL; + t->mInputBufferProvider = NULL; + t->mReformatBufferProvider = NULL; t->downmixerBufferProvider = NULL; t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; - + t->mFormat = format; + t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; + if (t->mFormat != t->mMixerInFormat) { + prepareTrackForReformat(t, n); + } status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask); if (status != OK) { ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); @@ -237,9 +347,9 @@ void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unuse if (pTrack->downmixerBufferProvider != NULL) { // this track had previously been configured with a downmixer, delete it ALOGV(" deleting old downmixer"); - pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider; delete pTrack->downmixerBufferProvider; pTrack->downmixerBufferProvider = NULL; + reconfigureBufferProviders(pTrack); } else { ALOGV(" nothing to do, no downmixer to delete"); } @@ -333,21 +443,51 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack" // initialization successful: - // - keep track of the real buffer provider in case it was set before - pDbp->mTrackBufferProvider = pTrack->bufferProvider; - // - we'll use the downmix effect integrated inside this - // track's buffer provider, and we'll use it as the track's buffer provider pTrack->downmixerBufferProvider = pDbp; - pTrack->bufferProvider = pDbp; - + reconfigureBufferProviders(pTrack); return NO_ERROR; noDownmixForActiveTrack: delete pDbp; pTrack->downmixerBufferProvider = NULL; + reconfigureBufferProviders(pTrack); return NO_INIT; } +void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { + ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); + if (pTrack->mReformatBufferProvider != NULL) { + delete pTrack->mReformatBufferProvider; + pTrack->mReformatBufferProvider = NULL; + reconfigureBufferProviders(pTrack); + } +} + +status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName) +{ + ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat); + // discard the previous reformatter if there was one + unprepareTrackForReformat(pTrack, trackName); + pTrack->mReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(pTrack->channelMask), + pTrack->mFormat, pTrack->mMixerInFormat); + reconfigureBufferProviders(pTrack); + return NO_ERROR; +} + +void AudioMixer::reconfigureBufferProviders(track_t* pTrack) +{ + pTrack->bufferProvider = pTrack->mInputBufferProvider; + if (pTrack->mReformatBufferProvider) { + pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider; + pTrack->bufferProvider = pTrack->mReformatBufferProvider; + } + if (pTrack->downmixerBufferProvider) { + pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider; + pTrack->bufferProvider = pTrack->downmixerBufferProvider; + } +} + void AudioMixer::deleteTrackName(int name) { ALOGV("AudioMixer::deleteTrackName(%d)", name); @@ -364,6 +504,8 @@ void AudioMixer::deleteTrackName(int name) track.resampler = NULL; // delete the downmixer unprepareTrackForDownmix(&mState.tracks[name], name); + // delete the reformatter + unprepareTrackForReformat(&mState.tracks[name], name); mTrackNames &= ~(1<<name); } @@ -394,6 +536,44 @@ void AudioMixer::disable(int name) } } +/* Sets the volume ramp variables for the AudioMixer. + * + * The volume ramp variables are used to transition between the previous + * volume to the target volume. The duration of the transition is + * set by ramp, which is either 0 for immediate, or typically one state + * framecount period. + * + * @param newFloatValue new volume target in float [0.0, 1.0]. + * @param ramp number of frames to increment over. ramp is 0 if the volume + * should be set immediately. + * @param volume reference to the U4.12 target volume, set on return. + * @param prevVolume reference to the U4.27 previous volume, set on return. + * @param volumeInc reference to the increment per output audio frame, set on return. + * @return true if the volume has changed, false if volume is same. + */ +static inline bool setVolumeRampVariables(float newFloatValue, int32_t ramp, + int16_t &volume, int32_t &prevVolume, int32_t &volumeInc) { + int32_t newValue = newFloatValue * AudioMixer::UNITY_GAIN_INT; + if (newValue > AudioMixer::UNITY_GAIN_INT) { + newValue = AudioMixer::UNITY_GAIN_INT; + } else if (newValue < 0) { + ALOGE("negative volume %.7g", newFloatValue); + newValue = 0; // should never happen, but for safety check. + } + if (newValue == volume) { + return false; + } + if (ramp != 0) { + volumeInc = ((newValue - volume) << 16) / ramp; + prevVolume = (volumeInc == 0 ? newValue : volume) << 16; + } else { + volumeInc = 0; + prevVolume = newValue << 16; + } + volume = newValue; + return true; +} + void AudioMixer::setParameter(int name, int target, int param, void *value) { name -= TRACK0; @@ -435,9 +615,20 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) invalidateState(1 << name); } break; - case FORMAT: - ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT); - break; + case FORMAT: { + audio_format_t format = static_cast<audio_format_t>(valueInt); + if (track.mFormat != format) { + ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); + track.mFormat = format; + ALOGV("setParameter(TRACK, FORMAT, %#x)", format); + //if (track.mFormat != track.mMixerInFormat) + { + ALOGD("Reformatting!"); + prepareTrackForReformat(&track, name); + } + invalidateState(1 << name); + } + } break; // FIXME do we want to support setting the downmix type from AudioFlinger? // for a specific track? or per mixer? /* case DOWNMIX_TYPE: @@ -484,41 +675,23 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) switch (param) { case VOLUME0: case VOLUME1: - if (track.volume[param-VOLUME0] != valueInt) { - ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt); - track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16; - track.volume[param-VOLUME0] = valueInt; - if (target == VOLUME) { - track.prevVolume[param-VOLUME0] = valueInt << 16; - track.volumeInc[param-VOLUME0] = 0; - } else { - int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0]; - int32_t volInc = d / int32_t(mState.frameCount); - track.volumeInc[param-VOLUME0] = volInc; - if (volInc == 0) { - track.prevVolume[param-VOLUME0] = valueInt << 16; - } - } + if (setVolumeRampVariables(*reinterpret_cast<float*>(value), + target == RAMP_VOLUME ? mState.frameCount : 0, + track.volume[param - VOLUME0], track.prevVolume[param - VOLUME0], + track.volumeInc[param - VOLUME0])) { + ALOGV("setParameter(%s, VOLUME%d: %04x)", + target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0, + track.volume[param - VOLUME0]); invalidateState(1 << name); } break; case AUXLEVEL: //ALOG_ASSERT(0 <= valueInt && valueInt <= MAX_GAIN_INT, "bad aux level %d", valueInt); - if (track.auxLevel != valueInt) { - ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt); - track.prevAuxLevel = track.auxLevel << 16; - track.auxLevel = valueInt; - if (target == VOLUME) { - track.prevAuxLevel = valueInt << 16; - track.auxInc = 0; - } else { - int32_t d = (valueInt<<16) - track.prevAuxLevel; - int32_t volInc = d / int32_t(mState.frameCount); - track.auxInc = volInc; - if (volInc == 0) { - track.prevAuxLevel = valueInt << 16; - } - } + if (setVolumeRampVariables(*reinterpret_cast<float*>(value), + target == RAMP_VOLUME ? mState.frameCount : 0, + track.auxLevel, track.prevAuxLevel, track.auxInc)) { + ALOGV("setParameter(%s, AUXLEVEL: %04x)", + target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel); invalidateState(1 << name); } break; @@ -550,8 +723,9 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) } else { quality = AudioResampler::DEFAULT_QUALITY; } + const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32; resampler = AudioResampler::create( - format, + bits, // the resampler sees the number of channels after the downmixer, if any (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount), devSampleRate, quality); @@ -596,21 +770,16 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider name -= TRACK0; ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - if (mState.tracks[name].downmixerBufferProvider != NULL) { - // update required? - if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) { - ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider); - // setting the buffer provider for a track that gets downmixed consists in: - // 1/ setting the buffer provider to the "downmix / buffer provider" wrapper - // so it's the one that gets called when the buffer provider is needed, - mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider; - // 2/ saving the buffer provider for the track so the wrapper can use it - // when it downmixes. - mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider; - } - } else { - mState.tracks[name].bufferProvider = bufferProvider; + if (mState.tracks[name].mInputBufferProvider == bufferProvider) { + return; // don't reset any buffer providers if identical. + } + if (mState.tracks[name].mReformatBufferProvider != NULL) { + mState.tracks[name].mReformatBufferProvider->reset(); + } else if (mState.tracks[name].downmixerBufferProvider != NULL) { } + + mState.tracks[name].mInputBufferProvider = bufferProvider; + reconfigureBufferProviders(&mState.tracks[name]); } @@ -769,7 +938,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram // always resample with unity gain when sending to auxiliary buffer to be able // to apply send level after resampling // TODO: modify each resampler to support aux channel? - t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT); memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) { @@ -779,7 +948,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram } } else { if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) { - t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT); memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); volumeRampStereo(t, out, outFrameCount, temp, aux); @@ -1301,6 +1470,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, AudioBufferProvider::Buffer& b(t.buffer); int32_t* out = t.mainBuffer; + float *fout = reinterpret_cast<float*>(out); size_t numFrames = state->frameCount; const int16_t vl = t.volume[0]; @@ -1314,9 +1484,10 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, // in == NULL can happen if the track was flushed just after having // been enabled for mixing. - if (in == NULL || ((unsigned long)in & 3)) { - memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); - ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: " + if (in == NULL || (((uintptr_t)in) & 3)) { + memset(out, 0, numFrames + * MAX_NUM_CHANNELS * audio_bytes_per_sample(t.mMixerFormat)); + ALOGE_IF((((uintptr_t)in) & 3), "process stereo track: input buffer alignment pb: " "buffer %p track %d, channels %d, needs %08x", in, i, t.channelCount, t.needs); return; @@ -1324,8 +1495,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, size_t outFrames = b.frameCount; switch (t.mMixerFormat) { - case AUDIO_FORMAT_PCM_FLOAT: { - float *fout = reinterpret_cast<float*>(out); + case AUDIO_FORMAT_PCM_FLOAT: do { uint32_t rl = *reinterpret_cast<const uint32_t *>(in); in += 2; @@ -1336,9 +1506,9 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, // Note: In case of later int16_t sink output, // conversion and clamping is done by memcpy_to_i16_from_float(). } while (--outFrames); - } break; + break; case AUDIO_FORMAT_PCM_16_BIT: - if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { + if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) { // volume is boosted, so we might need to clamp even though // we process only one track. do { diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 09e63a6..766ff60 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -31,7 +31,7 @@ #include <media/nbaio/NBLog.h> // FIXME This is actually unity gain, which might not be max in future, expressed in U.12 -#define MAX_GAIN_INT AudioMixer::UNITY_GAIN +#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT namespace android { @@ -58,7 +58,8 @@ public: // maximum number of channels supported for the content static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8; - static const uint16_t UNITY_GAIN = 0x1000; + static const uint16_t UNITY_GAIN_INT = 0x1000; + static const float UNITY_GAIN_FLOAT = 1.0f; enum { // names @@ -104,7 +105,10 @@ public: // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS // Allocate a track name. Returns new track name if successful, -1 on failure. - int getTrackName(audio_channel_mask_t channelMask, int sessionId); + // The failure could be because of an invalid channelMask or format, or that + // the track capacity of the mixer is exceeded. + int getTrackName(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); // Free an allocated track by name void deleteTrackName(int name); @@ -122,6 +126,13 @@ public: size_t getUnreleasedFrames(int name) const; + static inline bool isValidPcmTrackFormat(audio_format_t format) { + return format == AUDIO_FORMAT_PCM_16_BIT || + format == AUDIO_FORMAT_PCM_24_BIT_PACKED || + format == AUDIO_FORMAT_PCM_32_BIT || + format == AUDIO_FORMAT_PCM_FLOAT; + } + private: enum { @@ -143,6 +154,7 @@ private: struct state_t; struct track_t; class DownmixerBufferProvider; + class ReformatBufferProvider; typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); @@ -170,7 +182,7 @@ private: uint16_t frameCount; uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) - uint8_t format; // always 16 + uint8_t unused_padding; // formerly format, was always 16 uint16_t enabled; // actually bool audio_channel_mask_t channelMask; @@ -193,14 +205,19 @@ private: int32_t* auxBuffer; // 16-byte boundary - + AudioBufferProvider* mInputBufferProvider; // 4 bytes + ReformatBufferProvider* mReformatBufferProvider; // 4 bytes DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes int32_t sessionId; - audio_format_t mMixerFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + // 16-byte boundary + audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + audio_format_t mFormat; // input track format + audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + // each track must be converted to this format. - int32_t padding[1]; + int32_t mUnused[1]; // alignment padding // 16-byte boundary @@ -239,6 +256,35 @@ private: effect_config_t mDownmixConfig; }; + // AudioBufferProvider wrapper that reformats track to acceptable mixer input type + class ReformatBufferProvider : public AudioBufferProvider { + public: + ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat); + virtual ~ReformatBufferProvider(); + + // overrides AudioBufferProvider methods + virtual status_t getNextBuffer(Buffer* buffer, int64_t pts); + virtual void releaseBuffer(Buffer* buffer); + + void reset(); + inline bool requiresInternalBuffers() { + return true; //mInputFrameSize < mOutputFrameSize; + } + + AudioBufferProvider* mTrackBufferProvider; + int32_t mChannels; + audio_format_t mInputFormat; + audio_format_t mOutputFormat; + size_t mInputFrameSize; + size_t mOutputFrameSize; + // (only) required for reformatting to a larger size. + AudioBufferProvider::Buffer mBuffer; + void* mOutputData; + size_t mOutputCount; + size_t mConsumed; + }; + // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. uint32_t mTrackNames; @@ -266,6 +312,9 @@ private: static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask); static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); static void unprepareTrackForDownmix(track_t* pTrack, int trackName); + static status_t prepareTrackForReformat(track_t* pTrack, int trackNum); + static void unprepareTrackForReformat(track_t* pTrack, int trackName); + static void reconfigureBufferProviders(track_t* pTrack); static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index 3abe8fd..318eb57 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -455,13 +455,20 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const Constants& c(mConstants); const TC* const coefs = mConstants.mFirCoefs; TI* impulse = mInBuffer.getImpulse(); - size_t inputIndex = mInputIndex; + size_t inputIndex = 0; uint32_t phaseFraction = mPhaseFraction; const uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; // stereo output - size_t inFrameCount = getInFrameCountRequired(outFrameCount); const uint32_t phaseWrapLimit = c.mL << c.mShift; + size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) + / phaseWrapLimit; + // sanity check that inFrameCount is in signed 32 bit integer range. + ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); + + //ALOGV("inFrameCount:%d outFrameCount:%d" + // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); // NOTE: be very careful when modifying the code here. register // pressure is very high and a small change might cause the compiler @@ -471,29 +478,39 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // the following logic is a bit convoluted to keep the main processing loop // as tight as possible with register allocation. while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { + //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + + // check inputIndex overflow + ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", + inputIndex, mBuffer.frameCount); + // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). + // We may not fetch a new buffer if the existing data is sufficient. + while (mBuffer.frameCount == 0 && inFrameCount > 0) { mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { goto resample_exit; } + inFrameCount -= mBuffer.frameCount; if (phaseFraction >= phaseWrapLimit) { // read in data mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= mBuffer.frameCount) { - inputIndex -= mBuffer.frameCount; + inputIndex = 0; provider->releaseBuffer(&mBuffer); break; } mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } @@ -504,9 +521,6 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const int halfNumCoefs = c.mHalfNumCoefs; const TO* const volumeSimd = mVolumeSimd; - // reread the last input in. - mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); - // main processing loop while (CC_LIKELY(outputIndex < outputSampleCount)) { // caution: fir() is inlined and may be large. @@ -515,6 +529,10 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. // + //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + ALOG_ASSERT(phaseFraction < phaseWrapLimit); fir<CHANNELS, LOCKED, STRIDE>( &out[outputIndex], phaseFraction, phaseWrapLimit, @@ -524,26 +542,34 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, phaseFraction += phaseIncrement; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= frameCount) { goto done; // need a new buffer } mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } done: - // often arrives here when input buffer runs out - if (inputIndex >= frameCount) { - inputIndex -= frameCount; + // We arrive here when we're finished or when the input buffer runs out. + // Regardless we need to release the input buffer if we've acquired it. + if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) + ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", + inputIndex, frameCount); // must have been fully read. + inputIndex = 0; provider->releaseBuffer(&mBuffer); - // mBuffer.frameCount MUST be zero here. + ALOG_ASSERT(mBuffer.frameCount == 0); } } resample_exit: + // inputIndex must be zero in all three cases: + // (1) the buffer never was been acquired; (2) the buffer was + // released at "done:"; or (3) getNextBuffer() failed. + ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", + inputIndex, mBuffer.frameCount, phaseFraction); + ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer mInBuffer.setImpulse(impulse); - mInputIndex = inputIndex; mPhaseFraction = phaseFraction; } diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp new file mode 100644 index 0000000..0c9b976 --- /dev/null +++ b/services/audioflinger/FastCapture.cpp @@ -0,0 +1,222 @@ +/* + * 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. + */ + +#define LOG_TAG "FastCapture" +//#define LOG_NDEBUG 0 + +#define ATRACE_TAG ATRACE_TAG_AUDIO + +#include "Configuration.h" +#include <linux/futex.h> +#include <sys/syscall.h> +#include <media/AudioBufferProvider.h> +#include <utils/Log.h> +#include <utils/Trace.h> +#include "FastCapture.h" + +namespace android { + +/*static*/ const FastCaptureState FastCapture::initial; + +FastCapture::FastCapture() : FastThread(), + inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0), + readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0), + // dummyDumpState + totalNativeFramesRead(0) +{ + previous = &initial; + current = &initial; + + mDummyDumpState = &dummyDumpState; +} + +FastCapture::~FastCapture() +{ +} + +FastCaptureStateQueue* FastCapture::sq() +{ + return &mSQ; +} + +const FastThreadState *FastCapture::poll() +{ + return mSQ.poll(); +} + +void FastCapture::setLog(NBLog::Writer *logWriter __unused) +{ +} + +void FastCapture::onIdle() +{ + preIdle = *(const FastCaptureState *)current; + current = &preIdle; +} + +void FastCapture::onExit() +{ + delete[] readBuffer; +} + +bool FastCapture::isSubClassCommand(FastThreadState::Command command) +{ + switch ((FastCaptureState::Command) command) { + case FastCaptureState::READ: + case FastCaptureState::WRITE: + case FastCaptureState::READ_WRITE: + return true; + default: + return false; + } +} + +void FastCapture::onStateChange() +{ + const FastCaptureState * const current = (const FastCaptureState *) this->current; + const FastCaptureState * const previous = (const FastCaptureState *) this->previous; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; + const size_t frameCount = current->mFrameCount; + + bool eitherChanged = false; + + // check for change in input HAL configuration + NBAIO_Format previousFormat = format; + if (current->mInputSourceGen != inputSourceGen) { + inputSource = current->mInputSource; + inputSourceGen = current->mInputSourceGen; + if (inputSource == NULL) { + format = Format_Invalid; + sampleRate = 0; + } else { + format = inputSource->format(); + sampleRate = Format_sampleRate(format); + unsigned channelCount = Format_channelCount(format); + ALOG_ASSERT(channelCount == 1 || channelCount == 2); + } + dumpState->mSampleRate = sampleRate; + eitherChanged = true; + } + + // check for change in pipe + if (current->mPipeSinkGen != pipeSinkGen) { + pipeSink = current->mPipeSink; + pipeSinkGen = current->mPipeSinkGen; + eitherChanged = true; + } + + // input source and pipe sink must be compatible + if (eitherChanged && inputSource != NULL && pipeSink != NULL) { + ALOG_ASSERT(Format_isEqual(format, pipeSink->format())); + } + + if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { + // FIXME to avoid priority inversion, don't delete here + delete[] readBuffer; + readBuffer = NULL; + if (frameCount > 0 && sampleRate > 0) { + // FIXME new may block for unbounded time at internal mutex of the heap + // implementation; it would be better to have normal capture thread allocate for + // us to avoid blocking here and to prevent possible priority inversion + unsigned channelCount = Format_channelCount(format); + // FIXME frameSize + readBuffer = new short[frameCount * channelCount]; + periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 + underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 + overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 + warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + } else { + periodNs = 0; + underrunNs = 0; + overrunNs = 0; + forceNs = 0; + warmupNs = 0; + } + readBufferState = -1; + dumpState->mFrameCount = frameCount; + } + +} + +void FastCapture::onWork() +{ + const FastCaptureState * const current = (const FastCaptureState *) this->current; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; + const FastCaptureState::Command command = this->command; + const size_t frameCount = current->mFrameCount; + + if ((command & FastCaptureState::READ) /*&& isWarm*/) { + ALOG_ASSERT(inputSource != NULL); + ALOG_ASSERT(readBuffer != NULL); + dumpState->mReadSequence++; + ATRACE_BEGIN("read"); + ssize_t framesRead = inputSource->read(readBuffer, frameCount, + AudioBufferProvider::kInvalidPTS); + ATRACE_END(); + dumpState->mReadSequence++; + if (framesRead >= 0) { + LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); + totalNativeFramesRead += framesRead; + dumpState->mFramesRead = totalNativeFramesRead; + readBufferState = framesRead; + } else { + dumpState->mReadErrors++; + readBufferState = 0; + } + // FIXME rename to attemptedIO + attemptedWrite = true; + } + + if (command & FastCaptureState::WRITE) { + ALOG_ASSERT(pipeSink != NULL); + ALOG_ASSERT(readBuffer != NULL); + if (readBufferState < 0) { + unsigned channelCount = Format_channelCount(format); + // FIXME frameSize + memset(readBuffer, 0, frameCount * channelCount * sizeof(short)); + readBufferState = frameCount; + } + if (readBufferState > 0) { + ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState); + // FIXME This supports at most one fast capture client. + // To handle multiple clients this could be converted to an array, + // or with a lot more work the control block could be shared by all clients. + audio_track_cblk_t* cblk = current->mCblk; + if (cblk != NULL && framesWritten > 0) { + int32_t rear = cblk->u.mStreaming.mRear; + android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear); + cblk->mServer += framesWritten; + int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); + if (!(old & CBLK_FUTEX_WAKE)) { + // client is never in server process, so don't use FUTEX_WAKE_PRIVATE + (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1); + } + } + } + } +} + +FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), + mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) +{ +} + +FastCaptureDumpState::~FastCaptureDumpState() +{ +} + +} // namespace android diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h new file mode 100644 index 0000000..e535b9d --- /dev/null +++ b/services/audioflinger/FastCapture.h @@ -0,0 +1,78 @@ +/* + * 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_AUDIO_FAST_CAPTURE_H +#define ANDROID_AUDIO_FAST_CAPTURE_H + +#include "FastThread.h" +#include "StateQueue.h" +#include "FastCaptureState.h" + +namespace android { + +typedef StateQueue<FastCaptureState> FastCaptureStateQueue; + +struct FastCaptureDumpState : FastThreadDumpState { + FastCaptureDumpState(); + /*virtual*/ ~FastCaptureDumpState(); + + // FIXME by renaming, could pull up many of these to FastThreadDumpState + uint32_t mReadSequence; // incremented before and after each read() + uint32_t mFramesRead; // total number of frames read successfully + uint32_t mReadErrors; // total number of read() errors + uint32_t mSampleRate; + size_t mFrameCount; +}; + +class FastCapture : public FastThread { + +public: + FastCapture(); + virtual ~FastCapture(); + + FastCaptureStateQueue* sq(); + +private: + FastCaptureStateQueue mSQ; + + // callouts + virtual const FastThreadState *poll(); + virtual void setLog(NBLog::Writer *logWriter); + virtual void onIdle(); + virtual void onExit(); + virtual bool isSubClassCommand(FastThreadState::Command command); + virtual void onStateChange(); + virtual void onWork(); + + static const FastCaptureState initial; + FastCaptureState preIdle; // copy of state before we went into idle + // FIXME by renaming, could pull up many of these to FastThread + NBAIO_Source *inputSource; + int inputSourceGen; + NBAIO_Sink *pipeSink; + int pipeSinkGen; + short *readBuffer; + ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear + NBAIO_Format format; + unsigned sampleRate; + FastCaptureDumpState dummyDumpState; + uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead + +}; // class FastCapture + +} // namespace android + +#endif // ANDROID_AUDIO_FAST_CAPTURE_H diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp new file mode 100644 index 0000000..1d029b7 --- /dev/null +++ b/services/audioflinger/FastCaptureState.cpp @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#include "FastCaptureState.h" + +namespace android { + +FastCaptureState::FastCaptureState() : FastThreadState(), + mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mFrameCount(0) +{ +} + +FastCaptureState::~FastCaptureState() +{ +} + +} // android diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h new file mode 100644 index 0000000..29c865a --- /dev/null +++ b/services/audioflinger/FastCaptureState.h @@ -0,0 +1,51 @@ +/* + * 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_AUDIO_FAST_CAPTURE_STATE_H +#define ANDROID_AUDIO_FAST_CAPTURE_STATE_H + +#include <media/nbaio/NBAIO.h> +#include "FastThreadState.h" +#include <private/media/AudioTrackShared.h> + +namespace android { + +// Represent a single state of the fast capture +struct FastCaptureState : FastThreadState { + FastCaptureState(); + /*virtual*/ ~FastCaptureState(); + + // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread + NBAIO_Source *mInputSource; // HAL input device, must already be negotiated + // FIXME by renaming, could pull up these fields to FastThreadState + int mInputSourceGen; // increment when mInputSource is assigned + NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink + int mPipeSinkGen; // increment when mPipeSink is assigned + size_t mFrameCount; // number of frames per fast capture buffer + audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL + + // Extends FastThreadState::Command + static const Command + // The following commands also process configuration changes, and can be "or"ed: + READ = 0x8, // read from input source + WRITE = 0x10, // write to pipe sink + READ_WRITE = 0x18; // read from input source and write to pipe sink + +}; // struct FastCaptureState + +} // namespace android + +#endif // ANDROID_AUDIO_FAST_CAPTURE_STATE_H diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 1caed11..c9a3f10 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -37,6 +37,7 @@ #include <cpustats/ThreadCpuUsage.h> #endif #endif +#include <audio_utils/format.h> #include "AudioMixer.h" #include "FastMixer.h" @@ -53,8 +54,12 @@ FastMixer::FastMixer() : FastThread(), outputSink(NULL), outputSinkGen(0), mixer(NULL), - mixBuffer(NULL), - mixBufferState(UNDEFINED), + mSinkBuffer(NULL), + mSinkBufferSize(0), + mMixerBuffer(NULL), + mMixerBufferSize(0), + mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), + mMixerBufferState(UNDEFINED), format(Format_Invalid), sampleRate(0), fastTracksGen(0), @@ -109,7 +114,8 @@ void FastMixer::onIdle() void FastMixer::onExit() { delete mixer; - delete[] mixBuffer; + free(mMixerBuffer); + free(mSinkBuffer); } bool FastMixer::isSubClassCommand(FastThreadState::Command command) @@ -155,14 +161,23 @@ void FastMixer::onStateChange() // FIXME to avoid priority inversion, don't delete here delete mixer; mixer = NULL; - delete[] mixBuffer; - mixBuffer = NULL; + free(mMixerBuffer); + mMixerBuffer = NULL; + free(mSinkBuffer); + mSinkBuffer = NULL; if (frameCount > 0 && sampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); - mixBuffer = new short[frameCount * FCC_2]; + const size_t mixerFrameSize = FCC_2 * audio_bytes_per_sample(mMixerBufferFormat); + mMixerBufferSize = mixerFrameSize * frameCount; + (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); + const size_t sinkFrameSize = FCC_2 * audio_bytes_per_sample(format.mFormat); + if (sinkFrameSize > mixerFrameSize) { // need a sink buffer + mSinkBufferSize = sinkFrameSize * frameCount; + (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); + } periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 @@ -175,7 +190,7 @@ void FastMixer::onStateChange() forceNs = 0; warmupNs = 0; } - mixBufferState = UNDEFINED; + mMixerBufferState = UNDEFINED; #if !LOG_NDEBUG for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { fastTrackNames[i] = -1; @@ -193,7 +208,7 @@ void FastMixer::onStateChange() const unsigned currentTrackMask = current->mTrackMask; dumpState->mTrackMask = currentTrackMask; if (current->mFastTracksGen != fastTracksGen) { - ALOG_ASSERT(mixBuffer != NULL); + ALOG_ASSERT(mMixerBuffer != NULL); int name; // process removed tracks first to avoid running out of track names @@ -224,13 +239,20 @@ void FastMixer::onStateChange() AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); if (mixer != NULL) { - name = mixer->getTrackName(fastTrack->mChannelMask, AUDIO_SESSION_OUTPUT_MIX); + name = mixer->getTrackName(fastTrack->mChannelMask, + fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); fastTrackNames[i] = name; mixer->setBufferProvider(name, bufferProvider); mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, - (void *) mixBuffer); + (void *) mMixerBuffer); // newly allocated track names default to full scale volume + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + (void *)(uintptr_t)fastTrack->mFormat); mixer->enable(name); } generations[i] = fastTrack->mGeneration; @@ -252,13 +274,18 @@ void FastMixer::onStateChange() ALOG_ASSERT(name >= 0); mixer->setBufferProvider(name, bufferProvider); if (fastTrack->mVolumeProvider == NULL) { - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, - (void *) MAX_GAIN_INT); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, - (void *) MAX_GAIN_INT); + float f = AudioMixer::UNITY_GAIN_FLOAT; + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); } mixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::REMOVE, NULL); + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + (void *)(uintptr_t)fastTrack->mFormat); mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t) fastTrack->mChannelMask); // already enabled @@ -281,7 +308,7 @@ void FastMixer::onWork() const size_t frameCount = current->mFrameCount; if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { - ALOG_ASSERT(mixBuffer != NULL); + ALOG_ASSERT(mMixerBuffer != NULL); // for each track, update volume and check for underrun unsigned currentTrackMask = current->mTrackMask; while (currentTrackMask != 0) { @@ -309,12 +336,11 @@ void FastMixer::onWork() ALOG_ASSERT(name >= 0); if (fastTrack->mVolumeProvider != NULL) { gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, - (void *) (uintptr_t) - (float_from_gain(gain_minifloat_unpack_left(vlr)) * MAX_GAIN_INT)); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, - (void *) (uintptr_t) - (float_from_gain(gain_minifloat_unpack_right(vlr)) * MAX_GAIN_INT)); + float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); + float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); + + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); } // FIXME The current implementation of framesReady() for fast tracks // takes a tryLock, which can block @@ -358,26 +384,31 @@ void FastMixer::onWork() // process() is CPU-bound mixer->process(pts); - mixBufferState = MIXED; - } else if (mixBufferState == MIXED) { - mixBufferState = UNDEFINED; + mMixerBufferState = MIXED; + } else if (mMixerBufferState == MIXED) { + mMixerBufferState = UNDEFINED; } //bool didFullWrite = false; // dumpsys could display a count of partial writes - if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) { - if (mixBufferState == UNDEFINED) { - memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short)); - mixBufferState = ZEROED; + if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { + if (mMixerBufferState == UNDEFINED) { + memset(mMixerBuffer, 0, mMixerBufferSize); + mMixerBufferState = ZEROED; + } + void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; + if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format + memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat, + frameCount * Format_channelCount(format)); } // if non-NULL, then duplicate write() to this non-blocking sink NBAIO_Sink* teeSink; if ((teeSink = current->mTeeSink) != NULL) { - (void) teeSink->write(mixBuffer, frameCount); + (void) teeSink->write(mMixerBuffer, frameCount); } // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; ATRACE_BEGIN("write"); - ssize_t framesWritten = outputSink->write(mixBuffer, frameCount); + ssize_t framesWritten = outputSink->write(buffer, frameCount); ATRACE_END(); dumpState->mWriteSequence++; if (framesWritten >= 0) { diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index db89ef4..4671670 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -61,8 +61,16 @@ private: NBAIO_Sink *outputSink; int outputSinkGen; AudioMixer* mixer; - short *mixBuffer; - enum {UNDEFINED, MIXED, ZEROED} mixBufferState; + + // mSinkBuffer audio format is stored in format.mFormat. + void* mSinkBuffer; // used for mixer output format translation + // if sink format is different than mixer output. + size_t mSinkBufferSize; + void* mMixerBuffer; // mixer output buffer. + size_t mMixerBufferSize; + audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + + enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; NBAIO_Format format; unsigned sampleRate; int fastTracksGen; diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 8e6d0d4..3aa8dad 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -20,7 +20,7 @@ namespace android { FastTrack::FastTrack() : mBufferProvider(NULL), mVolumeProvider(NULL), - mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0) + mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mFormat(AUDIO_FORMAT_INVALID), mGeneration(0) { } diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index e388fb3..661c9ca 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -45,6 +45,7 @@ struct FastTrack { ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO + audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned }; diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 02b5094..6d84296 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -235,7 +235,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa param.addInt(String8(AudioParameter::keyInputSource), (int)patch->sinks[0].ext.mix.usecase.source); - ALOGW("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", param.toString().string()); status = thread->setParameters(param.toString()); } @@ -354,7 +354,7 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle } AudioParameter param; param.addInt(String8(AudioParameter::keyRouting), 0); - ALOGW("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", param.toString().string()); status = thread->setParameters(param.toString()); } diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index 152455d..8246fef 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -59,6 +59,13 @@ bool settingsAllowed() { return ok; } +bool modifyAudioRoutingAllowed() { + static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING"); + bool ok = checkCallingPermission(sModifyAudioRoutingAllowed); + if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING"); + return ok; +} + bool dumpAllowed() { // don't optimize for same pid, since mediaserver never dumps itself static const String16 sDump("android.permission.DUMP"); diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h index 531bc56..df6f6f4 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/services/audioflinger/ServiceUtilities.h @@ -24,6 +24,7 @@ bool recordingAllowed(); bool captureAudioOutputAllowed(); bool captureHotwordAllowed(); bool settingsAllowed(); +bool modifyAudioRoutingAllowed(); bool dumpAllowed(); } diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp index 0d5cd0c..6f4505e 100644 --- a/services/audioflinger/StateQueueInstantiations.cpp +++ b/services/audioflinger/StateQueueInstantiations.cpp @@ -16,12 +16,14 @@ #include "Configuration.h" #include "FastMixerState.h" +#include "FastCaptureState.h" #include "StateQueue.h" // FIXME hack for gcc namespace android { -template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue +template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue +template class StateQueue<FastCaptureState>; // typedef FastCaptureStateQueue } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 4972c7a..d6333be 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -38,6 +38,7 @@ #include <audio_utils/minifloat.h> // NBAIO implementations +#include <media/nbaio/AudioStreamInSource.h> #include <media/nbaio/AudioStreamOutSink.h> #include <media/nbaio/MonoPipe.h> #include <media/nbaio/MonoPipeReader.h> @@ -53,6 +54,7 @@ #include "AudioFlinger.h" #include "AudioMixer.h" #include "FastMixer.h" +#include "FastCapture.h" #include "ServiceUtilities.h" #include "SchedulingPolicyService.h" @@ -131,9 +133,17 @@ static const enum { // up large writes into smaller ones, and the wrapper would need to deal with scheduler. } kUseFastMixer = FastMixer_Static; +// Whether to use fast capture +static const enum { + FastCapture_Never, // never initialize or use: for debugging only + FastCapture_Always, // always initialize and use, even if not needed: for debugging only + FastCapture_Static, // initialize if needed, then use all the time if initialized +} kUseFastCapture = FastCapture_Static; + // Priorities for requestPriority static const int kPriorityAudioApp = 2; static const int kPriorityFastMixer = 3; +static const int kPriorityFastCapture = 3; // IAudioFlinger::createTrack() reports back to client the total size of shared memory area // for the track. The client then sub-divides this into smaller buffers for its use. @@ -142,8 +152,17 @@ static const int kPriorityFastMixer = 3; // FIXME It would be better for client to tell AudioFlinger the value of N, // so AudioFlinger could allocate the right amount of memory. // See the client's minBufCount and mNotificationFramesAct calculations for details. + +// This is the default value, if not specified by property. static const int kFastTrackMultiplier = 2; +// The minimum and maximum allowed values +static const int kFastTrackMultiplierMin = 1; +static const int kFastTrackMultiplierMax = 2; + +// The actual value to use, which can be specified per-device via property af.fast_track_multiplier. +static int sFastTrackMultiplier = kFastTrackMultiplier; + // See Thread::readOnlyHeap(). // Initially this heap is used to allocate client buffers for "fast" AudioRecord. // Eventually it will be the single buffer that FastCapture writes into via HAL read(), @@ -152,6 +171,22 @@ static const size_t kRecordThreadReadOnlyHeapSize = 0x1000; // ---------------------------------------------------------------------------- +static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT; + +static void sFastTrackMultiplierInit() +{ + char value[PROPERTY_VALUE_MAX]; + if (property_get("af.fast_track_multiplier", value, NULL) > 0) { + char *endptr; + unsigned long ul = strtoul(value, &endptr, 0); + if (*endptr == '\0' && kFastTrackMultiplierMin <= ul && ul <= kFastTrackMultiplierMax) { + sFastTrackMultiplier = (int) ul; + } + } +} + +// ---------------------------------------------------------------------------- + #ifdef ADD_BATTERY_DATA // To collect the amplifier usage static void addBatteryData(uint32_t params) { @@ -1356,7 +1391,12 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ) { // if frameCount not specified, then it defaults to fast mixer (HAL) frame count if (frameCount == 0) { - frameCount = mFrameCount * kFastTrackMultiplier; + // read the fast track multiplier property the first time it is needed + int ok = pthread_once(&sFastTrackMultiplierOnce, sFastTrackMultiplierInit); + if (ok != 0) { + ALOGE("%s pthread_once failed: %d", __func__, ok); + } + frameCount = mFrameCount * sFastTrackMultiplier; } ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d", frameCount, mFrameCount); @@ -2715,9 +2755,27 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud break; } if (initFastMixer) { + audio_format_t fastMixerFormat; + if (mMixerBufferEnabled && mEffectBufferEnabled) { + fastMixerFormat = AUDIO_FORMAT_PCM_FLOAT; + } else { + fastMixerFormat = AUDIO_FORMAT_PCM_16_BIT; + } + if (mFormat != fastMixerFormat) { + // change our Sink format to accept our intermediate precision + mFormat = fastMixerFormat; + free(mSinkBuffer); + mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); + const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; + (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize); + } // create a MonoPipe to connect our submix to FastMixer NBAIO_Format format = mOutputSink->format(); + // adjust format to match that of the Fast Mixer + format.mFormat = fastMixerFormat; + format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount; + // This pipe depth compensates for scheduling latency of the normal mixer thread. // When it wakes up after a maximum latency, it runs a few cycles quickly before // finally blocking. Note the pipe implementation rounds up the request to a power of 2. @@ -2758,6 +2816,8 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud // wrap the source side of the MonoPipe to make it an AudioBufferProvider fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe)); fastTrack->mVolumeProvider = NULL; + fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer + fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer fastTrack->mGeneration++; state->mFastTracksGen++; state->mTrackMask = 1; @@ -3210,6 +3270,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mBufferProvider = eabp; fastTrack->mVolumeProvider = vp; fastTrack->mChannelMask = track->mChannelMask; + fastTrack->mFormat = track->mFormat; fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -3319,9 +3380,11 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } // compute volume for this track - uint32_t vl, vr, va; + uint32_t vl, vr; // in U8.24 integer format + float vlf, vrf, vaf; // in [0.0, 1.0] float format if (track->isPausing() || mStreamTypes[track->streamType()].mute) { - vl = vr = va = 0; + vl = vr = 0; + vlf = vrf = vaf = 0.; if (track->isPausing()) { track->setPaused(); } @@ -3332,8 +3395,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac float v = masterVolume * typeVolume; AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy; gain_minifloat_packed_t vlr = proxy->getVolumeLR(); - float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); - float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); + vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); + vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); // track volumes come from shared memory, so can't be trusted and must be clamped if (vlf > GAIN_FLOAT_UNITY) { ALOGV("Track left volume out of range: %.3g", vlf); @@ -3344,20 +3407,22 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac vrf = GAIN_FLOAT_UNITY; } // now apply the master volume and stream type volume - // FIXME we're losing the wonderful dynamic range in the minifloat representation - float v8_24 = v * (MAX_GAIN_INT * MAX_GAIN_INT); - vl = (uint32_t) (v8_24 * vlf); - vr = (uint32_t) (v8_24 * vrf); + vlf *= v; + vrf *= v; // assuming master volume and stream type volume each go up to 1.0, - // vl and vr are now in 8.24 format - + // then derive vl and vr as U8.24 versions for the effect chain + const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT; + vl = (uint32_t) (scaleto8_24 * vlf); + vr = (uint32_t) (scaleto8_24 * vrf); + // vl and vr are now in U8.24 format uint16_t sendLevel = proxy->getSendLevel_U4_12(); // send level comes from shared memory and so may be corrupt if (sendLevel > MAX_GAIN_INT) { ALOGV("Track send level out of range: %04X", sendLevel); sendLevel = MAX_GAIN_INT; } - va = (uint32_t)(v * sendLevel); + // vaf is represented as [0.0, 1.0] float by rescaling sendLevel + vaf = v * sendLevel * (1. / MAX_GAIN_INT); } // Delegate volume control to effect in track effect chain if needed @@ -3374,29 +3439,13 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mHasVolumeController = false; } - // FIXME Use float - // Convert volumes from 8.24 to 4.12 format - // This additional clamping is needed in case chain->setVolume_l() overshot - vl = (vl + (1 << 11)) >> 12; - if (vl > MAX_GAIN_INT) { - vl = MAX_GAIN_INT; - } - vr = (vr + (1 << 11)) >> 12; - if (vr > MAX_GAIN_INT) { - vr = MAX_GAIN_INT; - } - - if (va > MAX_GAIN_INT) { - va = MAX_GAIN_INT; // va is uint32_t, so no need to check for - - } - // XXX: these things DON'T need to be done each time mAudioMixer->setBufferProvider(name, track); mAudioMixer->enable(name); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)(uintptr_t)vl); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)(uintptr_t)vr); - mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)(uintptr_t)va); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf); + mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf); mAudioMixer->setParameter( name, AudioMixer::TRACK, @@ -3601,9 +3650,10 @@ track_is_ready: ; } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId) +int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) { - return mAudioMixer->getTrackName(channelMask, sessionId); + return mAudioMixer->getTrackName(channelMask, format, sessionId); } // deleteTrackName_l() must be called with ThreadBase::mLock held @@ -3716,7 +3766,8 @@ bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePa delete mAudioMixer; mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); for (size_t i = 0; i < mTracks.size() ; i++) { - int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId); + int name = getTrackName_l(mTracks[i]->mChannelMask, + mTracks[i]->mFormat, mTracks[i]->mSessionId); if (name < 0) { break; } @@ -4007,7 +4058,7 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() // getTrackName_l() must be called with ThreadBase::mLock held int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused, - int sessionId __unused) + audio_format_t format __unused, int sessionId __unused) { return 0; } @@ -4708,16 +4759,151 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, #endif , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize, "RecordThreadRO", MemoryHeapBase::READ_ONLY)) + // mFastCapture below + , mFastCaptureFutex(0) + // mInputSource + // mPipeSink + // mPipeSource + , mPipeFramesP2(0) + // mPipeMemory + // mFastCaptureNBLogWriter + , mFastTrackAvail(true) { snprintf(mName, kNameLength, "AudioIn_%X", id); mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); readInputParameters_l(); + + // create an NBAIO source for the HAL input stream, and negotiate + mInputSource = new AudioStreamInSource(input->stream); + size_t numCounterOffers = 0; + const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; + ssize_t index = mInputSource->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + + // initialize fast capture depending on configuration + bool initFastCapture; + switch (kUseFastCapture) { + case FastCapture_Never: + initFastCapture = false; + break; + case FastCapture_Always: + initFastCapture = true; + break; + case FastCapture_Static: + uint32_t primaryOutputSampleRate; + { + AutoMutex _l(audioFlinger->mHardwareLock); + primaryOutputSampleRate = audioFlinger->mPrimaryOutputSampleRate; + } + initFastCapture = + // either capture sample rate is same as (a reasonable) primary output sample rate + (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) && + (mSampleRate == primaryOutputSampleRate)) || + // or primary output sample rate is unknown, and capture sample rate is reasonable + ((primaryOutputSampleRate == 0) && + ((mSampleRate == 44100 || mSampleRate == 48000)))) && + // and the buffer size is < 10 ms + (mFrameCount * 1000) / mSampleRate < 10; + break; + // case FastCapture_Dynamic: + } + + if (initFastCapture) { + // create a Pipe for FastMixer to write to, and for us and fast tracks to read from + NBAIO_Format format = mInputSource->format(); + size_t pipeFramesP2 = roundup(mFrameCount * 8); + size_t pipeSize = pipeFramesP2 * Format_frameSize(format); + void *pipeBuffer; + const sp<MemoryDealer> roHeap(readOnlyHeap()); + sp<IMemory> pipeMemory; + if ((roHeap == 0) || + (pipeMemory = roHeap->allocate(pipeSize)) == 0 || + (pipeBuffer = pipeMemory->pointer()) == NULL) { + ALOGE("not enough memory for pipe buffer size=%zu", pipeSize); + goto failed; + } + // pipe will be shared directly with fast clients, so clear to avoid leaking old information + memset(pipeBuffer, 0, pipeSize); + Pipe *pipe = new Pipe(pipeFramesP2, format, pipeBuffer); + const NBAIO_Format offers[1] = {format}; + size_t numCounterOffers = 0; + ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mPipeSink = pipe; + PipeReader *pipeReader = new PipeReader(*pipe); + numCounterOffers = 0; + index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mPipeSource = pipeReader; + mPipeFramesP2 = pipeFramesP2; + mPipeMemory = pipeMemory; + + // create fast capture + mFastCapture = new FastCapture(); + FastCaptureStateQueue *sq = mFastCapture->sq(); +#ifdef STATE_QUEUE_DUMP + // FIXME +#endif + FastCaptureState *state = sq->begin(); + state->mCblk = NULL; + state->mInputSource = mInputSource.get(); + state->mInputSourceGen++; + state->mPipeSink = pipe; + state->mPipeSinkGen++; + state->mFrameCount = mFrameCount; + state->mCommand = FastCaptureState::COLD_IDLE; + // already done in constructor initialization list + //mFastCaptureFutex = 0; + state->mColdFutexAddr = &mFastCaptureFutex; + state->mColdGen++; + state->mDumpState = &mFastCaptureDumpState; +#ifdef TEE_SINK + // FIXME +#endif + mFastCaptureNBLogWriter = audioFlinger->newWriter_l(kFastCaptureLogSize, "FastCapture"); + state->mNBLogWriter = mFastCaptureNBLogWriter.get(); + sq->end(); + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); + + // start the fast capture + mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO); + pid_t tid = mFastCapture->getTid(); + int err = requestPriority(getpid_cached, tid, kPriorityFastMixer); + if (err != 0) { + ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", + kPriorityFastCapture, getpid_cached, tid, err); + } + +#ifdef AUDIO_WATCHDOG + // FIXME +#endif + + } +failed: ; + + // FIXME mNormalSource } AudioFlinger::RecordThread::~RecordThread() { + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + if (state->mCommand == FastCaptureState::COLD_IDLE) { + int32_t old = android_atomic_inc(&mFastCaptureFutex); + if (old == -1) { + (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1); + } + } + state->mCommand = FastCaptureState::EXIT; + sq->end(); + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); + mFastCapture->join(); + mFastCapture.clear(); + } + mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter); mAudioFlinger->unregisterWriter(mNBLogWriter); delete[] mRsmpInBuffer; } @@ -4772,6 +4958,8 @@ reacquire_wakelock: // activeTracks accumulates a copy of a subset of mActiveTracks Vector< sp<RecordTrack> > activeTracks; + // reference to the (first and only) fast track + sp<RecordTrack> fastTrack; { // scope for mLock Mutex::Autolock _l(mLock); @@ -4853,6 +5041,11 @@ reacquire_wakelock: activeTracks.add(activeTrack); i++; + if (activeTrack->isFastTrack()) { + ALOG_ASSERT(!mFastTrackAvail); + ALOG_ASSERT(fastTrack == 0); + fastTrack = activeTrack; + } } if (doBroadcast) { mStartStopCond.broadcast(); @@ -4878,6 +5071,36 @@ reacquire_wakelock: effectChains[i]->process_l(); } + // Start the fast capture if it's not already running + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME && + (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) { + if (state->mCommand == FastCaptureState::COLD_IDLE) { + int32_t old = android_atomic_inc(&mFastCaptureFutex); + if (old == -1) { + (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1); + } + } + state->mCommand = FastCaptureState::READ_WRITE; +#if 0 // FIXME + mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? + FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); +#endif + state->mCblk = fastTrack != 0 ? fastTrack->cblk() : NULL; + sq->end(); + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); +#if 0 + if (kUseFastCapture == FastCapture_Dynamic) { + mNormalSource = mPipeSource; + } +#endif + } else { + sq->end(false /*didModify*/); + } + } + // 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. @@ -4885,24 +5108,45 @@ reacquire_wakelock: // copy to the right place. Permitted because mRsmpInBuffer was over-allocated. int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1); - ssize_t bytesRead = mInput->stream->read(mInput->stream, - &mRsmpInBuffer[rear * mChannelCount], mBufferSize); - if (bytesRead <= 0) { - ALOGE("read failed: bytesRead=%d < %u", bytesRead, mBufferSize); + ssize_t framesRead; + + // If an NBAIO source is present, use it to read the normal capture's data + if (mPipeSource != 0) { + size_t framesToRead = mBufferSize / mFrameSize; + framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount], + framesToRead, AudioBufferProvider::kInvalidPTS); + if (framesRead == 0) { + // since pipe is non-blocking, simulate blocking input + sleepUs = (framesToRead * 1000000LL) / mSampleRate; + } + // otherwise use the HAL / AudioStreamIn directly + } else { + ssize_t bytesRead = mInput->stream->read(mInput->stream, + &mRsmpInBuffer[rear * mChannelCount], mBufferSize); + if (bytesRead < 0) { + framesRead = bytesRead; + } else { + framesRead = bytesRead / mFrameSize; + } + } + + if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) { + ALOGE("read failed: framesRead=%d", framesRead); // Force input into standby so that it tries to recover at next read attempt inputStandBy(); sleepUs = kRecordThreadSleepUs; + } + if (framesRead <= 0) { continue; } - ALOG_ASSERT((size_t) bytesRead <= mBufferSize); - size_t framesRead = bytesRead / mFrameSize; ALOG_ASSERT(framesRead > 0); + if (mTeeSink != 0) { (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead); } // If destination is non-contiguous, we now correct for reading past end of buffer. size_t part1 = mRsmpInFramesP2 - rear; - if (framesRead > part1) { + if ((size_t) framesRead > part1) { memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount], (framesRead - part1) * mFrameSize); } @@ -4913,6 +5157,11 @@ reacquire_wakelock: for (size_t i = 0; i < size; i++) { activeTrack = activeTracks[i]; + // skip fast tracks, as those are handled directly by FastCapture + if (activeTrack->isFastTrack()) { + continue; + } + enum { OVERRUN_UNKNOWN, OVERRUN_TRUE, @@ -5141,6 +5390,30 @@ void AudioFlinger::RecordThread::standbyIfNotAlreadyInStandby() void AudioFlinger::RecordThread::inputStandBy() { + // Idle the fast capture if it's currently running + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + if (!(state->mCommand & FastCaptureState::IDLE)) { + state->mCommand = FastCaptureState::COLD_IDLE; + state->mColdFutexAddr = &mFastCaptureFutex; + state->mColdGen++; + mFastCaptureFutex = 0; + sq->end(); + // BLOCK_UNTIL_PUSHED would be insufficient, as we need it to stop doing I/O now + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_ACKED); +#if 0 + if (kUseFastCapture == FastCapture_Dynamic) { + // FIXME + } +#endif +#ifdef AUDIO_WATCHDOG + // FIXME +#endif + } else { + sq->end(false /*didModify*/); + } + } mInput->stream->common.standby(&mInput->stream->common); } @@ -5167,42 +5440,47 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe // use case: callback handler and frame count is default or at least as large as HAL ( (tid != -1) && - ((frameCount == 0) || + ((frameCount == 0) /*|| + // FIXME must be equal to pipe depth, so don't allow it to be specified by client // FIXME not necessarily true, should be native frame count for native SR! - (frameCount >= mFrameCount)) + (frameCount >= mFrameCount)*/) ) && // PCM data audio_is_linear_pcm(format) && + // native format + (format == mFormat) && // mono or stereo ( (channelMask == AUDIO_CHANNEL_IN_MONO) || (channelMask == AUDIO_CHANNEL_IN_STEREO) ) && - // hardware sample rate - // FIXME actually the native hardware sample rate + // native channel mask + (channelMask == mChannelMask) && + // native hardware sample rate (sampleRate == mSampleRate) && // record thread has an associated fast capture - hasFastCapture() - // fast capture does not require slots + hasFastCapture() && + // there are sufficient fast track slots available + mFastTrackAvail ) { - // if frameCount not specified, then it defaults to fast capture (HAL) frame count + // if frameCount not specified, then it defaults to pipe frame count if (frameCount == 0) { - // FIXME wrong mFrameCount - frameCount = mFrameCount * kFastTrackMultiplier; + frameCount = mPipeFramesP2; } ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d", frameCount, mFrameCount); } else { ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d " "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " - "hasFastCapture=%d tid=%d", + "hasFastCapture=%d tid=%d mFastTrackAvail=%d", frameCount, mFrameCount, format, audio_is_linear_pcm(format), - channelMask, sampleRate, mSampleRate, hasFastCapture(), tid); + channelMask, sampleRate, mSampleRate, hasFastCapture(), tid, mFastTrackAvail); *flags &= ~IAudioFlinger::TRACK_FAST; // FIXME It's not clear that we need to enforce this any more, since we have a pipe. // For compatibility with AudioRecord calculation, buffer depth is forced // to be at least 2 x the record thread frame count and cover audio hardware latency. // This is probably too conservative, but legacy application code may depend on it. // If you change this calculation, also review the start threshold which is related. + // FIXME It's not clear how input latency actually matters. Perhaps this should be 0. uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream); size_t mNormalFrameCount = 2048; // FIXME uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate); @@ -5424,6 +5702,10 @@ void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track) { mTracks.remove(track); // need anything related to effects here? + if (track->isFastTrack()) { + ALOG_ASSERT(!mFastTrackAvail); + mFastTrackAvail = true; + } } void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) @@ -5442,6 +5724,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a } else { fdprintf(fd, " No active record clients\n"); } + dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); dumpBase(fd, args); } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index f8037c6..07887fb 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -356,6 +356,8 @@ public: // If a thread does not have such a heap, this method returns 0. virtual sp<MemoryDealer> readOnlyHeap() const { return 0; } + virtual sp<IMemory> pipeMemory() const { return 0; } + mutable Mutex mLock; protected: @@ -674,7 +676,8 @@ protected: // Allocate a track name for a given channel mask. // Returns name >= 0 if successful, -1 on failure. - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0; + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) = 0; virtual void deleteTrackName_l(int name) = 0; // Time to sleep between cycles when: @@ -831,7 +834,8 @@ public: protected: virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove); - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t idleSleepTimeUs() const; virtual uint32_t suspendSleepTimeUs() const; @@ -884,7 +888,8 @@ public: status_t& status); protected: - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; @@ -1059,6 +1064,8 @@ public: virtual sp<MemoryDealer> readOnlyHeap() const { return mReadOnlyHeap; } + virtual sp<IMemory> pipeMemory() const { return mPipeMemory; } + sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l( const sp<AudioFlinger::Client>& client, uint32_t sampleRate, @@ -1110,7 +1117,7 @@ public: static void syncStartEventCallback(const wp<SyncEvent>& event); virtual size_t frameCount() const { return mFrameCount; } - bool hasFastCapture() const { return false; } + bool hasFastCapture() const { return mFastCapture != 0; } private: // Enter standby if not already in standby, and set mStandby flag @@ -1140,4 +1147,40 @@ private: const sp<NBAIO_Sink> mTeeSink; const sp<MemoryDealer> mReadOnlyHeap; + + // one-time initialization, no locks required + sp<FastCapture> mFastCapture; // non-0 if there is also a fast capture + // FIXME audio watchdog thread + + // contents are not guaranteed to be consistent, no locks required + FastCaptureDumpState mFastCaptureDumpState; +#ifdef STATE_QUEUE_DUMP + // FIXME StateQueue observer and mutator dump fields +#endif + // FIXME audio watchdog dump + + // accessible only within the threadLoop(), no locks required + // mFastCapture->sq() // for mutating and pushing state + int32_t mFastCaptureFutex; // for cold idle + + // The HAL input source is treated as non-blocking, + // but current implementation is blocking + sp<NBAIO_Source> mInputSource; + // The source for the normal capture thread to read from: mInputSource or mPipeSource + sp<NBAIO_Source> mNormalSource; + // If a fast capture is present, the non-blocking pipe sink written to by fast capture, + // otherwise clear + sp<NBAIO_Sink> mPipeSink; + // If a fast capture is present, the non-blocking pipe source read by normal thread, + // otherwise clear + sp<NBAIO_Source> mPipeSource; + // Depth of pipe from fast capture to normal thread and fast clients, always power of 2 + size_t mPipeFramesP2; + // If a fast capture is present, the Pipe as IMemory, otherwise clear + sp<IMemory> mPipeMemory; + + static const size_t kFastCaptureLogSize = 4 * 1024; + sp<NBLog::Writer> mFastCaptureNBLogWriter; + + bool mFastTrackAvail; // true if fast track available }; diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 5f13be3..4cba3fd 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -39,6 +39,13 @@ public: STARTING_2, // for RecordTrack only }; + // where to allocate the data buffer + enum alloc_type { + ALLOC_CBLK, // allocate immediately after control block + ALLOC_READONLY, // allocate from a separate read-only heap per thread + ALLOC_PIPE, // do not allocate; use the pipe buffer + }; + TrackBase(ThreadBase *thread, const sp<Client>& client, uint32_t sampleRate, @@ -50,7 +57,7 @@ public: int uid, IAudioFlinger::track_flags_t flags, bool isOut, - bool useReadOnlyHeap = false); + alloc_type alloc = ALLOC_CBLK); virtual ~TrackBase(); virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index cf71d65..4e8a058 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -73,7 +73,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( int clientUid, IAudioFlinger::track_flags_t flags, bool isOut, - bool useReadOnlyHeap) + alloc_type alloc) : RefBase(), mThread(thread), mClient(client), @@ -117,7 +117,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); size_t size = sizeof(audio_track_cblk_t); size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize; - if (sharedBuffer == 0 && !useReadOnlyHeap) { + if (sharedBuffer == 0 && alloc == ALLOC_CBLK) { size += bufferSize; } @@ -139,7 +139,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // construct the shared structure in-place. if (mCblk != NULL) { new(mCblk) audio_track_cblk_t(); - if (useReadOnlyHeap) { + switch (alloc) { + case ALLOC_READONLY: { const sp<MemoryDealer> roHeap(thread->readOnlyHeap()); if (roHeap == 0 || (mBufferMemory = roHeap->allocate(bufferSize)) == 0 || @@ -153,7 +154,17 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( return; } memset(mBuffer, 0, bufferSize); - } else { + } break; + case ALLOC_PIPE: + mBufferMemory = thread->pipeMemory(); + // mBuffer is the virtual address as seen from current process (mediaserver), + // and should normally be coming from mBufferMemory->pointer(). + // However in this case the TrackBase does not reference the buffer directly. + // It should references the buffer via the pipe. + // Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL. + mBuffer = NULL; + break; + case ALLOC_CBLK: // clear all buffers if (sharedBuffer == 0) { mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); @@ -164,6 +175,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic #endif } + break; } #ifdef TEE_SINK @@ -388,7 +400,7 @@ AudioFlinger::PlaybackThread::Track::Track( } mServerProxy = mAudioTrackServerProxy; - mName = thread->getTrackName_l(channelMask, sessionId); + mName = thread->getTrackName_l(channelMask, format, sessionId); if (mName < 0) { ALOGE("no more track names available"); return; @@ -1869,7 +1881,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, flags, false /*isOut*/, - (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/), + flags & IAudioFlinger::TRACK_FAST ? ALLOC_PIPE : ALLOC_CBLK), mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0), // See real initialization of mRsmpInFront at RecordThread::start() mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL) @@ -1888,9 +1900,14 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( mResampler = AudioResampler::create(16, thread->mChannelCount, sampleRate); // source SR mResampler->setSampleRate(thread->mSampleRate); - mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN); + mResampler->setVolume(AudioMixer::UNITY_GAIN_INT, AudioMixer::UNITY_GAIN_INT); mResamplerBufferProvider = new ResamplerBufferProvider(this); } + + if (flags & IAudioFlinger::TRACK_FAST) { + ALOG_ASSERT(thread->mFastTrackAvail); + thread->mFastTrackAvail = false; + } } AudioFlinger::RecordThread::RecordTrack::~RecordTrack() diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp index 2b33703..8cc386a 100644 --- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp @@ -470,6 +470,9 @@ status_t AudioPolicyService::listAudioPorts(audio_port_role_t role, unsigned int *generation) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } @@ -480,6 +483,9 @@ status_t AudioPolicyService::listAudioPorts(audio_port_role_t role, status_t AudioPolicyService::getAudioPort(struct audio_port *port) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } @@ -491,6 +497,9 @@ status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } @@ -501,6 +510,9 @@ status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch, status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } @@ -514,6 +526,9 @@ status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches, unsigned int *generation) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } @@ -524,6 +539,9 @@ status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches, status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config) { Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } if (mAudioPolicyManager == NULL) { return NO_INIT; } diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp index 8c3af5b..61edac2 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -223,7 +223,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, index = mAvailableOutputDevices.add(devDesc); if (index >= 0) { mAvailableOutputDevices[index]->mId = nextUniqueId(); - HwModule *module = getModuleForDevice(device); + sp<HwModule> module = getModuleForDevice(device); ALOG_ASSERT(module != NULL, "setDeviceConnectionState():" "could not find HW module for device %08x", device); mAvailableOutputDevices[index]->mModule = module; @@ -260,7 +260,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, // outputs must be closed after checkOutputForAllStrategies() is executed if (!outputs.isEmpty()) { for (size_t i = 0; i < outputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]); // close unused outputs after device disconnection or direct outputs that have been // opened by checkOutputsForDevice() to query dynamic parameters if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || @@ -311,7 +311,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, ALOGW("setDeviceConnectionState() device already connected: %d", device); return INVALID_OPERATION; } - HwModule *module = getModuleForDevice(device); + sp<HwModule> module = getModuleForDevice(device); if (module == NULL) { ALOGW("setDeviceConnectionState(): could not find HW module for device %08x", device); @@ -442,7 +442,7 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) checkOutputForAllStrategies(); updateDevicesAndOutputs(); - AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); // force routing command to audio hardware when ending call // even if no device change is needed @@ -454,7 +454,7 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) if (isStateInCall(state)) { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); // mute media and sonification strategies and delay device switch by the largest // latency of any output where either strategy is active. // This avoid sending the ring tone or music tail into the earpiece or headset. @@ -644,7 +644,7 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, if (mTestOutputs[mCurOutput] == 0) { ALOGV("getOutput() opening test output"); - AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL); outputDesc->mDevice = mTestDevice; outputDesc->mSamplingRate = mTestSamplingRate; outputDesc->mFormat = mTestFormat; @@ -696,10 +696,10 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, } if (profile != 0) { - AudioOutputDescriptor *outputDesc = NULL; + sp<AudioOutputDescriptor> outputDesc = NULL; for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); if (!desc->isDuplicated() && (profile == desc->mProfile)) { outputDesc = desc; // reuse direct output if currently open and configured with same parameters @@ -747,7 +747,6 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, if (output != 0) { mpClientInterface->closeOutput(output); } - delete outputDesc; return 0; } audio_io_handle_t srcOutput = getOutputForEffect(); @@ -804,7 +803,7 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h audio_io_handle_t outputPrimary = 0; for (size_t i = 0; i < outputs.size(); i++) { - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); if (!outputDesc->isDuplicated()) { int commonFlags = popcount(outputDesc->mProfile->mFlags & flags); if (commonFlags > maxCommonFlags) { @@ -839,7 +838,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, return BAD_VALUE; } - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); // increment usage count for this stream on the requested output: // NOTE that the usage count is the same for duplicated output and hardware output which is @@ -854,7 +853,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, uint32_t waitMs = 0; bool force = false; for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); if (desc != outputDesc) { // force a device change if any other output is managed by the same hw // module and has a current device selection that differs from selected device. @@ -907,7 +906,7 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, return BAD_VALUE; } - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); // handle special case for sonification while in call if (isInCall()) { @@ -932,7 +931,7 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, // one being selected for this output for (size_t i = 0; i < mOutputs.size(); i++) { audio_io_handle_t curOutput = mOutputs.keyAt(i); - AudioOutputDescriptor *desc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); if (curOutput != output && desc->isActive() && outputDesc->sharesHwModuleWith(desc) && @@ -965,10 +964,9 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output) #ifdef AUDIO_POLICY_TEST int testIndex = testOutputIndex(output); if (testIndex != 0) { - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); if (outputDesc->isActive()) { mpClientInterface->closeOutput(output); - delete mOutputs.valueAt(index); mOutputs.removeItem(output); mTestOutputs[testIndex] = 0; } @@ -976,7 +974,7 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output) } #endif //AUDIO_POLICY_TEST - AudioOutputDescriptor *desc = mOutputs.valueAt(index); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index); if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { if (desc->mDirectOpenCount <= 0) { ALOGW("releaseOutput() invalid open count %d for output %d", @@ -1045,7 +1043,7 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, return 0; } - AudioInputDescriptor *inputDesc = new AudioInputDescriptor(profile); + sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile); inputDesc->mInputSource = inputSource; inputDesc->mDevice = device; @@ -1069,7 +1067,6 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, if (input != 0) { mpClientInterface->closeInput(input); } - delete inputDesc; return 0; } addInput(input, inputDesc); @@ -1085,7 +1082,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input) ALOGW("startInput() unknown input %d", input); return BAD_VALUE; } - AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); #ifdef AUDIO_POLICY_TEST if (mTestInput == 0) @@ -1095,7 +1092,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input) // uses AUDIO_SOURCE_HOTWORD in which case it is closed. audio_io_handle_t activeInput = getActiveInput(); if (!isVirtualInputDevice(inputDesc->mDevice) && activeInput != 0) { - AudioInputDescriptor *activeDesc = mInputs.valueFor(activeInput); + sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput); if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) { ALOGW("startInput() preempting already started low-priority input %d", activeInput); stopInput(activeInput); @@ -1129,7 +1126,7 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input) ALOGW("stopInput() unknown input %d", input); return BAD_VALUE; } - AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); if (inputDesc->mRefCount == 0) { ALOGW("stopInput() input %d already stopped", input); @@ -1156,7 +1153,6 @@ void AudioPolicyManager::releaseInput(audio_io_handle_t input) return; } mpClientInterface->closeInput(input); - delete mInputs.valueAt(index); mInputs.removeItem(input); nextAudioPortGeneration(); mpClientInterface->onAudioPortListUpdate(); @@ -1265,7 +1261,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForEffects( audio_io_handle_t outputDeepBuffer = 0; for (size_t i = 0; i < outputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]); ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags); if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { outputOffloaded = outputs[i]; @@ -1327,14 +1323,14 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, desc->name, io, strategy, session, id); ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); - EffectDescriptor *pDesc = new EffectDescriptor(); - memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t)); - pDesc->mIo = io; - pDesc->mStrategy = (routing_strategy)strategy; - pDesc->mSession = session; - pDesc->mEnabled = false; + sp<EffectDescriptor> effectDesc = new EffectDescriptor(); + memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); + effectDesc->mIo = io; + effectDesc->mStrategy = (routing_strategy)strategy; + effectDesc->mSession = session; + effectDesc->mEnabled = false; - mEffects.add(id, pDesc); + mEffects.add(id, effectDesc); return NO_ERROR; } @@ -1347,21 +1343,20 @@ status_t AudioPolicyManager::unregisterEffect(int id) return INVALID_OPERATION; } - EffectDescriptor *pDesc = mEffects.valueAt(index); + sp<EffectDescriptor> effectDesc = mEffects.valueAt(index); - setEffectEnabled(pDesc, false); + setEffectEnabled(effectDesc, false); - if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) { + if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) { ALOGW("unregisterEffect() memory %d too big for total %d", - pDesc->mDesc.memoryUsage, mTotalEffectsMemory); - pDesc->mDesc.memoryUsage = mTotalEffectsMemory; + effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); + effectDesc->mDesc.memoryUsage = mTotalEffectsMemory; } - mTotalEffectsMemory -= pDesc->mDesc.memoryUsage; + mTotalEffectsMemory -= effectDesc->mDesc.memoryUsage; ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", - pDesc->mDesc.name, id, pDesc->mDesc.memoryUsage, mTotalEffectsMemory); + effectDesc->mDesc.name, id, effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); mEffects.removeItem(id); - delete pDesc; return NO_ERROR; } @@ -1377,43 +1372,43 @@ status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) return setEffectEnabled(mEffects.valueAt(index), enabled); } -status_t AudioPolicyManager::setEffectEnabled(EffectDescriptor *pDesc, bool enabled) +status_t AudioPolicyManager::setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled) { - if (enabled == pDesc->mEnabled) { + if (enabled == effectDesc->mEnabled) { ALOGV("setEffectEnabled(%s) effect already %s", enabled?"true":"false", enabled?"enabled":"disabled"); return INVALID_OPERATION; } if (enabled) { - if (mTotalEffectsCpuLoad + pDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { + if (mTotalEffectsCpuLoad + effectDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", - pDesc->mDesc.name, (float)pDesc->mDesc.cpuLoad/10); + effectDesc->mDesc.name, (float)effectDesc->mDesc.cpuLoad/10); return INVALID_OPERATION; } - mTotalEffectsCpuLoad += pDesc->mDesc.cpuLoad; + mTotalEffectsCpuLoad += effectDesc->mDesc.cpuLoad; ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); } else { - if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) { + if (mTotalEffectsCpuLoad < effectDesc->mDesc.cpuLoad) { ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", - pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); - pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; + effectDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); + effectDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; } - mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad; + mTotalEffectsCpuLoad -= effectDesc->mDesc.cpuLoad; ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); } - pDesc->mEnabled = enabled; + effectDesc->mEnabled = enabled; return NO_ERROR; } bool AudioPolicyManager::isNonOffloadableEffectEnabled() { for (size_t i = 0; i < mEffects.size(); i++) { - const EffectDescriptor * const pDesc = mEffects.valueAt(i); - if (pDesc->mEnabled && (pDesc->mStrategy == STRATEGY_MEDIA) && - ((pDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { + sp<EffectDescriptor> effectDesc = mEffects.valueAt(i); + if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) && + ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", - pDesc->mDesc.name, pDesc->mSession); + effectDesc->mDesc.name, effectDesc->mSession); return true; } } @@ -1424,7 +1419,7 @@ bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inP { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < mOutputs.size(); i++) { - const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { return true; } @@ -1437,7 +1432,7 @@ bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < mOutputs.size(); i++) { - const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && outputDesc->isStreamActive(stream, inPastMs, sysTime)) { return true; @@ -1449,7 +1444,7 @@ bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, bool AudioPolicyManager::isSourceActive(audio_source_t source) const { for (size_t i = 0; i < mInputs.size(); i++) { - const AudioInputDescriptor * inputDescriptor = mInputs.valueAt(i); + const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i); if ((inputDescriptor->mInputSource == (int)source || (source == AUDIO_SOURCE_VOICE_RECOGNITION && inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) @@ -1674,10 +1669,10 @@ status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused) return NO_ERROR; } -AudioPolicyManager::AudioOutputDescriptor *AudioPolicyManager::getOutputFromId( +sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromId( audio_port_handle_t id) const { - AudioOutputDescriptor *outputDesc = NULL; + sp<AudioOutputDescriptor> outputDesc = NULL; for (size_t i = 0; i < mOutputs.size(); i++) { outputDesc = mOutputs.valueAt(i); if (outputDesc->mId == id) { @@ -1687,10 +1682,10 @@ AudioPolicyManager::AudioOutputDescriptor *AudioPolicyManager::getOutputFromId( return outputDesc; } -AudioPolicyManager::AudioInputDescriptor *AudioPolicyManager::getInputFromId( +sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId( audio_port_handle_t id) const { - AudioInputDescriptor *inputDesc = NULL; + sp<AudioInputDescriptor> inputDesc = NULL; for (size_t i = 0; i < mInputs.size(); i++) { inputDesc = mInputs.valueAt(i); if (inputDesc->mId == id) { @@ -1700,8 +1695,11 @@ AudioPolicyManager::AudioInputDescriptor *AudioPolicyManager::getInputFromId( return inputDesc; } -AudioPolicyManager::HwModule *AudioPolicyManager::getModuleForDevice(audio_devices_t device) const +sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice( + audio_devices_t device) const { + sp <HwModule> module; + for (size_t i = 0; i < mHwModules.size(); i++) { if (mHwModules[i]->mHandle == 0) { continue; @@ -1722,18 +1720,20 @@ AudioPolicyManager::HwModule *AudioPolicyManager::getModuleForDevice(audio_devic } } } - return NULL; + return module; } -AudioPolicyManager::HwModule *AudioPolicyManager::getModuleFromName(const char *name) const +sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleFromName(const char *name) const { + sp <HwModule> module; + for (size_t i = 0; i < mHwModules.size(); i++) { if (strcmp(mHwModules[i]->mName, name) == 0) { return mHwModules[i]; } } - return NULL; + return module; } @@ -1783,7 +1783,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } // output mix to output device connection - AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id); + sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id); if (outputDesc == NULL) { ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id); return BAD_VALUE; @@ -1832,7 +1832,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { // input device to input mix connection - AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id); + sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id); if (inputDesc == NULL) { return BAD_VALUE; } @@ -1957,7 +1957,7 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle, struct audio_patch *patch = &patchDesc->mPatch; patchDesc->mUid = mUidCached; if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) { - AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id); + sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id); if (outputDesc == NULL) { ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id); return BAD_VALUE; @@ -1970,7 +1970,7 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle, NULL); } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { - AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id); + sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id); if (inputDesc == NULL) { ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id); return BAD_VALUE; @@ -2035,27 +2035,24 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * } ALOGV("setAudioPortConfig() on port handle %d", config->id); // Only support gain configuration for now - if (config->config_mask != AUDIO_PORT_CONFIG_GAIN || config->gain.index < 0) { - return BAD_VALUE; + if (config->config_mask != AUDIO_PORT_CONFIG_GAIN) { + return INVALID_OPERATION; } - sp<AudioPort> portDesc; - struct audio_port_config portConfig; + sp<AudioPortConfig> audioPortConfig; if (config->type == AUDIO_PORT_TYPE_MIX) { if (config->role == AUDIO_PORT_ROLE_SOURCE) { - AudioOutputDescriptor *outputDesc = getOutputFromId(config->id); + sp<AudioOutputDescriptor> outputDesc = getOutputFromId(config->id); if (outputDesc == NULL) { return BAD_VALUE; } - portDesc = outputDesc->mProfile; - outputDesc->toAudioPortConfig(&portConfig); + audioPortConfig = outputDesc; } else if (config->role == AUDIO_PORT_ROLE_SINK) { - AudioInputDescriptor *inputDesc = getInputFromId(config->id); + sp<AudioInputDescriptor> inputDesc = getInputFromId(config->id); if (inputDesc == NULL) { return BAD_VALUE; } - portDesc = inputDesc->mProfile; - inputDesc->toAudioPortConfig(&portConfig); + audioPortConfig = inputDesc; } else { return BAD_VALUE; } @@ -2071,47 +2068,22 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * if (deviceDesc == NULL) { return BAD_VALUE; } - portDesc = deviceDesc; - deviceDesc->toAudioPortConfig(&portConfig); + audioPortConfig = deviceDesc; } else { return BAD_VALUE; } - if ((size_t)config->gain.index >= portDesc->mGains.size()) { - return INVALID_OPERATION; - } - const struct audio_gain *gain = &portDesc->mGains[config->gain.index]->mGain; - if ((config->gain.mode & ~gain->mode) != 0) { - return BAD_VALUE; - } - if ((config->gain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { - if ((config->gain.values[0] < gain->min_value) || - (config->gain.values[0] > gain->max_value)) { - return BAD_VALUE; - } - } else { - if ((config->gain.channel_mask & ~gain->channel_mask) != 0) { - return BAD_VALUE; - } - size_t numValues = popcount(config->gain.channel_mask); - for (size_t i = 0; i < numValues; i++) { - if ((config->gain.values[i] < gain->min_value) || - (config->gain.values[i] > gain->max_value)) { - return BAD_VALUE; - } - } + struct audio_port_config backupConfig; + status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig); + if (status == NO_ERROR) { + struct audio_port_config newConfig; + audioPortConfig->toAudioPortConfig(&newConfig, config); + status = mpClientInterface->setAudioPortConfig(&newConfig, 0); } - if ((config->gain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { - if ((config->gain.ramp_duration_ms < gain->min_ramp_ms) || - (config->gain.ramp_duration_ms > gain->max_ramp_ms)) { - return BAD_VALUE; - } + if (status != NO_ERROR) { + audioPortConfig->applyAudioPortConfig(&backupConfig); } - portConfig.gain = config->gain; - - status_t status = mpClientInterface->setAudioPortConfig(&portConfig, 0); - return status; } @@ -2230,7 +2202,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa audio_devices_t profileTypes = outProfile->mSupportedDevices.types(); if ((profileTypes & outputDeviceTypes) && ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { - AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile); + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile); outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mDeviceType & profileTypes); audio_io_handle_t output = mpClientInterface->openOutput( @@ -2245,7 +2217,6 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa ALOGW("Cannot open output stream for device %08x on hw module %s", outputDesc->mDevice, mHwModules[i]->mName); - delete outputDesc; } else { for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) { audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType; @@ -2282,7 +2253,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa audio_devices_t profileTypes = inProfile->mSupportedDevices.types(); if (profileTypes & inputDeviceTypes) { - AudioInputDescriptor *inputDesc = new AudioInputDescriptor(inProfile); + sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile); inputDesc->mInputSource = AUDIO_SOURCE_MIC; inputDesc->mDevice = inProfile->mSupportedDevices[0]->mDeviceType; @@ -2310,7 +2281,6 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa inputDesc->mDevice, mHwModules[i]->mName); } - delete inputDesc; } } } @@ -2372,17 +2342,15 @@ AudioPolicyManager::~AudioPolicyManager() #endif //AUDIO_POLICY_TEST for (size_t i = 0; i < mOutputs.size(); i++) { mpClientInterface->closeOutput(mOutputs.keyAt(i)); - delete mOutputs.valueAt(i); } for (size_t i = 0; i < mInputs.size(); i++) { mpClientInterface->closeInput(mInputs.keyAt(i)); - delete mInputs.valueAt(i); - } - for (size_t i = 0; i < mHwModules.size(); i++) { - delete mHwModules[i]; } mAvailableOutputDevices.clear(); mAvailableInputDevices.clear(); + mOutputs.clear(); + mInputs.clear(); + mHwModules.clear(); } status_t AudioPolicyManager::initCheck() @@ -2486,15 +2454,14 @@ bool AudioPolicyManager::threadLoop() if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { param.remove(String8("test_cmd_policy_reopen")); - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); mpClientInterface->closeOutput(mPrimaryOutput); audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle; - delete mOutputs.valueFor(mPrimaryOutput); mOutputs.removeItem(mPrimaryOutput); - AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL); outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER; mPrimaryOutput = mpClientInterface->openOutput(moduleHandle, &outputDesc->mDevice, @@ -2542,7 +2509,7 @@ int AudioPolicyManager::testOutputIndex(audio_io_handle_t output) // --- -void AudioPolicyManager::addOutput(audio_io_handle_t output, AudioOutputDescriptor *outputDesc) +void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc) { outputDesc->mIoHandle = output; outputDesc->mId = nextUniqueId(); @@ -2550,7 +2517,7 @@ void AudioPolicyManager::addOutput(audio_io_handle_t output, AudioOutputDescript nextAudioPortGeneration(); } -void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc) +void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc) { inputDesc->mIoHandle = input; inputDesc->mId = nextUniqueId(); @@ -2571,7 +2538,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device, SortedVector<audio_io_handle_t>& outputs, const String8 address) { - AudioOutputDescriptor *desc; + sp<AudioOutputDescriptor> desc; if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { // first list already open outputs that can be routed to this device @@ -2714,7 +2681,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device, mPrimaryOutput); if (duplicatedOutput != 0) { // add duplicated output descriptor - AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(NULL); + sp<AudioOutputDescriptor> dupOutputDesc = new AudioOutputDescriptor(NULL); dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput); dupOutputDesc->mOutput2 = mOutputs.valueFor(output); dupOutputDesc->mSamplingRate = desc->mSamplingRate; @@ -2736,7 +2703,6 @@ status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device, } if (output == 0) { ALOGW("checkOutputsForDevice() could not open output for device %x", device); - delete desc; profiles.removeAt(profile_index); profile_index--; } else { @@ -2796,7 +2762,7 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, SortedVector<audio_io_handle_t>& inputs, const String8 address) { - AudioInputDescriptor *desc; + sp<AudioInputDescriptor> desc; if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { // first list already open inputs that can be routed to this device for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { @@ -2911,7 +2877,6 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, if (input == 0) { ALOGW("checkInputsForDevice() could not open input for device 0x%X", device); - delete desc; profiles.removeAt(profile_index); profile_index--; } else { @@ -2972,7 +2937,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) { ALOGV("closeOutput(%d)", output); - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); if (outputDesc == NULL) { ALOGW("closeOutput() unknown output %d", output); return; @@ -2980,11 +2945,11 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) // look for duplicated outputs connected to the output being removed. for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *dupOutputDesc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i); if (dupOutputDesc->isDuplicated() && (dupOutputDesc->mOutput1 == outputDesc || dupOutputDesc->mOutput2 == outputDesc)) { - AudioOutputDescriptor *outputDesc2; + sp<AudioOutputDescriptor> outputDesc2; if (dupOutputDesc->mOutput1 == outputDesc) { outputDesc2 = dupOutputDesc->mOutput2; } else { @@ -3002,7 +2967,6 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); mpClientInterface->closeOutput(duplicatedOutput); - delete mOutputs.valueFor(duplicatedOutput); mOutputs.removeItem(duplicatedOutput); } } @@ -3012,14 +2976,13 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) mpClientInterface->setParameters(output, param.toString()); mpClientInterface->closeOutput(output); - delete outputDesc; mOutputs.removeItem(output); mPreviousOutputs = mOutputs; nextAudioPortGeneration(); } SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device, - DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs) + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs) { SortedVector<audio_io_handle_t> outputs; @@ -3061,7 +3024,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) strategy, srcOutputs[0], dstOutputs[0]); // mute strategy while moving tracks from one output to another for (size_t i = 0; i < srcOutputs.size(); i++) { - AudioOutputDescriptor *desc = mOutputs.valueFor(srcOutputs[i]); + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]); if (desc->isStrategyActive(strategy)) { setStrategyMute(strategy, true, srcOutputs[i]); setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice); @@ -3073,17 +3036,17 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs); SortedVector<audio_io_handle_t> moved; for (size_t i = 0; i < mEffects.size(); i++) { - EffectDescriptor *desc = mEffects.valueAt(i); - if (desc->mSession == AUDIO_SESSION_OUTPUT_MIX && - desc->mIo != fxOutput) { - if (moved.indexOf(desc->mIo) < 0) { + sp<EffectDescriptor> effectDesc = mEffects.valueAt(i); + if (effectDesc->mSession == AUDIO_SESSION_OUTPUT_MIX && + effectDesc->mIo != fxOutput) { + if (moved.indexOf(effectDesc->mIo) < 0) { ALOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), fxOutput); - mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, desc->mIo, + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, effectDesc->mIo, fxOutput); - moved.add(desc->mIo); + moved.add(effectDesc->mIo); } - desc->mIo = fxOutput; + effectDesc->mIo = fxOutput; } } } @@ -3109,7 +3072,7 @@ void AudioPolicyManager::checkOutputForAllStrategies() audio_io_handle_t AudioPolicyManager::getA2dpOutput() { for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { return mOutputs.keyAt(i); } @@ -3167,7 +3130,7 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, { audio_devices_t device = AUDIO_DEVICE_NONE; - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle); if (index >= 0) { @@ -3213,7 +3176,7 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input) { - AudioInputDescriptor *inputDesc = mInputs.valueFor(input); + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle); if (index >= 0) { @@ -3247,7 +3210,7 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre devices = getDeviceForStrategy(strategy, true /*fromCache*/); SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs); for (size_t i = 0; i < outputs.size(); i++) { - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); if (outputDesc->isStrategyActive(strategy)) { devices = outputDesc->device(); break; @@ -3514,7 +3477,7 @@ void AudioPolicyManager::updateDevicesAndOutputs() mPreviousOutputs = mOutputs; } -uint32_t AudioPolicyManager::checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, +uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc, audio_devices_t prevDevice, uint32_t delayMs) { @@ -3543,7 +3506,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(AudioOutputDescriptor *ou } if (doMute) { for (size_t j = 0; j < mOutputs.size(); j++) { - AudioOutputDescriptor *desc = mOutputs.valueAt(j); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(j); // skip output if it does not share any device with current output if ((desc->supportedDevices() & outputDesc->supportedDevices()) == AUDIO_DEVICE_NONE) { @@ -3601,7 +3564,7 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output, audio_patch_handle_t *patchHandle) { ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs); - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); AudioParameter param; uint32_t muteWaitMs; @@ -3703,7 +3666,7 @@ status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output, int delayMs, audio_patch_handle_t *patchHandle) { - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); ssize_t index; if (patchHandle) { index = mAudioPatches.indexOfKey(*patchHandle); @@ -3730,7 +3693,7 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, { status_t status = NO_ERROR; - AudioInputDescriptor *inputDesc = mInputs.valueFor(input); + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) { inputDesc->mDevice = device; @@ -3785,7 +3748,7 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input, audio_patch_handle_t *patchHandle) { - AudioInputDescriptor *inputDesc = mInputs.valueFor(input); + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); ssize_t index; if (patchHandle) { index = mAudioPatches.indexOfKey(*patchHandle); @@ -3899,7 +3862,7 @@ bool AudioPolicyManager::isVirtualInputDevice(audio_devices_t device) audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs) { for (size_t i = 0; i < mInputs.size(); i++) { - const AudioInputDescriptor * input_descriptor = mInputs.valueAt(i); + const sp<AudioInputDescriptor> input_descriptor = mInputs.valueAt(i); if ((input_descriptor->mRefCount > 0) && (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) { return mInputs.keyAt(i); @@ -4144,7 +4107,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, audio_devices_t device) { float volume = 1.0; - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); StreamDescriptor &streamDesc = mStreams[stream]; if (device == AUDIO_DEVICE_NONE) { @@ -4300,7 +4263,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, audio_devices_t device) { StreamDescriptor &streamDesc = mStreams[stream]; - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); if (device == AUDIO_DEVICE_NONE) { device = outputDesc->device(); } @@ -4345,7 +4308,7 @@ void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, const routing_strategy stream_strategy = getStrategy(stream); if ((stream_strategy == STRATEGY_SONIFICATION) || ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) { - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", stream, starting, outputDesc->mDevice, stateChange); if (outputDesc->mRefCount[stream]) { @@ -4403,8 +4366,7 @@ uint32_t AudioPolicyManager::getMaxEffectsMemory() AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( const sp<IOProfile>& profile) - : mId(0), mIoHandle(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), - mChannelMask(0), mLatency(0), + : mId(0), mIoHandle(0), mLatency(0), mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0) { @@ -4419,9 +4381,13 @@ AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( mStrategyMutedByDevice[i] = false; } if (profile != NULL) { + mAudioPort = profile; mSamplingRate = profile->mSamplingRates[0]; mFormat = profile->mFormats[0]; mChannelMask = profile->mChannelMasks[0]; + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } mFlags = profile->mFlags; } } @@ -4445,7 +4411,7 @@ uint32_t AudioPolicyManager::AudioOutputDescriptor::latency() } bool AudioPolicyManager::AudioOutputDescriptor::sharesHwModuleWith( - const AudioOutputDescriptor *outputDesc) + const sp<AudioOutputDescriptor> outputDesc) { if (isDuplicated()) { return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); @@ -4528,31 +4494,16 @@ void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig( struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const { - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SOURCE; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->sample_rate = mSamplingRate; - dstConfig->channel_mask = mChannelMask; - dstConfig->format = mFormat; - dstConfig->gain.index = -1; dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT; - // use supplied variable configuration parameters if any + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; if (srcConfig != NULL) { - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - dstConfig->sample_rate = srcConfig->sample_rate; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - dstConfig->format = srcConfig->format; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { - dstConfig->gain = srcConfig->gain; - dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; - } + dstConfig->config_mask &= srcConfig->config_mask; } + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_MIX; dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; dstConfig->ext.mix.handle = mIoHandle; dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; @@ -4603,15 +4554,22 @@ status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd) // --- AudioInputDescriptor class implementation AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile) - : mId(0), mIoHandle(0), mSamplingRate(0), - mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0), + : mId(0), mIoHandle(0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mRefCount(0), mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile) { if (profile != NULL) { + mAudioPort = profile; mSamplingRate = profile->mSamplingRates[0]; mFormat = profile->mFormats[0]; mChannelMask = profile->mChannelMasks[0]; + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } else { + mSamplingRate = 0; + mFormat = AUDIO_FORMAT_DEFAULT; + mChannelMask = 0; } } @@ -4619,31 +4577,17 @@ void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig( struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const { - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SINK; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->sample_rate = mSamplingRate; - dstConfig->channel_mask = mChannelMask; - dstConfig->format = mFormat; - dstConfig->gain.index = -1; dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT; - // use supplied variable configuration parameters if any + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; if (srcConfig != NULL) { - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - dstConfig->sample_rate = srcConfig->sample_rate; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - dstConfig->format = srcConfig->format; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { - dstConfig->gain = srcConfig->gain; - dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; - } + dstConfig->config_mask &= srcConfig->config_mask; } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SINK; + dstConfig->type = AUDIO_PORT_TYPE_MIX; dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; dstConfig->ext.mix.handle = mIoHandle; dstConfig->ext.mix.usecase.source = mInputSource; @@ -4745,7 +4689,8 @@ status_t AudioPolicyManager::EffectDescriptor::dump(int fd) // --- HwModule class implementation AudioPolicyManager::HwModule::HwModule(const char *name) - : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), mHandle(0) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), + mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) { } @@ -4904,6 +4849,8 @@ void AudioPolicyManager::HwModule::dump(int fd) result.append(buffer); snprintf(buffer, SIZE, " - handle: %d\n", mHandle); result.append(buffer); + snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); + result.append(buffer); write(fd, result.string(), result.size()); if (mOutputProfiles.size()) { write(fd, " - outputs:\n", strlen(" - outputs:\n")); @@ -4931,6 +4878,15 @@ void AudioPolicyManager::HwModule::dump(int fd) // --- AudioPort class implementation + +AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module) : + mName(name), mType(type), mRole(role), mModule(module) +{ + mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const { port->role = mRole; @@ -5066,18 +5022,17 @@ audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name) return mode; } -void AudioPolicyManager::AudioPort::loadGain(cnode *root) +void AudioPolicyManager::AudioPort::loadGain(cnode *root, int index) { cnode *node = root->first_child; - sp<AudioGain> gain = new AudioGain(); + sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); while (node) { if (strcmp(node->name, GAIN_MODE) == 0) { gain->mGain.mode = loadGainMode((char *)node->value); } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { - if ((mType == AUDIO_PORT_TYPE_DEVICE && mRole == AUDIO_PORT_ROLE_SOURCE) || - (mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK)) { + if (mUseInChannelMask) { gain->mGain.channel_mask = (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, ARRAY_SIZE(sInChannelsNameToEnumTable), @@ -5116,13 +5071,53 @@ void AudioPolicyManager::AudioPort::loadGain(cnode *root) void AudioPolicyManager::AudioPort::loadGains(cnode *root) { cnode *node = root->first_child; + int index = 0; while (node) { ALOGV("loadGains() loading gain %s", node->name); - loadGain(node); + loadGain(node, index++); node = node->next; } } +status_t AudioPolicyManager::AudioPort::checkSamplingRate(uint32_t samplingRate) const +{ + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if (mSamplingRates[i] == samplingRate) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkChannelMask(audio_channel_mask_t channelMask) const +{ + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + if (mChannelMasks[i] == channelMask) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkFormat(audio_format_t format) const +{ + for (size_t i = 0; i < mFormats.size(); i ++) { + if (mFormats[i] == format) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkGain(const struct audio_gain_config *gainConfig, + int index) const +{ + if (index < 0 || (size_t)index >= mGains.size()) { + return BAD_VALUE; + } + return mGains[index]->checkConfig(gainConfig); +} + void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const { const size_t SIZE = 256; @@ -5181,11 +5176,72 @@ void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const // --- AudioGain class implementation -AudioPolicyManager::AudioGain::AudioGain() +AudioPolicyManager::AudioGain::AudioGain(int index, bool useInChannelMask) { + mIndex = index; + mUseInChannelMask = useInChannelMask; memset(&mGain, 0, sizeof(struct audio_gain)); } +void AudioPolicyManager::AudioGain::getDefaultConfig(struct audio_gain_config *config) +{ + config->index = mIndex; + config->mode = mGain.mode; + config->channel_mask = mGain.channel_mask; + if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + config->values[0] = mGain.default_value; + } else { + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(mGain.channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(mGain.channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + config->values[i] = mGain.default_value; + } + } + if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + config->ramp_duration_ms = mGain.min_ramp_ms; + } +} + +status_t AudioPolicyManager::AudioGain::checkConfig(const struct audio_gain_config *config) +{ + if ((config->mode & ~mGain.mode) != 0) { + return BAD_VALUE; + } + if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + if ((config->values[0] < mGain.min_value) || + (config->values[0] > mGain.max_value)) { + return BAD_VALUE; + } + } else { + if ((config->channel_mask & ~mGain.channel_mask) != 0) { + return BAD_VALUE; + } + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(config->channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(config->channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + if ((config->values[i] < mGain.min_value) || + (config->values[i] > mGain.max_value)) { + return BAD_VALUE; + } + } + } + if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + if ((config->ramp_duration_ms < mGain.min_ramp_ms) || + (config->ramp_duration_ms > mGain.max_ramp_ms)) { + return BAD_VALUE; + } + } + return NO_ERROR; +} + void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; @@ -5214,10 +5270,116 @@ void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const write(fd, result.string(), result.size()); } +// --- AudioPortConfig class implementation + +AudioPolicyManager::AudioPortConfig::AudioPortConfig() +{ + mSamplingRate = 0; + mChannelMask = AUDIO_CHANNEL_NONE; + mFormat = AUDIO_FORMAT_INVALID; + mGain.index = -1; +} + +status_t AudioPolicyManager::AudioPortConfig::applyAudioPortConfig( + const struct audio_port_config *config, + struct audio_port_config *backupConfig) +{ + struct audio_port_config localBackupConfig; + status_t status = NO_ERROR; + + localBackupConfig.config_mask = config->config_mask; + toAudioPortConfig(&localBackupConfig); + + if (mAudioPort == 0) { + status = NO_INIT; + goto exit; + } + if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + status = mAudioPort->checkSamplingRate(config->sample_rate); + if (status != NO_ERROR) { + goto exit; + } + mSamplingRate = config->sample_rate; + } + if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + status = mAudioPort->checkChannelMask(config->channel_mask); + if (status != NO_ERROR) { + goto exit; + } + mChannelMask = config->channel_mask; + } + if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + status = mAudioPort->checkFormat(config->format); + if (status != NO_ERROR) { + goto exit; + } + mFormat = config->format; + } + if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { + status = mAudioPort->checkGain(&config->gain, config->gain.index); + if (status != NO_ERROR) { + goto exit; + } + mGain = config->gain; + } + +exit: + if (status != NO_ERROR) { + applyAudioPortConfig(&localBackupConfig); + } + if (backupConfig != NULL) { + *backupConfig = localBackupConfig; + } + return status; +} + +void AudioPolicyManager::AudioPortConfig::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + dstConfig->sample_rate = mSamplingRate; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { + dstConfig->sample_rate = srcConfig->sample_rate; + } + } else { + dstConfig->sample_rate = 0; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + dstConfig->channel_mask = mChannelMask; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { + dstConfig->channel_mask = srcConfig->channel_mask; + } + } else { + dstConfig->channel_mask = AUDIO_CHANNEL_NONE; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + dstConfig->format = mFormat; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { + dstConfig->format = srcConfig->format; + } + } else { + dstConfig->format = AUDIO_FORMAT_INVALID; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { + dstConfig->gain = mGain; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { + dstConfig->gain = srcConfig->gain; + } + } else { + dstConfig->gain.index = -1; + } + if (dstConfig->gain.index != -1) { + dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; + } else { + dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; + } +} + // --- IOProfile class implementation AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role, - HwModule *module) + const sp<HwModule>& module) : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0) { } @@ -5245,32 +5407,13 @@ bool AudioPolicyManager::IOProfile::isCompatibleProfile(audio_devices_t device, if ((mFlags & flags) != flags) { return false; } - size_t i; - for (i = 0; i < mSamplingRates.size(); i++) - { - if (mSamplingRates[i] == samplingRate) { - break; - } - } - if (i == mSamplingRates.size()) { + if (checkSamplingRate(samplingRate) != NO_ERROR) { return false; } - for (i = 0; i < mFormats.size(); i++) - { - if (mFormats[i] == format) { - break; - } - } - if (i == mFormats.size()) { + if (checkChannelMask(channelMask) != NO_ERROR) { return false; } - for (i = 0; i < mChannelMasks.size(); i++) - { - if (mChannelMasks[i] == channelMask) { - break; - } - } - if (i == mChannelMasks.size()) { + if (checkFormat(format) != NO_ERROR) { return false; } return true; @@ -5322,6 +5465,21 @@ void AudioPolicyManager::IOProfile::log() // --- DeviceDescriptor implementation + +AudioPolicyManager::DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : + AudioPort(name, AUDIO_PORT_TYPE_DEVICE, + audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : + AUDIO_PORT_ROLE_SOURCE, + NULL), + mDeviceType(type), mAddress(""), + mChannelMask(AUDIO_CHANNEL_NONE), mId(0) +{ + mAudioPort = this; + if (mGains.size() > 0) { + mGains[0]->getDefaultConfig(&mGain); + } +} + bool AudioPolicyManager::DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const { // Devices are considered equal if they: @@ -5486,23 +5644,17 @@ void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig( struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const { + dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask &= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + dstConfig->id = mId; dstConfig->role = audio_is_output_device(mDeviceType) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; dstConfig->type = AUDIO_PORT_TYPE_DEVICE; - dstConfig->channel_mask = mChannelMask; - dstConfig->gain.index = -1; - dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK; - // use supplied variable configuration parameters if any - if (srcConfig != NULL) { - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { - dstConfig->gain = srcConfig->gain; - dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; - } - } dstConfig->ext.device.type = mDeviceType; dstConfig->ext.device.hw_module = mModule->mHandle; strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); @@ -5598,7 +5750,7 @@ void AudioPolicyManager::loadHwModule(cnode *root) { status_t status = NAME_NOT_FOUND; cnode *node; - HwModule *module = new HwModule(root->name); + sp<HwModule> module = new HwModule(root->name); node = config_find(root, DEVICES_TAG); if (node != NULL) { @@ -5640,8 +5792,6 @@ void AudioPolicyManager::loadHwModule(cnode *root) if (status == NO_ERROR) { mHwModules.add(module); - } else { - delete module; } } @@ -5660,9 +5810,10 @@ void AudioPolicyManager::loadHwModules(cnode *root) } } -void AudioPolicyManager::loadGlobalConfig(cnode *root, HwModule *module) +void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& module) { cnode *node = config_find(root, GLOBAL_CONFIG_TAG); + if (node == NULL) { return; } @@ -5695,6 +5846,12 @@ void AudioPolicyManager::loadGlobalConfig(cnode *root, HwModule *module) } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { mSpeakerDrcEnabled = stringToBool((char *)node->value); ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); + } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { + uint32_t major, minor; + sscanf((char *)node->value, "%u.%u", &major, &minor); + module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor); + ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u", + module->mHalVersion, major, minor); } node = node->next; } @@ -5726,9 +5883,10 @@ status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path) void AudioPolicyManager::defaultAudioPolicyConfig(void) { - HwModule *module; + sp<HwModule> module; sp<IOProfile> profile; - sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_IN_BUILTIN_MIC); + sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), + AUDIO_DEVICE_IN_BUILTIN_MIC); mAvailableOutputDevices.add(mDefaultOutputDevice); mAvailableInputDevices.add(defaultInputDevice); diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h index e012d63..adf1b33 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/AudioPolicyManager.h @@ -195,20 +195,23 @@ protected: class AudioGain: public RefBase { public: - AudioGain(); + AudioGain(int index, bool useInChannelMask); virtual ~AudioGain() {} void dump(int fd, int spaces, int index) const; + void getDefaultConfig(struct audio_gain_config *config); + status_t checkConfig(const struct audio_gain_config *config); + int mIndex; struct audio_gain mGain; + bool mUseInChannelMask; }; - class AudioPort: public RefBase + class AudioPort: public virtual RefBase { public: AudioPort(const String8& name, audio_port_type_t type, - audio_port_role_t role, HwModule *module) : - mName(name), mType(type), mRole(role), mModule(module) {} + audio_port_role_t role, const sp<HwModule>& module); virtual ~AudioPort() {} virtual void toAudioPort(struct audio_port *port) const; @@ -219,14 +222,20 @@ protected: void loadInChannels(char *name); audio_gain_mode_t loadGainMode(char *name); - void loadGain(cnode *root); + void loadGain(cnode *root, int index); void loadGains(cnode *root); + status_t checkSamplingRate(uint32_t samplingRate) const; + status_t checkChannelMask(audio_channel_mask_t channelMask) const; + status_t checkFormat(audio_format_t format) const; + status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; + void dump(int fd, int spaces) const; String8 mName; audio_port_type_t mType; audio_port_role_t mRole; + bool mUseInChannelMask; // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats // indicates the supported parameters should be read from the output stream // after it is opened for the first time @@ -234,9 +243,27 @@ protected: Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks Vector <audio_format_t> mFormats; // supported audio formats Vector < sp<AudioGain> > mGains; // gain controllers - HwModule *mModule; // audio HW module exposing this I/O stream + sp<HwModule> mModule; // audio HW module exposing this I/O stream + }; + + class AudioPortConfig: public virtual RefBase + { + public: + AudioPortConfig(); + virtual ~AudioPortConfig() {} + + status_t applyAudioPortConfig(const struct audio_port_config *config, + struct audio_port_config *backupConfig = NULL); + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const = 0; + sp<AudioPort> mAudioPort; + uint32_t mSamplingRate; + audio_format_t mFormat; + audio_channel_mask_t mChannelMask; + struct audio_gain_config mGain; }; + class AudioPatch: public RefBase { public: @@ -250,29 +277,15 @@ protected: audio_patch_handle_t mAfPatchHandle; }; - class DeviceDescriptor: public AudioPort + class DeviceDescriptor: public AudioPort, public AudioPortConfig { public: - DeviceDescriptor(const String8& name, audio_devices_t type, String8 address, - audio_channel_mask_t channelMask) : - AudioPort(name, AUDIO_PORT_TYPE_DEVICE, - audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : - AUDIO_PORT_ROLE_SOURCE, - NULL), - mDeviceType(type), mAddress(address), - mChannelMask(channelMask), mId(0) {} - - DeviceDescriptor(String8 name, audio_devices_t type) : - AudioPort(name, AUDIO_PORT_TYPE_DEVICE, - audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : - AUDIO_PORT_ROLE_SOURCE, - NULL), - mDeviceType(type), mAddress(""), - mChannelMask(0), mId(0) {} + DeviceDescriptor(const String8& name, audio_devices_t type); + virtual ~DeviceDescriptor() {} bool equals(const sp<DeviceDescriptor>& other) const; - void toAudioPortConfig(struct audio_port_config *dstConfig, + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig = NULL) const; virtual void toAudioPort(struct audio_port *port) const; @@ -317,7 +330,7 @@ protected: class IOProfile : public AudioPort { public: - IOProfile(const String8& name, audio_port_role_t role, HwModule *module); + IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); virtual ~IOProfile(); bool isCompatibleProfile(audio_devices_t device, @@ -335,7 +348,7 @@ protected: // direct output...). For outputs only. }; - class HwModule { + class HwModule : public RefBase{ public: HwModule(const char *name); ~HwModule(); @@ -346,8 +359,9 @@ protected: void dump(int fd); - const char *const mName; // base name of the audio HW module (primary, a2dp ...) - audio_module_handle_t mHandle; + const char *const mName; // base name of the audio HW module (primary, a2dp ...) + uint32_t mHalVersion; // audio HAL API version + audio_module_handle_t mHandle; Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf @@ -373,7 +387,7 @@ protected: // descriptor for audio outputs. Used to maintain current configuration of each opened audio output // and keep track of the usage of this output by each audio stream type. - class AudioOutputDescriptor + class AudioOutputDescriptor: public AudioPortConfig { public: AudioOutputDescriptor(const sp<IOProfile>& profile); @@ -386,7 +400,7 @@ protected: bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } audio_devices_t supportedDevices(); uint32_t latency(); - bool sharesHwModuleWith(const AudioOutputDescriptor *outputDesc); + bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc); bool isActive(uint32_t inPastMs = 0) const; bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0, @@ -395,23 +409,20 @@ protected: uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; - void toAudioPortConfig(struct audio_port_config *dstConfig, + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig = NULL) const; void toAudioPort(struct audio_port *port) const; audio_port_handle_t mId; audio_io_handle_t mIoHandle; // output handle - uint32_t mSamplingRate; // - audio_format_t mFormat; // - audio_channel_mask_t mChannelMask; // output configuration uint32_t mLatency; // audio_output_flags_t mFlags; // audio_devices_t mDevice; // current device this output is routed to audio_patch_handle_t mPatchHandle; uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output nsecs_t mStopTime[AUDIO_STREAM_CNT]; - AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output - AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output + sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output + sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter const sp<IOProfile> mProfile; // I/O profile this output derives from @@ -422,7 +433,7 @@ protected: // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. - class AudioInputDescriptor + class AudioInputDescriptor: public AudioPortConfig { public: AudioInputDescriptor(const sp<IOProfile>& profile); @@ -431,16 +442,13 @@ protected: audio_port_handle_t mId; audio_io_handle_t mIoHandle; // input handle - uint32_t mSamplingRate; // - audio_format_t mFormat; // input configuration - audio_channel_mask_t mChannelMask; // audio_devices_t mDevice; // current device this input is routed to audio_patch_handle_t mPatchHandle; uint32_t mRefCount; // number of AudioRecord clients using this output audio_source_t mInputSource; // input source selected by application (mediarecorder.h) const sp<IOProfile> mProfile; // I/O profile this output derives from - void toAudioPortConfig(struct audio_port_config *dstConfig, + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig = NULL) const; void toAudioPort(struct audio_port *port) const; }; @@ -463,7 +471,7 @@ protected: }; // stream descriptor used for volume control - class EffectDescriptor + class EffectDescriptor : public RefBase { public: @@ -476,8 +484,8 @@ protected: bool mEnabled; // enabled state: CPU load being used or not }; - void addOutput(audio_io_handle_t output, AudioOutputDescriptor *outputDesc); - void addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc); + void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc); + void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc); // return the strategy corresponding to a given stream type static routing_strategy getStrategy(audio_stream_type_t stream); @@ -618,7 +626,7 @@ protected: int testOutputIndex(audio_io_handle_t output); #endif //AUDIO_POLICY_TEST - status_t setEffectEnabled(EffectDescriptor *pDesc, bool enabled); + status_t setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled); // returns the category the device belongs to with regard to volume curve management static device_category getDeviceCategory(audio_devices_t device); @@ -627,7 +635,7 @@ protected: static audio_devices_t getDeviceForVolume(audio_devices_t device); SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device, - DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs); + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs); bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, SortedVector<audio_io_handle_t>& outputs2); @@ -635,7 +643,7 @@ protected: // if muting, wait for the audio in pcm buffer to be drained before proceeding // if unmuting, unmute only after the specified delay // Returns the number of ms waited - uint32_t checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, + uint32_t checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc, audio_devices_t prevDevice, uint32_t delayMs); @@ -659,10 +667,10 @@ protected: const sp<AudioPatch>& patch); status_t removeAudioPatch(audio_patch_handle_t handle); - AudioOutputDescriptor *getOutputFromId(audio_port_handle_t id) const; - AudioInputDescriptor *getInputFromId(audio_port_handle_t id) const; - HwModule *getModuleForDevice(audio_devices_t device) const; - HwModule *getModuleFromName(const char *name) const; + sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const; + sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const; + sp<HwModule> getModuleForDevice(audio_devices_t device) const; + sp<HwModule> getModuleFromName(const char *name) const; // // Audio policy configuration file parsing (audio_policy.conf) // @@ -677,7 +685,7 @@ protected: static audio_devices_t parseDeviceNames(char *name); void loadHwModule(cnode *root); void loadHwModules(cnode *root); - void loadGlobalConfig(cnode *root, HwModule *module); + void loadGlobalConfig(cnode *root, const sp<HwModule>& module); status_t loadAudioPolicyConfig(const char *path); void defaultAudioPolicyConfig(void); @@ -686,11 +694,11 @@ protected: AudioPolicyClientInterface *mpClientInterface; // audio policy client interface audio_io_handle_t mPrimaryOutput; // primary output handle // list of descriptors for outputs currently opened - DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mOutputs; // copy of mOutputs before setDeviceConnectionState() opens new outputs // reset to mOutputs when updateDevicesAndOutputs() is called. - DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs; - DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mPreviousOutputs; + DefaultKeyedVector<audio_io_handle_t, sp<AudioInputDescriptor> > mInputs; // list of input descriptors DeviceVector mAvailableOutputDevices; // all available output devices DeviceVector mAvailableInputDevices; // all available input devices int mPhoneState; // current phone state @@ -707,13 +715,13 @@ protected: static const uint32_t MAX_EFFECTS_MEMORY = 512; uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects uint32_t mTotalEffectsMemory; // current memory used by effects - KeyedVector<int, EffectDescriptor *> mEffects; // list of registered audio effects + KeyedVector<int, sp<EffectDescriptor> > mEffects; // list of registered audio effects bool mA2dpSuspended; // true if A2DP output is suspended sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path // to boost soft sounds, used to adjust volume curves accordingly - Vector <HwModule *> mHwModules; + Vector < sp<HwModule> > mHwModules; volatile int32_t mNextUniqueId; volatile int32_t mAudioPortGeneration; diff --git a/services/audiopolicy/audio_policy.conf b/services/audiopolicy/audio_policy.conf new file mode 100644 index 0000000..9b83fef --- /dev/null +++ b/services/audiopolicy/audio_policy.conf @@ -0,0 +1,145 @@ +# +# Template audio policy configuration file +# + +# Global configuration section: +# - before audio HAL version 3.0: +# lists input and output devices always present on the device +# as well as the output device selected by default. +# Devices are designated by a string that corresponds to the enum in audio.h +# +# global_configuration { +# attached_output_devices AUDIO_DEVICE_OUT_SPEAKER +# default_output_device AUDIO_DEVICE_OUT_SPEAKER +# attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX +# } +# +# - after and including audio HAL 3.0 the global_configuration section is included in each +# hardware module section. +# it also includes the audio HAL version of this hw module: +# global_configuration { +# ... +# audio_hal_version <major.minor> # audio HAL version in e.g. 3.0 +# } +# other attributes (attached devices, default device) have to be included in the +# global_configuration section of each hardware module + + +# audio hardware module section: contains descriptors for all audio hw modules present on the +# device. Each hw module node is named after the corresponding hw module library base name. +# For instance, "primary" corresponds to audio.primary.<device>.so. +# The "primary" module is mandatory and must include at least one output with +# AUDIO_OUTPUT_FLAG_PRIMARY flag. +# Each module descriptor contains one or more output profile descriptors and zero or more +# input profile descriptors. Each profile lists all the parameters supported by a given output +# or input stream category. +# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding +# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". +# +# For audio HAL version posterior to 3.0 the following sections or sub sections can be present in +# a hw module section: +# - A "global_configuration" section: see above +# - Optionally a "devices" section: +# This section contains descriptors for audio devices with attributes like an address or a +# gain controller. The syntax for the devices section and device descriptor is as follows: +# devices { +# <device name> { # <device name>: any string without space +# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER +# address <address> # optional: device address, char string less than 64 in length +# } +# } +# - one or more "gains" sections can be present in a device descriptor section. +# If present, they describe the capabilities of gain controllers attached to this input or +# output device. e.g. : +# <device name> { # <device name>: any string without space +# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER +# address <address> # optional: device address, char string less than 64 in length +# gains { +# <gain name> { +# mode <gain modes supported> # e.g. AUDIO_GAIN_MODE_CHANNELS +# channel_mask <controlled channels> # needed if mode AUDIO_GAIN_MODE_CHANNELS +# min_value_mB <min value in millibel> +# max_value_mB <max value in millibel> +# default_value_mB <default value in millibel> +# step_value_mB <step value in millibel> +# min_ramp_ms <min duration in ms> # needed if mode AUDIO_GAIN_MODE_RAMP +# max_ramp_ms <max duration ms> # needed if mode AUDIO_GAIN_MODE_RAMP +# } +# } +# } +# - when a device descriptor is present, output and input profiles can refer to this device by +# its name in their "devices" section instead of specifying a device type. e.g. : +# outputs { +# primary { +# sampling_rates 44100 +# channel_masks AUDIO_CHANNEL_OUT_STEREO +# formats AUDIO_FORMAT_PCM_16_BIT +# devices <device name> +# flags AUDIO_OUTPUT_FLAG_PRIMARY +# } +# } +# sample audio_policy.conf file below + +audio_hw_modules { + primary { + global_configuration { + attached_output_devices AUDIO_DEVICE_OUT_SPEAKER + default_output_device AUDIO_DEVICE_OUT_SPEAKER + attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC + audio_hal_version 3.0 + } + devices { + speaker { + type AUDIO_DEVICE_OUT_SPEAKER + gains { + gain_1 { + mode AUDIO_GAIN_MODE_JOINT + min_value_mB -8400 + max_value_mB 4000 + default_value_mB 0 + step_value_mB 100 + } + } + } + } + outputs { + primary { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices speaker + flags AUDIO_OUTPUT_FLAG_PRIMARY + } + } + inputs { + primary { + sampling_rates 8000|16000 + channel_masks AUDIO_CHANNEL_IN_MONO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_BUILTIN_MIC + } + } + } + r_submix { + global_configuration { + attached_input_devices AUDIO_DEVICE_IN_REMOTE_SUBMIX + audio_hal_version 2.0 + } + outputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX + } + } + inputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_IN_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_REMOTE_SUBMIX + } + } + } +} diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h index 79f20f1..2535a67 100644 --- a/services/audiopolicy/audio_policy_conf.h +++ b/services/audiopolicy/audio_policy_conf.h @@ -35,6 +35,7 @@ #define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device" #define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices" #define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled" +#define AUDIO_HAL_VERSION_TAG "audio_hal_version" // hw modules descriptions #define AUDIO_HW_MODULE_TAG "audio_hw_modules" diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 9fd35e1..638b871 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -852,13 +852,12 @@ status_t CameraService::connectDevice( switch(deviceVersion) { case CAMERA_DEVICE_API_VERSION_1_0: - ALOGW("Camera using old HAL version: %d", deviceVersion); - return -EOPNOTSUPP; - // TODO: don't allow 2.0 Only allow 2.1 and higher case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: case CAMERA_DEVICE_API_VERSION_3_1: + ALOGW("Camera using old HAL version: %d", deviceVersion); + return -EOPNOTSUPP; case CAMERA_DEVICE_API_VERSION_3_2: client = new CameraDeviceClient(this, cameraCb, String16(), cameraId, facing, callingPid, USE_CALLING_UID, getpid()); diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 4fce1b3..8154e59 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -310,6 +310,10 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, Mutex::Autolock icl(mBinderSerializationLock); + if (bufferProducer == NULL) { + ALOGE("%s: bufferProducer must not be null", __FUNCTION__); + return BAD_VALUE; + } if (!mDevice.get()) return DEAD_OBJECT; // Don't create multiple streams for the same target surface |