diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/AudioTrack.java | 61 | ||||
-rw-r--r-- | media/jni/Android.bp | 1 | ||||
-rw-r--r-- | media/jni/android_media_tv_Tuner.cpp | 21 | ||||
-rw-r--r-- | media/jni/soundpool/Sound.cpp | 12 | ||||
-rw-r--r-- | media/jni/soundpool/Stream.cpp | 4 | ||||
-rw-r--r-- | media/jni/soundpool/StreamManager.cpp | 26 | ||||
-rw-r--r-- | media/jni/soundpool/StreamManager.h | 10 | ||||
-rw-r--r-- | media/jni/soundpool/android_media_SoundPool.cpp | 3 |
8 files changed, 113 insertions, 25 deletions
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 1b05c3b57256..b265ebfc02a0 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -2077,6 +2077,65 @@ public class AudioTrack extends PlayerBase } /** + * Sets the streaming start threshold for an <code>AudioTrack</code>. + * <p> The streaming start threshold is the buffer level that the written audio + * data must reach for audio streaming to start after {@link #play()} is called. + * <p> For compressed streams, the size of a frame is considered to be exactly one byte. + * + * @param startThresholdInFrames the desired start threshold. + * @return the actual start threshold in frames value. This is + * an integer between 1 to the buffer capacity + * (see {@link #getBufferCapacityInFrames()}), + * and might change if the output sink changes after track creation. + * @throws IllegalStateException if the track is not initialized or the + * track transfer mode is not {@link #MODE_STREAM}. + * @throws IllegalArgumentException if startThresholdInFrames is not positive. + * @see #getStartThresholdInFrames() + */ + public @IntRange(from = 1) int setStartThresholdInFrames( + @IntRange (from = 1) int startThresholdInFrames) { + if (mState != STATE_INITIALIZED) { + throw new IllegalStateException("AudioTrack is not initialized"); + } + if (mDataLoadMode != MODE_STREAM) { + throw new IllegalStateException("AudioTrack must be a streaming track"); + } + if (startThresholdInFrames < 1) { + throw new IllegalArgumentException("startThresholdInFrames " + + startThresholdInFrames + " must be positive"); + } + return native_setStartThresholdInFrames(startThresholdInFrames); + } + + /** + * Returns the streaming start threshold of the <code>AudioTrack</code>. + * <p> The streaming start threshold is the buffer level that the written audio + * data must reach for audio streaming to start after {@link #play()} is called. + * When an <code>AudioTrack</code> is created, the streaming start threshold + * is the buffer capacity in frames. If the buffer size in frames is reduced + * by {@link #setBufferSizeInFrames(int)} to a value smaller than the start threshold + * then that value will be used instead for the streaming start threshold. + * <p> For compressed streams, the size of a frame is considered to be exactly one byte. + * + * @return the current start threshold in frames value. This is + * an integer between 1 to the buffer capacity + * (see {@link #getBufferCapacityInFrames()}), + * and might change if the output sink changes after track creation. + * @throws IllegalStateException if the track is not initialized or the + * track is not {@link #MODE_STREAM}. + * @see #setStartThresholdInFrames(int) + */ + public @IntRange (from = 1) int getStartThresholdInFrames() { + if (mState != STATE_INITIALIZED) { + throw new IllegalStateException("AudioTrack is not initialized"); + } + if (mDataLoadMode != MODE_STREAM) { + throw new IllegalStateException("AudioTrack must be a streaming track"); + } + return native_getStartThresholdInFrames(); + } + + /** * Returns the frame count of the native <code>AudioTrack</code> buffer. * @return current size in frames of the <code>AudioTrack</code> buffer. * @throws IllegalStateException @@ -4179,6 +4238,8 @@ public class AudioTrack extends PlayerBase private native int native_get_audio_description_mix_level_db(float[] level); private native int native_set_dual_mono_mode(int dualMonoMode); private native int native_get_dual_mono_mode(int[] dualMonoMode); + private native int native_setStartThresholdInFrames(int startThresholdInFrames); + private native int native_getStartThresholdInFrames(); //--------------------------------------------------------- // Utility methods diff --git a/media/jni/Android.bp b/media/jni/Android.bp index f65dfddef008..517e192e00af 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { name: "libmedia_jni", defaults: ["libcodec2-internal-defaults"], + min_sdk_version: "", srcs: [ "android_media_ImageWriter.cpp", diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 694b93919cde..3976086ea495 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -287,7 +287,10 @@ Dvr::~Dvr() { jint Dvr::close() { Result r = mDvrSp->close(); if (r == Result::SUCCESS) { - EventFlag::deleteEventFlag(&mDvrMQEventFlag); + if (mDvrMQEventFlag != nullptr) { + EventFlag::deleteEventFlag(&mDvrMQEventFlag); + } + mDvrMQ = nullptr; } return (jint) r; } @@ -723,13 +726,15 @@ Filter::~Filter() { env->DeleteWeakGlobalRef(mFilterObj); mFilterObj = NULL; - EventFlag::deleteEventFlag(&mFilterMQEventFlag); } int Filter::close() { Result r = mFilterSp->close(); if (r == Result::SUCCESS) { - EventFlag::deleteEventFlag(&mFilterMQEventFlag); + if (mFilterMQEventFlag != nullptr) { + EventFlag::deleteEventFlag(&mFilterMQEventFlag); + } + mFilterMQ = nullptr; } return (int)r; } @@ -3050,6 +3055,9 @@ static jint android_media_tv_Tuner_configure_filter( filterSp->mFilterMQ = std::make_unique<MQ>(filterMQDesc, true); EventFlag::createEventFlag( filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag)); + } else { + filterSp->mFilterMQ = nullptr; + filterSp->mFilterMQEventFlag = nullptr; } } return (jint) getQueueDescResult; @@ -3137,13 +3145,12 @@ static jint android_media_tv_Tuner_read_filter_fmq( } static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { + sp<Filter> filterSp = getFilter(env, filter); + if (filterSp == NULL) { ALOGD("Failed to close filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } - Result r = iFilterSp->close(); - return (jint) r; + return filterSp->close(); } static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) { diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp index f8b4bdb1f4d5..50e0d336f981 100644 --- a/media/jni/soundpool/Sound.cpp +++ b/media/jni/soundpool/Sound.cpp @@ -99,8 +99,8 @@ static status_t decode(int fd, int64_t offset, int64_t length, __func__); break; } - int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize); - ALOGV("%s: read %d", __func__, sampleSize); + ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize); + ALOGV("%s: read %zd", __func__, sampleSize); if (sampleSize < 0) { sampleSize = 0; sawInputEOS = true; @@ -124,8 +124,8 @@ static status_t decode(int fd, int64_t offset, int64_t length, } AMediaCodecBufferInfo info; - const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1); - ALOGV("%s: dequeueoutput returned: %d", __func__, status); + const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1); + ALOGV("%s: dequeueoutput returned: %zd", __func__, status); if (status >= 0) { if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { ALOGV("%s: output EOS", __func__); @@ -167,10 +167,10 @@ static status_t decode(int fd, int64_t offset, int64_t length, } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { ALOGV("%s: no output buffer right now", __func__); } else if (status <= AMEDIA_ERROR_BASE) { - ALOGE("%s: decode error: %d", __func__, status); + ALOGE("%s: decode error: %zd", __func__, status); break; } else { - ALOGV("%s: unexpected info code: %d", __func__, status); + ALOGV("%s: unexpected info code: %zd", __func__, status); } } diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp index 73e319a5902e..f261bab0a20c 100644 --- a/media/jni/soundpool/Stream.cpp +++ b/media/jni/soundpool/Stream.cpp @@ -317,7 +317,8 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID, // audio track while the new one is being started and avoids processing them with // wrong audio audio buffer size (mAudioBufferSize) auto toggle = mToggle ^ 1; - void* userData = (void*)((uintptr_t)this | toggle); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle); audio_channel_mask_t soundChannelMask = sound->getChannelMask(); // When sound contains a valid channel mask, use it as is. // Otherwise, use stream count to calculate channel mask. @@ -386,6 +387,7 @@ exit: void Stream::staticCallback(int event, void* user, void* info) { const auto userAsInt = (uintptr_t)user; + // NOLINTNEXTLINE(performance-no-int-to-ptr) auto stream = reinterpret_cast<Stream*>(userAsInt & ~1); stream->callback(event, info, int(userAsInt & 1), 0 /* tries */); } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 502ee00b583e..309d71cfd062 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -43,6 +43,14 @@ static constexpr bool kPlayOnCallingThread = true; // Amount of time for a StreamManager thread to wait before closing. static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND; +// Debug flag: +// kForceLockStreamManagerStop is set to true to force lock the StreamManager +// worker thread during stop. This limits concurrency of Stream processing. +// Normally we lock the StreamManager worker thread during stop ONLY +// for SoundPools configured with a single Stream. +// +static constexpr bool kForceLockStreamManagerStop = false; + //////////// StreamMap::StreamMap(int32_t streams) { @@ -103,6 +111,7 @@ StreamManager::StreamManager( : StreamMap(streams) , mAttributes(*attributes) , mOpPackageName(std::move(opPackageName)) + , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop) { ALOGV("%s(%d, %zu, ...)", __func__, streams, threads); forEach([this](Stream *stream) { @@ -113,7 +122,8 @@ StreamManager::StreamManager( }); mThreadPool = std::make_unique<ThreadPool>( - std::min(threads, (size_t)std::thread::hardware_concurrency()), + std::min((size_t)streams, // do not make more threads than streams to play + std::min(threads, (size_t)std::thread::hardware_concurrency())), "SoundPool_"); } @@ -330,7 +340,7 @@ ssize_t StreamManager::removeFromQueues_l( // streams on mProcessingStreams are undergoing processing by the StreamManager thread // and do not participate in normal stream migration. - return found; + return (ssize_t)found; } void StreamManager::addToRestartQueue_l(Stream *stream) { @@ -348,14 +358,14 @@ void StreamManager::addToActiveQueue_l(Stream *stream) { void StreamManager::run(int32_t id) { ALOGV("%s(%d) entering", __func__, id); - int64_t waitTimeNs = kWaitTimeBeforeCloseNs; + int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty. std::unique_lock lock(mStreamManagerLock); while (!mQuit) { - if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty. + if (waitTimeNs > 0) { mStreamManagerCondition.wait_for( lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); } - ALOGV("%s(%d) awake", __func__, id); + ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs); sanityCheckQueue_l(); @@ -375,12 +385,12 @@ void StreamManager::run(int32_t id) } mRestartStreams.erase(it); mProcessingStreams.emplace(stream); - lock.unlock(); + if (!mLockStreamManagerStop) lock.unlock(); stream->stop(); ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID()); if (Stream* nextStream = stream->playPairStream()) { ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID()); - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); if (nextStream->getStopTimeNs() > 0) { // the next stream was stopped before we can move it to the active queue. ALOGV("%s(%d) stopping started streamID:%d", @@ -390,7 +400,7 @@ void StreamManager::run(int32_t id) addToActiveQueue_l(nextStream); } } else { - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); mAvailableStreams.insert(stream); } mProcessingStreams.erase(stream); diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 81ac69eb4358..85b468cd2cbc 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -437,6 +437,14 @@ private: void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock); const audio_attributes_t mAttributes; + const std::string mOpPackageName; + + // For legacy compatibility, we lock the stream manager on stop when + // there is only one stream. This allows a play to be called immediately + // after stopping, otherwise it is possible that the play might be discarded + // (returns 0) because that stream may be in the worker thread call to stop. + const bool mLockStreamManagerStop; + std::unique_ptr<ThreadPool> mThreadPool; // locked internally // mStreamManagerLock is used to lock access for transitions between the @@ -477,8 +485,6 @@ private: // The paired stream may be active or restarting. // No particular order. std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock); - - const std::string mOpPackageName; }; } // namespace android::soundpool diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp index 357cc63bd41e..a66d99fbd9f4 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool.cpp @@ -34,7 +34,8 @@ static struct fields_t { jclass mSoundPoolClass; } fields; static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) { - return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext)); } static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; struct audio_attributes_fields_t { |