Merge "MediaPlayer2: remove MediaPlayer2Engine"
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 67003c1..51cef8c 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -33,6 +33,7 @@
* Do not #include files that aren't part of the NDK.
*/
#include <sys/cdefs.h>
+#include <stdbool.h>
#include <android/native_window.h>
#include "NdkCameraError.h"
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index f425830..b715b12 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -153,6 +153,11 @@
} ACameraDevice_StateCallbacks;
/**
+ * For backward compatiblity.
+ */
+typedef ACameraDevice_StateCallbacks ACameraDevice_stateCallbacks;
+
+/**
* Close the connection and free this ACameraDevice synchronously. Access to the ACameraDevice
* after calling this method will cause a crash.
*
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 48f4479..068a52b 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -100,7 +100,7 @@
return hidl_string(string.string());
}
-std::string ToHexString(const std::string& str) {
+std::string toHexString(const std::string& str) {
std::ostringstream out;
out << std::hex << std::setfill('0');
for (size_t i = 0; i < str.size(); i++) {
@@ -261,20 +261,39 @@
}
void DrmHal::closeOpenSessions() {
- if (mPlugin != NULL) {
- for (size_t i = 0; i < mOpenSessions.size(); i++) {
- mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
- DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
- }
+ Mutex::Autolock autoLock(mLock);
+ auto openSessions = mOpenSessions;
+ for (size_t i = 0; i < openSessions.size(); i++) {
+ mLock.unlock();
+ closeSession(openSessions[i]);
+ mLock.lock();
}
mOpenSessions.clear();
}
DrmHal::~DrmHal() {
- closeOpenSessions();
DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
}
+void DrmHal::cleanup() {
+ closeOpenSessions();
+
+ Mutex::Autolock autoLock(mLock);
+ reportPluginMetrics();
+ reportFrameworkMetrics();
+
+ setListener(NULL);
+ mInitCheck = NO_INIT;
+
+ if (mPlugin != NULL) {
+ if (!mPlugin->setListener(NULL).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
+ }
+ mPlugin.clear();
+ mPluginV1_1.clear();
+}
+
Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
Vector<sp<IDrmFactory>> factories;
@@ -512,22 +531,7 @@
}
status_t DrmHal::destroyPlugin() {
- Mutex::Autolock autoLock(mLock);
- INIT_CHECK();
-
- closeOpenSessions();
- reportPluginMetrics();
- reportFrameworkMetrics();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
- mPluginV1_1.clear();
+ cleanup();
return OK;
}
@@ -633,7 +637,6 @@
status_t response = toStatusT(status);
mMetrics.SetSessionEnd(sessionId);
mMetrics.mCloseSessionCounter.Increment(response);
- reportPluginMetrics();
return response;
}
mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
@@ -1267,17 +1270,7 @@
void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
{
- Mutex::Autolock autoLock(mLock);
- closeOpenSessions();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
+ cleanup();
}
void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
@@ -1299,13 +1292,13 @@
String8 description;
status_t result = getPropertyStringInternal(String8("vendor"), vendor);
if (result != OK) {
- ALOGE("Failed to get vendor from drm plugin. %d", result);
+ ALOGE("Failed to get vendor from drm plugin: %d", result);
} else {
item.setCString("vendor", vendor.c_str());
}
result = getPropertyStringInternal(String8("description"), description);
if (result != OK) {
- ALOGE("Failed to get description from drm plugin. %d", result);
+ ALOGE("Failed to get description from drm plugin: %d", result);
} else {
item.setCString("description", description.c_str());
}
@@ -1313,14 +1306,14 @@
std::string serializedMetrics;
result = mMetrics.GetSerializedMetrics(&serializedMetrics);
if (result != OK) {
- ALOGE("Failed to serialize Framework metrics: %d", result);
+ ALOGE("Failed to serialize framework metrics: %d", result);
}
- serializedMetrics = ToHexString(serializedMetrics);
+ serializedMetrics = toHexString(serializedMetrics);
if (!serializedMetrics.empty()) {
item.setCString("serialized_metrics", serializedMetrics.c_str());
}
if (!item.selfrecord()) {
- ALOGE("Failed to self record framework metrics.");
+ ALOGE("Failed to self record framework metrics");
}
}
@@ -1335,7 +1328,7 @@
status_t res = android::reportDrmPluginMetrics(
metrics, vendor, description);
if (res != OK) {
- ALOGE("Metrics were retrieved but could not be reported: %i", res);
+ ALOGE("Metrics were retrieved but could not be reported: %d", res);
}
}
}
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 2e29316..cf7d90f 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -46,10 +46,6 @@
class AudioMixer
{
public:
- // This mixer has a hard-coded upper limit of active track inputs;
- // the value is arbitrary but should be less than TRACK0 to avoid confusion.
- static constexpr int32_t MAX_NUM_TRACKS = 256;
-
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
@@ -61,12 +57,6 @@
static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
enum { // names
-
- // track names (MAX_NUM_TRACKS units)
- TRACK0 = 0x1000,
-
- // 0x2000 is unused
-
// setParameter targets
TRACK = 0x3000,
RESAMPLE = 0x3001,
@@ -105,23 +95,33 @@
// parameter 'value' is a pointer to the new playback rate.
};
- AudioMixer(size_t frameCount, uint32_t sampleRate, int32_t maxNumTracks = MAX_NUM_TRACKS)
- : mMaxNumTracks(maxNumTracks)
- , mSampleRate(sampleRate)
+ AudioMixer(size_t frameCount, uint32_t sampleRate)
+ : mSampleRate(sampleRate)
, mFrameCount(frameCount) {
pthread_once(&sOnceControl, &sInitRoutine);
}
- // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
+ // Create a new track in the mixer.
+ //
+ // \param name a unique user-provided integer associated with the track.
+ // If name already exists, the function will abort.
+ // \param channelMask output channel mask.
+ // \param format PCM format
+ // \param sessionId Session id for the track. Tracks with the same
+ // session id will be submixed together.
+ //
+ // \return OK on success.
+ // BAD_VALUE if the format does not satisfy isValidFormat()
+ // or the channelMask does not satisfy isValidChannelMask().
+ status_t create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);
- // Allocate a track name. Returns new track name if successful, -1 on failure.
- // 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);
+ bool exists(int name) const {
+ return mTracks.count(name) > 0;
+ }
- // Free an allocated track by name
- void deleteTrackName(int name);
+ // Free an allocated track by name.
+ void destroy(int name);
// Enable or disable an allocated track by name
void enable(int name);
@@ -149,6 +149,23 @@
mNBLogWriter = logWriter;
}
+ static inline bool isValidFormat(audio_format_t format) {
+ switch (format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static inline bool isValidChannelMask(audio_channel_mask_t channelMask) {
+ return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
+ }
+
private:
/* For multi-format functions (calls template functions
@@ -361,23 +378,9 @@
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);
- static inline bool isValidPcmTrackFormat(audio_format_t format) {
- switch (format) {
- case AUDIO_FORMAT_PCM_8_BIT:
- case AUDIO_FORMAT_PCM_16_BIT:
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_FLOAT:
- return true;
- default:
- return false;
- }
- }
-
static void sInitRoutine();
// initialization constants
- const int mMaxNumTracks;
const uint32_t mSampleRate;
const size_t mFrameCount;
@@ -390,12 +393,6 @@
std::unique_ptr<int32_t[]> mOutputTemp;
std::unique_ptr<int32_t[]> mResampleTemp;
- // fast lookup of previously deleted track names for reuse.
- // the AudioMixer tries to return the smallest unused name -
- // this is an arbitrary decision (actually any non-negative
- // integer that isn't in mTracks could be used).
- std::set<int /* name */> mUnusedNames; // set of unused track names (may be empty)
-
// track names grouped by main buffer, in no particular order of main buffer.
// however names for a particular main buffer are in order (by construction).
std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f1daeb4..2042913 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -90,34 +90,21 @@
return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
}
-int AudioMixer::getTrackName(
- audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
+status_t AudioMixer::create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{
- if (!isValidPcmTrackFormat(format)) {
- ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
- return -1;
- }
- if (mTracks.size() >= (size_t)mMaxNumTracks) {
- ALOGE("%s: out of track names (max = %d)", __func__, mMaxNumTracks);
- return -1;
- }
+ LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);
- // get a new name for the track.
- int name;
- if (mUnusedNames.size() != 0) {
- // reuse first name for deleted track.
- auto it = mUnusedNames.begin();
- name = *it;
- (void)mUnusedNames.erase(it);
- } else {
- // we're fully populated, so create a new name.
- name = mTracks.size();
+ if (!isValidChannelMask(channelMask)) {
+ ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
+ return BAD_VALUE;
}
- ALOGV("add track (%d)", name);
+ if (!isValidFormat(format)) {
+ ALOGE("%s invalid format: %#x", __func__, format);
+ return BAD_VALUE;
+ }
auto t = std::make_shared<Track>();
- mTracks[name] = t;
-
{
// TODO: move initialization to the Track constructor.
// assume default parameters for the track, except where noted below
@@ -179,12 +166,14 @@
status_t status = t->prepareForDownmix();
if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
- return -1;
+ return BAD_VALUE;
}
// prepareForDownmix() may change mDownmixRequiresFormat
ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
t->prepareForReformat();
- return TRACK0 + name;
+
+ mTracks[name] = t;
+ return OK;
}
}
@@ -193,7 +182,7 @@
// which will simplify this logic.
bool AudioMixer::setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (trackChannelMask == track->channelMask
@@ -361,23 +350,20 @@
}
}
-void AudioMixer::deleteTrackName(int name)
+void AudioMixer::destroy(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
ALOGV("deleteTrackName(%d)", name);
if (mTracks[name]->enabled) {
invalidate();
}
mTracks.erase(name); // deallocate track
- mUnusedNames.emplace(name); // recycle name
}
void AudioMixer::enable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (!track->enabled) {
@@ -389,8 +375,7 @@
void AudioMixer::disable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->enabled) {
@@ -528,8 +513,7 @@
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
@@ -808,7 +792,6 @@
size_t AudioMixer::getUnreleasedFrames(int name) const
{
- name -= TRACK0;
const auto it = mTracks.find(name);
if (it != mTracks.end()) {
return it->second->getUnreleasedFrames();
@@ -818,7 +801,7 @@
void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
{
- name -= TRACK0;
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->mInputBufferProvider == bufferProvider) {
diff --git a/media/libaudioprocessing/tests/test-mixer.cpp b/media/libaudioprocessing/tests/test-mixer.cpp
index b67810d..bc9d2a6 100644
--- a/media/libaudioprocessing/tests/test-mixer.cpp
+++ b/media/libaudioprocessing/tests/test-mixer.cpp
@@ -143,10 +143,6 @@
usage(progname);
return EXIT_FAILURE;
}
- if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
- fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
- return EXIT_FAILURE;
- }
size_t outputFrames = 0;
@@ -246,9 +242,10 @@
for (size_t i = 0; i < providers.size(); ++i) {
//printf("track %d out of %d\n", i, providers.size());
uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
- int32_t name = mixer->getTrackName(channelMask,
- formats[i], AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
+ const int name = i;
+ const status_t status = mixer->create(
+ name, channelMask, formats[i], AUDIO_SESSION_OUTPUT_MIX);
+ LOG_ALWAYS_FATAL_IF(status != OK);
names[i] = name;
mixer->setBufferProvider(name, &providers[i]);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
@@ -315,8 +312,10 @@
writeFile(outputFilename, outputAddr,
outputSampleRate, outputChannels, outputFrames, useMixerFloat);
if (auxFilename) {
- // Aux buffer is always in q4_27 format for now.
- memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in q4_27 format for O and earlier.
+ // memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in float format for P.
+ memcpy_to_i16_from_float((int16_t*)auxAddr, (const float*)auxAddr, outputFrames);
writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
}
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index bf91ea9..c64b003 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -188,6 +188,7 @@
Vector<Vector<uint8_t>> mOpenSessions;
void closeOpenSessions();
+ void cleanup();
/**
* mInitCheck is:
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 905817f..8a15a50 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -100,7 +100,6 @@
"SkipCutBuffer.cpp",
"StagefrightMediaScanner.cpp",
"StagefrightMetadataRetriever.cpp",
- "SurfaceMediaSource.cpp",
"SurfaceUtils.cpp",
"Utils.cpp",
"ThrottledSource.cpp",
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
deleted file mode 100644
index 4b3076a..0000000
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2011 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 "SurfaceMediaSource"
-
-#include <inttypes.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/SurfaceMediaSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <OMX_IVCommon.h>
-#include <media/hardware/HardwareAPI.h>
-#include <media/hardware/MetadataBufferType.h>
-
-#include <ui/GraphicBuffer.h>
-#include <gui/BufferItem.h>
-#include <gui/ISurfaceComposer.h>
-#include <OMX_Component.h>
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include <private/gui/ComposerService.h>
-
-namespace android {
-
-SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight) :
- mWidth(bufferWidth),
- mHeight(bufferHeight),
- mCurrentSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mNumPendingBuffers(0),
- mCurrentTimestamp(0),
- mFrameRate(30),
- mStarted(false),
- mNumFramesReceived(0),
- mNumFramesEncoded(0),
- mFirstFrameTimestamp(0),
- mMaxAcquiredBufferCount(4), // XXX double-check the default
- mUseAbsoluteTimestamps(false) {
- ALOGV("SurfaceMediaSource");
-
- if (bufferWidth == 0 || bufferHeight == 0) {
- ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
- }
-
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
- mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
- GRALLOC_USAGE_HW_TEXTURE);
-
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-
- // Note that we can't create an sp<...>(this) in a ctor that will not keep a
- // reference once the ctor ends, as that would cause the refcount of 'this'
- // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
- // that's what we create.
- wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
- sp<BufferQueue::ProxyConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
-
- status_t err = mConsumer->consumerConnect(proxy, false);
- if (err != NO_ERROR) {
- ALOGE("SurfaceMediaSource: error connecting to BufferQueue: %s (%d)",
- strerror(-err), err);
- }
-}
-
-SurfaceMediaSource::~SurfaceMediaSource() {
- ALOGV("~SurfaceMediaSource");
- CHECK(!mStarted);
-}
-
-nsecs_t SurfaceMediaSource::getTimestamp() {
- ALOGV("getTimestamp");
- Mutex::Autolock lock(mMutex);
- return mCurrentTimestamp;
-}
-
-void SurfaceMediaSource::setFrameAvailableListener(
- const sp<FrameAvailableListener>& listener) {
- ALOGV("setFrameAvailableListener");
- Mutex::Autolock lock(mMutex);
- mFrameAvailableListener = listener;
-}
-
-void SurfaceMediaSource::dumpState(String8& result) const
-{
- char buffer[1024];
- dumpState(result, "", buffer, 1024);
-}
-
-void SurfaceMediaSource::dumpState(
- String8& result,
- const char* /* prefix */,
- char* buffer,
- size_t /* SIZE */) const
-{
- Mutex::Autolock lock(mMutex);
-
- result.append(buffer);
- mConsumer->dumpState(result, "");
-}
-
-status_t SurfaceMediaSource::setFrameRate(int32_t fps)
-{
- ALOGV("setFrameRate");
- Mutex::Autolock lock(mMutex);
- const int MAX_FRAME_RATE = 60;
- if (fps < 0 || fps > MAX_FRAME_RATE) {
- return BAD_VALUE;
- }
- mFrameRate = fps;
- return OK;
-}
-
-MetadataBufferType SurfaceMediaSource::metaDataStoredInVideoBuffers() const {
- ALOGV("isMetaDataStoredInVideoBuffers");
- return kMetadataBufferTypeANWBuffer;
-}
-
-int32_t SurfaceMediaSource::getFrameRate( ) const {
- ALOGV("getFrameRate");
- Mutex::Autolock lock(mMutex);
- return mFrameRate;
-}
-
-status_t SurfaceMediaSource::start(MetaData *params)
-{
- ALOGV("start");
-
- Mutex::Autolock lock(mMutex);
-
- CHECK(!mStarted);
-
- mStartTimeNs = 0;
- int64_t startTimeUs;
- int32_t bufferCount = 0;
- if (params) {
- if (params->findInt64(kKeyTime, &startTimeUs)) {
- mStartTimeNs = startTimeUs * 1000;
- }
-
- if (!params->findInt32(kKeyNumBuffers, &bufferCount)) {
- ALOGE("Failed to find the advertised buffer count");
- return UNKNOWN_ERROR;
- }
-
- if (bufferCount <= 1) {
- ALOGE("bufferCount %d is too small", bufferCount);
- return BAD_VALUE;
- }
-
- mMaxAcquiredBufferCount = bufferCount;
- }
-
- CHECK_GT(mMaxAcquiredBufferCount, 1u);
-
- status_t err =
- mConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount);
-
- if (err != OK) {
- return err;
- }
-
- mNumPendingBuffers = 0;
- mStarted = true;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::setMaxAcquiredBufferCount(size_t count) {
- ALOGV("setMaxAcquiredBufferCount(%zu)", count);
- Mutex::Autolock lock(mMutex);
-
- CHECK_GT(count, 1u);
- mMaxAcquiredBufferCount = count;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::setUseAbsoluteTimestamps() {
- ALOGV("setUseAbsoluteTimestamps");
- Mutex::Autolock lock(mMutex);
- mUseAbsoluteTimestamps = true;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::stop()
-{
- ALOGV("stop");
- Mutex::Autolock lock(mMutex);
-
- if (!mStarted) {
- return OK;
- }
-
- mStarted = false;
- mFrameAvailableCondition.signal();
-
- while (mNumPendingBuffers > 0) {
- ALOGI("Still waiting for %zu buffers to be returned.",
- mNumPendingBuffers);
-
-#if DEBUG_PENDING_BUFFERS
- for (size_t i = 0; i < mPendingBuffers.size(); ++i) {
- ALOGI("%d: %p", i, mPendingBuffers.itemAt(i));
- }
-#endif
-
- mMediaBuffersAvailableCondition.wait(mMutex);
- }
-
- mMediaBuffersAvailableCondition.signal();
-
- return mConsumer->consumerDisconnect();
-}
-
-sp<MetaData> SurfaceMediaSource::getFormat()
-{
- ALOGV("getFormat");
-
- Mutex::Autolock lock(mMutex);
- sp<MetaData> meta = new MetaData;
-
- meta->setInt32(kKeyWidth, mWidth);
- meta->setInt32(kKeyHeight, mHeight);
- // The encoder format is set as an opaque colorformat
- // The encoder will later find out the actual colorformat
- // from the GL Frames itself.
- meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque);
- meta->setInt32(kKeyStride, mWidth);
- meta->setInt32(kKeySliceHeight, mHeight);
- meta->setInt32(kKeyFrameRate, mFrameRate);
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
- return meta;
-}
-
-// Pass the data to the MediaBuffer. Pass in only the metadata
-// Note: Call only when you have the lock
-void SurfaceMediaSource::passMetadataBuffer_l(MediaBufferBase **buffer,
- ANativeWindowBuffer *bufferHandle) const {
- *buffer = new MediaBuffer(sizeof(VideoNativeMetadata));
- VideoNativeMetadata *data = (VideoNativeMetadata *)(*buffer)->data();
- if (data == NULL) {
- ALOGE("Cannot allocate memory for metadata buffer!");
- return;
- }
- data->eType = metaDataStoredInVideoBuffers();
- data->pBuffer = bufferHandle;
- data->nFenceFd = -1;
- ALOGV("handle = %p, offset = %zu, length = %zu",
- bufferHandle, (*buffer)->range_length(), (*buffer)->range_offset());
-}
-
-status_t SurfaceMediaSource::read(
- MediaBufferBase **buffer, const ReadOptions * /* options */) {
- ALOGV("read");
- Mutex::Autolock lock(mMutex);
-
- *buffer = NULL;
-
- while (mStarted && mNumPendingBuffers == mMaxAcquiredBufferCount) {
- mMediaBuffersAvailableCondition.wait(mMutex);
- }
-
- // Update the current buffer info
- // TODO: mCurrentSlot can be made a bufferstate since there
- // can be more than one "current" slots.
-
- BufferItem item;
- // If the recording has started and the queue is empty, then just
- // wait here till the frames come in from the client side
- while (mStarted) {
-
- status_t err = mConsumer->acquireBuffer(&item, 0);
- if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- // wait for a buffer to be queued
- mFrameAvailableCondition.wait(mMutex);
- } else if (err == OK) {
- err = item.mFence->waitForever("SurfaceMediaSource::read");
- if (err) {
- ALOGW("read: failed to wait for buffer fence: %d", err);
- }
-
- // First time seeing the buffer? Added it to the SMS slot
- if (item.mGraphicBuffer != NULL) {
- mSlots[item.mSlot].mGraphicBuffer = item.mGraphicBuffer;
- }
- mSlots[item.mSlot].mFrameNumber = item.mFrameNumber;
-
- // check for the timing of this buffer
- if (mNumFramesReceived == 0 && !mUseAbsoluteTimestamps) {
- mFirstFrameTimestamp = item.mTimestamp;
- // Initial delay
- if (mStartTimeNs > 0) {
- if (item.mTimestamp < mStartTimeNs) {
- // This frame predates start of record, discard
- mConsumer->releaseBuffer(
- item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR, Fence::NO_FENCE);
- continue;
- }
- mStartTimeNs = item.mTimestamp - mStartTimeNs;
- }
- }
- item.mTimestamp = mStartTimeNs + (item.mTimestamp - mFirstFrameTimestamp);
-
- mNumFramesReceived++;
-
- break;
- } else {
- ALOGE("read: acquire failed with error code %d", err);
- return ERROR_END_OF_STREAM;
- }
-
- }
-
- // If the loop was exited as a result of stopping the recording,
- // it is OK
- if (!mStarted) {
- ALOGV("Read: SurfaceMediaSource is stopped. Returning ERROR_END_OF_STREAM.");
- return ERROR_END_OF_STREAM;
- }
-
- mCurrentSlot = item.mSlot;
-
- // First time seeing the buffer? Added it to the SMS slot
- if (item.mGraphicBuffer != NULL) {
- mSlots[item.mSlot].mGraphicBuffer = item.mGraphicBuffer;
- }
- mSlots[item.mSlot].mFrameNumber = item.mFrameNumber;
-
- mCurrentBuffers.push_back(mSlots[mCurrentSlot].mGraphicBuffer);
- int64_t prevTimeStamp = mCurrentTimestamp;
- mCurrentTimestamp = item.mTimestamp;
-
- mNumFramesEncoded++;
- // Pass the data to the MediaBuffer. Pass in only the metadata
-
- passMetadataBuffer_l(buffer, mSlots[mCurrentSlot].mGraphicBuffer->getNativeBuffer());
-
- (*buffer)->setObserver(this);
- (*buffer)->add_ref();
- (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000);
- ALOGV("Frames encoded = %d, timestamp = %" PRId64 ", time diff = %" PRId64,
- mNumFramesEncoded, mCurrentTimestamp / 1000,
- mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
-
- ++mNumPendingBuffers;
-
-#if DEBUG_PENDING_BUFFERS
- mPendingBuffers.push_back(*buffer);
-#endif
-
- ALOGV("returning mbuf %p", *buffer);
-
- return OK;
-}
-
-static buffer_handle_t getMediaBufferHandle(MediaBufferBase *buffer) {
- // need to convert to char* for pointer arithmetic and then
- // copy the byte stream into our handle
- buffer_handle_t bufferHandle;
- memcpy(&bufferHandle, (char*)(buffer->data()) + 4, sizeof(buffer_handle_t));
- return bufferHandle;
-}
-
-void SurfaceMediaSource::signalBufferReturned(MediaBufferBase *buffer) {
- ALOGV("signalBufferReturned");
-
- bool foundBuffer = false;
-
- Mutex::Autolock lock(mMutex);
-
- buffer_handle_t bufferHandle = getMediaBufferHandle(buffer);
-
- for (size_t i = 0; i < mCurrentBuffers.size(); i++) {
- if (mCurrentBuffers[i]->handle == bufferHandle) {
- mCurrentBuffers.removeAt(i);
- foundBuffer = true;
- break;
- }
- }
-
- if (!foundBuffer) {
- ALOGW("returned buffer was not found in the current buffer list");
- }
-
- for (int id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
- if (mSlots[id].mGraphicBuffer == NULL) {
- continue;
- }
-
- if (bufferHandle == mSlots[id].mGraphicBuffer->handle) {
- ALOGV("Slot %d returned, matches handle = %p", id,
- mSlots[id].mGraphicBuffer->handle);
-
- mConsumer->releaseBuffer(id, mSlots[id].mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
- Fence::NO_FENCE);
-
- buffer->setObserver(0);
- buffer->release();
-
- foundBuffer = true;
- break;
- }
- }
-
- if (!foundBuffer) {
- CHECK(!"signalBufferReturned: bogus buffer");
- }
-
-#if DEBUG_PENDING_BUFFERS
- for (size_t i = 0; i < mPendingBuffers.size(); ++i) {
- if (mPendingBuffers.itemAt(i) == buffer) {
- mPendingBuffers.removeAt(i);
- break;
- }
- }
-#endif
-
- --mNumPendingBuffers;
- mMediaBuffersAvailableCondition.broadcast();
-}
-
-// Part of the BufferQueue::ConsumerListener
-void SurfaceMediaSource::onFrameAvailable(const BufferItem& /* item */) {
- ALOGV("onFrameAvailable");
-
- sp<FrameAvailableListener> listener;
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- mFrameAvailableCondition.broadcast();
- listener = mFrameAvailableListener;
- }
-
- if (listener != NULL) {
- ALOGV("actually calling onFrameAvailable");
- listener->onFrameAvailable();
- }
-}
-
-// SurfaceMediaSource hijacks this event to assume
-// the prodcuer is disconnecting from the BufferQueue
-// and that it should stop the recording
-void SurfaceMediaSource::onBuffersReleased() {
- ALOGV("onBuffersReleased");
-
- Mutex::Autolock lock(mMutex);
-
- mFrameAvailableCondition.signal();
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mSlots[i].mGraphicBuffer = 0;
- }
-}
-
-void SurfaceMediaSource::onSidebandStreamChanged() {
- ALOG_ASSERT(false, "SurfaceMediaSource can't consume sideband streams");
-}
-
-} // end of namespace android
diff --git a/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h b/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h
deleted file mode 100644
index d49e44c..0000000
--- a/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2011 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_GUI_SURFACEMEDIASOURCE_H
-#define ANDROID_GUI_SURFACEMEDIASOURCE_H
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
-
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include <media/MediaSource.h>
-#include <media/stagefright/MediaBuffer.h>
-
-#include <media/hardware/MetadataBufferType.h>
-
-#include "foundation/ABase.h"
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class String8;
-class GraphicBuffer;
-
-// ASSUMPTIONS
-// 1. SurfaceMediaSource is initialized with width*height which
-// can never change. However, deqeueue buffer does not currently
-// enforce this as in BufferQueue, dequeue can be used by Surface
-// which can modify the default width and heght. Also neither the width
-// nor height can be 0.
-// 2. setSynchronousMode is never used (basically no one should call
-// setSynchronousMode(false)
-// 3. setCrop, setTransform, setScalingMode should never be used
-// 4. queueBuffer returns a filled buffer to the SurfaceMediaSource. In addition, a
-// timestamp must be provided for the buffer. The timestamp is in
-// nanoseconds, and must be monotonically increasing. Its other semantics
-// (zero point, etc) are client-dependent and should be documented by the
-// client.
-// 5. Once disconnected, SurfaceMediaSource can be reused (can not
-// connect again)
-// 6. Stop is a hard stop, the last few frames held by the encoder
-// may be dropped. It is possible to wait for the buffers to be
-// returned (but not implemented)
-
-#define DEBUG_PENDING_BUFFERS 0
-
-class SurfaceMediaSource : public MediaSource,
- public MediaBufferObserver,
- protected ConsumerListener {
-public:
- enum { MIN_UNDEQUEUED_BUFFERS = 4};
-
- struct FrameAvailableListener : public virtual RefBase {
- // onFrameAvailable() is called from queueBuffer() is the FIFO is
- // empty. You can use SurfaceMediaSource::getQueuedCount() to
- // figure out if there are more frames waiting.
- // This is called without any lock held can be called concurrently by
- // multiple threads.
- virtual void onFrameAvailable() = 0;
- };
-
- SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight);
-
- virtual ~SurfaceMediaSource();
-
- // For the MediaSource interface for use by StageFrightRecorder:
- virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
- virtual status_t read(MediaBufferBase **buffer,
- const ReadOptions *options = NULL);
- virtual sp<MetaData> getFormat();
-
- // Get / Set the frame rate used for encoding. Default fps = 30
- status_t setFrameRate(int32_t fps) ;
- int32_t getFrameRate( ) const;
-
- // The call for the StageFrightRecorder to tell us that
- // it is done using the MediaBuffer data so that its state
- // can be set to FREE for dequeuing
- virtual void signalBufferReturned(MediaBufferBase* buffer);
- // end of MediaSource interface
-
- // getTimestamp retrieves the timestamp associated with the image
- // set by the most recent call to read()
- //
- // The timestamp is in nanoseconds, and is monotonically increasing. Its
- // other semantics (zero point, etc) are source-dependent and should be
- // documented by the source.
- int64_t getTimestamp();
-
- // setFrameAvailableListener sets the listener object that will be notified
- // when a new frame becomes available.
- void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
-
- // dump our state in a String
- void dumpState(String8& result) const;
- void dumpState(String8& result, const char* prefix, char* buffer,
- size_t SIZE) const;
-
- // metaDataStoredInVideoBuffers tells the encoder what kind of metadata
- // is passed through the buffers. Currently, it is set to ANWBuffer
- MetadataBufferType metaDataStoredInVideoBuffers() const;
-
- sp<IGraphicBufferProducer> getProducer() const { return mProducer; }
-
- // To be called before start()
- status_t setMaxAcquiredBufferCount(size_t count);
-
- // To be called before start()
- status_t setUseAbsoluteTimestamps();
-
-protected:
-
- // Implementation of the BufferQueue::ConsumerListener interface. These
- // calls are used to notify the Surface of asynchronous events in the
- // BufferQueue.
- virtual void onFrameAvailable(const BufferItem& item);
-
- // Used as a hook to BufferQueue::disconnect()
- // This is called by the client side when it is done
- // TODO: Currently, this also sets mStopped to true which
- // is needed for unblocking the encoder which might be
- // waiting to read more frames. So if on the client side,
- // the same thread supplies the frames and also calls stop
- // on the encoder, the client has to call disconnect before
- // it calls stop.
- // In the case of the camera,
- // that need not be required since the thread supplying the
- // frames is separate than the one calling stop.
- virtual void onBuffersReleased();
-
- // SurfaceMediaSource can't handle sideband streams, so this is not expected
- // to ever be called. Does nothing.
- virtual void onSidebandStreamChanged();
-
- static bool isExternalFormat(uint32_t format);
-
-private:
- // A BufferQueue, represented by these interfaces, is the exchange point
- // between the producer and this consumer
- sp<IGraphicBufferProducer> mProducer;
- sp<IGraphicBufferConsumer> mConsumer;
-
- struct SlotData {
- sp<GraphicBuffer> mGraphicBuffer;
- uint64_t mFrameNumber;
- };
-
- // mSlots caches GraphicBuffers and frameNumbers from the buffer queue
- SlotData mSlots[BufferQueue::NUM_BUFFER_SLOTS];
-
- // The permenent width and height of SMS buffers
- int mWidth;
- int mHeight;
-
- // mCurrentSlot is the buffer slot index of the buffer that is currently
- // being used by buffer consumer
- // (e.g. StageFrightRecorder in the case of SurfaceMediaSource or GLTexture
- // in the case of Surface).
- // It is initialized to INVALID_BUFFER_SLOT,
- // indicating that no buffer slot is currently bound to the texture. Note,
- // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
- // that no buffer is bound to the texture. A call to setBufferCount will
- // reset mCurrentTexture to INVALID_BUFFER_SLOT.
- int mCurrentSlot;
-
- // mCurrentBuffers is a list of the graphic buffers that are being used by
- // buffer consumer (i.e. the video encoder). It's possible that these
- // buffers are not associated with any buffer slots, so we must track them
- // separately. Buffers are added to this list in read, and removed from
- // this list in signalBufferReturned
- Vector<sp<GraphicBuffer> > mCurrentBuffers;
-
- size_t mNumPendingBuffers;
-
-#if DEBUG_PENDING_BUFFERS
- Vector<MediaBuffer *> mPendingBuffers;
-#endif
-
- // mCurrentTimestamp is the timestamp for the current texture. It
- // gets set to mLastQueuedTimestamp each time updateTexImage is called.
- int64_t mCurrentTimestamp;
-
- // mFrameAvailableListener is the listener object that will be called when a
- // new frame becomes available. If it is not NULL it will be called from
- // queueBuffer.
- sp<FrameAvailableListener> mFrameAvailableListener;
-
- // mMutex is the mutex used to prevent concurrent access to the member
- // variables of SurfaceMediaSource objects. It must be locked whenever the
- // member variables are accessed.
- mutable Mutex mMutex;
-
- ////////////////////////// For MediaSource
- // Set to a default of 30 fps if not specified by the client side
- int32_t mFrameRate;
-
- // mStarted is a flag to check if the recording is going on
- bool mStarted;
-
- // mNumFramesReceived indicates the number of frames recieved from
- // the client side
- int mNumFramesReceived;
- // mNumFramesEncoded indicates the number of frames passed on to the
- // encoder
- int mNumFramesEncoded;
-
- // mFirstFrameTimestamp is the timestamp of the first received frame.
- // It is used to offset the output timestamps so recording starts at time 0.
- int64_t mFirstFrameTimestamp;
- // mStartTimeNs is the start time passed into the source at start, used to
- // offset timestamps.
- int64_t mStartTimeNs;
-
- size_t mMaxAcquiredBufferCount;
-
- bool mUseAbsoluteTimestamps;
-
- // mFrameAvailableCondition condition used to indicate whether there
- // is a frame available for dequeuing
- Condition mFrameAvailableCondition;
-
- Condition mMediaBuffersAvailableCondition;
-
- // Allocate and return a new MediaBuffer and pass the ANW buffer as metadata into it.
- void passMetadataBuffer_l(MediaBufferBase **buffer, ANativeWindowBuffer *bufferHandle) const;
-
- // Avoid copying and equating and default constructor
- DISALLOW_EVIL_CONSTRUCTORS(SurfaceMediaSource);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACEMEDIASOURCE_H
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index e67a949..be10fdc 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -1,46 +1,6 @@
// Build the unit tests.
cc_test {
- name: "SurfaceMediaSource_test",
-
- srcs: [
- "SurfaceMediaSource_test.cpp",
- "DummyRecorder.cpp",
- ],
-
- shared_libs: [
- "libEGL",
- "libGLESv2",
- "libbinder",
- "libcutils",
- "libgui",
- "libmedia",
- "libmediaextractor",
- "libstagefright",
- "libstagefright_foundation",
- "libstagefright_omx",
- "libsync",
- "libui",
- "libutils",
- "liblog",
- ],
-
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/av/media/libstagefright/include",
- "frameworks/native/include/media/openmax",
- "frameworks/native/include/media/hardware",
- ],
-
- cflags: [
- "-Werror",
- "-Wall",
- ],
-
- compile_multilib: "32",
-}
-
-cc_test {
name: "MediaCodecListOverrides_test",
srcs: ["MediaCodecListOverrides_test.cpp"],
diff --git a/media/libstagefright/tests/DummyRecorder.cpp b/media/libstagefright/tests/DummyRecorder.cpp
deleted file mode 100644
index c79e6b1..0000000
--- a/media/libstagefright/tests/DummyRecorder.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 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 "DummyRecorder"
-// #define LOG_NDEBUG 0
-
-#include <media/MediaSource.h>
-#include <media/stagefright/MediaBuffer.h>
-#include "DummyRecorder.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-// static
-void *DummyRecorder::threadWrapper(void *pthis) {
- ALOGV("ThreadWrapper: %p", pthis);
- DummyRecorder *writer = static_cast<DummyRecorder *>(pthis);
- writer->readFromSource();
- return NULL;
-}
-
-
-status_t DummyRecorder::start() {
- ALOGV("Start");
- mStarted = true;
-
- mSource->start();
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- int err = pthread_create(&mThread, &attr, threadWrapper, this);
- pthread_attr_destroy(&attr);
-
- if (err) {
- ALOGE("Error creating thread!");
- return -ENODEV;
- }
- return OK;
-}
-
-
-status_t DummyRecorder::stop() {
- ALOGV("Stop");
- mStarted = false;
-
- mSource->stop();
- void *dummy;
- pthread_join(mThread, &dummy);
- status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
-
- ALOGV("Ending the reading thread");
- return err;
-}
-
-// pretend to read the source buffers
-void DummyRecorder::readFromSource() {
- ALOGV("ReadFromSource");
- if (!mStarted) {
- return;
- }
-
- status_t err = OK;
- MediaBufferBase *buffer;
- ALOGV("A fake writer accessing the frames");
- while (mStarted && (err = mSource->read(&buffer)) == OK){
- // if not getting a valid buffer from source, then exit
- if (buffer == NULL) {
- return;
- }
- buffer->release();
- buffer = NULL;
- }
-}
-
-
-} // end of namespace android
diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h
deleted file mode 100644
index 0759777..0000000
--- a/media/libstagefright/tests/DummyRecorder.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2011 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 DUMMY_RECORDER_H_
-#define DUMMY_RECORDER_H_
-
-#include <pthread.h>
-#include <utils/String8.h>
-#include <media/stagefright/foundation/ABase.h>
-
-
-namespace android {
-
-struct MediaSource;
-class MediaBuffer;
-
-class DummyRecorder {
- public:
- // The media source from which this will receive frames
- sp<MediaSource> mSource;
- bool mStarted;
- pthread_t mThread;
-
- status_t start();
- status_t stop();
-
- // actual entry point for the thread
- void readFromSource();
-
- // static function to wrap the actual thread entry point
- static void *threadWrapper(void *pthis);
-
- explicit DummyRecorder(const sp<MediaSource> &source) : mSource(source)
- , mStarted(false) {}
- ~DummyRecorder( ) {}
-
- private:
-
- DISALLOW_EVIL_CONSTRUCTORS(DummyRecorder);
-};
-
-} // end of namespace android
-#endif
-
-
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
deleted file mode 100644
index 1b1c3b8..0000000
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ /dev/null
@@ -1,944 +0,0 @@
-/*
- * Copyright (C) 2011 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 "SurfaceMediaSource_test"
-
-#include <gtest/gtest.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <utils/Errors.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <GLES2/gl2.h>
-
-#include <media/stagefright/SurfaceMediaSource.h>
-#include <media/mediarecorder.h>
-
-#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <binder/ProcessState.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <OMX_Component.h>
-
-#include "DummyRecorder.h"
-
-
-namespace android {
-
-class GLTest : public ::testing::Test {
-protected:
-
- GLTest():
- mEglDisplay(EGL_NO_DISPLAY),
- mEglSurface(EGL_NO_SURFACE),
- mEglContext(EGL_NO_CONTEXT) {
- }
-
- virtual void SetUp() {
- ALOGV("GLTest::SetUp()");
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
- EGLint majorVersion;
- EGLint minorVersion;
- EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- RecordProperty("EglVersionMajor", majorVersion);
- RecordProperty("EglVersionMajor", minorVersion);
-
- EGLint numConfigs = 0;
- EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
- 1, &numConfigs));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
- if (displaySecsEnv != NULL) {
- mDisplaySecs = atoi(displaySecsEnv);
- if (mDisplaySecs < 0) {
- mDisplaySecs = 0;
- }
- } else {
- mDisplaySecs = 0;
- }
-
- if (mDisplaySecs > 0) {
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mSurfaceControl = mComposerClient->createSurface(
- String8("Test Surface"),
- getSurfaceWidth(), getSurfaceHeight(),
- PIXEL_FORMAT_RGB_888, 0);
-
- ASSERT_TRUE(mSurfaceControl != NULL);
- ASSERT_TRUE(mSurfaceControl->isValid());
-
- SurfaceComposerClient::Transaction{}
- .setLayer(mSurfaceControl, 0x7FFFFFFF)
- .show(mSurfaceControl)
- .apply();
-
- sp<ANativeWindow> window = mSurfaceControl->getSurface();
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- window.get(), NULL);
- } else {
- ALOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource");
- sp<IGraphicBufferProducer> sms = (new SurfaceMediaSource(
- getSurfaceWidth(), getSurfaceHeight()))->getProducer();
- sp<Surface> stc = new Surface(sms);
- sp<ANativeWindow> window = stc;
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- window.get(), NULL);
- }
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
- getContextAttribs());
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- EGLint w, h;
- EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- RecordProperty("EglSurfaceWidth", w);
- RecordProperty("EglSurfaceHeight", h);
-
- glViewport(0, 0, w, h);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- }
-
- virtual void TearDown() {
- // Display the result
- if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
- eglSwapBuffers(mEglDisplay, mEglSurface);
- sleep(mDisplaySecs);
- }
-
- if (mComposerClient != NULL) {
- mComposerClient->dispose();
- }
- if (mEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mEglContext);
- }
- if (mEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mEglSurface);
- }
- if (mEglDisplay != EGL_NO_DISPLAY) {
- eglTerminate(mEglDisplay);
- }
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- }
-
- virtual EGLint const* getConfigAttribs() {
- ALOGV("GLTest getConfigAttribs");
- static EGLint sDefaultConfigAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 16,
- EGL_STENCIL_SIZE, 8,
- EGL_NONE };
-
- return sDefaultConfigAttribs;
- }
-
- virtual EGLint const* getContextAttribs() {
- static EGLint sDefaultContextAttribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE };
-
- return sDefaultContextAttribs;
- }
-
- virtual EGLint getSurfaceWidth() {
- return 512;
- }
-
- virtual EGLint getSurfaceHeight() {
- return 512;
- }
-
- void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
- GLuint shader = glCreateShader(shaderType);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (shader) {
- glShaderSource(shader, 1, &pSource, NULL);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glCompileShader(shader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- GLint compiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (!compiled) {
- GLint infoLen = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (infoLen) {
- char* buf = (char*) malloc(infoLen);
- if (buf) {
- glGetShaderInfoLog(shader, infoLen, NULL, buf);
- printf("Shader compile log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- } else {
- char* buf = (char*) malloc(0x1000);
- if (buf) {
- glGetShaderInfoLog(shader, 0x1000, NULL, buf);
- printf("Shader compile log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- }
- glDeleteShader(shader);
- shader = 0;
- }
- }
- ASSERT_TRUE(shader != 0);
- *outShader = shader;
- }
-
- void createProgram(const char* pVertexSource, const char* pFragmentSource,
- GLuint* outPgm) {
- GLuint vertexShader, fragmentShader;
- {
- SCOPED_TRACE("compiling vertex shader");
- loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
- if (HasFatalFailure()) {
- return;
- }
- }
- {
- SCOPED_TRACE("compiling fragment shader");
- loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
- if (HasFatalFailure()) {
- return;
- }
- }
-
- GLuint program = glCreateProgram();
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (program) {
- glAttachShader(program, vertexShader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glAttachShader(program, fragmentShader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glLinkProgram(program);
- GLint linkStatus = GL_FALSE;
- glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
- if (linkStatus != GL_TRUE) {
- GLint bufLength = 0;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
- if (bufLength) {
- char* buf = (char*) malloc(bufLength);
- if (buf) {
- glGetProgramInfoLog(program, bufLength, NULL, buf);
- printf("Program link log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- }
- glDeleteProgram(program);
- program = 0;
- }
- }
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- ASSERT_TRUE(program != 0);
- *outPgm = program;
- }
-
- static int abs(int value) {
- return value > 0 ? value : -value;
- }
-
- ::testing::AssertionResult checkPixel(int x, int y, int r,
- int g, int b, int a, int tolerance=2) {
- GLubyte pixel[4];
- String8 msg;
- glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
- GLenum err = glGetError();
- if (err != GL_NO_ERROR) {
- msg += String8::format("error reading pixel: %#x", err);
- while ((err = glGetError()) != GL_NO_ERROR) {
- msg += String8::format(", %#x", err);
- }
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
- return ::testing::AssertionFailure(
- ::testing::Message(msg.string()));
- }
- if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
- msg += String8::format("r(%d isn't %d)", pixel[0], r);
- }
- if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("g(%d isn't %d)", pixel[1], g);
- }
- if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("b(%d isn't %d)", pixel[2], b);
- }
- if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("a(%d isn't %d)", pixel[3], a);
- }
- if (!msg.isEmpty()) {
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
- return ::testing::AssertionFailure(
- ::testing::Message(msg.string()));
- } else {
- return ::testing::AssertionSuccess();
- }
- }
-
- int mDisplaySecs;
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
-
- EGLDisplay mEglDisplay;
- EGLSurface mEglSurface;
- EGLContext mEglContext;
- EGLConfig mGlConfig;
-};
-
-///////////////////////////////////////////////////////////////////////
-// Class for the NON-GL tests
-///////////////////////////////////////////////////////////////////////
-class SurfaceMediaSourceTest : public ::testing::Test {
-public:
-
- SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
- void oneBufferPass(int width, int height );
- void oneBufferPassNoFill(int width, int height );
- static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ;
- static void fillYV12BufferRect(uint8_t* buf, int w, int h,
- int stride, const android_native_rect_t& rect) ;
-protected:
-
- virtual void SetUp() {
- android::ProcessState::self()->startThreadPool();
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
- }
-
- virtual void TearDown() {
- mSMS.clear();
- mSTC.clear();
- mANW.clear();
- }
-
- const int mYuvTexWidth;
- const int mYuvTexHeight;
-
- sp<SurfaceMediaSource> mSMS;
- sp<Surface> mSTC;
- sp<ANativeWindow> mANW;
-};
-
-///////////////////////////////////////////////////////////////////////
-// Class for the GL tests
-///////////////////////////////////////////////////////////////////////
-class SurfaceMediaSourceGLTest : public GLTest {
-public:
-
- SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
- virtual EGLint const* getConfigAttribs();
- void oneBufferPassGL(int num = 0);
- static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource,
- int outputFormat, int videoEncoder, int width, int height, int fps);
-protected:
-
- virtual void SetUp() {
- ALOGV("SMS-GLTest::SetUp()");
- android::ProcessState::self()->startThreadPool();
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
-
- // Doing the setup related to the GL Side
- GLTest::SetUp();
- }
-
- virtual void TearDown() {
- mSMS.clear();
- mSTC.clear();
- mANW.clear();
- GLTest::TearDown();
- }
-
- void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
-
- const int mYuvTexWidth;
- const int mYuvTexHeight;
-
- sp<SurfaceMediaSource> mSMS;
- sp<Surface> mSTC;
- sp<ANativeWindow> mANW;
-};
-
-/////////////////////////////////////////////////////////////////////
-// Methods in SurfaceMediaSourceGLTest
-/////////////////////////////////////////////////////////////////////
-EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() {
- ALOGV("SurfaceMediaSourceGLTest getConfigAttribs");
- static EGLint sDefaultConfigAttribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_RECORDABLE_ANDROID, EGL_TRUE,
- EGL_NONE };
-
- return sDefaultConfigAttribs;
-}
-
-// One pass of dequeuing and queuing a GLBuffer
-void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) {
- int d = num % 50;
- float f = 0.2f; // 0.1f * d;
-
- glClearColor(0, 0.3, 0, 0.6);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glEnable(GL_SCISSOR_TEST);
- glScissor(4 + d, 4 + d, 4, 4);
- glClearColor(1.0 - f, f, f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(24 + d, 48 + d, 4, 4);
- glClearColor(f, 1.0 - f, f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(37 + d, 17 + d, 4, 4);
- glClearColor(f, f, 1.0 - f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // The following call dequeues and queues the buffer
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- glDisable(GL_SCISSOR_TEST);
-}
-
-// Set up the MediaRecorder which runs in the same process as mediaserver
-sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
- int outputFormat, int videoEncoder, int width, int height, int fps) {
- sp<MediaRecorder> mr = new MediaRecorder(String16());
- mr->setVideoSource(videoSource);
- mr->setOutputFormat(outputFormat);
- mr->setVideoEncoder(videoEncoder);
- mr->setOutputFile(fd);
- mr->setVideoSize(width, height);
- mr->setVideoFrameRate(fps);
- mr->prepare();
- ALOGV("Starting MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->start());
- return mr;
-}
-
-// query the mediarecorder for a surfacemeidasource and create an egl surface with that
-void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) {
- sp<IGraphicBufferProducer> iST = mr->querySurfaceMediaSourceFromMediaServer();
- mSTC = new Surface(iST);
- mANW = mSTC;
-
- if (mEglSurface != EGL_NO_SURFACE) {
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- mEglSurface = EGL_NO_SURFACE;
- }
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-}
-
-
-/////////////////////////////////////////////////////////////////////
-// Methods in SurfaceMediaSourceTest
-/////////////////////////////////////////////////////////////////////
-
-// One pass of dequeuing and queuing the buffer. Fill it in with
-// cpu YV12 buffer
-void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) {
- ANativeWindowBuffer* anb;
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
- ASSERT_TRUE(anb != NULL);
-
-
- // Fill the buffer with the a checkerboard pattern
- uint8_t* img = NULL;
- sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride());
- buf->unlock();
-
- ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
- -1));
-}
-
-// Dequeuing and queuing the buffer without really filling it in.
-void SurfaceMediaSourceTest::oneBufferPassNoFill(
- int /* width */, int /* height */) {
- ANativeWindowBuffer* anb;
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
- ASSERT_TRUE(anb != NULL);
-
- // We do not fill the buffer in. Just queue it back.
- sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
- -1));
-}
-
-// Fill a YV12 buffer with a multi-colored checkerboard pattern
-void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
- const int blockWidth = w > 16 ? w / 16 : 1;
- const int blockHeight = h > 16 ? h / 16 : 1;
- const int yuvTexOffsetY = 0;
- int yuvTexStrideY = stride;
- int yuvTexOffsetV = yuvTexStrideY * h;
- int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
- int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
- int yuvTexStrideU = yuvTexStrideV;
- for (int x = 0; x < w; x++) {
- for (int y = 0; y < h; y++) {
- int parityX = (x / blockWidth) & 1;
- int parityY = (y / blockHeight) & 1;
- unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
- buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
- if (x < w / 2 && y < h / 2) {
- buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
- if (x * 2 < w / 2 && y * 2 < h / 2) {
- buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
- buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
- buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
- buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
- intensity;
- }
- }
- }
- }
-}
-
-// Fill a YV12 buffer with red outside a given rectangle and green inside it.
-void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w,
- int h, int stride, const android_native_rect_t& rect) {
- const int yuvTexOffsetY = 0;
- int yuvTexStrideY = stride;
- int yuvTexOffsetV = yuvTexStrideY * h;
- int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
- int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
- int yuvTexStrideU = yuvTexStrideV;
- for (int x = 0; x < w; x++) {
- for (int y = 0; y < h; y++) {
- bool inside = rect.left <= x && x < rect.right &&
- rect.top <= y && y < rect.bottom;
- buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
- if (x < w / 2 && y < h / 2) {
- bool inside = rect.left <= 2*x && 2*x < rect.right &&
- rect.top <= 2*y && 2*y < rect.bottom;
- buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
- buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
- inside ? 16 : 255;
- }
- }
- }
-} ///////// End of class SurfaceMediaSourceTest
-
-///////////////////////////////////////////////////////////////////
-// Class to imitate the recording /////////////////////////////
-// ////////////////////////////////////////////////////////////////
-struct SimpleDummyRecorder {
- sp<MediaSource> mSource;
-
- explicit SimpleDummyRecorder
- (const sp<MediaSource> &source): mSource(source) {}
-
- status_t start() { return mSource->start();}
- status_t stop() { return mSource->stop();}
-
- // fakes reading from a media source
- status_t readFromSource() {
- MediaBufferBase *buffer;
- status_t err = mSource->read(&buffer);
- if (err != OK) {
- return err;
- }
- buffer->release();
- buffer = NULL;
- return OK;
- }
-};
-///////////////////////////////////////////////////////////////////
-// TESTS
-// SurfaceMediaSourceTest class contains tests that fill the buffers
-// using the cpu calls
-// SurfaceMediaSourceGLTest class contains tests that fill the buffers
-// using the GL calls.
-// TODO: None of the tests actually verify the encoded images.. so at this point,
-// these are mostly functionality tests + visual inspection
-//////////////////////////////////////////////////////////////////////
-
-// Just pass one buffer from the native_window to the SurfaceMediaSource
-// Dummy Encoder
-static int testId = 1;
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing OneBufferPass ******************************");
-
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-}
-
-// Pass the buffer with the wrong height and weight and should not be accepted
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing Wrong size BufferPass ******************************");
-
- // setting the client side buffer size different than the server size
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
- 10, 10));
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- ANativeWindowBuffer* anb;
-
- // Note: make sure we get an ERROR back when dequeuing!
- ASSERT_NE(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
-}
-
-// pass multiple buffers from the native_window the SurfaceMediaSource
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder *********************");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- SimpleDummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 0;
- while (nFramesCount < 300) {
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-
- ASSERT_EQ(NO_ERROR, writer.readFromSource());
-
- nFramesCount++;
- }
- writer.stop();
-}
-
-// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************");
-
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- SimpleDummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 1;
- const int FRAMES_LAG = SurfaceMediaSource::MIN_UNDEQUEUED_BUFFERS;
-
- while (nFramesCount <= 300) {
- ALOGV("Frame: %d", nFramesCount);
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
- // Forcing the writer to lag behind a few frames
- if (nFramesCount > FRAMES_LAG) {
- ASSERT_EQ(NO_ERROR, writer.readFromSource());
- }
- nFramesCount++;
- }
- writer.stop();
-}
-
-// pass multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- DummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- ALOGV("Frame: %d", nFramesCount);
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-
- nFramesCount++;
- }
- writer.stop();
-}
-
-// Test to examine actual encoding using mediarecorder
-// We use the mediaserver to create a mediarecorder and send
-// it back to us. So SurfaceMediaSource lives in the same process
-// as the mediaserver.
-// Very close to the actual camera, except that the
-// buffers are filled and queueud by the CPU instead of GL.
-TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual MediaRecorder ***********");
- ALOGV("************** SurfaceMediaSource is same process as mediaserver ***********");
-
- const char *fileName = "/sdcard/outputSurfEncMSource.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd,
- VIDEO_SOURCE_SURFACE, OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264,
- mYuvTexWidth, mYuvTexHeight, 30);
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- sp<IGraphicBufferProducer> iST = mr->querySurfaceMediaSourceFromMediaServer();
- mSTC = new Surface(iST);
- mANW = mSTC;
- ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight);
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-
-//////////////////////////////////////////////////////////////////////
-// GL tests
-/////////////////////////////////////////////////////////////////////
-
-// Test to examine whether we can choose the Recordable Android GLConfig
-// DummyRecorder used- no real encoding here
-TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) {
- ALOGV("Test # %d", testId++);
- ALOGV("Verify creating a surface w/ right config + dummy writer*********");
-
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
-
- DummyRecorder writer(mSMS);
- writer.start();
-
- if (mEglSurface != EGL_NO_SURFACE) {
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- mEglSurface = EGL_NO_SURFACE;
- }
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL();
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- writer.stop();
-}
-// Test to examine whether we can render GL buffers in to the surface
-// created with the native window handle
-TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) {
- ALOGV("Test # %d", testId++);
- ALOGV("RenderingToRecordableEGLSurfaceWorks *********************");
- // Do the producer side of things
- glClearColor(0.6, 0.6, 0.6, 0.6);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glEnable(GL_SCISSOR_TEST);
- glScissor(4, 4, 4, 4);
- glClearColor(1.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(24, 48, 4, 4);
- glClearColor(0.0, 1.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(37, 17, 4, 4);
- glClearColor(0.0, 0.0, 1.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
-
- EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255));
- EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255));
- EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255));
- EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
-}
-
-// Test to examine the actual encoding with GL buffers
-// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
-// The same pattern is rendered every frame
-TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual Recorder ***********");
- ALOGV("************** GL Filling the buffers ***********");
- // Note: No need to set the colorformat for the buffers. The colorformat is
- // in the GRAlloc buffers itself.
-
- const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
- OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
-
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- setUpEGLSurfaceFromMediaRecorder(mr);
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL();
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-
-// Test to examine the actual encoding from the GL Buffers
-// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
-// A different pattern is rendered every frame
-TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual Recorder ***********");
- ALOGV("************** Diff GL Filling the buffers ***********");
- // Note: No need to set the colorformat for the buffers. The colorformat is
- // in the GRAlloc buffers itself.
-
- const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
- OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
-
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- setUpEGLSurfaceFromMediaRecorder(mr);
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL(nFramesCount);
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-} // namespace android
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 759d580..e61934f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -134,7 +134,7 @@
// media keys to.
boolean accept = allowedCommands != null || request.isTrusted();
ControllerInfoImpl impl = ControllerInfoImpl.from(request);
- if (accept && allowedCommands != null) {
+ if (accept) {
if (DEBUG) {
Log.d(TAG, "Accepting connection, request=" + request
+ " allowedCommands=" + allowedCommands);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index ea7e714..ecb19c1 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -17,9 +17,7 @@
package com.android.widget;
import android.annotation.NonNull;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
@@ -48,6 +46,7 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
@@ -92,13 +91,13 @@
private AudioAttributes mAudioAttributes;
private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
- private VideoView2.OnCustomActionListener mOnCustomActionListener;
- private VideoView2.OnPreparedListener mOnPreparedListener;
- private VideoView2.OnCompletionListener mOnCompletionListener;
- private VideoView2.OnErrorListener mOnErrorListener;
- private VideoView2.OnInfoListener mOnInfoListener;
- private VideoView2.OnViewTypeChangedListener mOnViewTypeChangedListener;
- private VideoView2.OnFullScreenRequestListener mOnFullScreenRequestListener;
+ private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
+ private Pair<Executor, VideoView2.OnPreparedListener> mPreparedListenerRecord;
+ private Pair<Executor, VideoView2.OnCompletionListener> mCompletionListenerRecord;
+ private Pair<Executor, VideoView2.OnErrorListener> mErrorListenerRecord;
+ private Pair<Executor, VideoView2.OnInfoListener> mInfoListenerRecord;
+ private Pair<Executor, VideoView2.OnViewTypeChangedListener> mViewTypeChangedListenerRecord;
+ private Pair<Executor, VideoView2.OnFullScreenRequestListener> mFullScreenRequestListenerRecord;
private VideoViewInterface mCurrentView;
private VideoTextureView mTextureView;
@@ -351,13 +350,12 @@
return mCurrentView.getViewType();
}
- // TODO: Handle executor properly for all the set listener methods.
@Override
public void setCustomActions_impl(
List<PlaybackState.CustomAction> actionList,
Executor executor, VideoView2.OnCustomActionListener listener) {
mCustomActionList = actionList;
- mOnCustomActionListener = listener;
+ mCustomActionListenerRecord = new Pair<>(executor, listener);
// Create a new playback builder in order to clear existing the custom actions.
mStateBuilder = null;
@@ -366,34 +364,34 @@
@Override
public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
- mOnPreparedListener = l;
+ mPreparedListenerRecord = new Pair<>(executor, l);
}
@Override
public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
- mOnCompletionListener = l;
+ mCompletionListenerRecord = new Pair<>(executor, l);
}
@Override
public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
- mOnErrorListener = l;
+ mErrorListenerRecord = new Pair<>(executor, l);
}
@Override
public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
- mOnInfoListener = l;
+ mInfoListenerRecord = new Pair<>(executor, l);
}
@Override
public void setOnViewTypeChangedListener_impl(Executor executor,
VideoView2.OnViewTypeChangedListener l) {
- mOnViewTypeChangedListener = l;
+ mViewTypeChangedListenerRecord = new Pair<>(executor, l);
}
@Override
public void setFullScreenRequestListener_impl(Executor executor,
VideoView2.OnFullScreenRequestListener l) {
- mOnFullScreenRequestListener = l;
+ mFullScreenRequestListenerRecord = new Pair<>(executor, l);
}
@Override
@@ -492,8 +490,10 @@
Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
}
mCurrentView = view;
- if (mOnViewTypeChangedListener != null) {
- mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
+ if (mViewTypeChangedListenerRecord != null) {
+ mViewTypeChangedListenerRecord.first.execute(() ->
+ mViewTypeChangedListenerRecord.second.onViewTypeChanged(
+ mInstance, view.getViewType()));
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -845,8 +845,9 @@
updatePlaybackState();
extractSubtitleTracks();
- if (mOnPreparedListener != null) {
- mOnPreparedListener.onPrepared(mInstance);
+ if (mPreparedListenerRecord != null) {
+ mPreparedListenerRecord.first.execute(() ->
+ mPreparedListenerRecord.second.onPrepared(mInstance));
}
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
@@ -908,8 +909,9 @@
mTargetState = STATE_PLAYBACK_COMPLETED;
updatePlaybackState();
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
+ if (mCompletionListenerRecord != null) {
+ mCompletionListenerRecord.first.execute(() ->
+ mCompletionListenerRecord.second.onCompletion(mInstance));
}
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
@@ -920,8 +922,9 @@
private MediaPlayer.OnInfoListener mInfoListener =
new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
- if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(mInstance, what, extra);
+ if (mInfoListenerRecord != null) {
+ mInfoListenerRecord.first.execute(() ->
+ mInfoListenerRecord.second.onInfo(mInstance, what, extra));
}
if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
@@ -946,45 +949,12 @@
}
/* If an error handler has been supplied, use it and finish. */
- if (mOnErrorListener != null) {
- if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
- return true;
- }
+ if (mErrorListenerRecord != null) {
+ mErrorListenerRecord.first.execute(() ->
+ mErrorListenerRecord.second.onError(
+ mInstance, frameworkErr, implErr));
}
- /* Otherwise, pop up an error dialog so the user knows that
- * something bad has happened. Only try and pop up the dialog
- * if we're attached to a window. When we're going away and no
- * longer have a window, don't bother showing the user an error.
- */
- if (mInstance.getWindowToken() != null) {
- int messageId;
-
- if (frameworkErr
- == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
- messageId = R.string.VideoView2_error_text_invalid_progressive_playback;
- } else {
- messageId = R.string.VideoView2_error_text_unknown;
- }
-
- Resources res = ApiHelper.getLibResources();
- new AlertDialog.Builder(mInstance.getContext())
- .setMessage(res.getString(messageId))
- .setPositiveButton(res.getString(R.string.VideoView2_error_button),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* If we get here, there is no onError listener, so
- * at least inform them that the video is over.
- */
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
- }
- })
- .setCancelable(false)
- .show();
- }
return true;
}
};
@@ -1020,10 +990,13 @@
mInstance.setSubtitleEnabled(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
- if (mOnFullScreenRequestListener != null) {
- mOnFullScreenRequestListener.onFullScreenRequest(
- mInstance,
- args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
+ if (mFullScreenRequestListenerRecord != null) {
+ mFullScreenRequestListenerRecord.first.execute(() ->
+ mFullScreenRequestListenerRecord.second.onFullScreenRequest(
+ mInstance,
+ args.getBoolean(
+ MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN))
+ );
}
break;
}
@@ -1033,7 +1006,8 @@
@Override
public void onCustomAction(String action, Bundle extras) {
- mOnCustomActionListener.onCustomAction(action, extras);
+ mCustomActionListenerRecord.first.execute(() ->
+ mCustomActionListenerRecord.second.onCustomAction(action, extras));
showController();
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 7bf0fd2..ab63b8e 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -36,11 +36,9 @@
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -601,7 +599,6 @@
}
}
- @Ignore
@Test
public void testGetServiceToken() {
SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
@@ -612,20 +609,19 @@
}
private void connectToService(SessionToken2 token) throws InterruptedException {
+ if (mSession != null) {
+ mSession.close();
+ }
mController = createController(token);
mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
mPlayer = (MockPlayer) mSession.getPlayer();
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_sessionService() throws InterruptedException {
testConnectToService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_libraryService() throws InterruptedException {
testConnectToService(MockMediaLibraryService2.ID);
@@ -645,7 +641,7 @@
}
};
TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
- mController = createController(TestUtils.getServiceToken(mContext, id));
+ connectToService(TestUtils.getServiceToken(mContext, id));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
@@ -668,15 +664,11 @@
*/
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_session() throws InterruptedException {
testControllerAfterSessionIsGone(mSession.getToken().getId());
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
@@ -708,15 +700,11 @@
testControllerAfterSessionIsGone(id);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testClose_sessionService() throws InterruptedException {
testCloseFromService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testClose_libraryService() throws InterruptedException {
testCloseFromService(MockMediaLibraryService2.ID);
@@ -751,7 +739,7 @@
waitForDisconnect(mController, true);
testNoInteraction();
- // Test with the newly created session.
+ // Ensure that the controller cannot use newly create session with the same ID.
sHandler.postAndSync(() -> {
// Recreated session has different session stub, so previously created controller
// shouldn't be available.
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 852f2fa..d61baab 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -26,7 +26,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,9 +39,6 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-@Ignore
-// TODO(jaewan): Reenable test when the media session service detects newly installed sesison
-// service app.
public class MediaSessionManager_MediaSession2 extends MediaSession2TestBase {
private static final String TAG = "MediaSessionManager_MediaSession2";
@@ -101,7 +97,7 @@
}
/**
- * Test if server recognizes session even if session refuses the connection from server.
+ * Test if server recognizes a session even if the session refuses the connection from server.
*
* @throws InterruptedException
*/
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 979290f..0ce5350 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -589,6 +589,13 @@
status = cmdStatus;
}
+ // Ignore error if non-offloadable effect is created on an offload thread.
+ // Will be switched to non-offload thread when the effect is enabled.
+ if (status != NO_ERROR && thread->type() == ThreadBase::OFFLOAD && !isOffloaded()) {
+ ALOGV("Ignore error %d on non-offloadable effect on offload thread", status);
+ status = NO_ERROR;
+ }
+
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index ace586c..ef466a2 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -79,7 +79,6 @@
unsigned i;
for (i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
- mFastTrackNames[i] = -1;
mGenerations[i] = 0;
}
#ifdef FAST_THREAD_STATISTICS
@@ -190,7 +189,7 @@
// 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
- mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
+ mMixer = new AudioMixer(frameCount, mSampleRate);
// FIXME See the other FIXME at FastMixer::setNBLogWriter()
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
@@ -235,7 +234,6 @@
dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != mFastTracksGen) {
ALOG_ASSERT(mMixerBuffer != NULL);
- int name;
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
@@ -245,9 +243,7 @@
const FastTrack* fastTrack = ¤t->mFastTracks[i];
ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
- mMixer->deleteTrackName(name);
+ mMixer->destroy(i);
}
#if !LOG_NDEBUG
mFastTrackNames[i] = -1;
@@ -265,10 +261,16 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1);
if (mMixer != NULL) {
- name = mMixer->getTrackName(fastTrack->mChannelMask,
+ const int name = i; // for clarity, choose name as fast track index.
+ status_t status = mMixer->create(
+ name,
+ fastTrack->mChannelMask,
fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
- mFastTrackNames[i] = name;
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name,
+ fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
mMixer->setBufferProvider(name, bufferProvider);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *)mMixerBuffer);
@@ -300,8 +302,7 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
mMixer->setBufferProvider(name, bufferProvider);
if (fastTrack->mVolumeProvider == NULL) {
float f = AudioMixer::UNITY_GAIN_FLOAT;
@@ -378,8 +379,7 @@
perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = trackFramesWritten;
fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
- int name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
if (fastTrack->mVolumeProvider != NULL) {
gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 930fa8d..235d23f 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -57,8 +57,6 @@
static const FastMixerState sInitial;
FastMixerState mPreIdle; // copy of state before we went into idle
- int mFastTrackNames[FastMixerState::kMaxFastTracks];
- // handles used by mixer to identify tracks
int mGenerations[FastMixerState::kMaxFastTracks];
// last observed mFastTracks[i].mGeneration
NBAIO_Sink* mOutputSink;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 27c6d35..e5cb8a2 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -497,9 +497,6 @@
patch->mPatchRecord->buffer(),
patch->mPatchRecord->bufferSize(),
AUDIO_OUTPUT_FLAG_NONE);
- if (patch->mPatchTrack == 0) {
- return NO_MEMORY;
- }
status = patch->mPatchTrack->initCheck();
if (status != NO_ERROR) {
return status;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e97bb06..6454be5 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -51,6 +51,11 @@
void flush();
void destroy();
int name() const { return mName; }
+ void setName(int name) {
+ LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0,
+ "%s both old name %d and new name %d are valid", __func__, mName, name);
+ mName = name;
+ }
virtual uint32_t sampleRate() const;
@@ -146,10 +151,7 @@
bool mResetDone;
const audio_stream_type_t mStreamType;
- int mName; // track name on the normal mixer,
- // allocated statically at track creation time,
- // and is even allocated (though unused) for fast tracks
- // FIXME don't allocate track name for fast tracks
+ int mName;
effect_buffer_t *mMainBuffer;
int32_t *mAuxBuffer;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d6021b3..cae296e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1661,6 +1661,7 @@
mSuspendedFrames(0),
mActiveTracks(&this->mLocalLog),
// mStreamTypes[] initialized in constructor body
+ mTracks(type == MIXER),
mOutput(output),
mLastWriteTime(-1), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
@@ -2157,6 +2158,53 @@
return track;
}
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::add(const sp<T> &track)
+{
+ const ssize_t index = mTracks.add(track);
+ if (index >= 0) {
+ // set name for track when adding.
+ int name;
+ if (mUnusedTrackNames.empty()) {
+ name = mTracks.size() - 1; // new name {0 ... size-1}.
+ } else {
+ // reuse smallest name for deleted track.
+ auto it = mUnusedTrackNames.begin();
+ name = *it;
+ (void)mUnusedTrackNames.erase(it);
+ }
+ track->setName(name);
+ } else {
+ LOG_ALWAYS_FATAL("cannot add track");
+ }
+ return index;
+}
+
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::remove(const sp<T> &track)
+{
+ const int name = track->name();
+ const ssize_t index = mTracks.remove(track);
+ if (index >= 0) {
+ // invalidate name when removing from mTracks.
+ LOG_ALWAYS_FATAL_IF(name < 0, "invalid name %d for track on mTracks", name);
+
+ if (mSaveDeletedTrackNames) {
+ // We can't directly access mAudioMixer since the caller may be outside of threadLoop.
+ // Instead, we add to mDeletedTrackNames which is solely used for mAudioMixer update,
+ // to be handled when MixerThread::prepareTracks_l() next changes mAudioMixer.
+ mDeletedTrackNames.emplace(name);
+ }
+
+ mUnusedTrackNames.emplace(name);
+ track->setName(T::TRACK_NAME_PENDING);
+ } else {
+ LOG_ALWAYS_FATAL_IF(name >= 0,
+ "valid name %d for track not in mTracks (returned %zd)", name, index);
+ }
+ return index;
+}
+
uint32_t AudioFlinger::PlaybackThread::correctLatency_l(uint32_t latency) const
{
return latency;
@@ -2313,9 +2361,6 @@
mLocalLog.log("removeTrack_l (%p) %s", track.get(), result.string());
mTracks.remove(track);
- deleteTrackName_l(track->name());
- // redundant as track is about to be destroyed, for dumpsys only
- track->mName = -1;
if (track->isFastTrack()) {
int index = track->mFastIndex;
ALOG_ASSERT(0 < index && index < (int)FastMixerState::sMaxFastTracks);
@@ -4111,6 +4156,14 @@
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
+ // clean up deleted track names in AudioMixer before allocating new tracks
+ (void)mTracks.processDeletedTrackNames([this](int name) {
+ // for each name, destroy it in the AudioMixer
+ if (mAudioMixer->exists(name)) {
+ mAudioMixer->destroy(name);
+ }
+ });
+ mTracks.clearDeletedTrackNames();
mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
@@ -4332,6 +4385,24 @@
// The first time a track is added we wait
// for all its buffers to be filled before processing it
int name = track->name();
+
+ // if an active track doesn't exist in the AudioMixer, create it.
+ if (!mAudioMixer->exists(name)) {
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ if (status != OK) {
+ ALOGW("%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name, track->mChannelMask, track->mFormat, track->mSessionId);
+ tracksToRemove->add(track);
+ track->invalidate(); // consider it dead.
+ continue;
+ }
+ }
+
// make sure that we have enough frames to mix one full buffer.
// enforce this condition only once to enable draining the buffer in case the client
// app does not call stop() and relies on underrun to stop:
@@ -4357,20 +4428,9 @@
size_t framesReady = track->framesReady();
if (ATRACE_ENABLED()) {
// I wish we had formatted trace names
- char traceName[16];
- strcpy(traceName, "nRdy");
- int name = track->name();
- if (AudioMixer::TRACK0 <= name &&
- name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) {
- name -= AudioMixer::TRACK0;
- traceName[4] = (name / 10) + '0';
- traceName[5] = (name % 10) + '0';
- } else {
- traceName[4] = '?';
- traceName[5] = '?';
- }
- traceName[6] = '\0';
- ATRACE_INT(traceName, framesReady);
+ std::string traceName("nRdy");
+ traceName += std::to_string(track->name());
+ ATRACE_INT(traceName.c_str(), framesReady);
}
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
@@ -4736,7 +4796,7 @@
}
// trackCountForUid_l() must be called with ThreadBase::mLock held
-uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid)
+uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid) const
{
uint32_t trackCount = 0;
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -4747,21 +4807,24 @@
return trackCount;
}
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId, uid_t uid)
+// isTrackAllowed_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const
{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
+ if (!PlaybackThread::isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ return false;
}
- return mAudioMixer->getTrackName(channelMask, format, sessionId);
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::MixerThread::deleteTrackName_l(int name)
-{
- ALOGV("remove track (%d) and delete from mixer", name);
- mAudioMixer->deleteTrackName(name);
+ // Check validity as we don't call AudioMixer::create() here.
+ if (!AudioMixer::isValidFormat(format)) {
+ ALOGW("%s: invalid format: %#x", __func__, format);
+ return false;
+ }
+ if (!AudioMixer::isValidChannelMask(channelMask)) {
+ ALOGW("%s: invalid channelMask: %#x", __func__, channelMask);
+ return false;
+ }
+ return true;
}
// checkForNewParameter_l() must be called with ThreadBase::mLock held
@@ -4854,13 +4917,18 @@
readOutputParameters_l();
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]->mFormat, mTracks[i]->mSessionId, mTracks[i]->uid());
- if (name < 0) {
- break;
- }
- mTracks[i]->mName = name;
+ for (const auto &track : mTracks) {
+ const int name = track->name();
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ ALOGW_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__,
+ name, track->mChannelMask, track->mFormat, track->mSessionId);
}
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
@@ -5309,21 +5377,6 @@
return !mStandby && !(trackPaused || (mHwPaused && !trackStopped));
}
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
- audio_format_t format __unused, audio_session_t sessionId __unused, uid_t uid)
-{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
- }
- return 0;
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused)
-{
-}
-
// checkForNewParameter_l() must be called with ThreadBase::mLock held
bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
@@ -5941,6 +5994,31 @@
}
}
+void AudioFlinger::DuplicatingThread::dumpInternals(int fd, const Vector<String16>& args __unused)
+{
+ MixerThread::dumpInternals(fd, args);
+
+ std::stringstream ss;
+ const size_t numTracks = mOutputTracks.size();
+ ss << " " << numTracks << " OutputTracks";
+ if (numTracks > 0) {
+ ss << ":";
+ for (const auto &track : mOutputTracks) {
+ const sp<ThreadBase> thread = track->thread().promote();
+ ss << " (" << track->name() << " : ";
+ if (thread.get() != nullptr) {
+ ss << thread.get() << ", " << thread->id();
+ } else {
+ ss << "null";
+ }
+ ss << ")";
+ }
+ }
+ ss << "\n";
+ std::string result = ss.str();
+ write(fd, result.c_str(), result.size());
+}
+
void AudioFlinger::DuplicatingThread::saveOutputTracks()
{
outputTracks = mOutputTracks;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 53cb8ad..ae14ac1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -624,6 +624,7 @@
static const int8_t kMaxTrackStartupRetriesOffload = 100;
static const int8_t kMaxTrackStopRetriesOffload = 2;
static constexpr uint32_t kMaxTracksPerUid = 40;
+ static constexpr size_t kMaxTracks = 256;
// Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
// if delay is greater, the estimated time for timeLoopNextNs is reset.
@@ -778,6 +779,16 @@
virtual bool isOutput() const override { return true; }
+ // returns true if the track is allowed to be added to the thread.
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask __unused,
+ audio_format_t format __unused,
+ audio_session_t sessionId __unused,
+ uid_t uid) const {
+ return trackCountForUid_l(uid) < PlaybackThread::kMaxTracksPerUid
+ && mTracks.size() < PlaybackThread::kMaxTracks;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -866,12 +877,6 @@
protected:
ActiveTracks<Track> mActiveTracks;
- // 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, audio_format_t format,
- audio_session_t sessionId, uid_t uid) = 0;
- virtual void deleteTrackName_l(int name) = 0;
-
// Time to sleep between cycles when:
virtual uint32_t activeSleepTimeUs() const; // mixer state MIXER_TRACKS_ENABLED
virtual uint32_t idleSleepTimeUs() const = 0; // mixer state MIXER_IDLE
@@ -899,7 +904,7 @@
&& mHwSupportsPause
&& (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
- uint32_t trackCountForUid_l(uid_t uid);
+ uint32_t trackCountForUid_l(uid_t uid) const;
private:
@@ -916,7 +921,64 @@
virtual void dumpInternals(int fd, const Vector<String16>& args);
void dumpTracks(int fd, const Vector<String16>& args);
- SortedVector< sp<Track> > mTracks;
+ // The Tracks class manages names for all tracks
+ // added and removed from the Thread.
+ template <typename T>
+ class Tracks {
+ public:
+ Tracks(bool saveDeletedTrackNames) :
+ mSaveDeletedTrackNames(saveDeletedTrackNames) { }
+
+ // SortedVector methods
+ ssize_t add(const sp<T> &track);
+ ssize_t remove(const sp<T> &track);
+ size_t size() const {
+ return mTracks.size();
+ }
+ bool isEmpty() const {
+ return mTracks.isEmpty();
+ }
+ ssize_t indexOf(const sp<T> &item) {
+ return mTracks.indexOf(item);
+ }
+ sp<T> operator[](size_t index) const {
+ return mTracks[index];
+ }
+ typename SortedVector<sp<T>>::iterator begin() {
+ return mTracks.begin();
+ }
+ typename SortedVector<sp<T>>::iterator end() {
+ return mTracks.end();
+ }
+
+ size_t processDeletedTrackNames(std::function<void(int)> f) {
+ const size_t size = mDeletedTrackNames.size();
+ if (size > 0) {
+ for (const int name : mDeletedTrackNames) {
+ f(name);
+ }
+ }
+ return size;
+ }
+
+ void clearDeletedTrackNames() { mDeletedTrackNames.clear(); }
+
+ private:
+ // Track names pending deletion for MIXER type threads
+ const bool mSaveDeletedTrackNames; // true to enable tracking
+ std::set<int> mDeletedTrackNames;
+
+ // Fast lookup of previously deleted track names for reuse.
+ // This is an arbitrary decision (actually any non-negative
+ // integer that isn't in mTracks[*]->names() could be used) - we attempt
+ // to use the smallest possible available name.
+ std::set<int> mUnusedTrackNames;
+
+ SortedVector<sp<T>> mTracks; // wrapped SortedVector.
+ };
+
+ Tracks<Track> mTracks;
+
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
AudioStreamOut *mOutput;
@@ -1023,11 +1085,11 @@
status_t& status);
virtual void dumpInternals(int fd, const Vector<String16>& args);
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const override;
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid);
- virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
virtual void cacheParameters_l();
@@ -1105,9 +1167,6 @@
virtual void flushHw_l();
protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid);
- virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
@@ -1211,6 +1270,8 @@
virtual ~DuplicatingThread();
// Thread virtuals
+ virtual void dumpInternals(int fd, const Vector<String16>& args) override;
+
void addOutputTrack(MixerThread* thread);
void removeOutputTrack(MixerThread* thread);
uint32_t waitTimeMs() const { return mWaitTimeMs; }
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index a3ea756..a7e966f 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -54,6 +54,11 @@
TYPE_PATCH,
};
+ enum {
+ TRACK_NAME_PENDING = -1,
+ TRACK_NAME_FAILURE = -2,
+ };
+
TrackBase(ThreadBase *thread,
const sp<Client>& client,
uint32_t sampleRate,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 67f27d0..9b93939 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -394,7 +394,7 @@
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
mStreamType(streamType),
- mName(-1), // see note below
+ mName(TRACK_NAME_FAILURE), // set to TRACK_NAME_PENDING on constructor success.
mMainBuffer(thread->sinkBuffer()),
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
@@ -427,9 +427,8 @@
}
mServerProxy = mAudioTrackServerProxy;
- mName = thread->getTrackName_l(channelMask, format, sessionId, uid);
- if (mName < 0) {
- ALOGE("no more track names available");
+ if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ ALOGE("no more tracks available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
@@ -448,6 +447,7 @@
mFastIndex = i;
thread->mFastTrackAvailMask &= ~(1 << i);
}
+ mName = TRACK_NAME_PENDING;
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -466,7 +466,7 @@
status_t AudioFlinger::PlaybackThread::Track::initCheck() const
{
status_t status = TrackBase::initCheck();
- if (status == NO_ERROR && mName < 0) {
+ if (status == NO_ERROR && mName == TRACK_NAME_FAILURE) {
status = NO_MEMORY;
}
return status;
@@ -527,10 +527,12 @@
if (isFastTrack()) {
result.appendFormat("F%c %3d", trackType, mFastIndex);
- } else if (mName >= AudioMixer::TRACK0) {
- result.appendFormat("%c %4d", trackType, mName - AudioMixer::TRACK0);
+ } else if (mName == TRACK_NAME_PENDING) {
+ result.appendFormat("%c pend", trackType);
+ } else if (mName == TRACK_NAME_FAILURE) {
+ result.appendFormat("%c fail", trackType);
} else {
- result.appendFormat("%c none", trackType);
+ result.appendFormat("%c %4d", trackType, mName);
}
char nowInUnderrun;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index afd23a6..e8416d4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -67,7 +67,9 @@
const char *device_address,
const char *device_name)
{
- return setDeviceConnectionStateInt(device, state, device_address, device_name);
+ status_t status = setDeviceConnectionStateInt(device, state, device_address, device_name);
+ nextAudioPortGeneration();
+ return status;
}
void AudioPolicyManager::broadcastDeviceConnectionState(audio_devices_t device,