Merge "libavservices_minijail: convert to Android.bp"
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/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index d35a52b..a81fe8c 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -6998,19 +6998,24 @@
* <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
* </ul>
* </li>
+ * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+ * the same.</li>
* <li>The logical camera device must be LIMITED or higher device.</li>
* </ul>
* <p>Both the logical camera device and its underlying physical devices support the
* mandatory stream combinations required for their device levels.</p>
* <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
* <ul>
- * <li>Replacing one logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
+ * <li>For each guaranteed stream combination, the logical camera supports replacing one
+ * logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
* or raw stream with two physical streams of the same size and format, each from a
* separate physical camera, given that the size and format are supported by both
* physical cameras.</li>
- * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
- * advertise RAW capability, but the underlying physical cameras do. This is usually
- * the case when the physical cameras have different sensor sizes.</li>
+ * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+ * cameras do, the logical camera will support guaranteed stream combinations for RAW
+ * capability, except that the RAW streams will be physical streams, each from a separate
+ * physical camera. This is usually the case when the physical cameras have different
+ * sensor sizes.</li>
* </ul>
* <p>Using physical streams in place of a logical stream of the same size and format will
* not slow down the frame rate of the capture, as long as the minimum frame duration
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/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ff440bc..b4fa3c5 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -438,7 +438,11 @@
return 0;
}
- virtual uint32_t getUnderrunFrames() const {
+ virtual uint32_t getUnderrunFrames() const override {
+ return 0;
+ }
+
+ virtual uint32_t getUnderrunCount() const override {
return 0;
}
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..93ed5f2 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) {
@@ -1410,13 +1393,12 @@
// been enabled for mixing.
if (t->mIn == nullptr) break;
- if (CC_UNLIKELY(aux != NULL)) {
- aux += outFrames;
- }
(t.get()->*t->hook)(
outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
- mResampleTemp.get() /* naked ptr */, aux);
+ mResampleTemp.get() /* naked ptr */,
+ aux != nullptr ? aux + outFrames : nullptr);
outFrames += t->buffer.frameCount;
+
t->bufferProvider->releaseBuffer(&t->buffer);
}
}
@@ -1704,7 +1686,7 @@
out += outFrames * channels;
if (aux != NULL) {
- aux += channels;
+ aux += outFrames;
}
numFrames -= b.frameCount;
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/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 17fa01c..1fa8789 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -9,7 +9,7 @@
srcs: [
"JAudioTrack.cpp",
- "MediaPlayer2Manager.cpp",
+ "MediaPlayer2AudioOutput.cpp",
"mediaplayer2.cpp",
],
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
new file mode 100644
index 0000000..a8e1d1f
--- /dev/null
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -0,0 +1,727 @@
+/*
+**
+** Copyright 2018, 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 "MediaPlayer2AudioOutput"
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
+
+#include <cutils/properties.h> // for property_get
+#include <utils/Log.h>
+
+#include <media/AudioPolicyHelper.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace {
+
+const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
+
+} // anonymous namespace
+
+namespace android {
+
+// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
+/* static */ int MediaPlayer2AudioOutput::mMinBufferCount = 4;
+/* static */ bool MediaPlayer2AudioOutput::mIsOnEmulator = false;
+
+status_t MediaPlayer2AudioOutput::dump(int fd, const Vector<String16>& args) const {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ result.append(" MediaPlayer2AudioOutput\n");
+ snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
+ mStreamType, mLeftVolume, mRightVolume);
+ result.append(buffer);
+ snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
+ mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
+ result.append(buffer);
+ snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
+ mAuxEffectId, mSendLevel);
+ result.append(buffer);
+
+ ::write(fd, result.string(), result.size());
+ if (mTrack != 0) {
+ mTrack->dump(fd, args);
+ }
+ return NO_ERROR;
+}
+
+MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
+ const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
+ : mCallback(NULL),
+ mCallbackCookie(NULL),
+ mCallbackData(NULL),
+ mStreamType(AUDIO_STREAM_MUSIC),
+ mLeftVolume(1.0),
+ mRightVolume(1.0),
+ mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
+ mSampleRateHz(0),
+ mMsecsPerFrame(0),
+ mFrameSize(0),
+ mSessionId(sessionId),
+ mUid(uid),
+ mPid(pid),
+ mSendLevel(0.0),
+ mAuxEffectId(0),
+ mFlags(AUDIO_OUTPUT_FLAG_NONE),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mDeviceCallbackEnabled(false),
+ mDeviceCallback(deviceCallback) {
+ ALOGV("MediaPlayer2AudioOutput(%d)", sessionId);
+ if (attr != NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAttributes != NULL) {
+ memcpy(mAttributes, attr, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attr);
+ }
+ } else {
+ mAttributes = NULL;
+ }
+
+ setMinBufferCount();
+}
+
+MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() {
+ close();
+ free(mAttributes);
+ delete mCallbackData;
+}
+
+//static
+void MediaPlayer2AudioOutput::setMinBufferCount() {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ mIsOnEmulator = true;
+ mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
+ }
+}
+
+// static
+bool MediaPlayer2AudioOutput::isOnEmulator() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mIsOnEmulator;
+}
+
+// static
+int MediaPlayer2AudioOutput::getMinBufferCount() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mMinBufferCount;
+}
+
+ssize_t MediaPlayer2AudioOutput::bufferSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount() * mFrameSize;
+}
+
+ssize_t MediaPlayer2AudioOutput::frameCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::channelCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->channelCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::frameSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mFrameSize;
+}
+
+uint32_t MediaPlayer2AudioOutput::latency () const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->latency();
+}
+
+float MediaPlayer2AudioOutput::msecsPerFrame() const {
+ Mutex::Autolock lock(mLock);
+ return mMsecsPerFrame;
+}
+
+status_t MediaPlayer2AudioOutput::getPosition(uint32_t *position) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getPosition(position);
+}
+
+status_t MediaPlayer2AudioOutput::getTimestamp(AudioTimestamp &ts) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getTimestamp(ts);
+}
+
+// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
+// as it acquires locks and may query the audio driver.
+//
+// Some calls could conceivably retrieve extrapolated data instead of
+// accessing getTimestamp() or getPosition() every time a data buffer with
+// a media time is received.
+//
+// Calculate duration of played samples if played at normal rate (i.e., 1.0).
+int64_t MediaPlayer2AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0 || mSampleRateHz == 0) {
+ return 0;
+ }
+
+ uint32_t numFramesPlayed;
+ int64_t numFramesPlayedAtUs;
+ AudioTimestamp ts;
+
+ status_t res = mTrack->getTimestamp(ts);
+ if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
+ numFramesPlayed = ts.mPosition;
+ numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
+ //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
+ numFramesPlayed = 0;
+ numFramesPlayedAtUs = nowUs;
+ //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
+ // numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else { // case 3: transitory at new track or audio fast tracks.
+ res = mTrack->getPosition(&numFramesPlayed);
+ CHECK_EQ(res, (status_t)OK);
+ numFramesPlayedAtUs = nowUs;
+ numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
+ //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ }
+
+ // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
+ // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
+ int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
+ + nowUs - numFramesPlayedAtUs;
+ if (durationUs < 0) {
+ // Occurs when numFramesPlayed position is very small and the following:
+ // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
+ // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
+ // (2) In case 3, using getPosition and adding mAudioSink->latency() to
+ // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
+ //
+ // Both of these are transitory conditions.
+ ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
+ durationUs = 0;
+ }
+ ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
+ (long long)durationUs, (long long)nowUs,
+ numFramesPlayed, (long long)numFramesPlayedAtUs);
+ return durationUs;
+}
+
+status_t MediaPlayer2AudioOutput::getFramesWritten(uint32_t *frameswritten) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ ExtendedTimestamp ets;
+ status_t status = mTrack->getTimestamp(&ets);
+ if (status == OK || status == WOULD_BLOCK) {
+ *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
+ }
+ return status;
+}
+
+status_t MediaPlayer2AudioOutput::setParameters(const String8& keyValuePairs) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->setParameters(keyValuePairs);
+}
+
+String8 MediaPlayer2AudioOutput::getParameters(const String8& keys) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return String8::empty();
+ }
+ return mTrack->getParameters(keys);
+}
+
+void MediaPlayer2AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+ Mutex::Autolock lock(mLock);
+ if (attributes == NULL) {
+ free(mAttributes);
+ mAttributes = NULL;
+ } else {
+ if (mAttributes == NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ }
+ memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attributes);
+ }
+}
+
+void MediaPlayer2AudioOutput::setAudioStreamType(audio_stream_type_t streamType) {
+ Mutex::Autolock lock(mLock);
+ // do not allow direct stream type modification if attributes have been set
+ if (mAttributes == NULL) {
+ mStreamType = streamType;
+ }
+}
+
+void MediaPlayer2AudioOutput::close_l() {
+ mTrack.clear();
+}
+
+status_t MediaPlayer2AudioOutput::open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo,
+ bool doNotReconnect,
+ uint32_t suggestedFrameCount) {
+ ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
+ format, bufferCount, mSessionId, flags);
+
+ // offloading is only supported in callback mode for now.
+ // offloadInfo must be present if offload flag is set
+ if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
+ ((cb == NULL) || (offloadInfo == NULL))) {
+ return BAD_VALUE;
+ }
+
+ // compute frame count for the AudioTrack internal buffer
+ size_t frameCount;
+ if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ frameCount = 0; // AudioTrack will get frame count from AudioFlinger
+ } else {
+ // try to estimate the buffer processing fetch size from AudioFlinger.
+ // framesPerBuffer is approximate and generally correct, except when it's not :-).
+ uint32_t afSampleRate;
+ size_t afFrameCount;
+ if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ const size_t framesPerBuffer =
+ (unsigned long long)sampleRate * afFrameCount / afSampleRate;
+
+ if (bufferCount == 0) {
+ // use suggestedFrameCount
+ bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
+ }
+ // Check argument bufferCount against the mininum buffer count
+ if (bufferCount != 0 && bufferCount < mMinBufferCount) {
+ ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
+ bufferCount = mMinBufferCount;
+ }
+ // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
+ // which will be the minimum size permitted.
+ frameCount = bufferCount * framesPerBuffer;
+ }
+
+ if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
+ channelMask = audio_channel_out_mask_from_count(channelCount);
+ if (0 == channelMask) {
+ ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
+ return NO_INIT;
+ }
+ }
+
+ Mutex::Autolock lock(mLock);
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
+ sp<AudioTrack> t;
+ CallbackData *newcbd = NULL;
+
+ ALOGV("creating new AudioTrack");
+
+ if (mCallback != NULL) {
+ newcbd = new CallbackData(this);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ CallbackWrapper,
+ newcbd,
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_CALLBACK,
+ offloadInfo,
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ 1.0f, // default value for maxRequiredSpeed
+ mSelectedDeviceId);
+ } else {
+ // TODO: Due to buffer memory concerns, we use a max target playback speed
+ // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
+ // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
+ const float targetSpeed =
+ std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
+ ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
+ "track target speed:%f clamped from playback speed:%f",
+ targetSpeed, mPlaybackRate.mSpeed);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ NULL, // callback
+ NULL, // user data
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_DEFAULT,
+ NULL, // offload info
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ targetSpeed,
+ mSelectedDeviceId);
+ }
+
+ if ((t == 0) || (t->initCheck() != NO_ERROR)) {
+ ALOGE("Unable to create audio track");
+ delete newcbd;
+ // t goes out of scope, so reference count drops to zero
+ return NO_INIT;
+ } else {
+ // successful AudioTrack initialization implies a legacy stream type was generated
+ // from the audio attributes
+ mStreamType = t->streamType();
+ }
+
+ CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
+
+ mCallbackData = newcbd;
+ ALOGV("setVolume");
+ t->setVolume(mLeftVolume, mRightVolume);
+
+ mSampleRateHz = sampleRate;
+ mFlags = flags;
+ mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
+ mFrameSize = t->frameSize();
+ mTrack = t;
+
+ return updateTrack_l();
+}
+
+status_t MediaPlayer2AudioOutput::updateTrack_l() {
+ if (mTrack == NULL) {
+ return NO_ERROR;
+ }
+
+ status_t res = NO_ERROR;
+ // Note some output devices may give us a direct track even though we don't specify it.
+ // Example: Line application b/17459982.
+ if ((mTrack->getFlags()
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
+ res = mTrack->setPlaybackRate(mPlaybackRate);
+ if (res == NO_ERROR) {
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mTrack->attachAuxEffect(mAuxEffectId);
+ }
+ }
+ mTrack->setOutputDevice(mSelectedDeviceId);
+ if (mDeviceCallbackEnabled) {
+ mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ ALOGV("updateTrack_l() DONE status %d", res);
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::start() {
+ ALOGV("start");
+ Mutex::Autolock lock(mLock);
+ if (mCallbackData != NULL) {
+ mCallbackData->endTrackSwitch();
+ }
+ if (mTrack != 0) {
+ mTrack->setVolume(mLeftVolume, mRightVolume);
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ status_t status = mTrack->start();
+ return status;
+ }
+ return NO_INIT;
+}
+
+ssize_t MediaPlayer2AudioOutput::write(const void* buffer, size_t size, bool blocking) {
+ Mutex::Autolock lock(mLock);
+ LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
+ //ALOGV("write(%p, %u)", buffer, size);
+ if (mTrack != 0) {
+ return mTrack->write(buffer, size, blocking);
+ }
+ return NO_INIT;
+}
+
+void MediaPlayer2AudioOutput::stop() {
+ ALOGV("stop");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->stop();
+ }
+}
+
+void MediaPlayer2AudioOutput::flush() {
+ ALOGV("flush");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->flush();
+ }
+}
+
+void MediaPlayer2AudioOutput::pause() {
+ ALOGV("pause");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->pause();
+ }
+}
+
+void MediaPlayer2AudioOutput::close() {
+ ALOGV("close");
+ sp<AudioTrack> track;
+ {
+ Mutex::Autolock lock(mLock);
+ track = mTrack;
+ close_l(); // clears mTrack
+ }
+ // destruction of the track occurs outside of mutex.
+}
+
+void MediaPlayer2AudioOutput::setVolume(float left, float right) {
+ ALOGV("setVolume(%f, %f)", left, right);
+ Mutex::Autolock lock(mLock);
+ mLeftVolume = left;
+ mRightVolume = right;
+ if (mTrack != 0) {
+ mTrack->setVolume(left, right);
+ }
+}
+
+status_t MediaPlayer2AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate) {
+ ALOGV("setPlaybackRate(%f %f %d %d)",
+ rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ // remember rate so that we can set it when the track is opened
+ mPlaybackRate = rate;
+ return OK;
+ }
+ status_t res = mTrack->setPlaybackRate(rate);
+ if (res != NO_ERROR) {
+ return res;
+ }
+ // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
+ CHECK_GT(rate.mSpeed, 0.f);
+ mPlaybackRate = rate;
+ if (mSampleRateHz != 0) {
+ mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
+ }
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::getPlaybackRate(AudioPlaybackRate *rate) {
+ ALOGV("setPlaybackRate");
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ *rate = mTrack->getPlaybackRate();
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setAuxEffectSendLevel(float level) {
+ ALOGV("setAuxEffectSendLevel(%f)", level);
+ Mutex::Autolock lock(mLock);
+ mSendLevel = level;
+ if (mTrack != 0) {
+ return mTrack->setAuxEffectSendLevel(level);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::attachAuxEffect(int effectId) {
+ ALOGV("attachAuxEffect(%d)", effectId);
+ Mutex::Autolock lock(mLock);
+ mAuxEffectId = effectId;
+ if (mTrack != 0) {
+ return mTrack->attachAuxEffect(effectId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setOutputDevice(audio_port_handle_t deviceId) {
+ ALOGV("setOutputDevice(%d)", deviceId);
+ Mutex::Autolock lock(mLock);
+ mSelectedDeviceId = deviceId;
+ if (mTrack != 0) {
+ return mTrack->setOutputDevice(deviceId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId) {
+ ALOGV("getRoutedDeviceId");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mRoutedDeviceId = mTrack->getRoutedDeviceId();
+ }
+ *deviceId = mRoutedDeviceId;
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::enableAudioDeviceCallback(bool enabled) {
+ ALOGV("enableAudioDeviceCallback, %d", enabled);
+ Mutex::Autolock lock(mLock);
+ mDeviceCallbackEnabled = enabled;
+ if (mTrack != 0) {
+ status_t status;
+ if (enabled) {
+ status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ } else {
+ status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ return status;
+ }
+ return NO_ERROR;
+}
+
+// static
+void MediaPlayer2AudioOutput::CallbackWrapper(
+ int event, void *cookie, void *info) {
+ //ALOGV("callbackwrapper");
+ CallbackData *data = (CallbackData*)cookie;
+ // lock to ensure we aren't caught in the middle of a track switch.
+ data->lock();
+ MediaPlayer2AudioOutput *me = data->getOutput();
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ if (me == NULL) {
+ // no output set, likely because the track was scheduled to be reused
+ // by another player, but the format turned out to be incompatible.
+ data->unlock();
+ if (buffer != NULL) {
+ buffer->size = 0;
+ }
+ return;
+ }
+
+ switch(event) {
+ case AudioTrack::EVENT_MORE_DATA: {
+ size_t actualSize = (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie,
+ CB_EVENT_FILL_BUFFER);
+
+ // Log when no data is returned from the callback.
+ // (1) We may have no data (especially with network streaming sources).
+ // (2) We may have reached the EOS and the audio track is not stopped yet.
+ // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
+ // NuPlayer2Renderer will return zero when it doesn't have data (it doesn't block to fill).
+ //
+ // This is a benign busy-wait, with the next data request generated 10 ms or more later;
+ // nevertheless for power reasons, we don't want to see too many of these.
+
+ ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
+
+ buffer->size = actualSize;
+ } break;
+
+ case AudioTrack::EVENT_STREAM_END:
+ // currently only occurs for offloaded callbacks
+ ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_STREAM_END);
+ break;
+
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK :
+ ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
+ break;
+
+ case AudioTrack::EVENT_UNDERRUN:
+ // This occurs when there is no data available, typically
+ // when there is a failure to supply data to the AudioTrack. It can also
+ // occur in non-offloaded mode when the audio device comes out of standby.
+ //
+ // If an AudioTrack underruns it outputs silence. Since this happens suddenly
+ // it may sound like an audible pop or glitch.
+ //
+ // The underrun event is sent once per track underrun; the condition is reset
+ // when more data is sent to the AudioTrack.
+ ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
+ break;
+
+ default:
+ ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
+ }
+
+ data->unlock();
+}
+
+audio_session_t MediaPlayer2AudioOutput::getSessionId() const
+{
+ Mutex::Autolock lock(mLock);
+ return mSessionId;
+}
+
+uint32_t MediaPlayer2AudioOutput::getSampleRate() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->getSampleRate();
+}
+
+int64_t MediaPlayer2AudioOutput::getBufferDurationInUs() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ int64_t duration;
+ if (mTrack->getBufferDurationInUs(&duration) != OK) {
+ return 0;
+ }
+ return duration;
+}
+
+} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.cpp b/media/libmediaplayer2/MediaPlayer2Manager.cpp
deleted file mode 100644
index 44df2ac..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.cpp
+++ /dev/null
@@ -1,2086 +0,0 @@
-/*
-**
-** Copyright 2017, 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.
-*/
-
-// Proxy for media player implementations
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer2Manager"
-#include <utils/Log.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <dirent.h>
-#include <unistd.h>
-
-#include <string.h>
-
-#include <cutils/atomic.h>
-#include <cutils/properties.h> // for property_get
-
-#include <utils/misc.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
-#include <utils/Errors.h> // for status_t
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <media/AudioPolicyHelper.h>
-#include <media/DataSourceDesc.h>
-#include <media/MediaHTTPService.h>
-#include <media/Metadata.h>
-#include <media/AudioTrack.h>
-#include <media/MemoryLeakTrackUtil.h>
-#include <media/NdkWrapper.h>
-
-#include <media/stagefright/InterfaceUtils.h>
-#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooperRoster.h>
-#include <media/stagefright/SurfaceUtils.h>
-#include <mediautils/BatteryNotifier.h>
-
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <memunreachable/memunreachable.h>
-#include <system/audio.h>
-#include <system/window.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <nuplayer2/NuPlayer2Driver.h>
-#include "MediaPlayer2Manager.h"
-
-static const int kDumpLockRetries = 50;
-static const int kDumpLockSleepUs = 20000;
-
-namespace {
-using android::media::Metadata;
-using android::status_t;
-using android::OK;
-using android::BAD_VALUE;
-using android::NOT_ENOUGH_DATA;
-using android::Parcel;
-
-// Max number of entries in the filter.
-const int kMaxFilterSize = 64; // I pulled that out of thin air.
-
-const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
-
-// FIXME: Move all the metadata related function in the Metadata.cpp
-
-
-// Unmarshall a filter from a Parcel.
-// Filter format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | number of entries (n) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 1 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 2 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// ....
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type n |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that should start with a filter.
-// @param[out] filter On exit contains the list of metadata type to be
-// filtered.
-// @param[out] status On exit contains the status code to be returned.
-// @return true if the parcel starts with a valid filter.
-bool unmarshallFilter(const Parcel& p,
- Metadata::Filter *filter,
- status_t *status)
-{
- int32_t val;
- if (p.readInt32(&val) != OK)
- {
- ALOGE("Failed to read filter's length");
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- if( val > kMaxFilterSize || val < 0)
- {
- ALOGE("Invalid filter len %d", val);
- *status = BAD_VALUE;
- return false;
- }
-
- const size_t num = val;
-
- filter->clear();
- filter->setCapacity(num);
-
- size_t size = num * sizeof(Metadata::Type);
-
-
- if (p.dataAvail() < size)
- {
- ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- const Metadata::Type *data =
- static_cast<const Metadata::Type*>(p.readInplace(size));
-
- if (NULL == data)
- {
- ALOGE("Filter had no data");
- *status = BAD_VALUE;
- return false;
- }
-
- // TODO: The stl impl of vector would be more efficient here
- // because it degenerates into a memcpy on pod types. Try to
- // replace later or use stl::set.
- for (size_t i = 0; i < num; ++i)
- {
- filter->add(*data);
- ++data;
- }
- *status = OK;
- return true;
-}
-
-// @param filter Of metadata type.
-// @param val To be searched.
-// @return true if a match was found.
-bool findMetadata(const Metadata::Filter& filter, const int32_t val)
-{
- // Deal with empty and ANY right away
- if (filter.isEmpty()) return false;
- if (filter[0] == Metadata::kAny) return true;
-
- return filter.indexOf(val) >= 0;
-}
-
-} // anonymous namespace
-
-
-namespace {
-using android::Parcel;
-using android::String16;
-
-// marshalling tag indicating flattened utf16 tags
-// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
-const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
-
-// Audio attributes format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | usage |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | content_type |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | source |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flags |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flattened tags in UTF16 |
-// | ... |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that contains audio attributes.
-// @param[out] attributes On exit points to an initialized audio_attributes_t structure
-// @param[out] status On exit contains the status code to be returned.
-void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes)
-{
- attributes->usage = (audio_usage_t) parcel.readInt32();
- attributes->content_type = (audio_content_type_t) parcel.readInt32();
- attributes->source = (audio_source_t) parcel.readInt32();
- attributes->flags = (audio_flags_mask_t) parcel.readInt32();
- const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
- if (hasFlattenedTag) {
- // the tags are UTF16, convert to UTF8
- String16 tags = parcel.readString16();
- ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
- if (realTagSize <= 0) {
- strcpy(attributes->tags, "");
- } else {
- // copy the flattened string into the attributes as the destination for the conversion:
- // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
- size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
- utf16_to_utf8(tags.string(), tagSize, attributes->tags,
- sizeof(attributes->tags) / sizeof(attributes->tags[0]));
- }
- } else {
- ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
- strcpy(attributes->tags, "");
- }
-}
-} // anonymous namespace
-
-
-namespace android {
-
-extern ALooperRoster gLooperRoster;
-
-MediaPlayer2Manager gMediaPlayer2Manager;
-
-// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
-/* static */ int MediaPlayer2Manager::AudioOutput::mMinBufferCount = 4;
-/* static */ bool MediaPlayer2Manager::AudioOutput::mIsOnEmulator = false;
-
-// static
-MediaPlayer2Manager& MediaPlayer2Manager::get() {
- return gMediaPlayer2Manager;
-}
-
-MediaPlayer2Manager::MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager created");
- // TODO: remove all unnecessary pid/uid handling.
- mPid = IPCThreadState::self()->getCallingPid();
- mUid = IPCThreadState::self()->getCallingUid();
- mNextConnId = 1;
-}
-
-MediaPlayer2Manager::~MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager destroyed");
-}
-
-sp<MediaPlayer2Engine> MediaPlayer2Manager::create(
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId)
-{
- int32_t connId = android_atomic_inc(&mNextConnId);
-
- sp<Client> c = new Client(
- mPid, connId, client, audioSessionId, mUid);
-
- if (!c->init()) {
- return NULL;
- }
-
- ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, mPid, mUid);
-
- wp<Client> w = c;
- {
- Mutex::Autolock lock(mLock);
- mClients.add(w);
- }
- return c;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::dump(int fd, const Vector<String16>& args) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- result.append(" AudioOutput\n");
- snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
- mStreamType, mLeftVolume, mRightVolume);
- result.append(buffer);
- snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
- result.append(buffer);
- snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
- mAuxEffectId, mSendLevel);
- result.append(buffer);
-
- ::write(fd, result.string(), result.size());
- if (mTrack != 0) {
- mTrack->dump(fd, args);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append(" Client\n");
- snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
- mPid, mConnId, mStatus, mLoop?"true": "false");
- result.append(buffer);
-
- sp<MediaPlayer2Interface> p;
- sp<AudioOutput> audioOutput;
- bool locked = false;
- for (int i = 0; i < kDumpLockRetries; ++i) {
- if (mLock.tryLock() == NO_ERROR) {
- locked = true;
- break;
- }
- usleep(kDumpLockSleepUs);
- }
-
- if (locked) {
- p = mPlayer;
- audioOutput = mAudioOutput;
- mLock.unlock();
- } else {
- result.append(" lock is taken, no dump from player and audio output\n");
- }
- write(fd, result.string(), result.size());
-
- if (p != NULL) {
- p->dump(fd, args);
- }
- if (audioOutput != 0) {
- audioOutput->dump(fd, args);
- }
- write(fd, "\n", 1);
- return NO_ERROR;
-}
-
-/**
- * The only arguments this understands right now are -c, -von and -voff,
- * which are parsed by ALooperRoster::dump()
- */
-status_t MediaPlayer2Manager::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction.
-
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump MediaPlayer2Manager from pid=%d, uid=%d\n",
- mPid, mUid);
- result.append(buffer);
- } else {
- Mutex::Autolock lock(mLock);
- for (int i = 0, n = mClients.size(); i < n; ++i) {
- sp<Client> c = mClients[i].promote();
- if (c != 0) c->dump(fd, args);
- clients.add(c);
- }
-
- result.append(" Files opened and/or mapped:\n");
- snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
- FILE *f = fopen(buffer, "r");
- if (f) {
- while (!feof(f)) {
- fgets(buffer, SIZE, f);
- if (strstr(buffer, " /storage/") ||
- strstr(buffer, " /system/sounds/") ||
- strstr(buffer, " /data/") ||
- strstr(buffer, " /system/media/")) {
- result.append(" ");
- result.append(buffer);
- }
- }
- fclose(f);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
- DIR *d = opendir(buffer);
- if (d) {
- struct dirent *ent;
- while((ent = readdir(d)) != NULL) {
- if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
- snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
- struct stat s;
- if (lstat(buffer, &s) == 0) {
- if ((s.st_mode & S_IFMT) == S_IFLNK) {
- char linkto[256];
- int len = readlink(buffer, linkto, sizeof(linkto));
- if(len > 0) {
- if(len > 255) {
- linkto[252] = '.';
- linkto[253] = '.';
- linkto[254] = '.';
- linkto[255] = 0;
- } else {
- linkto[len] = 0;
- }
- if (strstr(linkto, "/storage/") == linkto ||
- strstr(linkto, "/system/sounds/") == linkto ||
- strstr(linkto, "/data/") == linkto ||
- strstr(linkto, "/system/media/") == linkto) {
- result.append(" ");
- result.append(buffer);
- result.append(" -> ");
- result.append(linkto);
- result.append("\n");
- }
- }
- } else {
- result.append(" unexpected type for ");
- result.append(buffer);
- result.append("\n");
- }
- }
- }
- }
- closedir(d);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- gLooperRoster.dump(fd, args);
-
- bool dumpMem = false;
- bool unreachableMemory = false;
- for (size_t i = 0; i < args.size(); i++) {
- if (args[i] == String16("-m")) {
- dumpMem = true;
- } else if (args[i] == String16("--unreachable")) {
- unreachableMemory = true;
- }
- }
- if (dumpMem) {
- result.append("\nDumping memory:\n");
- std::string s = dumpMemoryAddresses(100 /* limit */);
- result.append(s.c_str(), s.size());
- }
- if (unreachableMemory) {
- result.append("\nDumping unreachable memory:\n");
- // TODO - should limit be an argument parameter?
- // TODO: enable GetUnreachableMemoryString if it's part of stable API
- //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
- //result.append(s.c_str(), s.size());
- }
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-void MediaPlayer2Manager::removeClient(const wp<Client>& client)
-{
- Mutex::Autolock lock(mLock);
- mClients.remove(client);
-}
-
-bool MediaPlayer2Manager::hasClient(wp<Client> client)
-{
- Mutex::Autolock lock(mLock);
- return mClients.indexOf(client) != NAME_NOT_FOUND;
-}
-
-MediaPlayer2Manager::Client::Client(
- pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid)
-{
- ALOGV("Client(%d) constructor", connId);
- mPid = pid;
- mConnId = connId;
- mClient = client;
- mLoop = false;
- mStatus = NO_INIT;
- mAudioSessionId = audioSessionId;
- mUid = uid;
- mAudioAttributes = NULL;
-
-#if CALLBACK_ANTAGONIZER
- ALOGD("create Antagonizer");
- mAntagonizer = new Antagonizer(notify, this);
-#endif
-}
-
-bool MediaPlayer2Manager::Client::init() {
- sp<MediaPlayer2Interface> p = new NuPlayer2Driver(mPid, mUid);
- status_t init_result = p->initCheck();
- if (init_result != NO_ERROR) {
- ALOGE("Failed to create player object, initCheck failed(%d)", init_result);
- return false;
- }
-
- p->setNotifyCallback(this, notify);
- mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
- mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
- mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
- p->setAudioSink(mAudioOutput);
-
- mPlayer = p;
- return true;
-}
-
-MediaPlayer2Manager::Client::~Client()
-{
- ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
- mAudioOutput.clear();
- wp<Client> client(this);
- disconnect();
- gMediaPlayer2Manager.removeClient(client);
- if (mAudioAttributes != NULL) {
- free(mAudioAttributes);
- }
- mAudioDeviceUpdatedListener.clear();
-}
-
-void MediaPlayer2Manager::Client::disconnect()
-{
- ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
- // grab local reference and clear main reference to prevent future
- // access to object
- sp<MediaPlayer2Interface> p;
- {
- Mutex::Autolock l(mLock);
- p = mPlayer;
- mClient.clear();
- mPlayer.clear();
- }
-
- // clear the notification to prevent callbacks to dead client
- // and reset the player. We assume the player will serialize
- // access to itself if necessary.
- if (p != 0) {
- p->setNotifyCallback(0, 0);
-#if CALLBACK_ANTAGONIZER
- ALOGD("kill Antagonizer");
- mAntagonizer->kill();
-#endif
- p->reset();
- }
-
- {
- Mutex::Autolock l(mLock);
- disconnectNativeWindow_l();
- }
-
- IPCThreadState::self()->flushCommands();
-}
-
-void MediaPlayer2Manager::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
- audio_io_handle_t audioIo,
- audio_port_handle_t deviceId) {
- sp<MediaPlayer2Interface> listener = mListener.promote();
- if (listener != NULL) {
- listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
- } else {
- ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
- }
-}
-
-status_t MediaPlayer2Manager::Client::setDataSource(
- const sp<DataSourceDesc> &dsd) {
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) {
- return NO_INIT;
- }
-
- if (dsd == NULL) {
- return BAD_VALUE;
- }
-
- status_t status = p->setDataSource(dsd);
- if (status != OK) {
- ALOGE("setDataSource error: %d", status);
- return status;
- }
-
- return status;
-}
-
-void MediaPlayer2Manager::Client::disconnectNativeWindow_l() {
- if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
- status_t err = native_window_api_disconnect(
- mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
- mConnectedWindow.clear();
-}
-
-status_t MediaPlayer2Manager::Client::setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("[%d] setVideoSurfaceTexture(%p)",
- mConnId,
- (nww == NULL ? NULL : nww->getANativeWindow()));
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- if (nww != NULL && nww->getANativeWindow() != NULL) {
- if (mConnectedWindow != NULL
- && mConnectedWindow->getANativeWindow() == nww->getANativeWindow()) {
- return OK;
- }
- status_t err = native_window_api_connect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGE("setVideoSurfaceTexture failed: %d", err);
- // Note that we must do the reset before disconnecting from the ANW.
- // Otherwise queue/dequeue calls could be made on the disconnected
- // ANW, which may result in errors.
- reset();
-
- Mutex::Autolock lock(mLock);
- disconnectNativeWindow_l();
-
- return err;
- }
- }
-
- // Note that we must set the player's new GraphicBufferProducer before
- // disconnecting the old one. Otherwise queue/dequeue calls could be made
- // on the disconnected ANW, which may result in errors.
- status_t err = p->setVideoSurfaceTexture(nww);
-
- mLock.lock();
- disconnectNativeWindow_l();
-
- if (err == OK) {
- mConnectedWindow = nww;
- mLock.unlock();
- } else if (nww != NULL) {
- mLock.unlock();
- status_t err = native_window_api_disconnect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
-
- return err;
-}
-
-status_t MediaPlayer2Manager::Client::invoke(const Parcel& request,
- Parcel *reply)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) return UNKNOWN_ERROR;
- return p->invoke(request, reply);
-}
-
-// This call doesn't need to access the native player.
-status_t MediaPlayer2Manager::Client::setMetadataFilter(const Parcel& filter)
-{
- status_t status;
- media::Metadata::Filter allow, drop;
-
- if (unmarshallFilter(filter, &allow, &status) &&
- unmarshallFilter(filter, &drop, &status)) {
- Mutex::Autolock lock(mLock);
-
- mMetadataAllow = allow;
- mMetadataDrop = drop;
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::Client::getMetadata(
- bool update_only, bool /*apply_filter*/, Parcel *reply)
-{
- sp<MediaPlayer2Interface> player = getPlayer();
- if (player == 0) return UNKNOWN_ERROR;
-
- status_t status;
- // Placeholder for the return code, updated by the caller.
- reply->writeInt32(-1);
-
- media::Metadata::Filter ids;
-
- // We don't block notifications while we fetch the data. We clear
- // mMetadataUpdated first so we don't lose notifications happening
- // during the rest of this call.
- {
- Mutex::Autolock lock(mLock);
- if (update_only) {
- ids = mMetadataUpdated;
- }
- mMetadataUpdated.clear();
- }
-
- media::Metadata metadata(reply);
-
- metadata.appendHeader();
- status = player->getMetadata(ids, reply);
-
- if (status != OK) {
- metadata.resetParcel();
- ALOGE("getMetadata failed %d", status);
- return status;
- }
-
- // FIXME: ement filtering on the result. Not critical since
- // filtering takes place on the update notifications already. This
- // would be when all the metadata are fetch and a filter is set.
-
- // Everything is fine, update the metadata length.
- metadata.updateLength();
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::setBufferingSettings(
- const BufferingSettings& buffering)
-{
- ALOGV("[%d] setBufferingSettings{%s}",
- mConnId, buffering.toString().string());
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setBufferingSettings(buffering);
-}
-
-status_t MediaPlayer2Manager::Client::getBufferingSettings(
- BufferingSettings* buffering /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- // TODO: create mPlayer on demand.
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getBufferingSettings(buffering);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getBufferingSettings{%s}",
- mConnId, buffering->toString().string());
- } else {
- ALOGE("[%d] getBufferingSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::prepareAsync()
-{
- ALOGV("[%d] prepareAsync", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->prepareAsync();
-#if CALLBACK_ANTAGONIZER
- ALOGD("start Antagonizer");
- if (ret == NO_ERROR) mAntagonizer->start();
-#endif
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::start()
-{
- ALOGV("[%d] start", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- p->setLooping(mLoop);
- return p->start();
-}
-
-status_t MediaPlayer2Manager::Client::stop()
-{
- ALOGV("[%d] stop", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->stop();
-}
-
-status_t MediaPlayer2Manager::Client::pause()
-{
- ALOGV("[%d] pause", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->pause();
-}
-
-status_t MediaPlayer2Manager::Client::isPlaying(bool* state)
-{
- *state = false;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- *state = p->isPlaying();
- ALOGV("[%d] isPlaying: %d", mConnId, *state);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
- ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setPlaybackSettings(rate);
-}
-
-status_t MediaPlayer2Manager::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getPlaybackSettings(rate);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
- } else {
- ALOGV("[%d] getPlaybackSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setSyncSettings(
- const AVSyncSettings& sync, float videoFpsHint)
-{
- ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
- mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setSyncSettings(sync, videoFpsHint);
-}
-
-status_t MediaPlayer2Manager::Client::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getSyncSettings(sync, videoFps);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getSyncSettings(%u, %u, %f, %f)",
- mConnId, sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
- } else {
- ALOGV("[%d] getSyncSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getCurrentPosition(int *msec)
-{
- ALOGV("getCurrentPosition");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getCurrentPosition(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getCurrentPosition = %d", mConnId, *msec);
- } else {
- ALOGE("getCurrentPosition returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getDuration(int *msec)
-{
- ALOGV("getDuration");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getDuration(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getDuration = %d", mConnId, *msec);
- } else {
- ALOGE("getDuration returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setNextPlayer(const sp<MediaPlayer2Engine>& player) {
- ALOGV("setNextPlayer");
- Mutex::Autolock l(mLock);
- sp<Client> c = static_cast<Client*>(player.get());
- if (c != NULL && !gMediaPlayer2Manager.hasClient(c)) {
- return BAD_VALUE;
- }
-
- mNextClient = c;
-
- if (c != NULL) {
- if (mAudioOutput != NULL) {
- mAudioOutput->setNextOutput(c->mAudioOutput);
- } else {
- ALOGE("no current audio output");
- }
-
- if ((mPlayer != NULL) && (mNextClient->getPlayer() != NULL)) {
- mPlayer->setNextPlayer(mNextClient->getPlayer());
- }
- }
-
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
- ALOGV("[%d] seekTo(%d, %d)", mConnId, msec, mode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->seekTo(msec, mode);
-}
-
-status_t MediaPlayer2Manager::Client::reset()
-{
- ALOGV("[%d] reset", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->reset();
-}
-
-status_t MediaPlayer2Manager::Client::notifyAt(int64_t mediaTimeUs)
-{
- ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->notifyAt(mediaTimeUs);
-}
-
-status_t MediaPlayer2Manager::Client::setAudioStreamType(audio_stream_type_t type)
-{
- ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
- // TODO: for hardware output, call player instead
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAudioAttributes_l(const Parcel &parcel)
-{
- if (mAudioAttributes != NULL) { free(mAudioAttributes); }
- mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAudioAttributes == NULL) {
- return NO_MEMORY;
- }
- unmarshallAudioAttributes(parcel, mAudioAttributes);
-
- ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
- mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
- mAudioAttributes->tags);
-
- if (mAudioOutput != 0) {
- mAudioOutput->setAudioAttributes(mAudioAttributes);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setLooping(int loop)
-{
- ALOGV("[%d] setLooping(%d)", mConnId, loop);
- mLoop = loop;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p != 0) return p->setLooping(loop);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setVolume(float leftVolume, float rightVolume)
-{
- ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
-
- // for hardware output, call player instead
- sp<MediaPlayer2Interface> p = getPlayer();
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
- }
-
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAuxEffectSendLevel(float level)
-{
- ALOGV("[%d] setAuxEffectSendLevel(%f)", mConnId, level);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->setAuxEffectSendLevel(level);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::attachAuxEffect(int effectId)
-{
- ALOGV("[%d] attachAuxEffect(%d)", mConnId, effectId);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->attachAuxEffect(effectId);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setParameter(int key, const Parcel &request) {
- ALOGV("[%d] setParameter(%d)", mConnId, key);
- switch (key) {
- case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
- {
- Mutex::Autolock l(mLock);
- return setAudioAttributes_l(request);
- }
- default:
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) { return UNKNOWN_ERROR; }
- return p->setParameter(key, request);
- }
-}
-
-status_t MediaPlayer2Manager::Client::getParameter(int key, Parcel *reply) {
- ALOGV("[%d] getParameter(%d)", mConnId, key);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->getParameter(key, reply);
-}
-
-void MediaPlayer2Manager::Client::notify(
- const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj)
-{
- sp<MediaPlayer2Engine> spListener = listener.promote();
- if (spListener == NULL) {
- return;
- }
- Client* client = static_cast<Client*>(spListener.get());
-
- sp<MediaPlayer2EngineClient> c;
- sp<Client> nextClient;
- status_t errStartNext = NO_ERROR;
- {
- Mutex::Autolock l(client->mLock);
- c = client->mClient;
- if (msg == MEDIA2_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
- nextClient = client->mNextClient;
-
- if (client->mAudioOutput != NULL)
- client->mAudioOutput->switchToNextOutput();
-
- errStartNext = nextClient->start();
- }
- }
-
- if (nextClient != NULL) {
- sp<MediaPlayer2EngineClient> nc;
- {
- Mutex::Autolock l(nextClient->mLock);
- nc = nextClient->mClient;
- }
- if (nc != NULL) {
- if (errStartNext == NO_ERROR) {
- nc->notify(srcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
- } else {
- nc->notify(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
- ALOGE("gapless:start playback for next track failed, err(%d)", errStartNext);
- }
- }
- }
-
- if (MEDIA2_INFO == msg &&
- MEDIA2_INFO_METADATA_UPDATE == ext1) {
- const media::Metadata::Type metadata_type = ext2;
-
- if(client->shouldDropMetadata(metadata_type)) {
- return;
- }
-
- // Update the list of metadata that have changed. getMetadata
- // also access mMetadataUpdated and clears it.
- client->addNewMetadataUpdate(metadata_type);
- }
-
- if (c != NULL) {
- ALOGV("[%d] notify (%p, %lld, %d, %d, %d)", client->mConnId, spListener.get(),
- (long long)srcId, msg, ext1, ext2);
- c->notify(srcId, msg, ext1, ext2, obj);
- }
-}
-
-
-bool MediaPlayer2Manager::Client::shouldDropMetadata(media::Metadata::Type code) const
-{
- Mutex::Autolock lock(mLock);
-
- if (findMetadata(mMetadataDrop, code)) {
- return true;
- }
-
- if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
- return false;
- } else {
- return true;
- }
-}
-
-
-void MediaPlayer2Manager::Client::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
- Mutex::Autolock lock(mLock);
- if (mMetadataUpdated.indexOf(metadata_type) < 0) {
- mMetadataUpdated.add(metadata_type);
- }
-}
-
-// Modular DRM
-status_t MediaPlayer2Manager::Client::prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId)
-{
- ALOGV("[%d] prepareDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->prepareDrm(uuid, drmSessionId);
- ALOGV("prepareDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::releaseDrm()
-{
- ALOGV("[%d] releaseDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->releaseDrm();
- ALOGV("releaseDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("[%d] setOutputDevice", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->setOutputDevice(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("[%d] getRoutedDeviceId", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->getRoutedDeviceId(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("[%d] enableAudioDeviceCallback, %d", mConnId, enabled);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->enableAudioDeviceCallback(enabled);
- }
- }
- return NO_INIT;
-}
-
-#if CALLBACK_ANTAGONIZER
-const int Antagonizer::interval = 10000; // 10 msecs
-
-Antagonizer::Antagonizer(
- MediaPlayer2Manager::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client) :
- mExit(false), mActive(false), mClient(client), mCb(cb)
-{
- createThread(callbackThread, this);
-}
-
-void Antagonizer::kill()
-{
- Mutex::Autolock _l(mLock);
- mActive = false;
- mExit = true;
- mCondition.wait(mLock);
-}
-
-int Antagonizer::callbackThread(void* user)
-{
- ALOGD("Antagonizer started");
- Antagonizer* p = reinterpret_cast<Antagonizer*>(user);
- while (!p->mExit) {
- if (p->mActive) {
- ALOGV("send event");
- p->mCb(p->mClient, 0, 0, 0);
- }
- usleep(interval);
- }
- Mutex::Autolock _l(p->mLock);
- p->mCondition.signal();
- ALOGD("Antagonizer stopped");
- return 0;
-}
-#endif
-
-#undef LOG_TAG
-#define LOG_TAG "AudioSink"
-MediaPlayer2Manager::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
- const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
- : mCallback(NULL),
- mCallbackCookie(NULL),
- mCallbackData(NULL),
- mStreamType(AUDIO_STREAM_MUSIC),
- mLeftVolume(1.0),
- mRightVolume(1.0),
- mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
- mSampleRateHz(0),
- mMsecsPerFrame(0),
- mFrameSize(0),
- mSessionId(sessionId),
- mUid(uid),
- mPid(pid),
- mSendLevel(0.0),
- mAuxEffectId(0),
- mFlags(AUDIO_OUTPUT_FLAG_NONE),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mDeviceCallbackEnabled(false),
- mDeviceCallback(deviceCallback)
-{
- ALOGV("AudioOutput(%d)", sessionId);
- if (attr != NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAttributes != NULL) {
- memcpy(mAttributes, attr, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attr);
- }
- } else {
- mAttributes = NULL;
- }
-
- setMinBufferCount();
-}
-
-MediaPlayer2Manager::AudioOutput::~AudioOutput()
-{
- close();
- free(mAttributes);
- delete mCallbackData;
-}
-
-//static
-void MediaPlayer2Manager::AudioOutput::setMinBufferCount()
-{
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.kernel.qemu", value, 0)) {
- mIsOnEmulator = true;
- mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
- }
-}
-
-// static
-bool MediaPlayer2Manager::AudioOutput::isOnEmulator()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mIsOnEmulator;
-}
-
-// static
-int MediaPlayer2Manager::AudioOutput::getMinBufferCount()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mMinBufferCount;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::bufferSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount() * mFrameSize;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::channelCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->channelCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mFrameSize;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::latency () const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->latency();
-}
-
-float MediaPlayer2Manager::AudioOutput::msecsPerFrame() const
-{
- Mutex::Autolock lock(mLock);
- return mMsecsPerFrame;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPosition(uint32_t *position) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getPosition(position);
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getTimestamp(AudioTimestamp &ts) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getTimestamp(ts);
-}
-
-// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
-// as it acquires locks and may query the audio driver.
-//
-// Some calls could conceivably retrieve extrapolated data instead of
-// accessing getTimestamp() or getPosition() every time a data buffer with
-// a media time is received.
-//
-// Calculate duration of played samples if played at normal rate (i.e., 1.0).
-int64_t MediaPlayer2Manager::AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0 || mSampleRateHz == 0) {
- return 0;
- }
-
- uint32_t numFramesPlayed;
- int64_t numFramesPlayedAtUs;
- AudioTimestamp ts;
-
- status_t res = mTrack->getTimestamp(ts);
- if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
- numFramesPlayed = ts.mPosition;
- numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
- //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
- numFramesPlayed = 0;
- numFramesPlayedAtUs = nowUs;
- //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
- // numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else { // case 3: transitory at new track or audio fast tracks.
- res = mTrack->getPosition(&numFramesPlayed);
- CHECK_EQ(res, (status_t)OK);
- numFramesPlayedAtUs = nowUs;
- numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
- //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- }
-
- // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
- // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
- int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
- + nowUs - numFramesPlayedAtUs;
- if (durationUs < 0) {
- // Occurs when numFramesPlayed position is very small and the following:
- // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
- // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
- // (2) In case 3, using getPosition and adding mAudioSink->latency() to
- // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
- //
- // Both of these are transitory conditions.
- ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
- durationUs = 0;
- }
- ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
- (long long)durationUs, (long long)nowUs,
- numFramesPlayed, (long long)numFramesPlayedAtUs);
- return durationUs;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- ExtendedTimestamp ets;
- status_t status = mTrack->getTimestamp(&ets);
- if (status == OK || status == WOULD_BLOCK) {
- *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setParameters(const String8& keyValuePairs)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->setParameters(keyValuePairs);
-}
-
-String8 MediaPlayer2Manager::AudioOutput::getParameters(const String8& keys)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return String8::empty();
- return mTrack->getParameters(keys);
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
- Mutex::Autolock lock(mLock);
- if (attributes == NULL) {
- free(mAttributes);
- mAttributes = NULL;
- } else {
- if (mAttributes == NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- }
- memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attributes);
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioStreamType(audio_stream_type_t streamType)
-{
- Mutex::Autolock lock(mLock);
- // do not allow direct stream type modification if attributes have been set
- if (mAttributes == NULL) {
- mStreamType = streamType;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::deleteRecycledTrack_l()
-{
- ALOGV("deleteRecycledTrack_l");
- if (mRecycledTrack != 0) {
-
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(NULL);
- mCallbackData->endTrackSwitch();
- }
-
- if ((mRecycledTrack->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) {
- int32_t msec = 0;
- if (!mRecycledTrack->stopped()) { // check if active
- (void)mRecycledTrack->pendingDuration(&msec);
- }
- mRecycledTrack->stop(); // ensure full data drain
- ALOGD("deleting recycled track, waiting for data drain (%d msec)", msec);
- if (msec > 0) {
- static const int32_t WAIT_LIMIT_MS = 3000;
- if (msec > WAIT_LIMIT_MS) {
- msec = WAIT_LIMIT_MS;
- }
- usleep(msec * 1000LL);
- }
- }
- // An offloaded track isn't flushed because the STREAM_END is reported
- // slightly prematurely to allow time for the gapless track switch
- // but this means that if we decide not to recycle the track there
- // could be a small amount of residual data still playing. We leave
- // AudioFlinger to drain the track.
-
- mRecycledTrack.clear();
- close_l();
- delete mCallbackData;
- mCallbackData = NULL;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::close_l()
-{
- mTrack.clear();
-}
-
-status_t MediaPlayer2Manager::AudioOutput::open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo,
- bool doNotReconnect,
- uint32_t suggestedFrameCount)
-{
- ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
- format, bufferCount, mSessionId, flags);
-
- // offloading is only supported in callback mode for now.
- // offloadInfo must be present if offload flag is set
- if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
- ((cb == NULL) || (offloadInfo == NULL))) {
- return BAD_VALUE;
- }
-
- // compute frame count for the AudioTrack internal buffer
- size_t frameCount;
- if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
- frameCount = 0; // AudioTrack will get frame count from AudioFlinger
- } else {
- // try to estimate the buffer processing fetch size from AudioFlinger.
- // framesPerBuffer is approximate and generally correct, except when it's not :-).
- uint32_t afSampleRate;
- size_t afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- const size_t framesPerBuffer =
- (unsigned long long)sampleRate * afFrameCount / afSampleRate;
-
- if (bufferCount == 0) {
- // use suggestedFrameCount
- bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
- }
- // Check argument bufferCount against the mininum buffer count
- if (bufferCount != 0 && bufferCount < mMinBufferCount) {
- ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
- bufferCount = mMinBufferCount;
- }
- // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
- // which will be the minimum size permitted.
- frameCount = bufferCount * framesPerBuffer;
- }
-
- if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
- channelMask = audio_channel_out_mask_from_count(channelCount);
- if (0 == channelMask) {
- ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
- return NO_INIT;
- }
- }
-
- Mutex::Autolock lock(mLock);
- mCallback = cb;
- mCallbackCookie = cookie;
-
- // Check whether we can recycle the track
- bool reuse = false;
- bool bothOffloaded = false;
-
- if (mRecycledTrack != 0) {
- // check whether we are switching between two offloaded tracks
- bothOffloaded = (flags & mRecycledTrack->getFlags()
- & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0;
-
- // check if the existing track can be reused as-is, or if a new track needs to be created.
- reuse = true;
-
- if ((mCallbackData == NULL && mCallback != NULL) ||
- (mCallbackData != NULL && mCallback == NULL)) {
- // recycled track uses callbacks but the caller wants to use writes, or vice versa
- ALOGV("can't chain callback and write");
- reuse = false;
- } else if ((mRecycledTrack->getSampleRate() != sampleRate) ||
- (mRecycledTrack->channelCount() != (uint32_t)channelCount) ) {
- ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch",
- mRecycledTrack->getSampleRate(), sampleRate,
- mRecycledTrack->channelCount(), channelCount);
- reuse = false;
- } else if (flags != mFlags) {
- ALOGV("output flags differ %08x/%08x", flags, mFlags);
- reuse = false;
- } else if (mRecycledTrack->format() != format) {
- reuse = false;
- }
- } else {
- ALOGV("no track available to recycle");
- }
-
- ALOGV_IF(bothOffloaded, "both tracks offloaded");
-
- // If we can't recycle and both tracks are offloaded
- // we must close the previous output before opening a new one
- if (bothOffloaded && !reuse) {
- ALOGV("both offloaded and not recycling");
- deleteRecycledTrack_l();
- }
-
- sp<AudioTrack> t;
- CallbackData *newcbd = NULL;
-
- // We don't attempt to create a new track if we are recycling an
- // offloaded track. But, if we are recycling a non-offloaded or we
- // are switching where one is offloaded and one isn't then we create
- // the new track in advance so that we can read additional stream info
-
- if (!(reuse && bothOffloaded)) {
- ALOGV("creating new AudioTrack");
-
- if (mCallback != NULL) {
- newcbd = new CallbackData(this);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- CallbackWrapper,
- newcbd,
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_CALLBACK,
- offloadInfo,
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- 1.0f, // default value for maxRequiredSpeed
- mSelectedDeviceId);
- } else {
- // TODO: Due to buffer memory concerns, we use a max target playback speed
- // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
- // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
- const float targetSpeed =
- std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
- ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
- "track target speed:%f clamped from playback speed:%f",
- targetSpeed, mPlaybackRate.mSpeed);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- NULL, // callback
- NULL, // user data
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_DEFAULT,
- NULL, // offload info
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- targetSpeed,
- mSelectedDeviceId);
- }
-
- if ((t == 0) || (t->initCheck() != NO_ERROR)) {
- ALOGE("Unable to create audio track");
- delete newcbd;
- // t goes out of scope, so reference count drops to zero
- return NO_INIT;
- } else {
- // successful AudioTrack initialization implies a legacy stream type was generated
- // from the audio attributes
- mStreamType = t->streamType();
- }
- }
-
- if (reuse) {
- CHECK(mRecycledTrack != NULL);
-
- if (!bothOffloaded) {
- if (mRecycledTrack->frameCount() != t->frameCount()) {
- ALOGV("framecount differs: %zu/%zu frames",
- mRecycledTrack->frameCount(), t->frameCount());
- reuse = false;
- }
- }
-
- if (reuse) {
- ALOGV("chaining to next output and recycling track");
- close_l();
- mTrack = mRecycledTrack;
- mRecycledTrack.clear();
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(this);
- }
- delete newcbd;
- return updateTrack();
- }
- }
-
- // we're not going to reuse the track, unblock and flush it
- // this was done earlier if both tracks are offloaded
- if (!bothOffloaded) {
- deleteRecycledTrack_l();
- }
-
- CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
-
- mCallbackData = newcbd;
- ALOGV("setVolume");
- t->setVolume(mLeftVolume, mRightVolume);
-
- mSampleRateHz = sampleRate;
- mFlags = flags;
- mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
- mFrameSize = t->frameSize();
- mTrack = t;
-
- return updateTrack();
-}
-
-status_t MediaPlayer2Manager::AudioOutput::updateTrack() {
- if (mTrack == NULL) {
- return NO_ERROR;
- }
-
- status_t res = NO_ERROR;
- // Note some output devices may give us a direct track even though we don't specify it.
- // Example: Line application b/17459982.
- if ((mTrack->getFlags()
- & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = mTrack->setPlaybackRate(mPlaybackRate);
- if (res == NO_ERROR) {
- mTrack->setAuxEffectSendLevel(mSendLevel);
- res = mTrack->attachAuxEffect(mAuxEffectId);
- }
- }
- mTrack->setOutputDevice(mSelectedDeviceId);
- if (mDeviceCallbackEnabled) {
- mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- }
- ALOGV("updateTrack() DONE status %d", res);
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::start()
-{
- ALOGV("start");
- Mutex::Autolock lock(mLock);
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch();
- }
- if (mTrack != 0) {
- mTrack->setVolume(mLeftVolume, mRightVolume);
- mTrack->setAuxEffectSendLevel(mSendLevel);
- status_t status = mTrack->start();
- return status;
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
- Mutex::Autolock lock(mLock);
- mNextOutput = nextOutput;
-}
-
-void MediaPlayer2Manager::AudioOutput::switchToNextOutput() {
- ALOGV("switchToNextOutput");
-
- // Try to acquire the callback lock before moving track (without incurring deadlock).
- const unsigned kMaxSwitchTries = 100;
- Mutex::Autolock lock(mLock);
- for (unsigned tries = 0;;) {
- if (mTrack == 0) {
- return;
- }
- if (mNextOutput != NULL && mNextOutput != this) {
- if (mCallbackData != NULL) {
- // two alternative approaches
-#if 1
- CallbackData *callbackData = mCallbackData;
- mLock.unlock();
- // proper acquisition sequence
- callbackData->lock();
- mLock.lock();
- // Caution: it is unlikely that someone deleted our callback or changed our target
- if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain correct lock sequence");
- callbackData->unlock();
- continue;
- }
- callbackData->mSwitching = true; // begin track switch
- callbackData->setOutput(NULL);
-#else
- // tryBeginTrackSwitch() returns false if the callback has the lock.
- if (!mCallbackData->tryBeginTrackSwitch()) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain callback lock");
- mLock.unlock();
- usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput
- mLock.lock();
- continue;
- }
-#endif
- }
-
- Mutex::Autolock nextLock(mNextOutput->mLock);
-
- // If the next output track is not NULL, then it has been
- // opened already for playback.
- // This is possible even without the next player being started,
- // for example, the next player could be prepared and seeked.
- //
- // Presuming it isn't advisable to force the track over.
- if (mNextOutput->mTrack == NULL) {
- ALOGD("Recycling track for gapless playback");
- delete mNextOutput->mCallbackData;
- mNextOutput->mCallbackData = mCallbackData;
- mNextOutput->mRecycledTrack = mTrack;
- mNextOutput->mSampleRateHz = mSampleRateHz;
- mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
- mNextOutput->mFlags = mFlags;
- mNextOutput->mFrameSize = mFrameSize;
- close_l();
- mCallbackData = NULL; // destruction handled by mNextOutput
- } else {
- ALOGW("Ignoring gapless playback because next player has already started");
- // remove track in case resource needed for future players.
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch(); // release lock for callbacks before close.
- }
- close_l();
- }
- }
- break;
- }
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::write(const void* buffer, size_t size, bool blocking)
-{
- Mutex::Autolock lock(mLock);
- LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
-
- //ALOGV("write(%p, %u)", buffer, size);
- if (mTrack != 0) {
- return mTrack->write(buffer, size, blocking);
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::stop()
-{
- ALOGV("stop");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->stop();
-}
-
-void MediaPlayer2Manager::AudioOutput::flush()
-{
- ALOGV("flush");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->flush();
-}
-
-void MediaPlayer2Manager::AudioOutput::pause()
-{
- ALOGV("pause");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->pause();
-}
-
-void MediaPlayer2Manager::AudioOutput::close()
-{
- ALOGV("close");
- sp<AudioTrack> track;
- {
- Mutex::Autolock lock(mLock);
- track = mTrack;
- close_l(); // clears mTrack
- }
- // destruction of the track occurs outside of mutex.
-}
-
-void MediaPlayer2Manager::AudioOutput::setVolume(float left, float right)
-{
- ALOGV("setVolume(%f, %f)", left, right);
- Mutex::Autolock lock(mLock);
- mLeftVolume = left;
- mRightVolume = right;
- if (mTrack != 0) {
- mTrack->setVolume(left, right);
- }
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate)
-{
- ALOGV("setPlaybackRate(%f %f %d %d)",
- rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- // remember rate so that we can set it when the track is opened
- mPlaybackRate = rate;
- return OK;
- }
- status_t res = mTrack->setPlaybackRate(rate);
- if (res != NO_ERROR) {
- return res;
- }
- // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
- CHECK_GT(rate.mSpeed, 0.f);
- mPlaybackRate = rate;
- if (mSampleRateHz != 0) {
- mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
- }
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
-{
- ALOGV("setPlaybackRate");
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return NO_INIT;
- }
- *rate = mTrack->getPlaybackRate();
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setAuxEffectSendLevel(float level)
-{
- ALOGV("setAuxEffectSendLevel(%f)", level);
- Mutex::Autolock lock(mLock);
- mSendLevel = level;
- if (mTrack != 0) {
- return mTrack->setAuxEffectSendLevel(level);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::attachAuxEffect(int effectId)
-{
- ALOGV("attachAuxEffect(%d)", effectId);
- Mutex::Autolock lock(mLock);
- mAuxEffectId = effectId;
- if (mTrack != 0) {
- return mTrack->attachAuxEffect(effectId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("setOutputDevice(%d)", deviceId);
- Mutex::Autolock lock(mLock);
- mSelectedDeviceId = deviceId;
- if (mTrack != 0) {
- return mTrack->setOutputDevice(deviceId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("getRoutedDeviceId");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mRoutedDeviceId = mTrack->getRoutedDeviceId();
- }
- *deviceId = mRoutedDeviceId;
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("enableAudioDeviceCallback, %d", enabled);
- Mutex::Autolock lock(mLock);
- mDeviceCallbackEnabled = enabled;
- if (mTrack != 0) {
- status_t status;
- if (enabled) {
- status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- } else {
- status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
- }
- return status;
- }
- return NO_ERROR;
-}
-
-// static
-void MediaPlayer2Manager::AudioOutput::CallbackWrapper(
- int event, void *cookie, void *info) {
- //ALOGV("callbackwrapper");
- CallbackData *data = (CallbackData*)cookie;
- // lock to ensure we aren't caught in the middle of a track switch.
- data->lock();
- AudioOutput *me = data->getOutput();
- AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
- if (me == NULL) {
- // no output set, likely because the track was scheduled to be reused
- // by another player, but the format turned out to be incompatible.
- data->unlock();
- if (buffer != NULL) {
- buffer->size = 0;
- }
- return;
- }
-
- switch(event) {
- case AudioTrack::EVENT_MORE_DATA: {
- size_t actualSize = (*me->mCallback)(
- me, buffer->raw, buffer->size, me->mCallbackCookie,
- CB_EVENT_FILL_BUFFER);
-
- // Log when no data is returned from the callback.
- // (1) We may have no data (especially with network streaming sources).
- // (2) We may have reached the EOS and the audio track is not stopped yet.
- // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
- // NuPlayer2Renderer will return zero when it doesn't have data (it doesn't block to fill).
- //
- // This is a benign busy-wait, with the next data request generated 10 ms or more later;
- // nevertheless for power reasons, we don't want to see too many of these.
-
- ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
-
- buffer->size = actualSize;
- } break;
-
- case AudioTrack::EVENT_STREAM_END:
- // currently only occurs for offloaded callbacks
- ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_STREAM_END);
- break;
-
- case AudioTrack::EVENT_NEW_IAUDIOTRACK :
- ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
- break;
-
- case AudioTrack::EVENT_UNDERRUN:
- // This occurs when there is no data available, typically
- // when there is a failure to supply data to the AudioTrack. It can also
- // occur in non-offloaded mode when the audio device comes out of standby.
- //
- // If an AudioTrack underruns it outputs silence. Since this happens suddenly
- // it may sound like an audible pop or glitch.
- //
- // The underrun event is sent once per track underrun; the condition is reset
- // when more data is sent to the AudioTrack.
- ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
- break;
-
- default:
- ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
- }
-
- data->unlock();
-}
-
-audio_session_t MediaPlayer2Manager::AudioOutput::getSessionId() const
-{
- Mutex::Autolock lock(mLock);
- return mSessionId;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::getSampleRate() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->getSampleRate();
-}
-
-int64_t MediaPlayer2Manager::AudioOutput::getBufferDurationInUs() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return 0;
- }
- int64_t duration;
- if (mTrack->getBufferDurationInUs(&duration) != OK) {
- return 0;
- }
- return duration;
-}
-
-} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.h b/media/libmediaplayer2/MediaPlayer2Manager.h
deleted file mode 100644
index 7e6f0de..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
-**
-** Copyright 2017, 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_MEDIAPLAYER2MANAGER_H
-#define ANDROID_MEDIAPLAYER2MANAGER_H
-
-#include <arpa/inet.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-#include <media/Metadata.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <system/audio.h>
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AudioPlaybackRate;
-class AudioTrack;
-struct AVSyncSettings;
-class DataSource;
-struct MediaHTTPService;
-class MediaPlayer2EngineClient;
-
-#define CALLBACK_ANTAGONIZER 0
-#if CALLBACK_ANTAGONIZER
-class Antagonizer {
-public:
- Antagonizer(
- MediaPlayer2Interface::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client);
- void start() { mActive = true; }
- void stop() { mActive = false; }
- void kill();
-private:
- static const int interval;
- Antagonizer();
- static int callbackThread(void *cookie);
- Mutex mLock;
- Condition mCondition;
- bool mExit;
- bool mActive;
- wp<MediaPlayer2Engine> mClient;
- MediaPlayer2Interface::NotifyCallback mCb;
-};
-#endif
-
-class MediaPlayer2Manager {
- class Client;
-
- class AudioOutput : public MediaPlayer2Interface::AudioSink
- {
- class CallbackData;
-
- public:
- AudioOutput(
- audio_session_t sessionId,
- uid_t uid,
- int pid,
- const audio_attributes_t * attr,
- const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
- virtual ~AudioOutput();
-
- virtual bool ready() const { return mTrack != 0; }
- virtual ssize_t bufferSize() const;
- virtual ssize_t frameCount() const;
- virtual ssize_t channelCount() const;
- virtual ssize_t frameSize() const;
- virtual uint32_t latency() const;
- virtual float msecsPerFrame() const;
- virtual status_t getPosition(uint32_t *position) const;
- virtual status_t getTimestamp(AudioTimestamp &ts) const;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const;
- virtual audio_session_t getSessionId() const;
- virtual uint32_t getSampleRate() const;
- virtual int64_t getBufferDurationInUs() const;
-
- virtual status_t open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
- const audio_offload_info_t *offloadInfo = NULL,
- bool doNotReconnect = false,
- uint32_t suggestedFrameCount = 0);
-
- virtual status_t start();
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
- virtual void stop();
- virtual void flush();
- virtual void pause();
- virtual void close();
- void setAudioStreamType(audio_stream_type_t streamType);
- virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
- void setAudioAttributes(const audio_attributes_t * attributes);
-
- void setVolume(float left, float right);
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
-
- status_t setAuxEffectSendLevel(float level);
- status_t attachAuxEffect(int effectId);
- virtual status_t dump(int fd, const Vector<String16>& args) const;
-
- static bool isOnEmulator();
- static int getMinBufferCount();
- void setNextOutput(const sp<AudioOutput>& nextOutput);
- void switchToNextOutput();
- virtual bool needsTrailingPadding() { return mNextOutput == NULL; }
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- static void setMinBufferCount();
- static void CallbackWrapper(
- int event, void *me, void *info);
- void deleteRecycledTrack_l();
- void close_l();
- status_t updateTrack();
-
- sp<AudioTrack> mTrack;
- sp<AudioTrack> mRecycledTrack;
- sp<AudioOutput> mNextOutput;
- AudioCallback mCallback;
- void * mCallbackCookie;
- CallbackData * mCallbackData;
- audio_stream_type_t mStreamType;
- audio_attributes_t * mAttributes;
- float mLeftVolume;
- float mRightVolume;
- AudioPlaybackRate mPlaybackRate;
- uint32_t mSampleRateHz; // sample rate of the content, as set in open()
- float mMsecsPerFrame;
- size_t mFrameSize;
- audio_session_t mSessionId;
- uid_t mUid;
- int mPid;
- float mSendLevel;
- int mAuxEffectId;
- audio_output_flags_t mFlags;
- audio_port_handle_t mSelectedDeviceId;
- audio_port_handle_t mRoutedDeviceId;
- bool mDeviceCallbackEnabled;
- wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
- mutable Mutex mLock;
-
- // static variables below not protected by mutex
- static bool mIsOnEmulator;
- static int mMinBufferCount; // 12 for emulator; otherwise 4
-
- // CallbackData is what is passed to the AudioTrack as the "user" data.
- // We need to be able to target this to a different Output on the fly,
- // so we can't use the Output itself for this.
- class CallbackData {
- friend AudioOutput;
- public:
- explicit CallbackData(AudioOutput *cookie) {
- mData = cookie;
- mSwitching = false;
- }
- AudioOutput * getOutput() const { return mData; }
- void setOutput(AudioOutput* newcookie) { mData = newcookie; }
- // lock/unlock are used by the callback before accessing the payload of this object
- void lock() const { mLock.lock(); }
- void unlock() const { mLock.unlock(); }
-
- // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
- // to the next sink.
-
- // tryBeginTrackSwitch() returns true only if it obtains the lock.
- bool tryBeginTrackSwitch() {
- LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
- if (mLock.tryLock() != OK) {
- return false;
- }
- mSwitching = true;
- return true;
- }
- void endTrackSwitch() {
- if (mSwitching) {
- mLock.unlock();
- }
- mSwitching = false;
- }
- private:
- AudioOutput * mData;
- mutable Mutex mLock; // a recursive mutex might make this unnecessary.
- bool mSwitching;
- DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
- };
-
- }; // AudioOutput
-
-
-public:
- MediaPlayer2Manager();
- virtual ~MediaPlayer2Manager();
-
- static MediaPlayer2Manager& get();
-
- // MediaPlayer2Manager interface
- virtual sp<MediaPlayer2Engine> create(const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- void removeClient(const wp<Client>& client);
- bool hasClient(wp<Client> client);
-
-private:
- class Client : public MediaPlayer2Engine {
- // MediaPlayer2Engine interface
- virtual void disconnect();
- virtual status_t setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww) override;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) override;
- virtual status_t prepareAsync();
- virtual status_t start();
- virtual status_t stop();
- virtual status_t pause();
- virtual status_t isPlaying(bool* state);
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
- virtual status_t setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
- virtual status_t getSyncSettings(AVSyncSettings* rate /* nonnull */,
- float* videoFps /* nonnull */);
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
- virtual status_t getCurrentPosition(int* msec);
- virtual status_t getDuration(int* msec);
- virtual status_t reset();
- virtual status_t notifyAt(int64_t mediaTimeUs);
- virtual status_t setAudioStreamType(audio_stream_type_t type);
- virtual status_t setLooping(int loop);
- virtual status_t setVolume(float leftVolume, float rightVolume);
- virtual status_t invoke(const Parcel& request, Parcel *reply);
- virtual status_t setMetadataFilter(const Parcel& filter);
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *reply);
- virtual status_t setAuxEffectSendLevel(float level);
- virtual status_t attachAuxEffect(int effectId);
- virtual status_t setParameter(int key, const Parcel &request);
- virtual status_t getParameter(int key, Parcel *reply);
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& player);
-
- virtual status_t setDataSource(const sp<DataSourceDesc> &dsd);
-
- static void notify(const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj);
-
- pid_t pid() const { return mPid; }
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- audio_session_t getAudioSessionId() { return mAudioSessionId; }
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
- virtual status_t releaseDrm();
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
- {
- public:
- AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener) {
- mListener = listener;
- }
- ~AudioDeviceUpdatedNotifier() {}
-
- virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId);
-
- private:
- wp<MediaPlayer2Interface> mListener;
- };
-
- friend class MediaPlayer2Manager;
- Client(pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid);
- Client();
- virtual ~Client();
- bool init();
-
- void deletePlayer();
-
- sp<MediaPlayer2Interface> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
-
-
-
- // @param type Of the metadata to be tested.
- // @return true if the metadata should be dropped according to
- // the filters.
- bool shouldDropMetadata(media::Metadata::Type type) const;
-
- // Add a new element to the set of metadata updated. Noop if
- // the element exists already.
- // @param type Of the metadata to be recorded.
- void addNewMetadataUpdate(media::Metadata::Type type);
-
- // Disconnect from the currently connected ANativeWindow.
- void disconnectNativeWindow_l();
-
- status_t setAudioAttributes_l(const Parcel &request);
-
- mutable Mutex mLock;
- sp<MediaPlayer2Interface> mPlayer;
- sp<MediaPlayer2EngineClient> mClient;
- sp<AudioOutput> mAudioOutput;
- pid_t mPid;
- status_t mStatus;
- bool mLoop;
- int32_t mConnId;
- audio_session_t mAudioSessionId;
- audio_attributes_t * mAudioAttributes;
- uid_t mUid;
- sp<ANativeWindowWrapper> mConnectedWindow;
- sp<Client> mNextClient;
-
- // Metadata filters.
- media::Metadata::Filter mMetadataAllow; // protected by mLock
- media::Metadata::Filter mMetadataDrop; // protected by mLock
-
- // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
- // notification we try to update mMetadataUpdated which is a
- // set: no duplicate.
- // getMetadata clears this set.
- media::Metadata::Filter mMetadataUpdated; // protected by mLock
-
- sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedListener;
-#if CALLBACK_ANTAGONIZER
- Antagonizer* mAntagonizer;
-#endif
- }; // Client
-
-// ----------------------------------------------------------------------------
-
- pid_t mPid;
- uid_t mUid;
-
- mutable Mutex mLock;
- SortedVector< wp<Client> > mClients;
- int32_t mNextConnId;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2MANAGER_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
new file mode 100644
index 0000000..5d5b8e4
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -0,0 +1,191 @@
+/*
+**
+** Copyright 2018, 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_MEDIAPLAYER2AUDIOOUTPUT_H
+#define ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
+
+#include <mediaplayer2/MediaPlayer2Interface.h>
+
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class AudioTrack;
+
+class MediaPlayer2AudioOutput : public MediaPlayer2Interface::AudioSink
+{
+ class CallbackData;
+
+public:
+ MediaPlayer2AudioOutput(audio_session_t sessionId,
+ uid_t uid,
+ int pid,
+ const audio_attributes_t * attr,
+ const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
+ virtual ~MediaPlayer2AudioOutput();
+
+ virtual bool ready() const {
+ return mTrack != 0;
+ }
+ virtual ssize_t bufferSize() const;
+ virtual ssize_t frameCount() const;
+ virtual ssize_t channelCount() const;
+ virtual ssize_t frameSize() const;
+ virtual uint32_t latency() const;
+ virtual float msecsPerFrame() const;
+ virtual status_t getPosition(uint32_t *position) const;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const;
+ virtual audio_session_t getSessionId() const;
+ virtual uint32_t getSampleRate() const;
+ virtual int64_t getBufferDurationInUs() const;
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL,
+ bool doNotReconnect = false,
+ uint32_t suggestedFrameCount = 0);
+
+ virtual status_t start();
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
+ virtual void stop();
+ virtual void flush();
+ virtual void pause();
+ virtual void close();
+ void setAudioStreamType(audio_stream_type_t streamType);
+ virtual audio_stream_type_t getAudioStreamType() const {
+ return mStreamType;
+ }
+ void setAudioAttributes(const audio_attributes_t * attributes);
+
+ void setVolume(float left, float right);
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
+
+ status_t setAuxEffectSendLevel(float level);
+ status_t attachAuxEffect(int effectId);
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+
+ static bool isOnEmulator();
+ static int getMinBufferCount();
+ virtual bool needsTrailingPadding() {
+ return true;
+ // TODO: return correct value.
+ //return mNextOutput == NULL;
+ }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ // AudioRouting
+ virtual status_t setOutputDevice(audio_port_handle_t deviceId);
+ virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+ virtual status_t enableAudioDeviceCallback(bool enabled);
+
+private:
+ static void setMinBufferCount();
+ static void CallbackWrapper(int event, void *me, void *info);
+ void deleteRecycledTrack_l();
+ void close_l();
+ status_t updateTrack_l();
+
+ sp<AudioTrack> mTrack;
+ AudioCallback mCallback;
+ void * mCallbackCookie;
+ CallbackData * mCallbackData;
+ audio_stream_type_t mStreamType;
+ audio_attributes_t * mAttributes;
+ float mLeftVolume;
+ float mRightVolume;
+ AudioPlaybackRate mPlaybackRate;
+ uint32_t mSampleRateHz; // sample rate of the content, as set in open()
+ float mMsecsPerFrame;
+ size_t mFrameSize;
+ audio_session_t mSessionId;
+ uid_t mUid;
+ int mPid;
+ float mSendLevel;
+ int mAuxEffectId;
+ audio_output_flags_t mFlags;
+ audio_port_handle_t mSelectedDeviceId;
+ audio_port_handle_t mRoutedDeviceId;
+ bool mDeviceCallbackEnabled;
+ wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ mutable Mutex mLock;
+
+ // static variables below not protected by mutex
+ static bool mIsOnEmulator;
+ static int mMinBufferCount; // 12 for emulator; otherwise 4
+
+ // CallbackData is what is passed to the AudioTrack as the "user" data.
+ // We need to be able to target this to a different Output on the fly,
+ // so we can't use the Output itself for this.
+ class CallbackData {
+ friend MediaPlayer2AudioOutput;
+ public:
+ explicit CallbackData(MediaPlayer2AudioOutput *cookie) {
+ mData = cookie;
+ mSwitching = false;
+ }
+ MediaPlayer2AudioOutput *getOutput() const {
+ return mData;
+ }
+ void setOutput(MediaPlayer2AudioOutput* newcookie) {
+ mData = newcookie;
+ }
+ // lock/unlock are used by the callback before accessing the payload of this object
+ void lock() const {
+ mLock.lock();
+ }
+ void unlock() const {
+ mLock.unlock();
+ }
+
+ // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
+ // to the next sink.
+
+ // tryBeginTrackSwitch() returns true only if it obtains the lock.
+ bool tryBeginTrackSwitch() {
+ LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
+ if (mLock.tryLock() != OK) {
+ return false;
+ }
+ mSwitching = true;
+ return true;
+ }
+ void endTrackSwitch() {
+ if (mSwitching) {
+ mLock.unlock();
+ }
+ mSwitching = false;
+ }
+
+ private:
+ MediaPlayer2AudioOutput *mData;
+ mutable Mutex mLock; // a recursive mutex might make this unnecessary.
+ bool mSwitching;
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
+ };
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
deleted file mode 100644
index 2d1a24b..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2017 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_MEDIAPLAYER2ENGINE_H
-#define ANDROID_MEDIAPLAYER2ENGINE_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-#include <utils/KeyedVector.h>
-#include <system/audio.h>
-
-#include <media/MediaSource.h>
-
-// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
-// global, and not in android::
-struct sockaddr_in;
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AVSyncSettings;
-struct AudioPlaybackRate;
-struct BufferingSettings;
-class DataSource;
-struct DataSourceDesc;
-struct IStreamSource;
-struct MediaHTTPService;
-class Parcel;
-
-typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
-
-class MediaPlayer2Engine: public RefBase
-{
-public:
- virtual void disconnect() = 0;
-
- virtual status_t setDataSource(const sp<DataSourceDesc>& source) = 0;
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) = 0;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual status_t isPlaying(bool* state) = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) = 0;
- virtual status_t getSyncSettings(AVSyncSettings* sync /* nonnull */,
- float* videoFps /* nonnull */) = 0;
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int* msec) = 0;
- virtual status_t getDuration(int* msec) = 0;
- virtual status_t notifyAt(int64_t mediaTimeUs) = 0;
- virtual status_t reset() = 0;
- virtual status_t setAudioStreamType(audio_stream_type_t type) = 0;
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
- virtual status_t setAuxEffectSendLevel(float level) = 0;
- virtual status_t attachAuxEffect(int effectId) = 0;
- virtual status_t setParameter(int key, const Parcel& request) = 0;
- virtual status_t getParameter(int key, Parcel* reply) = 0;
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& next) = 0;
-
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId) = 0;
- virtual status_t releaseDrm() = 0;
-
- // Invoke a generic method on the player by using opaque parcels
- // for the request and reply.
- // @param request Parcel that must start with the media player
- // interface token.
- // @param[out] reply Parcel to hold the reply data. Cannot be null.
- // @return OK if the invocation was made successfully.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
-
- // Set a new metadata filter.
- // @param filter A set of allow and drop rules serialized in a Parcel.
- // @return OK if the invocation was made successfully.
- virtual status_t setMetadataFilter(const Parcel& filter) = 0;
-
- // Retrieve a set of metadata.
- // @param update_only Include only the metadata that have changed
- // since the last invocation of getMetadata.
- // The set is built using the unfiltered
- // notifications the native player sent to the
- // MediaPlayer2Manager during that period of
- // time. If false, all the metadatas are considered.
- // @param apply_filter If true, once the metadata set has been built based
- // on the value update_only, the current filter is
- // applied.
- // @param[out] metadata On exit contains a set (possibly empty) of metadata.
- // Valid only if the call returned OK.
- // @return OK if the invocation was made successfully.
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *metadata) = 0;
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId) = 0;
- virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
- virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINE_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
deleted file mode 100644
index 0b066aa..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017 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_MEDIAPLAYER2ENGINECLIENT_H
-#define ANDROID_MEDIAPLAYER2ENGINECLIENT_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class MediaPlayer2EngineClient: public RefBase
-{
-public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINECLIENT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index b1cdf96..02bf891 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -21,18 +21,17 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/RefBase.h>
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
-#include <media/AVSyncSettings.h>
#include <media/BufferingSettings.h>
#include <media/Metadata.h>
#include <media/stagefright/foundation/AHandler.h>
-#include <mediaplayer2/mediaplayer2.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
// global, and not in android::
@@ -40,14 +39,10 @@
namespace android {
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
class Parcel;
struct ANativeWindowWrapper;
-template<typename T> class SortedVector;
-
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -58,14 +53,14 @@
// duration below which we do not allow deep audio buffering
#define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
-// abstract base class - use MediaPlayer2Interface
-class MediaPlayer2Interface : public AHandler
+class MediaPlayer2InterfaceListener: public RefBase
{
public:
- // callback mechanism for passing messages to MediaPlayer2 object
- typedef void (*NotifyCallback)(const wp<MediaPlayer2Engine> &listener,
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj);
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+};
+class MediaPlayer2Interface : public AHandler {
+public:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
@@ -79,29 +74,28 @@
// Callback returns the number of bytes actually written to the buffer.
typedef size_t (*AudioCallback)(
- AudioSink *audioSink, void *buffer, size_t size, void *cookie,
- cb_event_t event);
+ AudioSink *audioSink, void *buffer, size_t size, void *cookie, cb_event_t event);
- virtual ~AudioSink() {}
- virtual bool ready() const = 0; // audio output is open and ready
- virtual ssize_t bufferSize() const = 0;
- virtual ssize_t frameCount() const = 0;
- virtual ssize_t channelCount() const = 0;
- virtual ssize_t frameSize() const = 0;
- virtual uint32_t latency() const = 0;
- virtual float msecsPerFrame() const = 0;
- virtual status_t getPosition(uint32_t *position) const = 0;
- virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
+ virtual ~AudioSink() {}
+ virtual bool ready() const = 0; // audio output is open and ready
+ virtual ssize_t bufferSize() const = 0;
+ virtual ssize_t frameCount() const = 0;
+ virtual ssize_t channelCount() const = 0;
+ virtual ssize_t frameSize() const = 0;
+ virtual uint32_t latency() const = 0;
+ virtual float msecsPerFrame() const = 0;
+ virtual status_t getPosition(uint32_t *position) const = 0;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual audio_session_t getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
- virtual uint32_t getSampleRate() const = 0;
- virtual int64_t getBufferDurationInUs() const = 0;
+ virtual uint32_t getSampleRate() const = 0;
+ virtual int64_t getBufferDurationInUs() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data.
- virtual status_t open(
+ virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
@@ -112,7 +106,7 @@
bool doNotReconnect = false,
uint32_t suggestedFrameCount = 0) = 0;
- virtual status_t start() = 0;
+ virtual status_t start() = 0;
/* Input parameter |size| is in byte units stored in |buffer|.
* Data is copied over and actual number of bytes written (>= 0)
@@ -124,19 +118,25 @@
* buffer, unless an error occurs or the copy operation is
* prematurely stopped.
*/
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
- virtual void stop() = 0;
- virtual void flush() = 0;
- virtual void pause() = 0;
- virtual void close() = 0;
+ virtual void stop() = 0;
+ virtual void flush() = 0;
+ virtual void pause() = 0;
+ virtual void close() = 0;
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual bool needsTrailingPadding() { return true; }
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
+ virtual bool needsTrailingPadding() {
+ return true;
+ }
- virtual status_t setParameters(const String8& /* keyValuePairs */) { return NO_ERROR; }
- virtual String8 getParameters(const String8& /* keys */) { return String8::empty(); }
+ virtual status_t setParameters(const String8& /* keyValuePairs */) {
+ return NO_ERROR;
+ }
+ virtual String8 getParameters(const String8& /* keys */) {
+ return String8::empty();
+ }
// AudioRouting
virtual status_t setOutputDevice(audio_port_handle_t deviceId);
@@ -144,46 +144,48 @@
virtual status_t enableAudioDeviceCallback(bool enabled);
};
- MediaPlayer2Interface() : mClient(0), mNotify(0) { }
- virtual ~MediaPlayer2Interface() { }
- virtual status_t initCheck() = 0;
+ MediaPlayer2Interface() : mListener(NULL) { }
+ virtual ~MediaPlayer2Interface() { }
+ virtual status_t initCheck() = 0;
- virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
-
- virtual status_t setDataSource(const sp<DataSourceDesc>& /* dsd */) {
- return INVALID_OPERATION;
+ virtual void setAudioSink(const sp<AudioSink>& audioSink) {
+ mAudioSink = audioSink;
}
- // pass the buffered native window to the media player service
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
+ virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) {
+ virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) = 0;
+
+ virtual status_t playNextDataSource(int64_t srcId) = 0;
+
+ // pass the buffered native window to the media player service
+ virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
+
+ virtual status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
*buffering = BufferingSettings();
return OK;
}
- virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
+ virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
return OK;
}
- virtual status_t prepare() = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual bool isPlaying() = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
+ virtual status_t prepareAsync() = 0;
+ virtual status_t start() = 0;
+ virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+ virtual bool isPlaying() = 0;
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
// by default, players only support setting rate to the default
if (!isAudioPlaybackRateEqual(rate, AUDIO_PLAYBACK_RATE_DEFAULT)) {
return BAD_VALUE;
}
return OK;
}
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
*rate = AUDIO_PLAYBACK_RATE_DEFAULT;
return OK;
}
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
+ virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
// By default, players only support setting sync source to default; all other sync
// settings are ignored. There is no requirement for getters to return set values.
if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
@@ -191,27 +193,23 @@
}
return OK;
}
- virtual status_t getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
+ virtual status_t getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
*sync = AVSyncSettings();
*videoFps = -1.f;
return OK;
}
- virtual status_t seekTo(
- int msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int *msec) = 0;
- virtual status_t getDuration(int *msec) = 0;
- virtual status_t reset() = 0;
- virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
+ virtual status_t seekTo(
+ int64_t msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
+ virtual status_t getCurrentPosition(int64_t *msec) = 0;
+ virtual status_t getDuration(int64_t *msec) = 0;
+ virtual status_t reset() = 0;
+ virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
return INVALID_OPERATION;
}
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setParameter(int key, const Parcel &request) = 0;
- virtual status_t getParameter(int key, Parcel *reply) = 0;
-
- virtual status_t setNextPlayer(const sp<MediaPlayer2Interface>& /* next */) {
- return OK;
- }
+ virtual status_t setLooping(int loop) = 0;
+ virtual status_t setParameter(int key, const Parcel &request) = 0;
+ virtual status_t getParameter(int key, Parcel *reply) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
@@ -220,7 +218,7 @@
// data sent by the java layer.
// @param[out] reply Parcel to hold the reply data. Cannot be null.
// @return OK if the call was successful.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+ virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
// The Client in the MetadataPlayerService calls this method on
// the native player to retrieve all or a subset of metadata.
@@ -229,28 +227,26 @@
// the known metadata should be returned.
// @param[inout] records Parcel where the player appends its metadata.
// @return OK if the call was successful.
- virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
- Parcel* /* records */) {
+ virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
+ Parcel* /* records */) {
return INVALID_OPERATION;
};
- void setNotifyCallback(
- const wp<MediaPlayer2Engine> &client, NotifyCallback notifyFunc) {
- Mutex::Autolock autoLock(mNotifyLock);
- mClient = client; mNotify = notifyFunc;
+ void setListener(const sp<MediaPlayer2InterfaceListener> &listener) {
+ Mutex::Autolock autoLock(mListenerLock);
+ mListener = listener;
}
- void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0,
- const Parcel *obj=NULL) {
- NotifyCallback notifyCB;
- wp<MediaPlayer2Engine> client;
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+ sp<MediaPlayer2InterfaceListener> listener;
{
- Mutex::Autolock autoLock(mNotifyLock);
- notifyCB = mNotify;
- client = mClient;
+ Mutex::Autolock autoLock(mListenerLock);
+ listener = mListener;
}
- if (notifyCB) notifyCB(client, srcId, msg, ext1, ext2, obj);
+ if (listener) {
+ listener->notify(srcId, msg, ext1, ext2, obj);
+ }
}
virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
@@ -260,7 +256,8 @@
virtual void onMessageReceived(const sp<AMessage> & /* msg */) override { }
// Modular DRM
- virtual status_t prepareDrm(const uint8_t /* uuid */[16], const Vector<uint8_t>& /* drmSessionId */) {
+ virtual status_t prepareDrm(const uint8_t /* uuid */[16],
+ const Vector<uint8_t>& /* drmSessionId */) {
return INVALID_OPERATION;
}
virtual status_t releaseDrm() {
@@ -268,14 +265,11 @@
}
protected:
- sp<AudioSink> mAudioSink;
+ sp<AudioSink> mAudioSink;
private:
- friend class MediaPlayer2Manager;
-
- Mutex mNotifyLock;
- wp<MediaPlayer2Engine> mClient;
- NotifyCallback mNotify;
+ Mutex mListenerLock;
+ sp<MediaPlayer2InterfaceListener> mListener;
};
}; // namespace android
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
new file mode 100644
index 0000000..260c7ed
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2018 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_MEDIAPLAYER2_TYPES_H
+#define ANDROID_MEDIAPLAYER2_TYPES_H
+
+#include <media/mediaplayer_common.h>
+
+#include <media/MediaSource.h>
+
+namespace android {
+
+typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
+
+enum media2_event_type {
+ MEDIA2_NOP = 0, // interface test message
+ MEDIA2_PREPARED = 1,
+ MEDIA2_PLAYBACK_COMPLETE = 2,
+ MEDIA2_BUFFERING_UPDATE = 3,
+ MEDIA2_SEEK_COMPLETE = 4,
+ MEDIA2_SET_VIDEO_SIZE = 5,
+ MEDIA2_STARTED = 6,
+ MEDIA2_PAUSED = 7,
+ MEDIA2_STOPPED = 8,
+ MEDIA2_SKIPPED = 9,
+ MEDIA2_NOTIFY_TIME = 98,
+ MEDIA2_TIMED_TEXT = 99,
+ MEDIA2_ERROR = 100,
+ MEDIA2_INFO = 200,
+ MEDIA2_SUBTITLE_DATA = 201,
+ MEDIA2_META_DATA = 202,
+ MEDIA2_DRM_INFO = 210,
+ MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
+};
+
+// Generic error codes for the media player framework. Errors are fatal, the
+// playback must abort.
+//
+// Errors are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below.
+// In this situation, 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_ERROR.
+// 'ext1' should be a value from the enum media2_error_type.
+// 'ext2' contains an implementation dependant error code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
+// 2xx: Media errors (e.g Codec not supported). There is a problem with the
+// media itself.
+// 3xx: Runtime errors. Some extraordinary condition arose making the playback
+// impossible.
+//
+enum media2_error_type {
+ // 0xx
+ MEDIA2_ERROR_UNKNOWN = 1,
+ // 1xx
+ // MEDIA2_ERROR_SERVER_DIED = 100,
+ // 2xx
+ MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ // 3xx
+ MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
+};
+
+
+// Info and warning codes for the media player framework. These are non fatal,
+// the playback is going on but there might be some user visible issues.
+//
+// Info and warning messages are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below. In this situation,
+// 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_INFO.
+// 'ext1' should be a value from the enum media2_info_type.
+// 'ext2' contains an implementation dependant info code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 7xx: Android Player info/warning (e.g player lagging behind.)
+// 8xx: Media info/warning (e.g media badly interleaved.)
+//
+enum media2_info_type {
+ // 0xx
+ MEDIA2_INFO_UNKNOWN = 1,
+ // The player was started because it was used as the next player for another
+ // player, which just completed playback
+ MEDIA2_INFO_STARTED_AS_NEXT = 2,
+ // The player just pushed the very first video frame for rendering
+ MEDIA2_INFO_VIDEO_RENDERING_START = 3,
+ // The player just pushed the very first audio frame for rendering
+ MEDIA2_INFO_AUDIO_RENDERING_START = 4,
+ // The player just completed the playback of this data source
+ MEDIA2_INFO_PLAYBACK_COMPLETE = 5,
+ // The player just completed the playback of the full play list
+ MEDIA2_INFO_PLAYLIST_END = 6,
+
+ //1xx
+ // The player just prepared a data source.
+ MEDIA2_INFO_PREPARED = 100,
+ // The player just completed a call play().
+ MEDIA2_INFO_COMPLETE_CALL_PLAY = 101,
+ // The player just completed a call pause().
+ MEDIA2_INFO_COMPLETE_CALL_PAUSE = 102,
+ // The player just completed a call seekTo.
+ MEDIA2_INFO_COMPLETE_CALL_SEEK = 103,
+
+ // 7xx
+ // The video is too complex for the decoder: it can't decode frames fast
+ // enough. Possibly only the audio plays fine at this stage.
+ MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
+ // MediaPlayer2 is temporarily pausing playback internally in order to
+ // buffer more data.
+ MEDIA2_INFO_BUFFERING_START = 701,
+ // MediaPlayer2 is resuming playback after filling buffers.
+ MEDIA2_INFO_BUFFERING_END = 702,
+ // Bandwidth in recent past
+ MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
+
+ // 8xx
+ // Bad interleaving means that a media has been improperly interleaved or not
+ // interleaved at all, e.g has all the video samples first then all the audio
+ // ones. Video is playing but a lot of disk seek may be happening.
+ MEDIA2_INFO_BAD_INTERLEAVING = 800,
+ // The media is not seekable (e.g live stream).
+ MEDIA2_INFO_NOT_SEEKABLE = 801,
+ // New media metadata is available.
+ MEDIA2_INFO_METADATA_UPDATE = 802,
+ // Audio can not be played.
+ MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
+ // Video can not be played.
+ MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
+
+ //9xx
+ MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
+};
+
+enum media_player2_states {
+ MEDIA_PLAYER2_STATE_ERROR = 0,
+ MEDIA_PLAYER2_IDLE = 1 << 0,
+ MEDIA_PLAYER2_INITIALIZED = 1 << 1,
+ MEDIA_PLAYER2_PREPARING = 1 << 2,
+ MEDIA_PLAYER2_PREPARED = 1 << 3,
+ MEDIA_PLAYER2_STARTED = 1 << 4,
+ MEDIA_PLAYER2_PAUSED = 1 << 5,
+ MEDIA_PLAYER2_STOPPED = 1 << 6,
+ MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
+};
+
+// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
+// The same enum space is used for both set and get, in case there are future keys that
+// can be both set and get. But as of now, all parameters are either set only or get only.
+enum media2_parameter_keys {
+ // Streaming/buffering parameters
+ MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
+
+ // Return a Parcel containing a single int, which is the channel count of the
+ // audio track, or zero for error (e.g. no audio track) or unknown.
+ MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
+
+ // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
+ // values used for rewinding or reverse playback.
+ MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
+
+ // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
+ MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
+};
+
+// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
+enum media_player2_invoke_ids {
+ MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+ MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
+ MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
+ MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+ MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2_TYPES_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index e9d6f84..3433cb1 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -17,179 +17,27 @@
#ifndef ANDROID_MEDIAPLAYER2_H
#define ANDROID_MEDIAPLAYER2_H
-#include <media/mediaplayer_common.h>
-
-#include <arpa/inet.h>
-
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/BufferingSettings.h>
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
+#include <media/Metadata.h>
+#include <media/mediaplayer_common.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/ThreadDefs.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <system/audio-base.h>
namespace android {
-struct AVSyncSettings;
struct ANativeWindowWrapper;
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
+class MediaPlayer2AudioOutput;
-enum media2_event_type {
- MEDIA2_NOP = 0, // interface test message
- MEDIA2_PREPARED = 1,
- MEDIA2_PLAYBACK_COMPLETE = 2,
- MEDIA2_BUFFERING_UPDATE = 3,
- MEDIA2_SEEK_COMPLETE = 4,
- MEDIA2_SET_VIDEO_SIZE = 5,
- MEDIA2_STARTED = 6,
- MEDIA2_PAUSED = 7,
- MEDIA2_STOPPED = 8,
- MEDIA2_SKIPPED = 9,
- MEDIA2_NOTIFY_TIME = 98,
- MEDIA2_TIMED_TEXT = 99,
- MEDIA2_ERROR = 100,
- MEDIA2_INFO = 200,
- MEDIA2_SUBTITLE_DATA = 201,
- MEDIA2_META_DATA = 202,
- MEDIA2_DRM_INFO = 210,
- MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
-};
-
-// Generic error codes for the media player framework. Errors are fatal, the
-// playback must abort.
-//
-// Errors are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below.
-// In this situation, 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_ERROR.
-// 'ext1' should be a value from the enum media2_error_type.
-// 'ext2' contains an implementation dependant error code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
-// 2xx: Media errors (e.g Codec not supported). There is a problem with the
-// media itself.
-// 3xx: Runtime errors. Some extraordinary condition arose making the playback
-// impossible.
-//
-enum media2_error_type {
- // 0xx
- MEDIA2_ERROR_UNKNOWN = 1,
- // 1xx
- // MEDIA2_ERROR_SERVER_DIED = 100,
- // 2xx
- MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
- // 3xx
- MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
-};
-
-
-// Info and warning codes for the media player framework. These are non fatal,
-// the playback is going on but there might be some user visible issues.
-//
-// Info and warning messages are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below. In this situation,
-// 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_INFO.
-// 'ext1' should be a value from the enum media2_info_type.
-// 'ext2' contains an implementation dependant info code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 7xx: Android Player info/warning (e.g player lagging behind.)
-// 8xx: Media info/warning (e.g media badly interleaved.)
-//
-enum media2_info_type {
- // 0xx
- MEDIA2_INFO_UNKNOWN = 1,
- // The player was started because it was used as the next player for another
- // player, which just completed playback
- MEDIA2_INFO_STARTED_AS_NEXT = 2,
- // The player just pushed the very first video frame for rendering
- MEDIA2_INFO_RENDERING_START = 3,
- // 7xx
- // The video is too complex for the decoder: it can't decode frames fast
- // enough. Possibly only the audio plays fine at this stage.
- MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
- // MediaPlayer2 is temporarily pausing playback internally in order to
- // buffer more data.
- MEDIA2_INFO_BUFFERING_START = 701,
- // MediaPlayer2 is resuming playback after filling buffers.
- MEDIA2_INFO_BUFFERING_END = 702,
- // Bandwidth in recent past
- MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
-
- // 8xx
- // Bad interleaving means that a media has been improperly interleaved or not
- // interleaved at all, e.g has all the video samples first then all the audio
- // ones. Video is playing but a lot of disk seek may be happening.
- MEDIA2_INFO_BAD_INTERLEAVING = 800,
- // The media is not seekable (e.g live stream).
- MEDIA2_INFO_NOT_SEEKABLE = 801,
- // New media metadata is available.
- MEDIA2_INFO_METADATA_UPDATE = 802,
- // Audio can not be played.
- MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
- // Video can not be played.
- MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
-
- //9xx
- MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
-};
-
-
-
-enum media_player2_states {
- MEDIA_PLAYER2_STATE_ERROR = 0,
- MEDIA_PLAYER2_IDLE = 1 << 0,
- MEDIA_PLAYER2_INITIALIZED = 1 << 1,
- MEDIA_PLAYER2_PREPARING = 1 << 2,
- MEDIA_PLAYER2_PREPARED = 1 << 3,
- MEDIA_PLAYER2_STARTED = 1 << 4,
- MEDIA_PLAYER2_PAUSED = 1 << 5,
- MEDIA_PLAYER2_STOPPED = 1 << 6,
- MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
-};
-
-// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
-// The same enum space is used for both set and get, in case there are future keys that
-// can be both set and get. But as of now, all parameters are either set only or get only.
-enum media2_parameter_keys {
- // Streaming/buffering parameters
- MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
-
- // Return a Parcel containing a single int, which is the channel count of the
- // audio track, or zero for error (e.g. no audio track) or unknown.
- MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
-
- // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
- // values used for rewinding or reverse playback.
- MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
-
- // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
- MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
-};
-
-// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
-enum media_player2_invoke_ids {
- MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
- MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
- MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
- MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
- MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
-};
-
-// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayer2Listener: virtual public RefBase
{
@@ -197,20 +45,24 @@
virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
-class MediaPlayer2 : public MediaPlayer2EngineClient
+class MediaPlayer2 : public MediaPlayer2InterfaceListener
{
public:
- MediaPlayer2();
~MediaPlayer2();
+
+ static sp<MediaPlayer2> Create();
+ static status_t DumpAll(int fd, const Vector<String16>& args);
+
void disconnect();
status_t getSrcId(int64_t *srcId);
status_t setDataSource(const sp<DataSourceDesc> &dsd);
+ status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd);
+ status_t playNextDataSource(int64_t srcId);
status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww);
status_t setListener(const sp<MediaPlayer2Listener>& listener);
status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */);
status_t setBufferingSettings(const BufferingSettings& buffering);
- status_t prepare();
status_t prepareAsync();
status_t start();
status_t stop();
@@ -225,11 +77,11 @@
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(
- int msec,
+ int64_t msec,
MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
status_t notifyAt(int64_t mediaTimeUs);
- status_t getCurrentPosition(int *msec);
- status_t getDuration(int *msec);
+ status_t getCurrentPosition(int64_t *msec);
+ status_t getDuration(int64_t *msec);
status_t reset();
status_t setAudioStreamType(audio_stream_type_t type);
status_t getAudioStreamType(audio_stream_type_t *type);
@@ -247,7 +99,6 @@
status_t attachAuxEffect(int effectId);
status_t setParameter(int key, const Parcel& request);
status_t getParameter(int key, Parcel* reply);
- status_t setNextMediaPlayer(const sp<MediaPlayer2>& player);
// Modular DRM
status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
@@ -257,30 +108,48 @@
audio_port_handle_t getRoutedDeviceId();
status_t enableAudioDeviceCallback(bool enabled);
-private:
- void clear_l();
- status_t seekTo_l(int msec, MediaPlayer2SeekMode mode);
- status_t prepareAsync_l();
- status_t getDuration_l(int *msec);
- status_t attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId);
- status_t reset_l();
- status_t checkStateForKeySet_l(int key);
+ status_t dump(int fd, const Vector<String16>& args);
- sp<MediaPlayer2Engine> mPlayer;
+private:
+ MediaPlayer2();
+ bool init();
+
+ // @param type Of the metadata to be tested.
+ // @return true if the metadata should be dropped according to
+ // the filters.
+ bool shouldDropMetadata(media::Metadata::Type type) const;
+
+ // Add a new element to the set of metadata updated. Noop if
+ // the element exists already.
+ // @param type Of the metadata to be recorded.
+ void addNewMetadataUpdate(media::Metadata::Type type);
+
+ // Disconnect from the currently connected ANativeWindow.
+ void disconnectNativeWindow_l();
+
+ status_t setAudioAttributes_l(const Parcel &request);
+
+ void clear_l();
+ status_t seekTo_l(int64_t msec, MediaPlayer2SeekMode mode);
+ status_t prepareAsync_l();
+ status_t getDuration_l(int64_t *msec);
+ status_t reset_l();
+ status_t checkStateForKeySet_l(int key);
+
+ pid_t mPid;
+ uid_t mUid;
+ sp<MediaPlayer2Interface> mPlayer;
+ sp<MediaPlayer2AudioOutput> mAudioOutput;
int64_t mSrcId;
thread_id_t mLockThreadId;
- Mutex mLock;
+ mutable Mutex mLock;
Mutex mNotifyLock;
- Condition mSignal;
sp<MediaPlayer2Listener> mListener;
- void* mCookie;
media_player2_states mCurrentState;
- int mCurrentPosition;
+ int64_t mCurrentPosition;
MediaPlayer2SeekMode mCurrentSeekMode;
- int mSeekPosition;
+ int64_t mSeekPosition;
MediaPlayer2SeekMode mSeekMode;
- bool mPrepareSync;
- status_t mPrepareStatus;
audio_stream_type_t mStreamType;
Parcel* mAudioAttributesParcel;
bool mLoop;
@@ -289,7 +158,20 @@
int mVideoWidth;
int mVideoHeight;
audio_session_t mAudioSessionId;
+ audio_attributes_t * mAudioAttributes;
float mSendLevel;
+
+ sp<ANativeWindowWrapper> mConnectedWindow;
+
+ // Metadata filters.
+ media::Metadata::Filter mMetadataAllow; // protected by mLock
+ media::Metadata::Filter mMetadataDrop; // protected by mLock
+
+ // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
+ // notification we try to update mMetadataUpdated which is a
+ // set: no duplicate.
+ // getMetadata clears this set.
+ media::Metadata::Filter mMetadataUpdated; // protected by mLock
};
}; // namespace android
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index ab30273..c465caa 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -18,44 +18,391 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer2Native"
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <utils/Log.h>
-
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
-#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
-#include <media/AVSyncSettings.h>
-#include <media/DataSource.h>
#include <media/DataSourceDesc.h>
#include <media/MediaAnalyticsItem.h>
+#include <media/MemoryLeakTrackUtil.h>
+#include <media/Metadata.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
#include <mediaplayer2/mediaplayer2.h>
-#include <binder/MemoryBase.h>
-
-#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <system/audio.h>
#include <system/window.h>
-#include "MediaPlayer2Manager.h"
+#include <nuplayer2/NuPlayer2Driver.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
namespace android {
-MediaPlayer2::MediaPlayer2()
-{
+extern ALooperRoster gLooperRoster;
+
+namespace {
+
+const int kDumpLockRetries = 50;
+const int kDumpLockSleepUs = 20000;
+
+// Max number of entries in the filter.
+const int kMaxFilterSize = 64; // I pulled that out of thin air.
+
+// FIXME: Move all the metadata related function in the Metadata.cpp
+
+// Unmarshall a filter from a Parcel.
+// Filter format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | number of entries (n) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// ....
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type n |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that should start with a filter.
+// @param[out] filter On exit contains the list of metadata type to be
+// filtered.
+// @param[out] status On exit contains the status code to be returned.
+// @return true if the parcel starts with a valid filter.
+bool unmarshallFilter(const Parcel& p,
+ media::Metadata::Filter *filter,
+ status_t *status) {
+ int32_t val;
+ if (p.readInt32(&val) != OK) {
+ ALOGE("Failed to read filter's length");
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ if (val > kMaxFilterSize || val < 0) {
+ ALOGE("Invalid filter len %d", val);
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ const size_t num = val;
+
+ filter->clear();
+ filter->setCapacity(num);
+
+ size_t size = num * sizeof(media::Metadata::Type);
+
+
+ if (p.dataAvail() < size) {
+ ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ const media::Metadata::Type *data =
+ static_cast<const media::Metadata::Type*>(p.readInplace(size));
+
+ if (NULL == data) {
+ ALOGE("Filter had no data");
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ // TODO: The stl impl of vector would be more efficient here
+ // because it degenerates into a memcpy on pod types. Try to
+ // replace later or use stl::set.
+ for (size_t i = 0; i < num; ++i) {
+ filter->add(*data);
+ ++data;
+ }
+ *status = OK;
+ return true;
+}
+
+// @param filter Of metadata type.
+// @param val To be searched.
+// @return true if a match was found.
+bool findMetadata(const media::Metadata::Filter& filter, const int32_t val) {
+ // Deal with empty and ANY right away
+ if (filter.isEmpty()) {
+ return false;
+ }
+ if (filter[0] == media::Metadata::kAny) {
+ return true;
+ }
+
+ return filter.indexOf(val) >= 0;
+}
+
+// marshalling tag indicating flattened utf16 tags
+// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
+const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
+
+// Audio attributes format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | usage |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | content_type |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flags |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flattened tags in UTF16 |
+// | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that contains audio attributes.
+// @param[out] attributes On exit points to an initialized audio_attributes_t structure
+// @param[out] status On exit contains the status code to be returned.
+void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes) {
+ attributes->usage = (audio_usage_t) parcel.readInt32();
+ attributes->content_type = (audio_content_type_t) parcel.readInt32();
+ attributes->source = (audio_source_t) parcel.readInt32();
+ attributes->flags = (audio_flags_mask_t) parcel.readInt32();
+ const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
+ if (hasFlattenedTag) {
+ // the tags are UTF16, convert to UTF8
+ String16 tags = parcel.readString16();
+ ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
+ if (realTagSize <= 0) {
+ strcpy(attributes->tags, "");
+ } else {
+ // copy the flattened string into the attributes as the destination for the conversion:
+ // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
+ size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
+ AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
+ utf16_to_utf8(tags.string(), tagSize, attributes->tags,
+ sizeof(attributes->tags) / sizeof(attributes->tags[0]));
+ }
+ } else {
+ ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
+ strcpy(attributes->tags, "");
+ }
+}
+
+class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback {
+public:
+ AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener)
+ : mListener(listener) { }
+
+ ~AudioDeviceUpdatedNotifier() { }
+
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) override {
+ sp<MediaPlayer2Interface> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+ } else {
+ ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
+ }
+ }
+
+private:
+ wp<MediaPlayer2Interface> mListener;
+};
+
+class proxyListener : public MediaPlayer2InterfaceListener {
+public:
+ proxyListener(const wp<MediaPlayer2> &player)
+ : mPlayer(player) { }
+
+ ~proxyListener() { };
+
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+ sp<MediaPlayer2> player = mPlayer.promote();
+ if (player != NULL) {
+ player->notify(srcId, msg, ext1, ext2, obj);
+ }
+ }
+
+private:
+ wp<MediaPlayer2> mPlayer;
+};
+
+Mutex sRecordLock;
+SortedVector<wp<MediaPlayer2> > *sPlayers;
+
+void ensureInit_l() {
+ if (sPlayers == NULL) {
+ sPlayers = new SortedVector<wp<MediaPlayer2> >();
+ }
+}
+
+void addPlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->add(player);
+}
+
+void removePlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->remove(player);
+}
+
+/**
+ * The only arguments this understands right now are -c, -von and -voff,
+ * which are parsed by ALooperRoster::dump()
+ */
+status_t dumpPlayers(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ SortedVector< sp<MediaPlayer2> > players; //to serialise the mutex unlock & client destruction.
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: can't dump MediaPlayer2\n");
+ result.append(buffer);
+ } else {
+ {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ for (int i = 0, n = sPlayers->size(); i < n; ++i) {
+ sp<MediaPlayer2> p = (*sPlayers)[i].promote();
+ if (p != 0) {
+ p->dump(fd, args);
+ }
+ players.add(p);
+ }
+ }
+
+ result.append(" Files opened and/or mapped:\n");
+ snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
+ FILE *f = fopen(buffer, "r");
+ if (f) {
+ while (!feof(f)) {
+ fgets(buffer, SIZE, f);
+ if (strstr(buffer, " /storage/") ||
+ strstr(buffer, " /system/sounds/") ||
+ strstr(buffer, " /data/") ||
+ strstr(buffer, " /system/media/")) {
+ result.append(" ");
+ result.append(buffer);
+ }
+ }
+ fclose(f);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
+ DIR *d = opendir(buffer);
+ if (d) {
+ struct dirent *ent;
+ while((ent = readdir(d)) != NULL) {
+ if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
+ snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
+ struct stat s;
+ if (lstat(buffer, &s) == 0) {
+ if ((s.st_mode & S_IFMT) == S_IFLNK) {
+ char linkto[256];
+ int len = readlink(buffer, linkto, sizeof(linkto));
+ if(len > 0) {
+ if(len > 255) {
+ linkto[252] = '.';
+ linkto[253] = '.';
+ linkto[254] = '.';
+ linkto[255] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+ if (strstr(linkto, "/storage/") == linkto ||
+ strstr(linkto, "/system/sounds/") == linkto ||
+ strstr(linkto, "/data/") == linkto ||
+ strstr(linkto, "/system/media/") == linkto) {
+ result.append(" ");
+ result.append(buffer);
+ result.append(" -> ");
+ result.append(linkto);
+ result.append("\n");
+ }
+ }
+ } else {
+ result.append(" unexpected type for ");
+ result.append(buffer);
+ result.append("\n");
+ }
+ }
+ }
+ }
+ closedir(d);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ gLooperRoster.dump(fd, args);
+
+ bool dumpMem = false;
+ bool unreachableMemory = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args[i] == String16("-m")) {
+ dumpMem = true;
+ } else if (args[i] == String16("--unreachable")) {
+ unreachableMemory = true;
+ }
+ }
+ if (dumpMem) {
+ result.append("\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ result.append(s.c_str(), s.size());
+ }
+ if (unreachableMemory) {
+ result.append("\nDumping unreachable memory:\n");
+ // TODO - should limit be an argument parameter?
+ // TODO: enable GetUnreachableMemoryString if it's part of stable API
+ //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+ //result.append(s.c_str(), s.size());
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+} // anonymous namespace
+
+//static
+sp<MediaPlayer2> MediaPlayer2::Create() {
+ sp<MediaPlayer2> player = new MediaPlayer2();
+
+ if (!player->init()) {
+ return NULL;
+ }
+
+ ALOGV("Create new player(%p)", player.get());
+
+ addPlayer(player);
+ return player;
+}
+
+// static
+status_t MediaPlayer2::DumpAll(int fd, const Vector<String16>& args) {
+ return dumpPlayers(fd, args);
+}
+
+MediaPlayer2::MediaPlayer2() {
ALOGV("constructor");
mSrcId = 0;
+ mLockThreadId = 0;
mListener = NULL;
- mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
@@ -63,19 +410,21 @@
mSeekPosition = -1;
mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mCurrentState = MEDIA_PLAYER2_IDLE;
- mPrepareSync = false;
- mPrepareStatus = NO_ERROR;
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
- mLockThreadId = 0;
mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
+
+ // TODO: get pid and uid from JAVA
+ mPid = IPCThreadState::self()->getCallingPid();
+ mUid = IPCThreadState::self()->getCallingUid();
+
+ mAudioAttributes = NULL;
}
-MediaPlayer2::~MediaPlayer2()
-{
+MediaPlayer2::~MediaPlayer2() {
ALOGV("destructor");
if (mAudioAttributesParcel != NULL) {
delete mAudioAttributesParcel;
@@ -83,13 +432,21 @@
}
AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
- IPCThreadState::self()->flushCommands();
+ removePlayer(this);
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
}
-void MediaPlayer2::disconnect()
-{
+bool MediaPlayer2::init() {
+ // TODO: after merge with NuPlayer2Driver, MediaPlayer2 will have its own
+ // looper for notification.
+ return true;
+}
+
+void MediaPlayer2::disconnect() {
ALOGV("disconnect");
- sp<MediaPlayer2Engine> p;
+ sp<MediaPlayer2Interface> p;
{
Mutex::Autolock _l(mLock);
p = mPlayer;
@@ -97,13 +454,17 @@
}
if (p != 0) {
- p->disconnect();
+ p->setListener(NULL);
+ p->reset();
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+ disconnectNativeWindow_l();
}
}
-// always call with lock held
-void MediaPlayer2::clear_l()
-{
+void MediaPlayer2::clear_l() {
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
@@ -111,8 +472,7 @@
mVideoWidth = mVideoHeight = 0;
}
-status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener)
-{
+status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener) {
ALOGV("setListener");
Mutex::Autolock _l(mLock);
mListener = listener;
@@ -129,109 +489,236 @@
return OK;
}
-status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId)
-{
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> p;
- { // scope for the lock
- Mutex::Autolock _l(mLock);
-
- if ( !( (mCurrentState & MEDIA_PLAYER2_IDLE) ||
- (mCurrentState == MEDIA_PLAYER2_STATE_ERROR ) ) ) {
- ALOGE("attachNewPlayer called in state %d", mCurrentState);
- return INVALID_OPERATION;
- }
-
- clear_l();
- p = mPlayer;
- mPlayer = player;
- mSrcId = srcId;
- if (player != 0) {
- mCurrentState = MEDIA_PLAYER2_INITIALIZED;
- err = NO_ERROR;
- } else {
- ALOGE("Unable to create media player");
- }
- }
-
- if (p != 0) {
- p->disconnect();
- }
-
- return err;
-}
-
-status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd)
-{
+status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd) {
if (dsd == NULL) {
return BAD_VALUE;
}
- ALOGV("setDataSource type(%d)", dsd->mType);
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> player(MediaPlayer2Manager::get().create(this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(dsd)) {
- player.clear();
+ ALOGV("setDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
+
+ sp<MediaPlayer2Interface> oldPlayer;
+
+ Mutex::Autolock _l(mLock);
+ {
+ if (!((mCurrentState & MEDIA_PLAYER2_IDLE)
+ || mCurrentState == MEDIA_PLAYER2_STATE_ERROR)) {
+ ALOGE("setDataSource called in wrong state %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ sp<MediaPlayer2Interface> player = new NuPlayer2Driver(mPid, mUid);
+ status_t err = player->initCheck();
+ if (err != NO_ERROR) {
+ ALOGE("Failed to create player object, initCheck failed(%d)", err);
+ return err;
+ }
+
+ clear_l();
+
+ player->setListener(new proxyListener(this));
+ mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid,
+ mPid, mAudioAttributes, new AudioDeviceUpdatedNotifier(player));
+ player->setAudioSink(mAudioOutput);
+
+ err = player->setDataSource(dsd);
+ if (err != OK) {
+ ALOGE("setDataSource error: %d", err);
+ return err;
+ }
+
+ sp<MediaPlayer2Interface> oldPlayer = mPlayer;
+ mPlayer = player;
+ mSrcId = dsd->mId;
+ mCurrentState = MEDIA_PLAYER2_INITIALIZED;
}
- err = attachNewPlayer(player, dsd->mId);
- return err;
+
+ if (oldPlayer != NULL) {
+ oldPlayer->setListener(NULL);
+ oldPlayer->reset();
+ }
+
+ return OK;
}
-status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply)
-{
+status_t MediaPlayer2::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
+ if (dsd == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("prepareNextDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
+
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == NULL) {
+ ALOGE("prepareNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
+ }
+ return mPlayer->prepareNextDataSource(dsd);
+}
+
+status_t MediaPlayer2::playNextDataSource(int64_t srcId) {
+ ALOGV("playNextDataSource srcId(%lld)", (long long)srcId);
+
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == NULL) {
+ ALOGE("playNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
+ }
+ mSrcId = srcId;
+ return mPlayer->playNextDataSource(srcId);
+}
+
+status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) {
Mutex::Autolock _l(mLock);
const bool hasBeenInitialized =
(mCurrentState != MEDIA_PLAYER2_STATE_ERROR) &&
((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE);
- if ((mPlayer != NULL) && hasBeenInitialized) {
- ALOGV("invoke %zu", request.dataSize());
- return mPlayer->invoke(request, reply);
+ if ((mPlayer == NULL) || !hasBeenInitialized) {
+ ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
}
- ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
- return INVALID_OPERATION;
+ ALOGV("invoke %zu", request.dataSize());
+ return mPlayer->invoke(request, reply);
}
-status_t MediaPlayer2::setMetadataFilter(const Parcel& filter)
-{
+// This call doesn't need to access the native player.
+status_t MediaPlayer2::setMetadataFilter(const Parcel& filter) {
ALOGD("setMetadataFilter");
- Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
+
+ status_t status;
+ media::Metadata::Filter allow, drop;
+
+ if (unmarshallFilter(filter, &allow, &status) &&
+ unmarshallFilter(filter, &drop, &status)) {
+ Mutex::Autolock lock(mLock);
+
+ mMetadataAllow = allow;
+ mMetadataDrop = drop;
}
- return mPlayer->setMetadataFilter(filter);
+ return status;
}
-status_t MediaPlayer2::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
-{
+status_t MediaPlayer2::getMetadata(bool update_only, bool /* apply_filter */, Parcel *reply) {
ALOGD("getMetadata");
+ sp<MediaPlayer2Interface> player;
+ media::Metadata::Filter ids;
Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
+ {
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ player = mPlayer;
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
+
+ // We don't block notifications while we fetch the data. We clear
+ // mMetadataUpdated first so we don't lose notifications happening
+ // during the rest of this call.
+ if (update_only) {
+ ids = mMetadataUpdated;
+ }
+ mMetadataUpdated.clear();
+ }
+
+ media::Metadata metadata(reply);
+
+ metadata.appendHeader();
+ status_t status = player->getMetadata(ids, reply);
+
+ if (status != OK) {
+ metadata.resetParcel();
+ ALOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: ement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ // Everything is fine, update the metadata length.
+ metadata.updateLength();
+ return OK;
+}
+
+void MediaPlayer2::disconnectNativeWindow_l() {
+ if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
+ status_t err = native_window_api_disconnect(
+ mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+ mConnectedWindow.clear();
+}
+
+status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) {
+ ANativeWindow *anw = (nww == NULL ? NULL : nww->getANativeWindow());
+ ALOGV("setVideoSurfaceTexture(%p)", anw);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getMetadata(update_only, apply_filter, metadata);
+
+ if (anw != NULL) {
+ if (mConnectedWindow != NULL
+ && mConnectedWindow->getANativeWindow() == anw) {
+ return OK;
+ }
+ status_t err = native_window_api_connect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGE("setVideoSurfaceTexture failed: %d", err);
+ // Note that we must do the reset before disconnecting from the ANW.
+ // Otherwise queue/dequeue calls could be made on the disconnected
+ // ANW, which may result in errors.
+ mPlayer->reset();
+ disconnectNativeWindow_l();
+ return err;
+ }
+ }
+
+ // Note that we must set the player's new GraphicBufferProducer before
+ // disconnecting the old one. Otherwise queue/dequeue calls could be made
+ // on the disconnected ANW, which may result in errors.
+ status_t err = mPlayer->setVideoSurfaceTexture(nww);
+
+ disconnectNativeWindow_l();
+
+ if (err == OK) {
+ mConnectedWindow = nww;
+ mLock.unlock();
+ } else if (anw != NULL) {
+ mLock.unlock();
+ status_t err = native_window_api_disconnect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+
+ return err;
}
-status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("setVideoSurfaceTexture");
- Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return NO_INIT;
- return mPlayer->setVideoSurfaceTexture(nww);
-}
-
-status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */)
-{
+status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
ALOGV("getBufferingSettings");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getBufferingSettings(buffering);
+
+ status_t ret = mPlayer->getBufferingSettings(buffering);
+ if (ret == NO_ERROR) {
+ ALOGV("getBufferingSettings{%s}", buffering->toString().string());
+ } else {
+ ALOGE("getBufferingSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering)
-{
- ALOGV("setBufferingSettings");
+status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering) {
+ ALOGV("setBufferingSettings{%s}", buffering.toString().string());
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
@@ -240,14 +727,37 @@
return mPlayer->setBufferingSettings(buffering);
}
-// must call with lock held
-status_t MediaPlayer2::prepareAsync_l()
-{
- if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED) ) ) {
+status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) {
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
+ mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAudioAttributes == NULL) {
+ return NO_MEMORY;
+ }
+ unmarshallAudioAttributes(parcel, mAudioAttributes);
+
+ ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
+ mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
+ mAudioAttributes->tags);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAudioAttributes(mAudioAttributes);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2::prepareAsync() {
+ ALOGV("prepareAsync");
+ Mutex::Autolock _l(mLock);
+ if ((mPlayer != 0) && (mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED))) {
if (mAudioAttributesParcel != NULL) {
- mPlayer->setParameter(MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
- } else {
- mPlayer->setAudioStreamType(mStreamType);
+ status_t err = setAudioAttributes_l(*mAudioAttributesParcel);
+ if (err != OK) {
+ return err;
+ }
+ } else if (mAudioOutput != 0) {
+ mAudioOutput->setAudioStreamType(mStreamType);
}
mCurrentState = MEDIA_PLAYER2_PREPARING;
return mPlayer->prepareAsync();
@@ -256,44 +766,7 @@
return INVALID_OPERATION;
}
-// TODO: In case of error, prepareAsync provides the caller with 2 error codes,
-// one defined in the Android framework and one provided by the implementation
-// that generated the error. The sync version of prepare returns only 1 error
-// code.
-status_t MediaPlayer2::prepare()
-{
- ALOGV("prepare");
- Mutex::Autolock _l(mLock);
- mLockThreadId = getThreadId();
- if (mPrepareSync) {
- mLockThreadId = 0;
- return -EALREADY;
- }
- mPrepareSync = true;
- status_t ret = prepareAsync_l();
- if (ret != NO_ERROR) {
- mLockThreadId = 0;
- return ret;
- }
-
- if (mPrepareSync) {
- mSignal.wait(mLock); // wait for prepare done
- mPrepareSync = false;
- }
- ALOGV("prepare complete - status=%d", mPrepareStatus);
- mLockThreadId = 0;
- return mPrepareStatus;
-}
-
-status_t MediaPlayer2::prepareAsync()
-{
- ALOGV("prepareAsync");
- Mutex::Autolock _l(mLock);
- return prepareAsync_l();
-}
-
-status_t MediaPlayer2::start()
-{
+status_t MediaPlayer2::start() {
ALOGV("start");
status_t ret = NO_ERROR;
@@ -306,8 +779,14 @@
} else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_PREPARED |
MEDIA_PLAYER2_PLAYBACK_COMPLETE | MEDIA_PLAYER2_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(mLeftVolume, mRightVolume);
+ }
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAuxEffectSendLevel(mSendLevel);
+ }
mCurrentState = MEDIA_PLAYER2_STARTED;
ret = mPlayer->start();
if (ret != NO_ERROR) {
@@ -327,8 +806,7 @@
return ret;
}
-status_t MediaPlayer2::stop()
-{
+status_t MediaPlayer2::stop() {
ALOGV("stop");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER2_STOPPED) return NO_ERROR;
@@ -346,8 +824,7 @@
return INVALID_OPERATION;
}
-status_t MediaPlayer2::pause()
-{
+status_t MediaPlayer2::pause() {
ALOGV("pause");
Mutex::Autolock _l(mLock);
if (mCurrentState & (MEDIA_PLAYER2_PAUSED|MEDIA_PLAYER2_PLAYBACK_COMPLETE))
@@ -365,12 +842,10 @@
return INVALID_OPERATION;
}
-bool MediaPlayer2::isPlaying()
-{
+bool MediaPlayer2::isPlaying() {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- bool temp = false;
- mPlayer->isPlaying(&temp);
+ bool temp = mPlayer->isPlaying();
ALOGV("isPlaying: %d", temp);
if ((mCurrentState & MEDIA_PLAYER2_STARTED) && ! temp) {
ALOGE("internal/external state mismatch corrected");
@@ -385,13 +860,12 @@
return false;
}
-status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
+status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate) {
ALOGV("setPlaybackSettings: %f %f %d %d",
rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
// Negative speed and pitch does not make sense. Further validation will
// be done by the respective mediaplayers.
- if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
+ if (rate.mSpeed <= 0.f || rate.mPitch < 0.f) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
@@ -399,36 +873,27 @@
return INVALID_OPERATION;
}
- if (rate.mSpeed != 0.f && !(mCurrentState & MEDIA_PLAYER2_STARTED)
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
- }
-
status_t err = mPlayer->setPlaybackSettings(rate);
- if (err == OK) {
- if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER2_STARTED) {
- mCurrentState = MEDIA_PLAYER2_PAUSED;
- } else if (rate.mSpeed != 0.f
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mCurrentState = MEDIA_PLAYER2_STARTED;
- }
- }
return err;
}
-status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
+status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
return mPlayer->getPlaybackSettings(rate);
+ status_t ret = mPlayer->getPlaybackSettings(rate);
+ if (ret == NO_ERROR) {
+ ALOGV("getPlaybackSettings(%f, %f, %d, %d)",
+ rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
+ } else {
+ ALOGV("getPlaybackSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
-{
+status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) {
ALOGV("setSyncSettings: %u %u %f %f",
sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
Mutex::Autolock _l(mLock);
@@ -437,124 +902,128 @@
}
status_t MediaPlayer2::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return INVALID_OPERATION;
- return mPlayer->getSyncSettings(sync, videoFps);
+ status_t ret = mPlayer->getSyncSettings(sync, videoFps);
+ if (ret == NO_ERROR) {
+ ALOGV("getSyncSettings(%u, %u, %f, %f)",
+ sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
+ } else {
+ ALOGV("getSyncSettings returned %d", ret);
+ }
+ return ret;
+
}
-status_t MediaPlayer2::getVideoWidth(int *w)
-{
+status_t MediaPlayer2::getVideoWidth(int *w) {
ALOGV("getVideoWidth");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*w = mVideoWidth;
return NO_ERROR;
}
-status_t MediaPlayer2::getVideoHeight(int *h)
-{
+status_t MediaPlayer2::getVideoHeight(int *h) {
ALOGV("getVideoHeight");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*h = mVideoHeight;
return NO_ERROR;
}
-status_t MediaPlayer2::getCurrentPosition(int *msec)
-{
+status_t MediaPlayer2::getCurrentPosition(int64_t *msec) {
ALOGV("getCurrentPosition");
Mutex::Autolock _l(mLock);
- if (mPlayer != 0) {
- if (mCurrentPosition >= 0) {
- ALOGV("Using cached seek position: %d", mCurrentPosition);
- *msec = mCurrentPosition;
- return NO_ERROR;
- }
- return mPlayer->getCurrentPosition(msec);
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+ if (mCurrentPosition >= 0) {
+ ALOGV("Using cached seek position: %lld", (long long)mCurrentPosition);
+ *msec = mCurrentPosition;
+ return NO_ERROR;
+ }
+ status_t ret = mPlayer->getCurrentPosition(msec);
+ if (ret == NO_ERROR) {
+ ALOGV("getCurrentPosition = %lld", (long long)*msec);
+ } else {
+ ALOGE("getCurrentPosition returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::getDuration_l(int *msec)
-{
+status_t MediaPlayer2::getDuration(int64_t *msec) {
+ Mutex::Autolock _l(mLock);
ALOGV("getDuration_l");
bool isValidState = (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_STOPPED | MEDIA_PLAYER2_PLAYBACK_COMPLETE));
- if (mPlayer != 0 && isValidState) {
- int durationMs;
- status_t ret = mPlayer->getDuration(&durationMs);
-
- if (ret != OK) {
- // Do not enter error state just because no duration was available.
- durationMs = -1;
- ret = OK;
- }
-
- if (msec) {
- *msec = durationMs;
- }
- return ret;
+ if (mPlayer == 0 || !isValidState) {
+ ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
+ mPlayer.get(), mCurrentState);
+ return INVALID_OPERATION;
}
- ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
- mPlayer.get(), mCurrentState);
- return INVALID_OPERATION;
-}
+ int64_t durationMs;
+ status_t ret = mPlayer->getDuration(&durationMs);
-status_t MediaPlayer2::getDuration(int *msec)
-{
- Mutex::Autolock _l(mLock);
- return getDuration_l(msec);
-}
-
-status_t MediaPlayer2::seekTo_l(int msec, MediaPlayer2SeekMode mode)
-{
- ALOGV("seekTo (%d, %d)", msec, mode);
- if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED |
- MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) ) {
- if ( msec < 0 ) {
- ALOGW("Attempt to seek to invalid position: %d", msec);
- msec = 0;
- }
-
- int durationMs;
- status_t err = mPlayer->getDuration(&durationMs);
-
- if (err != OK) {
- ALOGW("Stream has no duration and is therefore not seekable.");
- return err;
- }
-
- if (msec > durationMs) {
- ALOGW("Attempt to seek to past end of file: request = %d, "
- "durationMs = %d",
- msec,
- durationMs);
-
- msec = durationMs;
- }
-
- // cache duration
- mCurrentPosition = msec;
- mCurrentSeekMode = mode;
- if (mSeekPosition < 0) {
- mSeekPosition = msec;
- mSeekMode = mode;
- return mPlayer->seekTo(msec, mode);
- }
- else {
- ALOGV("Seek in progress - queue up seekTo[%d, %d]", msec, mode);
- return NO_ERROR;
- }
+ if (ret == NO_ERROR) {
+ ALOGV("getDuration = %lld", (long long)durationMs);
+ } else {
+ ALOGE("getDuration returned %d", ret);
+ // Do not enter error state just because no duration was available.
+ durationMs = -1;
}
- ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(),
- mCurrentState);
- return INVALID_OPERATION;
+
+ if (msec) {
+ *msec = durationMs;
+ }
+ return OK;
}
-status_t MediaPlayer2::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
+status_t MediaPlayer2::seekTo_l(int64_t msec, MediaPlayer2SeekMode mode) {
+ ALOGV("seekTo (%lld, %d)", (long long)msec, mode);
+ if ((mPlayer == 0) || !(mCurrentState & (MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED |
+ MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
+ ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u",
+ mPlayer.get(), mCurrentState);
+ return INVALID_OPERATION;
+ }
+ if (msec < 0) {
+ ALOGW("Attempt to seek to invalid position: %lld", (long long)msec);
+ msec = 0;
+ }
+
+ int64_t durationMs;
+ status_t err = mPlayer->getDuration(&durationMs);
+
+ if (err != OK) {
+ ALOGW("Stream has no duration and is therefore not seekable.");
+ return err;
+ }
+
+ if (msec > durationMs) {
+ ALOGW("Attempt to seek to past end of file: request = %lld, durationMs = %lld",
+ (long long)msec, (long long)durationMs);
+
+ msec = durationMs;
+ }
+
+ // cache duration
+ mCurrentPosition = msec;
+ mCurrentSeekMode = mode;
+ if (mSeekPosition < 0) {
+ mSeekPosition = msec;
+ mSeekMode = mode;
+ return mPlayer->seekTo(msec, mode);
+ }
+ ALOGV("Seek in progress - queue up seekTo[%lld, %d]", (long long)msec, mode);
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2::seekTo(int64_t msec, MediaPlayer2SeekMode mode) {
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
status_t result = seekTo_l(msec, mode);
@@ -563,27 +1032,27 @@
return result;
}
-status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs)
-{
+status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs) {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- return mPlayer->notifyAt(mediaTimeUs);
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+
+ return mPlayer->notifyAt(mediaTimeUs);
}
-status_t MediaPlayer2::reset_l()
-{
+status_t MediaPlayer2::reset_l() {
mLoop = false;
- if (mCurrentState == MEDIA_PLAYER2_IDLE) return NO_ERROR;
- mPrepareSync = false;
+ if (mCurrentState == MEDIA_PLAYER2_IDLE) {
+ return NO_ERROR;
+ }
if (mPlayer != 0) {
status_t ret = mPlayer->reset();
if (ret != NO_ERROR) {
ALOGE("reset() failed with return code (%d)", ret);
mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
} else {
- mPlayer->disconnect();
+ mPlayer->setListener(NULL);
mCurrentState = MEDIA_PLAYER2_IDLE;
}
// setDataSource has to be called again to create a
@@ -595,8 +1064,7 @@
return NO_ERROR;
}
-status_t MediaPlayer2::reset()
-{
+status_t MediaPlayer2::reset() {
ALOGV("reset");
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
@@ -606,8 +1074,7 @@
return result;
}
-status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type)
-{
+status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type) {
ALOGV("MediaPlayer2::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
@@ -622,16 +1089,14 @@
return OK;
}
-status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type)
-{
+status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type) {
ALOGV("getAudioStreamType");
Mutex::Autolock _l(mLock);
*type = mStreamType;
return OK;
}
-status_t MediaPlayer2::setLooping(int loop)
-{
+status_t MediaPlayer2::setLooping(int loop) {
ALOGV("MediaPlayer2::setLooping");
Mutex::Autolock _l(mLock);
mLoop = (loop != 0);
@@ -651,20 +1116,18 @@
return false;
}
-status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume)
-{
+status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume) {
ALOGV("MediaPlayer2::setVolume(%f, %f)", leftVolume, rightVolume);
Mutex::Autolock _l(mLock);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
- if (mPlayer != 0) {
- return mPlayer->setVolume(leftVolume, rightVolume);
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(leftVolume, rightVolume);
}
return OK;
}
-status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId)
-{
+status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId) {
ALOGV("MediaPlayer2::setAudioSessionId(%d)", sessionId);
Mutex::Autolock _l(mLock);
if (!(mCurrentState & MEDIA_PLAYER2_IDLE)) {
@@ -682,40 +1145,36 @@
return NO_ERROR;
}
-audio_session_t MediaPlayer2::getAudioSessionId()
-{
+audio_session_t MediaPlayer2::getAudioSessionId() {
Mutex::Autolock _l(mLock);
return mAudioSessionId;
}
-status_t MediaPlayer2::setAuxEffectSendLevel(float level)
-{
+status_t MediaPlayer2::setAuxEffectSendLevel(float level) {
ALOGV("MediaPlayer2::setAuxEffectSendLevel(%f)", level);
Mutex::Autolock _l(mLock);
mSendLevel = level;
- if (mPlayer != 0) {
- return mPlayer->setAuxEffectSendLevel(level);
+ if (mAudioOutput != 0) {
+ return mAudioOutput->setAuxEffectSendLevel(level);
}
return OK;
}
-status_t MediaPlayer2::attachAuxEffect(int effectId)
-{
+status_t MediaPlayer2::attachAuxEffect(int effectId) {
ALOGV("MediaPlayer2::attachAuxEffect(%d)", effectId);
Mutex::Autolock _l(mLock);
- if (mPlayer == 0 ||
+ if (mAudioOutput == 0 ||
(mCurrentState & MEDIA_PLAYER2_IDLE) ||
(mCurrentState == MEDIA_PLAYER2_STATE_ERROR )) {
ALOGE("attachAuxEffect called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
return INVALID_OPERATION;
}
- return mPlayer->attachAuxEffect(effectId);
+ return mAudioOutput->attachAuxEffect(effectId);
}
// always call with lock held
-status_t MediaPlayer2::checkStateForKeySet_l(int key)
-{
+status_t MediaPlayer2::checkStateForKeySet_l(int key) {
switch(key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
@@ -732,8 +1191,7 @@
return OK;
}
-status_t MediaPlayer2::setParameter(int key, const Parcel& request)
-{
+status_t MediaPlayer2::setParameter(int key, const Parcel& request) {
ALOGV("MediaPlayer2::setParameter(%d)", key);
status_t status = INVALID_OPERATION;
Mutex::Autolock _l(mLock);
@@ -743,10 +1201,15 @@
switch (key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
// save the marshalled audio attributes
- if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; };
+ if (mAudioAttributesParcel != NULL) {
+ delete mAudioAttributesParcel;
+ }
mAudioAttributesParcel = new Parcel();
mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize());
- status = OK;
+ status = setAudioAttributes_l(request);
+ if (status != OK) {
+ return status;
+ }
break;
default:
ALOGV_IF(mPlayer == NULL, "setParameter: no active player");
@@ -759,25 +1222,69 @@
return status;
}
-status_t MediaPlayer2::getParameter(int key, Parcel *reply)
-{
+status_t MediaPlayer2::getParameter(int key, Parcel *reply) {
ALOGV("MediaPlayer2::getParameter(%d)", key);
Mutex::Autolock _l(mLock);
- if (mPlayer != NULL) {
- status_t status = mPlayer->getParameter(key, reply);
- if (status != OK) {
- ALOGD("getParameter returns %d", status);
+ if (key == MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES) {
+ if (reply == NULL) {
+ return BAD_VALUE;
}
- return status;
+ if (mAudioAttributesParcel != NULL) {
+ reply->appendFrom(mAudioAttributesParcel, 0, mAudioAttributesParcel->dataSize());
+ }
+ return OK;
}
- ALOGV("getParameter: no active player");
- return INVALID_OPERATION;
+
+ if (mPlayer == NULL) {
+ ALOGV("getParameter: no active player");
+ return INVALID_OPERATION;
+ }
+
+ status_t status = mPlayer->getParameter(key, reply);
+ if (status != OK) {
+ ALOGD("getParameter returns %d", status);
+ }
+ return status;
}
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
-{
+bool MediaPlayer2::shouldDropMetadata(media::Metadata::Type code) const {
+ Mutex::Autolock lock(mLock);
+
+ if (findMetadata(mMetadataDrop, code)) {
+ return true;
+ }
+
+ if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void MediaPlayer2::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
+ Mutex::Autolock lock(mLock);
+ if (mMetadataUpdated.indexOf(metadata_type) < 0) {
+ mMetadataUpdated.add(metadata_type);
+ }
+}
+
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
+
+ if (MEDIA2_INFO == msg && MEDIA2_INFO_METADATA_UPDATE == ext1) {
+ const media::Metadata::Type metadata_type = ext2;
+
+ if(shouldDropMetadata(metadata_type)) {
+ return;
+ }
+
+ // Update the list of metadata that have changed. getMetadata
+ // also access mMetadataUpdated and clears it.
+ addNewMetadataUpdate(metadata_type);
+ }
+
bool send = true;
bool locked = false;
@@ -808,12 +1315,6 @@
case MEDIA2_PREPARED:
ALOGV("MediaPlayer2::notify() prepared");
mCurrentState = MEDIA_PLAYER2_PREPARED;
- if (mPrepareSync) {
- ALOGV("signal application thread");
- mPrepareSync = false;
- mPrepareStatus = NO_ERROR;
- mSignal.signal();
- }
break;
case MEDIA2_DRM_INFO:
ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)",
@@ -834,14 +1335,6 @@
// ext2: Implementation dependant error code.
ALOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
- if (mPrepareSync)
- {
- ALOGV("signal application thread");
- mPrepareSync = false;
- mPrepareStatus = ext1;
- mSignal.signal();
- send = false;
- }
break;
case MEDIA2_INFO:
// ext1: Media framework error code.
@@ -853,7 +1346,8 @@
case MEDIA2_SEEK_COMPLETE:
ALOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition || (mSeekMode != mCurrentSeekMode)) {
- ALOGV("Executing queued seekTo(%d, %d)", mCurrentPosition, mCurrentSeekMode);
+ ALOGV("Executing queued seekTo(%lld, %d)",
+ (long long)mCurrentPosition, mCurrentSeekMode);
mSeekPosition = -1;
mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
seekTo_l(mCurrentPosition, mCurrentSeekMode);
@@ -901,24 +1395,8 @@
}
}
-status_t MediaPlayer2::setNextMediaPlayer(const sp<MediaPlayer2>& next) {
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- if (next != NULL && !(next->mCurrentState &
- (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- ALOGE("next player is not prepared");
- return INVALID_OPERATION;
- }
-
- return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer);
-}
-
// Modular DRM
-status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
-{
+status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
// TODO change to ALOGV
ALOGD("prepareDrm: uuid: %p drmSessionId: %p(%zu)", uuid,
drmSessionId.array(), drmSessionId.size());
@@ -951,8 +1429,7 @@
return status;
}
-status_t MediaPlayer2::releaseDrm()
-{
+status_t MediaPlayer2::releaseDrm() {
Mutex::Autolock _l(mLock);
if (mPlayer == NULL) {
return NO_INIT;
@@ -980,39 +1457,74 @@
return status;
}
-status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId)
-{
+status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("setOutputDevice: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("setOutputDevice: audio sink not init");
return NO_INIT;
}
- return mPlayer->setOutputDevice(deviceId);
+ return mAudioOutput->setOutputDevice(deviceId);
}
-audio_port_handle_t MediaPlayer2::getRoutedDeviceId()
-{
+audio_port_handle_t MediaPlayer2::getRoutedDeviceId() {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("getRoutedDeviceId: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("getRoutedDeviceId: audio sink not init");
return AUDIO_PORT_HANDLE_NONE;
}
audio_port_handle_t deviceId;
- status_t status = mPlayer->getRoutedDeviceId(&deviceId);
+ status_t status = mAudioOutput->getRoutedDeviceId(&deviceId);
if (status != NO_ERROR) {
return AUDIO_PORT_HANDLE_NONE;
}
return deviceId;
}
-status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled)
-{
+status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
+ if (mAudioOutput == NULL) {
ALOGV("addAudioDeviceCallback: player not init");
return NO_INIT;
}
- return mPlayer->enableAudioDeviceCallback(enabled);
+ return mAudioOutput->enableAudioDeviceCallback(enabled);
+}
+
+status_t MediaPlayer2::dump(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ result.append(" MediaPlayer2\n");
+ snprintf(buffer, 255, " pid(%d), looping(%s)\n", mPid, mLoop?"true": "false");
+ result.append(buffer);
+
+ sp<MediaPlayer2Interface> player;
+ sp<MediaPlayer2AudioOutput> audioOutput;
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleepUs);
+ }
+
+ if (locked) {
+ player = mPlayer;
+ audioOutput = mAudioOutput;
+ mLock.unlock();
+ } else {
+ result.append(" lock is taken, no dump from player and audio output\n");
+ }
+ write(fd, result.string(), result.size());
+
+ if (player != NULL) {
+ player->dump(fd, args);
+ }
+ if (audioOutput != 0) {
+ audioOutput->dump(fd, args);
+ }
+ write(fd, "\n", 1);
+ return NO_ERROR;
}
} // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 462a904..5971a8b 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -423,6 +423,14 @@
msg->post();
}
+void NuPlayer2::playNextDataSource(int64_t srcId) {
+ disconnectSource();
+
+ sp<AMessage> msg = new AMessage(kWhatPlayNextDataSource, this);
+ msg->setInt64("srcId", srcId);
+ msg->post();
+}
+
status_t NuPlayer2::getBufferingSettings(
BufferingSettings *buffering /* nonnull */) {
sp<AMessage> msg = new AMessage(kWhatGetBufferingSettings, this);
@@ -538,6 +546,11 @@
}
void NuPlayer2::resetAsync() {
+ disconnectSource();
+ (new AMessage(kWhatReset, this))->post();
+}
+
+void NuPlayer2::disconnectSource() {
sp<Source> source;
{
Mutex::Autolock autoLock(mSourceLock);
@@ -554,7 +567,6 @@
source->disconnect();
}
- (new AMessage(kWhatReset, this))->post();
}
status_t NuPlayer2::notifyAt(int64_t mediaTimeUs) {
@@ -671,6 +683,32 @@
break;
}
+ case kWhatPlayNextDataSource:
+ {
+ ALOGV("kWhatPlayNextDataSource");
+ int64_t srcId;
+ CHECK(msg->findInt64("srcId", &srcId));
+ if (srcId != mNextSrcId) {
+ notifyListener(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, 0);
+ return;
+ }
+
+ mResetting = true;
+ stopPlaybackTimer("kWhatPlayNextDataSource");
+ stopRebufferingTimer(true);
+
+ mDeferredActions.push_back(
+ new FlushDecoderAction(
+ FLUSH_CMD_SHUTDOWN /* audio */,
+ FLUSH_CMD_SHUTDOWN /* video */));
+
+ mDeferredActions.push_back(
+ new SimpleAction(&NuPlayer2::performPlayNextDataSource));
+
+ processDeferredActions();
+ break;
+ }
+
case kWhatGetBufferingSettings:
{
sp<AReplyToken> replyID;
@@ -1382,7 +1420,7 @@
handleFlushComplete(audio, false /* isDecoder */);
finishFlushIfPossible();
} else if (what == Renderer::kWhatVideoRenderingStart) {
- notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_VIDEO_RENDERING_START, 0);
} else if (what == Renderer::kWhatMediaRenderingStart) {
ALOGV("media rendering started");
notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
@@ -2403,6 +2441,67 @@
mIsDrmProtected = false;
}
+void NuPlayer2::performPlayNextDataSource() {
+ ALOGV("performPlayNextDataSource");
+
+ CHECK(mAudioDecoder == NULL);
+ CHECK(mVideoDecoder == NULL);
+
+ stopPlaybackTimer("performPlayNextDataSource");
+ stopRebufferingTimer(true);
+
+ cancelPollDuration();
+
+ ++mScanSourcesGeneration;
+ mScanSourcesPending = false;
+
+ ++mRendererGeneration;
+
+ if (mSource != NULL) {
+ mSource->stop();
+ }
+
+ long previousSrcId;
+ {
+ Mutex::Autolock autoLock(mSourceLock);
+ mSource = mNextSource;
+ mNextSource = NULL;
+ previousSrcId = mSrcId;
+ mSrcId = mNextSrcId;
+ ++mNextSrcId; // to distinguish the two sources.
+ }
+
+ if (mDriver != NULL) {
+ sp<NuPlayer2Driver> driver = mDriver.promote();
+ if (driver != NULL) {
+ notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAYBACK_COMPLETE, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0);
+ }
+ }
+
+ mStarted = false;
+ mPrepared = true; // TODO: what if it's not prepared
+ mResetting = false;
+ mSourceStarted = false;
+
+ // Modular DRM
+ if (mCrypto != NULL) {
+ // decoders will be flushed before this so their mCrypto would go away on their own
+ // TODO change to ALOGV
+ ALOGD("performReset mCrypto: %p", mCrypto.get());
+ mCrypto.clear();
+ }
+ mIsDrmProtected = false;
+
+ if (mRenderer != NULL) {
+ mRenderer->resume();
+ }
+
+ onStart();
+ mPausedByClient = false;
+ notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
+}
+
void NuPlayer2::performScanSources() {
ALOGV("performScanSources");
@@ -2666,10 +2765,10 @@
break;
}
- int posMs;
+ int64_t posMs;
int64_t timeUs, posUs;
driver->getCurrentPosition(&posMs);
- posUs = (int64_t) posMs * 1000ll;
+ posUs = posMs * 1000ll;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
if (posUs < timeUs) {
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index e7b774c..96f85f9 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -44,6 +44,7 @@
void setDataSourceAsync(const sp<DataSourceDesc> &dsd);
void prepareNextDataSourceAsync(const sp<DataSourceDesc> &dsd);
+ void playNextDataSource(int64_t srcId);
status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */);
status_t setBufferingSettings(const BufferingSettings& buffering);
@@ -121,6 +122,7 @@
kWhatSetDataSource = '=DaS',
kWhatPrepare = 'prep',
kWhatPrepareNextDataSource = 'pNDS',
+ kWhatPlayNextDataSource = 'plNS',
kWhatSetVideoSurface = '=VSu',
kWhatSetAudioSink = '=AuS',
kWhatMoreDataQueued = 'more',
@@ -269,6 +271,8 @@
mFlushComplete[1][1] = false;
}
+ void disconnectSource();
+
status_t createNuPlayer2Source(const sp<DataSourceDesc> &dsd,
sp<Source> *source,
DATA_SOURCE_TYPE *dataSourceType);
@@ -314,6 +318,7 @@
void performSeek(int64_t seekTimeUs, MediaPlayer2SeekMode mode);
void performDecoderFlush(FlushCommand audio, FlushCommand video);
void performReset();
+ void performPlayNextDataSource();
void performScanSources();
void performSetSurface(const sp<ANativeWindowWrapper> &nw);
void performResumeDecoders(bool needNotify);
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index 60a07a3..03d17a5 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -107,7 +107,6 @@
NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid)
: mState(STATE_IDLE),
- mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
mSrcId(0),
mSetSurfaceInProgress(false),
@@ -174,7 +173,7 @@
}
status_t NuPlayer2Driver::setDataSource(const sp<DataSourceDesc> &dsd) {
- ALOGV("setDataSource(%p) callback source", this);
+ ALOGV("setDataSource(%p)", this);
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
@@ -193,6 +192,25 @@
return mAsyncResult;
}
+status_t NuPlayer2Driver::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
+ ALOGV("prepareNextDataSource(%p)", this);
+ Mutex::Autolock autoLock(mLock);
+
+ mPlayer->prepareNextDataSourceAsync(dsd);
+
+ return OK;
+}
+
+status_t NuPlayer2Driver::playNextDataSource(int64_t srcId) {
+ ALOGV("playNextDataSource(%p)", this);
+ Mutex::Autolock autoLock(mLock);
+
+ mSrcId = srcId;
+ mPlayer->playNextDataSource(srcId);
+
+ return OK;
+}
+
status_t NuPlayer2Driver::setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww) {
ALOGV("setVideoSurfaceTexture(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -245,42 +263,6 @@
return mPlayer->setBufferingSettings(buffering);
}
-status_t NuPlayer2Driver::prepare() {
- ALOGV("prepare(%p)", this);
- Mutex::Autolock autoLock(mLock);
- return prepare_l();
-}
-
-status_t NuPlayer2Driver::prepare_l() {
- switch (mState) {
- case STATE_UNPREPARED:
- mState = STATE_PREPARING;
-
- // Make sure we're not posting any notifications, success or
- // failure information is only communicated through our result
- // code.
- mIsAsyncPrepare = false;
- mPlayer->prepareAsync();
- while (mState == STATE_PREPARING) {
- mCondition.wait(mLock);
- }
- return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
- case STATE_STOPPED:
- // this is really just paused. handle as seek to start
- mAtEOS = false;
- mState = STATE_STOPPED_AND_PREPARING;
- mIsAsyncPrepare = false;
- mPlayer->seekToAsync(0, MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC /* mode */,
- true /* needNotify */);
- while (mState == STATE_STOPPED_AND_PREPARING) {
- mCondition.wait(mLock);
- }
- return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
- default:
- return INVALID_OPERATION;
- };
-}
-
status_t NuPlayer2Driver::prepareAsync() {
ALOGV("prepareAsync(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -288,14 +270,12 @@
switch (mState) {
case STATE_UNPREPARED:
mState = STATE_PREPARING;
- mIsAsyncPrepare = true;
mPlayer->prepareAsync();
return OK;
case STATE_STOPPED:
// this is really just paused. handle as seek to start
mAtEOS = false;
mState = STATE_STOPPED_AND_PREPARING;
- mIsAsyncPrepare = true;
mPlayer->seekToAsync(0, MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC /* mode */,
true /* needNotify */);
return OK;
@@ -312,19 +292,6 @@
status_t NuPlayer2Driver::start_l() {
switch (mState) {
- case STATE_UNPREPARED:
- {
- status_t err = prepare_l();
-
- if (err != OK) {
- return err;
- }
-
- CHECK_EQ(mState, STATE_PREPARED);
-
- // fall through
- }
-
case STATE_PAUSED:
case STATE_STOPPED_AND_PREPARED:
case STATE_PREPARED:
@@ -387,7 +354,7 @@
// down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
// current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
// getCurrentPosition here.
- int unused;
+ int64_t unused;
getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
@@ -417,7 +384,7 @@
status_t err = mPlayer->setPlaybackSettings(rate);
if (err == OK) {
// try to update position
- int unused;
+ int64_t unused;
getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
@@ -444,8 +411,8 @@
return mPlayer->getSyncSettings(sync, videoFps);
}
-status_t NuPlayer2Driver::seekTo(int msec, MediaPlayer2SeekMode mode) {
- ALOGD("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState);
+status_t NuPlayer2Driver::seekTo(int64_t msec, MediaPlayer2SeekMode mode) {
+ ALOGD("seekTo(%p) (%lld ms, %d) at state %d", this, (long long)msec, mode, mState);
Mutex::Autolock autoLock(mLock);
int64_t seekTimeUs = msec * 1000ll;
@@ -470,13 +437,13 @@
return OK;
}
-status_t NuPlayer2Driver::getCurrentPosition(int *msec) {
+status_t NuPlayer2Driver::getCurrentPosition(int64_t *msec) {
int64_t tempUs = 0;
{
Mutex::Autolock autoLock(mLock);
if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
- *msec = (int)divRound(tempUs, (int64_t)(1000));
+ *msec = divRound(tempUs, (int64_t)(1000));
return OK;
}
}
@@ -492,11 +459,11 @@
} else {
mPositionUs = tempUs;
}
- *msec = (int)divRound(tempUs, (int64_t)(1000));
+ *msec = divRound(tempUs, (int64_t)(1000));
return OK;
}
-status_t NuPlayer2Driver::getDuration(int *msec) {
+status_t NuPlayer2Driver::getDuration(int64_t *msec) {
Mutex::Autolock autoLock(mLock);
if (mDurationUs < 0) {
@@ -562,7 +529,7 @@
// always provide duration and playing time, even if they have 0/unknown values.
// getDuration() uses mLock for mutex -- careful where we use it.
- int duration_ms = -1;
+ int64_t duration_ms = -1;
getDuration(&duration_ms);
mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
@@ -626,8 +593,6 @@
case STATE_PREPARING:
{
- CHECK(mIsAsyncPrepare);
-
notifyListener_l(mSrcId, MEDIA2_PREPARED);
break;
}
@@ -696,7 +661,7 @@
case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK:
{
int trackIndex = request.readInt32();
- int msec = 0;
+ int64_t msec = 0;
// getCurrentPosition should always return OK
getCurrentPosition(&msec);
return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
@@ -824,10 +789,6 @@
wasSeeking = false;
mState = STATE_STOPPED_AND_PREPARED;
mCondition.broadcast();
- if (!mIsAsyncPrepare) {
- // if we are preparing synchronously, no need to notify listener
- return;
- }
} else if (mState == STATE_STOPPED) {
// no need to notify listener
return;
@@ -947,58 +908,60 @@
ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
this, (long long)srcId, msg, ext1, ext2,
(in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
- switch (msg) {
- case MEDIA2_PLAYBACK_COMPLETE:
- {
- if (mState != STATE_RESET_IN_PROGRESS) {
- if (mAutoLoop) {
- audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
- if (mAudioSink != NULL) {
- streamType = mAudioSink->getAudioStreamType();
+ if (srcId == mSrcId) {
+ switch (msg) {
+ case MEDIA2_PLAYBACK_COMPLETE:
+ {
+ if (mState != STATE_RESET_IN_PROGRESS) {
+ if (mAutoLoop) {
+ audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
+ if (mAudioSink != NULL) {
+ streamType = mAudioSink->getAudioStreamType();
+ }
+ if (streamType == AUDIO_STREAM_NOTIFICATION) {
+ ALOGW("disabling auto-loop for notification");
+ mAutoLoop = false;
+ }
}
- if (streamType == AUDIO_STREAM_NOTIFICATION) {
- ALOGW("disabling auto-loop for notification");
- mAutoLoop = false;
+ if (mLooping || mAutoLoop) {
+ mPlayer->seekToAsync(0);
+ if (mAudioSink != NULL) {
+ // The renderer has stopped the sink at the end in order to play out
+ // the last little bit of audio. In looping mode, we need to restart it.
+ mAudioSink->start();
+ }
+ // don't send completion event when looping
+ return;
}
- }
- if (mLooping || mAutoLoop) {
- mPlayer->seekToAsync(0);
- if (mAudioSink != NULL) {
- // The renderer has stopped the sink at the end in order to play out
- // the last little bit of audio. If we're looping, we need to restart it.
- mAudioSink->start();
+ if (property_get_bool("persist.debug.sf.stats", false)) {
+ Vector<String16> args;
+ dump(-1, args);
}
- // don't send completion event when looping
- return;
+ mPlayer->pause();
+ mState = STATE_PAUSED;
}
- if (property_get_bool("persist.debug.sf.stats", false)) {
- Vector<String16> args;
- dump(-1, args);
- }
- mPlayer->pause();
- mState = STATE_PAUSED;
+ // fall through
}
- // fall through
- }
- case MEDIA2_ERROR:
- {
- // when we have an error, add it to the analytics for this playback.
- // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
- // [test against msg is due to fall through from previous switch value]
- if (msg == MEDIA2_ERROR) {
- mAnalyticsItem->setInt32(kPlayerError, ext1);
- if (ext2 != 0) {
- mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ case MEDIA2_ERROR:
+ {
+ // when we have an error, add it to the analytics for this playback.
+ // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
+ // [test against msg is due to fall through from previous switch value]
+ if (msg == MEDIA2_ERROR) {
+ mAnalyticsItem->setInt32(kPlayerError, ext1);
+ if (ext2 != 0) {
+ mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ }
+ mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
}
- mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
+ mAtEOS = true;
+ break;
}
- mAtEOS = true;
- break;
- }
- default:
- break;
+ default:
+ break;
+ }
}
sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
@@ -1025,6 +988,15 @@
Mutex::Autolock autoLock(mLock);
+ if (srcId != mSrcId) {
+ if (err == OK) {
+ notifyListener_l(srcId, MEDIA2_PREPARED);
+ } else {
+ notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ }
+ return;
+ }
+
if (mState != STATE_PREPARING) {
// We were preparing asynchronously when the client called
// reset(), we sent a premature "prepared" notification and
@@ -1041,14 +1013,10 @@
// update state before notifying client, so that if client calls back into NuPlayer2Driver
// in response, NuPlayer2Driver has the right state
mState = STATE_PREPARED;
- if (mIsAsyncPrepare) {
- notifyListener_l(srcId, MEDIA2_PREPARED);
- }
+ notifyListener_l(srcId, MEDIA2_PREPARED);
} else {
mState = STATE_UNPREPARED;
- if (mIsAsyncPrepare) {
- notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
- }
+ notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
}
sp<MetaData> meta = mPlayer->getFileMeta();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 4c57cfd..4da2566 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -28,17 +28,18 @@
struct NuPlayer2Driver : public MediaPlayer2Interface {
explicit NuPlayer2Driver(pid_t pid, uid_t uid);
- virtual status_t initCheck();
+ virtual status_t initCheck() override;
virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) override;
+ virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) override;
+ virtual status_t playNextDataSource(int64_t srcId) override;
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww);
+ virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww) override;
virtual status_t getBufferingSettings(
BufferingSettings* buffering /* nonnull */) override;
virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
- virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
@@ -49,9 +50,9 @@
virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps);
virtual status_t seekTo(
- int msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
- virtual status_t getCurrentPosition(int *msec);
- virtual status_t getDuration(int *msec);
+ int64_t msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
+ virtual status_t getCurrentPosition(int64_t *msec);
+ virtual status_t getDuration(int64_t *msec);
virtual status_t reset();
virtual status_t notifyAt(int64_t mediaTimeUs) override;
virtual status_t setLooping(int loop);
@@ -114,7 +115,6 @@
State mState;
- bool mIsAsyncPrepare;
status_t mAsyncResult;
// The following are protected through "mLock"
@@ -147,7 +147,6 @@
void updateMetrics(const char *where);
void logMetrics(const char *where);
- status_t prepare_l();
status_t start_l();
void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
const Parcel *in = NULL);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 541093a..6da6c13 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -7289,12 +7289,16 @@
}
}
- float rate;
- if (params->findFloat("operating-rate", &rate) && rate > 0) {
- status_t err = setOperatingRate(rate, mIsVideo);
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!params->findFloat("operating-rate", &rateFloat)) {
+ params->findInt32("operating-rate", &rateInt);
+ rateFloat = (float) rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ status_t err = setOperatingRate(rateFloat, mIsVideo);
if (err != OK) {
- ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
- return err;
+ ALOGI("Failed to set parameter 'operating-rate' (err %d)", err);
}
}
@@ -7319,10 +7323,8 @@
}
}
- status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
- if (err != OK) {
- err = OK; // ignore failure
- }
+ // Ignore errors as failure is expected for codecs that aren't video encoders.
+ (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
return setVendorParameters(params);
}
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/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index bd2443d..d2eee33 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -64,10 +64,87 @@
return ICrypto::kDestinationTypeNativeHandle;
}
+// Codec2Buffer
+
+bool Codec2Buffer::canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const {
+ if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
+ return false;
+ }
+ if (buffer->data().type() != C2BufferData::LINEAR) {
+ return false;
+ }
+ if (buffer->data().linearBlocks().size() == 0u) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ } else if (buffer->data().linearBlocks().size() > 1u) {
+ // We don't know how to copy more than one blocks.
+ return false;
+ }
+ if (buffer->data().linearBlocks()[0].size() > capacity()) {
+ // It won't fit.
+ return false;
+ }
+ return true;
+}
+
+bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
+ // We assume that all canCopyLinear() checks passed.
+ if (buffer->data().linearBlocks().size() == 0u) {
+ setRange(0, 0);
+ return true;
+ }
+ C2ReadView view = buffer->data().linearBlocks()[0].map().get();
+ if (view.error() != C2_OK) {
+ ALOGD("Error while mapping: %d", view.error());
+ return false;
+ }
+ if (view.capacity() > capacity()) {
+ ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)",
+ view.capacity(), capacity());
+ return false;
+ }
+ memcpy(base(), view.data(), view.capacity());
+ setRange(0, view.capacity());
+ return true;
+}
+
+// LocalLinearBuffer
+
+bool LocalLinearBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LocalLinearBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
+}
+
+// DummyContainerBuffer
+
+DummyContainerBuffer::DummyContainerBuffer(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(nullptr, 1)),
+ mBufferRef(buffer) {
+ setRange(0, buffer ? 1 : 0);
+}
+
+std::shared_ptr<C2Buffer> DummyContainerBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
+}
+
+bool DummyContainerBuffer::canCopy(const std::shared_ptr<C2Buffer> &) const {
+ return !mBufferRef;
+}
+
+bool DummyContainerBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ mBufferRef = buffer;
+ setRange(0, mBufferRef ? 1 : 0);
+ return true;
+}
+
// LinearBlockBuffer
// static
-sp<LinearBlockBuffer> LinearBlockBuffer::allocate(
+sp<LinearBlockBuffer> LinearBlockBuffer::Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) {
C2WriteView writeView(block->map().get());
if (writeView.error() != C2_OK) {
@@ -76,15 +153,23 @@
return new LinearBlockBuffer(format, std::move(writeView), block);
}
-C2ConstLinearBlock LinearBlockBuffer::share() {
- return mBlock->share(offset(), size(), C2Fence());
+std::shared_ptr<C2Buffer> LinearBlockBuffer::asC2Buffer() {
+ return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence()));
+}
+
+bool LinearBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LinearBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
}
LinearBlockBuffer::LinearBlockBuffer(
const sp<AMessage> &format,
C2WriteView&& writeView,
const std::shared_ptr<C2LinearBlock> &block)
- : MediaCodecBuffer(format, new ABuffer(writeView.data(), writeView.size())),
+ : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())),
mWriteView(writeView),
mBlock(block) {
}
@@ -92,23 +177,34 @@
// ConstLinearBlockBuffer
// static
-sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block) {
- C2ReadView readView(block.map().get());
+sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer) {
+ if (!buffer
+ || buffer->data().type() != C2BufferData::LINEAR
+ || buffer->data().linearBlocks().size() != 1u) {
+ return nullptr;
+ }
+ C2ReadView readView(buffer->data().linearBlocks()[0].map().get());
if (readView.error() != C2_OK) {
return nullptr;
}
- return new ConstLinearBlockBuffer(format, std::move(readView));
+ return new ConstLinearBlockBuffer(format, std::move(readView), buffer);
}
ConstLinearBlockBuffer::ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView&& readView)
- : MediaCodecBuffer(format, new ABuffer(
+ C2ReadView&& readView,
+ const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(
// NOTE: ABuffer only takes non-const pointer but this data is
// supposed to be read-only.
const_cast<uint8_t *>(readView.data()), readView.capacity())),
- mReadView(readView) {
+ mReadView(readView),
+ mBufferRef(buffer) {
+}
+
+std::shared_ptr<C2Buffer> ConstLinearBlockBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
}
} // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 6fba890..449c6aa 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -138,7 +138,7 @@
virtual bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) = 0;
+ sp<MediaCodecBuffer> *clientBuffer) = 0;
/**
* Register codec specific data as a buffer to be consistent with
@@ -147,9 +147,7 @@
virtual bool registerCsd(
const C2StreamCsdInfo::output * /* csd */,
size_t * /* index */,
- sp<MediaCodecBuffer> * /* codecBuffer */) {
- return false;
- }
+ sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
/**
* Release the buffer obtained from registerBuffer() and get the
@@ -181,23 +179,6 @@
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
-template <class T>
-ssize_t findBufferSlot(
- std::vector<T> *buffers,
- size_t maxSize,
- std::function<bool(const T&)> pred) {
- auto it = std::find_if(buffers->begin(), buffers->end(), pred);
- if (it == buffers->end()) {
- if (buffers->size() < maxSize) {
- buffers->emplace_back();
- return buffers->size() - 1;
- } else {
- return -1;
- }
- }
- return std::distance(buffers->begin(), it);
-}
-
sp<LinearBlockBuffer> allocateLinearBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
@@ -205,41 +186,209 @@
const C2MemoryUsage &usage) {
std::shared_ptr<C2LinearBlock> block;
- status_t err = pool->fetchLinearBlock(
- size,
- usage,
- &block);
+ status_t err = pool->fetchLinearBlock(size, usage, &block);
if (err != OK) {
return nullptr;
}
- return LinearBlockBuffer::allocate(format, block);
+ return LinearBlockBuffer::Allocate(format, block);
}
-class Buffer1D : public C2Buffer {
+class BuffersArrayImpl;
+
+/**
+ * Flexible buffer slots implementation.
+ */
+class FlexBuffersImpl {
public:
- explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+ FlexBuffersImpl() = default;
+
+ /**
+ * Assign an empty slot for a buffer and return the index. If there's no
+ * empty slot, just add one at the end and return it.
+ *
+ * \param buffer[in] a new buffer to assign a slot.
+ * \return index of the assigned slot.
+ */
+ size_t assignSlot(const sp<Codec2Buffer> &buffer) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == nullptr
+ && mBuffers[i].compBuffer.expired()) {
+ mBuffers[i].clientBuffer = buffer;
+ return i;
+ }
+ }
+ mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
+ return mBuffers.size() - 1;
+ }
+
+ /**
+ * Release the slot from the client, and get the C2Buffer object back from
+ * the previously assigned buffer. Note that the slot is not completely free
+ * until the returned C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously assigned a slot.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> releaseSlot(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == buffer) {
+ c2Buffer = mBuffers[i].clientBuffer.promote();
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+private:
+ friend class BuffersArrayImpl;
+
+ struct Entry {
+ wp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ };
+ std::vector<Entry> mBuffers;
};
-class Buffer2D : public C2Buffer {
+/**
+ * Static buffer slots implementation based on a fixed-size array.
+ */
+class BuffersArrayImpl {
public:
- explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+ /**
+ * Initialize buffer array from the original |impl|. The buffers known by
+ * the client is preserved, and the empty slots are populated so that the
+ * array size is at least |minSize|.
+ *
+ * \param impl[in] FlexBuffersImpl object used so far.
+ * \param minSize[in] minimum size of the buffer array.
+ * \param allocate[in] function to allocate a client buffer for an empty slot.
+ */
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
+ sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer.promote();
+ bool ownedByClient = (clientBuffer != nullptr);
+ if (!ownedByClient) {
+ clientBuffer = allocate();
+ }
+ mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
+ }
+ for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
+ mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
+ }
+ }
+
+ /**
+ * Grab a buffer from the underlying array which matches the criteria.
+ *
+ * \param index[out] index of the slot.
+ * \param buffer[out] the matching buffer.
+ * \param match[in] a function to test whether the buffer matches the
+ * criteria or not.
+ * \return OK if successful,
+ * NO_MEMORY if there's no available slot meets the criteria.
+ */
+ status_t grabBuffer(
+ size_t *index,
+ sp<Codec2Buffer> *buffer,
+ std::function<bool(const sp<Codec2Buffer> &)> match =
+ [](const sp<Codec2Buffer> &) { return true; }) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()
+ && match(mBuffers[i].clientBuffer)) {
+ mBuffers[i].ownedByClient = true;
+ *buffer = mBuffers[i].clientBuffer;
+ (*buffer)->meta()->clear();
+ (*buffer)->setRange(0, (*buffer)->capacity());
+ *index = i;
+ return OK;
+ }
+ }
+ return NO_MEMORY;
+ }
+
+ /**
+ * Return the buffer from the client, and get the C2Buffer object back from
+ * the buffer. Note that the slot is not completely free until the returned
+ * C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously grabbed.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> returnBuffer(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer == buffer) {
+ if (!mBuffers[i].ownedByClient) {
+ ALOGD("Client returned a buffer it does not own according to our record: %zu", i);
+ }
+ c2Buffer = mBuffers[i].clientBuffer;
+ mBuffers[i].ownedByClient = false;
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+ /**
+ * Populate |array| with the underlying buffer array.
+ *
+ * \param array[out] an array to be filled with the underlying buffer array.
+ */
+ void getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+ array->clear();
+ for (const Entry &entry : mBuffers) {
+ array->push(entry.clientBuffer);
+ }
+ }
+
+ /**
+ * The client abandoned all known buffers, so reclaim the ownership.
+ */
+ void flush() {
+ for (Entry &entry : mBuffers) {
+ entry.ownedByClient = false;
+ }
+ }
+
+private:
+ struct Entry {
+ const sp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ bool ownedByClient;
+ };
+ std::vector<Entry> mBuffers;
};
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
public:
InputBuffersArray() = default;
- void add(
- size_t index,
- const sp<LinearBlockBuffer> &clientBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -249,52 +398,30 @@
}
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) {
- mBufferArray[i].available = false;
- *index = i;
- *buffer = mBufferArray[i].clientBuffer;
- (*buffer)->meta()->clear();
- (*buffer)->setRange(0, (*buffer)->capacity());
- return true;
- }
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(index, &c2Buffer);
+ if (err == OK) {
+ c2Buffer->setFormat(mFormat);
+ *buffer = c2Buffer;
+ return true;
}
return false;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- std::shared_ptr<C2Buffer> buffer = std::make_shared<Buffer1D>(mBufferArray[i].clientBuffer->share());
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer = buffer;
- return buffer;
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
void flush() override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- }
+ mImpl.flush();
}
private:
- struct Entry {
- sp<LinearBlockBuffer> clientBuffer;
- std::weak_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -302,68 +429,42 @@
using CCodecBufferChannel::InputBuffers::InputBuffers;
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- *buffer = nullptr;
- ssize_t ret = findBufferSlot<wp<LinearBlockBuffer>>(
- &mBuffers, kMinBufferArraySize,
- [] (const auto &elem) { return elem.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- // TODO: proper max input size and usage
+ // TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
- mBuffers[ret] = newBuffer;
- *index = ret;
+ *index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
- if (it == mBuffers.end()) {
- return nullptr;
- }
- sp<LinearBlockBuffer> codecBuffer = it->promote();
- // We got sp<> reference from the caller so this should never happen..
- CHECK(codecBuffer != nullptr);
- return std::make_shared<Buffer1D>(codecBuffer->share());
+ return mImpl.releaseSlot(buffer);
}
void flush() override {
+ // This is no-op by default unless we're in array mode where we need to keep
+ // track of the flushed work.
}
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
array->setFormat(mFormat);
- // TODO
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<LinearBlockBuffer> clientBuffer = mBuffers[i].promote();
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- // TODO: read usage from intf
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
- available = true;
- }
- array->add(
- i,
- clientBuffer,
- available);
- }
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+ });
return std::move(array);
}
private:
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<wp<LinearBlockBuffer>> mBuffers;
+ FlexBuffersImpl mImpl;
};
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -396,18 +497,11 @@
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
- void add(
- size_t index,
- const sp<MediaCodecBuffer> &clientBuffer,
- const std::shared_ptr<C2Buffer> &compBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].compBuffer = compBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -419,125 +513,69 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].compBuffer = buffer;
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [buffer](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->canCopy(buffer);
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ if (!c2Buffer->copy(buffer)) {
+ return false;
+ }
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available
- && mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) {
- // TODO: proper format update
- sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
- mFormat = mFormat->dup();
- mFormat->setBuffer("csd-0", csdBuffer);
-
- memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
- mBufferArray[i].clientBuffer->setRange(0, csd->flexCount());
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [csd](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->base() != nullptr
+ && clientBuffer->capacity() >= csd->flexCount();
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ // TODO: proper format update
+ sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
+ mFormat = mFormat->dup();
+ mFormat->setBuffer("csd-0", csdBuffer);
+
+ memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
+ c2Buffer->setRange(0, csd->flexCount());
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- mBufferArray[i].available = true;
- return std::move(mBufferArray[i].compBuffer);
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
- void flush(
- const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
+ void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
(void) flushedWork;
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer.reset();
- }
+ mImpl.flush();
}
- virtual bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) = 0;
-
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
private:
- struct Entry {
- sp<MediaCodecBuffer> clientBuffer;
- std::shared_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
-class LinearOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- C2ReadView view = buffer->data().linearBlocks().front().map().get();
- if (clientBuffer->capacity() < view.capacity()) {
- ALOGV("view.capacity() = %u", view.capacity());
- return false;
- }
- clientBuffer->setRange(0u, view.capacity());
- memcpy(clientBuffer->data(), view.data(), view.capacity());
- return true;
- }
-};
-
-class GraphicOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- clientBuffer->setRange(0u, 1u);
- return true;
- }
-};
-
-// Flexible in a sense that it does not have fixed array size.
class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
@@ -545,56 +583,31 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) override {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- sp<MediaCodecBuffer> newBuffer = convert(buffer);
- mBuffers[ret] = { newBuffer, buffer };
- *index = ret;
- *codecBuffer = newBuffer;
+ sp<MediaCodecBuffer> *clientBuffer) override {
+ sp<Codec2Buffer> newBuffer = wrap(buffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
// TODO: proper format update
sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
mFormat = mFormat->dup();
mFormat->setBuffer("csd-0", csdBuffer);
- sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(mFormat, csdBuffer);
- mBuffers[ret] = { newBuffer, nullptr };
- *index = ret;
- *codecBuffer = newBuffer;
+
+ sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(mFormat, csdBuffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(
const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find_if(
- mBuffers.begin(), mBuffers.end(),
- [buffer] (const auto &elem) {
- return elem.clientBuffer.promote() == buffer;
- });
- if (it == mBuffers.end()) {
- return nullptr;
- }
- return std::move(it->bufferRef);
+ return mImpl.releaseSlot(buffer);
}
void flush(
@@ -604,27 +617,31 @@
// track of the flushed work.
}
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) = 0;
+ std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
+ std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray);
+ array->setFormat(mFormat);
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [this]() { return allocateArrayBuffer(); });
+ return std::move(array);
+ }
-protected:
- struct BufferInfo {
- // wp<> of MediaCodecBuffer for MediaCodec.
- wp<MediaCodecBuffer> clientBuffer;
- // Buffer reference to hold until clientBuffer is valid.
- std::shared_ptr<C2Buffer> bufferRef;
- };
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<BufferInfo> mBuffers;
+ virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+
+ virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+
+private:
+ FlexBuffersImpl mImpl;
};
class LinearOutputBuffers : public FlexOutputBuffers {
public:
using FlexOutputBuffers::FlexOutputBuffers;
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
if (buffer == nullptr) {
- return new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 0));
+ return new DummyContainerBuffer(mFormat, buffer);
}
if (buffer->data().type() != C2BufferData::LINEAR) {
// We expect linear output buffers from the component.
@@ -634,28 +651,12 @@
// We expect one and only one linear block from the component.
return nullptr;
}
- return ConstLinearBlockBuffer::allocate(mFormat, buffer->data().linearBlocks().front());
+ return ConstLinearBlockBuffer::Allocate(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ // TODO: proper max output size
+ return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
}
};
@@ -663,29 +664,12 @@
public:
using FlexOutputBuffers::FlexOutputBuffers;
- sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
- return new MediaCodecBuffer(
- mFormat, buffer ? new ABuffer(nullptr, 1) : new ABuffer(nullptr, 0));
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+ return new DummyContainerBuffer(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 1));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ return new DummyContainerBuffer(mFormat);
}
};
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/codec2/1.0/InputSurfaceConnection.cpp b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
index 32d6404..08b5d65 100644
--- a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
+++ b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
@@ -22,7 +22,6 @@
#include <C2BlockInternal.h>
#include <C2PlatformSupport.h>
-#include <gui/Surface.h>
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
#include <system/window.h>
diff --git a/media/libstagefright/codec2/C2.cpp b/media/libstagefright/codec2/C2.cpp
index a51b073..359d4e5 100644
--- a/media/libstagefright/codec2/C2.cpp
+++ b/media/libstagefright/codec2/C2.cpp
@@ -22,8 +22,6 @@
#include <C2ParamDef.h>
#include <C2Work.h>
-namespace android {
-
/**
* There is nothing here yet. This library is built to see what symbols and methods get
* defined as part of the API include files.
@@ -32,5 +30,4 @@
* Codec2 clients.
*/
-} // namespace android
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 01e9e43..aaefd31 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -366,26 +366,6 @@
}
}
-namespace {
-
-class GraphicBuffer : public C2Buffer {
-public:
- GraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop)
- : C2Buffer({ block->share(crop, ::android::C2Fence()) }) {}
-};
-
-
-class LinearBuffer : public C2Buffer {
-public:
- LinearBuffer(
- const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size)
- : C2Buffer({ block->share(offset, size, ::android::C2Fence()) }) {}
-};
-
-} // namespace
-
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block) {
return createLinearBuffer(block, block->offset(), block->size());
@@ -393,7 +373,7 @@
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
- return std::make_shared<LinearBuffer>(block, offset, size);
+ return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::android::C2Fence()));
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
@@ -402,9 +382,8 @@
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop) {
- return std::make_shared<GraphicBuffer>(block, crop);
+ const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
+ return C2Buffer::CreateGraphicBuffer(block->share(crop, ::android::C2Fence()));
}
} // namespace android
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
index 00e3924..508f9ae 100644
--- a/media/libstagefright/codec2/include/C2.h
+++ b/media/libstagefright/codec2/include/C2.h
@@ -17,40 +17,18 @@
#ifndef C2_H_
#define C2_H_
-#include <string>
-#include <vector>
-#include <list>
-
-#ifdef __ANDROID__
-
-#include <utils/Errors.h> // for status_t
-#include <utils/Timers.h> // for nsecs_t
-
-namespace android {
-
-#else
-
#include <errno.h>
-typedef int64_t nsecs_t;
-enum {
- GRALLOC_USAGE_SW_READ_OFTEN,
- GRALLOC_USAGE_RENDERSCRIPT,
- GRALLOC_USAGE_HW_TEXTURE,
- GRALLOC_USAGE_HW_COMPOSER,
- GRALLOC_USAGE_HW_VIDEO_ENCODER,
- GRALLOC_USAGE_PROTECTED,
- GRALLOC_USAGE_SW_WRITE_OFTEN,
- GRALLOC_USAGE_HW_RENDER,
-};
+#include <string>
-#endif
+/** nanoseconds with arbitrary origin. */
+typedef int64_t c2_nsecs_t;
/** \mainpage Codec2
*
- * Codec2 is a frame-based data processing API used by android.
+ * Codec2 is a generic frame-based data processing API.
*
- * The framework accesses components via the \ref API.
+ * The media subsystem accesses components via the \ref API.
*/
/** \ingroup API
@@ -109,53 +87,35 @@
* c2_status_t: status codes used.
*/
enum c2_status_t : int32_t {
-
/*
- * Use android status constants if available. Otherwise, define the android status constants as
- * additional enum values using POSIX errno constants.
+ * Use POSIX errno constants.
*/
-#ifndef __ANDROID__
- ALREADY_EXISTS = -EEXIST,
- BAD_VALUE = -EINVAL,
- BAD_INDEX = -EOVERFLOW,
- FAILED_TRANSACTION = -ENOTSUP,
- INVALID_OPERATION = -ENOSYS,
- NAME_NOT_FOUND = -ENOENT,
- NO_MEMORY = -ENOMEM,
- NO_INIT = -ENODEV,
- OK = 0,
- PERMISSION_DENIED = -EPERM,
- TIMED_OUT = -ETIMEDOUT,
- UNKNOWN_ERROR = -EFAULT,
- UNKNOWN_TRANSACTION = -EBADMSG,
- WOULD_BLOCK = -EWOULDBLOCK,
-#endif
-
- C2_OK = OK, ///< operation completed successfully
+ C2_OK = 0, ///< operation completed successfully
// bad input
- C2_BAD_VALUE = BAD_VALUE, ///< argument has invalid value (user error)
- C2_BAD_INDEX = BAD_INDEX, ///< argument uses invalid index (user error)
- C2_CANNOT_DO = FAILED_TRANSACTION, ///< argument/index is valid but not possible
+ C2_BAD_VALUE = EINVAL, ///< argument has invalid value (user error)
+ C2_BAD_INDEX = ENXIO, ///< argument uses invalid index (user error)
+ C2_CANNOT_DO = ENOTSUP, ///< argument/index is valid but not possible
// bad sequencing of events
- C2_DUPLICATE = ALREADY_EXISTS, ///< object already exists
- C2_NOT_FOUND = NAME_NOT_FOUND, ///< object not found
- C2_BAD_STATE = INVALID_OPERATION, ///< operation is not permitted in the current state
- C2_BLOCKING = WOULD_BLOCK, ///< operation would block but blocking is not permitted
+ C2_DUPLICATE = EEXIST, ///< object already exists
+ C2_NOT_FOUND = ENOENT, ///< object not found
+ C2_BAD_STATE = EPERM, ///< operation is not permitted in the current state
+ C2_BLOCKING = EWOULDBLOCK, ///< operation would block but blocking is not permitted
+ C2_CANCELED = EINTR, ///< operation interrupted/canceled
// bad environment
- C2_NO_MEMORY = NO_MEMORY, ///< not enough memory to complete operation
- C2_REFUSED = PERMISSION_DENIED, ///< missing permission to complete operation
+ C2_NO_MEMORY = ENOMEM, ///< not enough memory to complete operation
+ C2_REFUSED = EACCES, ///< missing permission to complete operation
- C2_TIMED_OUT = TIMED_OUT, ///< operation did not complete within timeout
+ C2_TIMED_OUT = ETIMEDOUT, ///< operation did not complete within timeout
// bad versioning
- C2_OMITTED = UNKNOWN_TRANSACTION, ///< operation is not implemented/supported (optional only)
+ C2_OMITTED = ENOSYS, ///< operation is not implemented/supported (optional only)
// unknown fatal
- C2_CORRUPTED = UNKNOWN_ERROR, ///< some unexpected error prevented the operation
- C2_NO_INIT = NO_INIT, ///< status has not been initialized
+ C2_CORRUPTED = EFAULT, ///< some unexpected error prevented the operation
+ C2_NO_INIT = ENODEV, ///< status has not been initialized
};
/**
@@ -185,11 +145,12 @@
type args& operator=(const type args&) = delete; \
type(const type args&) = delete; \
-#define C2_PURE __attribute__((pure))
-#define C2_CONST __attribute__((const))
-#define C2_HIDE __attribute__((visibility("hidden")))
-#define C2_INTERNAL __attribute__((internal_linkage))
#define C2_ALLOW_OVERFLOW __attribute__((no_sanitize("integer")))
+#define C2_CONST __attribute__((const))
+#define C2_HIDE __attribute__((visibility("hidden")))
+#define C2_INTERNAL __attribute__((internal_linkage))
+#define C2_PACK __attribute__((packed))
+#define C2_PURE __attribute__((pure))
#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
inline bool operator!=(const type &other) const { return !(*this == other); } \
@@ -548,32 +509,28 @@
/// @}
-#ifdef __ANDROID__
-} // namespace android
-#endif
-
#include <functional>
template<typename T>
-struct std::less<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() < rh.peeku();
}
};
template<typename T>
-struct std::less_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() <= rh.peeku();
}
};
template<typename T>
-struct std::greater<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() > rh.peeku();
}
};
template<typename T>
-struct std::greater_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() >= rh.peeku();
}
};
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 034075f..e49f82b 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -18,27 +18,20 @@
#define C2BUFFER_H_
#include <C2.h>
+#include <C2BufferBase.h>
#include <C2Param.h> // for C2Info
-#include <list>
#include <memory>
+#include <vector>
#ifdef __ANDROID__
-
-// #include <system/window.h>
-#include <cutils/native_handle.h>
-#include <hardware/gralloc.h> // TODO: remove
-
-typedef native_handle_t C2Handle;
-
+#include <android-C2Buffer.h>
#else
typedef void* C2Handle;
#endif
-namespace android {
-
/// \defgroup buffer Buffers
/// @{
@@ -89,7 +82,7 @@
* \retval C2_REFUSED no permission to wait for the fence (unexpected - system)
* \retval C2_CORRUPTED some unknown error prevented waiting for the fence (unexpected)
*/
- c2_status_t wait(nsecs_t timeoutNs);
+ c2_status_t wait(c2_nsecs_t timeoutNs);
/**
* Used to check if this fence is valid (if there is a chance for it to be signaled.)
@@ -550,41 +543,9 @@
ALLOCATIONS
**************************************************************************************************/
-/// \defgroup allocator Allocation and memory placement
+/// \ingroup allocator Allocation and memory placement
/// @{
-/**
- * Buffer/memory usage bits. These are used by the allocators to select optimal memory type/pool and
- * buffer layout.
- *
- * \note This struct has public fields without getters/setters. All methods are inline.
- */
-struct C2MemoryUsage {
-// public:
- // TODO: match these to gralloc1.h
- enum Consumer : uint64_t {
- // \todo do we need to distinguish often from rarely?
- CPU_READ = GRALLOC_USAGE_SW_READ_OFTEN,
- RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
- HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
- HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- enum Producer : uint64_t {
- CPU_WRITE = GRALLOC_USAGE_SW_WRITE_OFTEN,
- RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
- HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
- HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- uint64_t consumer; // e.g. input
- uint64_t producer; // e.g. output
-};
-
class C2LinearAllocation;
class C2GraphicAllocation;
@@ -2301,6 +2262,10 @@
/// @}
-} // namespace android
+// expose some objects in android namespace
+namespace android {
+ /// \deprecated
+ typedef ::C2Fence C2Fence;
+}
#endif // C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/C2BufferBase.h b/media/libstagefright/codec2/include/C2BufferBase.h
new file mode 100644
index 0000000..68411f2
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2BufferBase.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 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 C2BUFFER_BASE_H_
+#define C2BUFFER_BASE_H_
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These shall be used by the allocators to select optimal memory type/
+ * pool and buffer layout. Usage bits are conceptually separated into read and write usage, while
+ * the buffer use life-cycle is separated into producers (writers) and consumers (readers).
+ * These two concepts are related but not equivalent: consumers may only read buffers and only
+ * producers may write to buffers; note, however, that buffer producers may also want or need to
+ * read the buffers.
+ *
+ * Read and write buffer usage bits shall be or-ed to arrive at the full buffer usage. Admittedly,
+ * this does not account for the amount of reading and writing (e.g. a buffer may have one or more
+ * readers); however, the proper information necessary to properly weigh the various usages would be
+ * the amount of data read/written for each usage type. This would result in an integer array of
+ * size 64 (or the number of distinct usages) for memory usage, and likely such detailed information
+ * would not always be available.
+ *
+ * That platform-agnostic Codec 2.0 API only defines the bare minimum usages. Platforms shall define
+ * usage bits that are appropriate for the platform.
+ */
+struct C2MemoryUsage {
+// public:
+ /**
+ * Buffer read usage.
+ */
+ enum Read : uint64_t {
+ /** Buffer is read by the CPU. */
+ CPU_READ = 1 << 0,
+ /**
+ * Buffer shall only be read by trusted hardware. The definition of trusted hardware is
+ * platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * readable memory resulting in bus fault. This flag can be used when buffer access must be
+ * protected.
+ */
+ READ_PROTECTED = 1 << 1,
+ };
+
+ /**
+ * Buffer write usage.
+ */
+ enum Write : uint64_t {
+ /** Buffer is writted to by the CPU. */
+ CPU_WRITE = 1 << 2,
+ /**
+ * Buffer shall only be written to by trusted hardware. The definition of trusted hardware
+ * is platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * writable memory resulting in bus fault. This flag can be used when buffer integrity must
+ * be protected.
+ */
+ WRITE_PROTECTED = 1 << 3,
+ };
+
+ enum : uint64_t {
+ /**
+ * Buffer usage bits reserved for the platform. We don't separately reserve read and
+ * write usages as platforms may have asymmetric distribution between them.
+ */
+ PLATFORM_MASK = ~(CPU_READ | CPU_WRITE | READ_PROTECTED | WRITE_PROTECTED),
+ };
+
+ /** Create a usage from separate consumer and producer usage mask. \deprecated */
+ inline C2MemoryUsage(uint64_t consumer, uint64_t producer)
+ : expected(consumer | producer) { }
+
+ inline explicit C2MemoryUsage(uint64_t expected_)
+ : expected(expected_) { }
+
+ uint64_t expected; // expected buffer usage
+};
+
+/// @}
+
+#endif // C2BUFFER_BASE_H_
+
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index 721966b..64bd1cb 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -29,13 +29,9 @@
#include <C2Param.h>
#include <C2Work.h>
-namespace android {
-
/// \defgroup components Components
/// @{
-class C2Component;
-
struct C2FieldSupportedValuesQuery {
enum type_t : uint32_t {
POSSIBLE, ///< query all possible values regardless of other settings
@@ -947,6 +943,11 @@
/// @}
-} // namespace android
+namespace android {
+ /// \deprecated
+ typedef ::C2Component C2Component;
+ /// \deprecated
+ typedef ::C2ComponentInterface C2ComponentInterface;
+}
#endif // C2COMPONENT_H_
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
index 2a2b9de..3f149bb 100644
--- a/media/libstagefright/codec2/include/C2Config.h
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -19,8 +19,6 @@
#include <C2ParamDef.h>
-namespace android {
-
/// \defgroup config Component configuration
/// @{
@@ -262,6 +260,4 @@
/// @}
-} // namespace android
-
#endif
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
index 4d9f707..181697d 100644
--- a/media/libstagefright/codec2/include/C2Param.h
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -23,13 +23,10 @@
#include <stdint.h>
#include <algorithm>
-#include <list>
#include <string>
#include <type_traits>
-
-#define C2_PACK __attribute__((packed))
-
-namespace android {
+#include <utility>
+#include <vector>
/// \addtogroup Parameters
/// @{
@@ -89,7 +86,6 @@
*/
/// \ingroup internal
-struct _C2ParamManipulator;
/**
* Parameter base class.
@@ -733,17 +729,6 @@
};
/**
- * Structure uniquely specifying a field, an array element of a field, or a
- * parameter in a configuration
- */
-struct C2ParamOrField : public C2ParamField {
-//public:
- template<typename S>
- inline C2ParamOrField(S* param)
- : C2ParamField(param->index(), 0u, param->size()) {}
-};
-
-/**
* A shared (union) representation of numeric values
*/
class C2Value {
@@ -1376,6 +1361,4 @@
/// @}
-} // namespace android
-
#endif // C2PARAM_H_
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
index 3691e01..f0b6223 100644
--- a/media/libstagefright/codec2/include/C2ParamDef.h
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -24,8 +24,6 @@
#include <C2Param.h>
-namespace android {
-
/// \addtogroup Parameters
/// @{
@@ -905,6 +903,4 @@
/// @}
-} // namespace android
-
#endif // C2PARAM_DEF_H_
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index b6c5814..a2f02e5 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -28,8 +28,6 @@
#include <list>
#include <vector>
-namespace android {
-
/// \defgroup work Work and data processing
/// @{
@@ -75,9 +73,8 @@
// WORK
// ================================================================================================
-// c2_node_id_t-s
+/** Unique ID for a processing node. */
typedef uint32_t c2_node_id_t;
-typedef c2_node_id_t c2_node_id_t;
enum {
kParamIndexWorkOrdinal,
@@ -211,6 +208,4 @@
/// @}
-} // namespace android
-
#endif // C2WORK_H_
diff --git a/media/libstagefright/codec2/include/android-C2Buffer.h b/media/libstagefright/codec2/include/android-C2Buffer.h
new file mode 100644
index 0000000..c71f2cf
--- /dev/null
+++ b/media/libstagefright/codec2/include/android-C2Buffer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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_C2BUFFER_H_
+#define ANDROID_C2BUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+/* Use android native handle for C2Handle */
+typedef ::native_handle_t C2Handle;
+
+namespace android {
+
+/**
+ * Android platform buffer/memory usage bits.
+ */
+struct C2AndroidMemoryUsage : public C2MemoryUsage {
+// public:
+ /**
+ * Reuse gralloc flags where possible, as Codec 2.0 API only uses bits 0 and 1.
+ */
+ enum Consumer : uint64_t {
+ RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
+ HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
+ HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ enum Producer : uint64_t {
+ RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
+ HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+ HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ /**
+ * Convert from gralloc usage.
+ */
+ static C2MemoryUsage FromGrallocUsage(uint64_t usage);
+
+ /**
+ * Convert to gralloc usage.
+ */
+ uint64_t asGrallocUsage() const;
+};
+
+} // namespace android
+
+#endif // ANDROID_C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
index fc19acd..b24a416 100644
--- a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -25,9 +25,6 @@
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
namespace android {
-
-class C2Allocator;
-
namespace hardware {
namespace media {
namespace c2 {
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
index 1a29add..168b889 100644
--- a/media/libstagefright/codec2/tests/C2Param_test.cpp
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -23,8 +23,6 @@
#include <util/C2ParamUtils.h>
#include <C2ParamDef.h>
-namespace android {
-
void PrintTo(const _C2FieldId &id, ::std::ostream* os) {
*os << "@" << id._mOffset << "+" << id._mSize;
}
@@ -2397,15 +2395,11 @@
// ***********************
-}
-
#include <util/C2ParamUtils.h>
#include <C2Config.h>
#include <C2Component.h>
#include <unordered_map>
-namespace android {
-
C2ENUM(
MetadataType, int32_t,
kInvalid = -1,
@@ -2935,4 +2929,3 @@
EXPECT_EQ(15.25f, fp);
}
-} // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index b287ca8..b255eec 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -29,6 +29,36 @@
namespace android {
+namespace {
+ enum : uint64_t {
+ /**
+ * Usage mask that is passed through from gralloc to Codec 2.0 usage.
+ */
+ PASSTHROUGH_USAGE_MASK =
+ ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+ };
+
+ // verify that passthrough mask is within the platform mask
+ static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, "");
+}
+
+C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) {
+ // gralloc does not support WRITE_PROTECTED
+ return C2MemoryUsage(
+ ((usage & GRALLOC_USAGE_SW_READ_MASK) ? C2MemoryUsage::CPU_READ : 0) |
+ ((usage & GRALLOC_USAGE_SW_WRITE_MASK) ? C2MemoryUsage::CPU_WRITE : 0) |
+ ((usage & GRALLOC_USAGE_PROTECTED) ? C2MemoryUsage::READ_PROTECTED : 0) |
+ (usage & PASSTHROUGH_USAGE_MASK));
+}
+
+uint64_t C2AndroidMemoryUsage::asGrallocUsage() const {
+ // gralloc does not support WRITE_PROTECTED
+ return (((expected & C2MemoryUsage::CPU_READ) ? GRALLOC_USAGE_SW_READ_MASK : 0) |
+ ((expected & C2MemoryUsage::CPU_WRITE) ? GRALLOC_USAGE_SW_WRITE_MASK : 0) |
+ ((expected & C2MemoryUsage::READ_PROTECTED) ? GRALLOC_USAGE_PROTECTED : 0) |
+ (expected & PASSTHROUGH_USAGE_MASK));
+}
+
using ::android::hardware::graphics::allocator::V2_0::IAllocator;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index 4328a8d..a9613e4 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -20,6 +20,7 @@
#include <ion/ion.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <C2AllocatorIon.h>
#include <C2Buffer.h>
@@ -232,10 +233,10 @@
int prot = PROT_NONE;
int flags = MAP_PRIVATE;
- if (usage.consumer & C2MemoryUsage::CPU_READ) {
+ if (usage.expected & C2MemoryUsage::CPU_READ) {
prot |= PROT_READ;
}
- if (usage.producer & C2MemoryUsage::CPU_WRITE) {
+ if (usage.expected & C2MemoryUsage::CPU_WRITE) {
prot |= PROT_WRITE;
flags = MAP_SHARED;
}
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 511ffe0..47fdca1 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -18,14 +18,13 @@
#define LOG_TAG "C2Buffer"
#include <utils/Log.h>
+#include <list>
#include <map>
#include <mutex>
#include <C2BufferPriv.h>
#include <C2BlockInternal.h>
-namespace android {
-
namespace {
// This anonymous namespace contains the helper classes that allow our implementation to create
@@ -34,63 +33,63 @@
// Inherit from the parent, share with the friend.
class ReadViewBuddy : public C2ReadView {
using C2ReadView::C2ReadView;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class WriteViewBuddy : public C2WriteView {
using C2WriteView::C2WriteView;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class ConstLinearBlockBuddy : public C2ConstLinearBlock {
using C2ConstLinearBlock::C2ConstLinearBlock;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class LinearBlockBuddy : public C2LinearBlock {
using C2LinearBlock::C2LinearBlock;
- friend class ::android::C2BasicLinearBlockPool;
+ friend class ::C2BasicLinearBlockPool;
};
class AcquirableReadViewBuddy : public C2Acquirable<C2ReadView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class AcquirableWriteViewBuddy : public C2Acquirable<C2WriteView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class GraphicViewBuddy : public C2GraphicView {
using C2GraphicView::C2GraphicView;
- friend class ::android::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2ConstGraphicBlock;
+ friend class ::C2GraphicBlock;
};
class AcquirableConstGraphicViewBuddy : public C2Acquirable<const C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstGraphicBlock;
+ friend class ::C2ConstGraphicBlock;
};
class AcquirableGraphicViewBuddy : public C2Acquirable<C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class ConstGraphicBlockBuddy : public C2ConstGraphicBlock {
using C2ConstGraphicBlock::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class GraphicBlockBuddy : public C2GraphicBlock {
using C2GraphicBlock::C2GraphicBlock;
- friend class ::android::C2BasicGraphicBlockPool;
+ friend class ::C2BasicGraphicBlockPool;
};
class BufferDataBuddy : public C2BufferData {
using C2BufferData::C2BufferData;
- friend class ::android::C2Buffer;
+ friend class ::C2Buffer;
};
} // namespace
@@ -803,4 +802,3 @@
return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
}
-} // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 05b46c3..c4ed2f4 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -136,13 +136,13 @@
switch (id) {
case C2BlockPool::BASIC_LINEAR:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicLinearBlockPool>(allocator);
}
break;
case C2BlockPool::BASIC_GRAPHIC:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicGraphicBlockPool>(allocator);
}
break;
@@ -345,7 +345,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) {
+ std::function<void(::C2ComponentInterface*)> deleter) {
interface->reset();
if (mInit != C2_OK) {
return mInit;
@@ -362,7 +362,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *component,
- std::function<void(::android::C2Component*)> deleter) {
+ std::function<void(::C2Component*)> deleter) {
component->reset();
if (mInit != C2_OK) {
return mInit;
diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
index 875a8c2..977cf7b 100644
--- a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
+++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
@@ -21,8 +21,6 @@
#include <C2Buffer.h>
-namespace android {
-
class C2BasicLinearBlockPool : public C2BlockPool {
public:
explicit C2BasicLinearBlockPool(const std::shared_ptr<C2Allocator> &allocator);
@@ -73,6 +71,4 @@
const std::shared_ptr<C2Allocator> mAllocator;
};
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
index cfea104..7168498 100644
--- a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
+++ b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
@@ -22,8 +22,6 @@
#include <functional>
#include <memory>
-namespace android {
-
/**
* Component factory object that enables to create a component and/or interface from a dynamically
* linked library. This is needed because the component/interfaces are managed objects, but we
@@ -36,8 +34,8 @@
*/
class C2ComponentFactory {
public:
- typedef std::function<void(::android::C2Component*)> ComponentDeleter;
- typedef std::function<void(::android::C2ComponentInterface*)> InterfaceDeleter;
+ typedef std::function<void(::C2Component*)> ComponentDeleter;
+ typedef std::function<void(::C2ComponentInterface*)> InterfaceDeleter;
/**
* Creates a component.
@@ -81,9 +79,12 @@
virtual ~C2ComponentFactory() = default;
- typedef ::android::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
- typedef void (*DestroyCodec2FactoryFunc)(::android::C2ComponentFactory*);
+ typedef ::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
+ typedef void (*DestroyCodec2FactoryFunc)(::C2ComponentFactory*);
};
-} // namespace android
+
+namespace android {
+ typedef ::C2ComponentFactory C2ComponentFactory;
+}
#endif // STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
index 41132b9..5b995f6 100644
--- a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
+++ b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
@@ -20,8 +20,6 @@
#include <errno.h>
#include <C2.h>
-namespace android {
-
// standard ERRNO mappings
template<int N> constexpr c2_status_t _c2_errno2status_impl();
template<> constexpr c2_status_t _c2_errno2status_impl<0>() { return C2_OK; }
@@ -52,7 +50,5 @@
return _c2_map_errno_impl<N...>::map(result);
}
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
index 76b02ed..afa51ee 100644
--- a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
@@ -86,6 +86,7 @@
* \retval nullptr if the platform component store could not be obtained
*/
std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore();
+
} // namespace android
#endif // STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
index 3168248..1accc2c 100644
--- a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -21,13 +21,14 @@
#include <util/_C2MacroUtils.h>
#include <iostream>
+#include <list>
+#include <utility>
+#include <vector>
/** \file
* Utilities for parameter handling to be used by Codec2 implementations.
*/
-namespace android {
-
/// \cond INTERNAL
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
@@ -313,7 +314,5 @@
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
-} // namespace android
-
#endif // C2UTILS_PARAM_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
index 9c68369..25003cb 100644
--- a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
@@ -19,8 +19,6 @@
#include <C2Buffer.h>
-namespace android {
-
struct _C2BlockPoolData;
/**
@@ -64,7 +62,5 @@
const C2Rect &allottedCrop = C2Rect(~0u, ~0u));
};
-}
-
#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
index 5bf3009..c805830 100644
--- a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
@@ -19,8 +19,6 @@
#include <C2Param.h>
-namespace android {
-
struct C2_HIDE _C2ParamInspector {
inline static uint32_t getIndex(const C2ParamField &pf) {
return pf._mIndex;
@@ -44,7 +42,6 @@
}
};
-}
#endif // ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 8022b84..95b0c61 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -59,11 +59,6 @@
#include <C2PlatformSupport.h>
#include <C2Work.h>
-extern "C" ::android::C2ComponentFactory *CreateCodec2Factory();
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory *);
-
-#include "../avcdec/C2SoftAvcDec.h"
-
using namespace android;
using namespace std::chrono_literals;
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 6d85e83..9766b41 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -24,17 +24,100 @@
namespace android {
+class Codec2Buffer : public MediaCodecBuffer {
+public:
+ using MediaCodecBuffer::MediaCodecBuffer;
+ ~Codec2Buffer() override = default;
+
+ /**
+ * \return C2Buffer object represents this buffer.
+ */
+ virtual std::shared_ptr<C2Buffer> asC2Buffer() = 0;
+
+ /**
+ * Test if we can copy the content of |buffer| into this object.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if the content of buffer can be copied over to this buffer
+ * false otherwise.
+ */
+ virtual bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ (void)buffer;
+ return false;
+ }
+
+ /**
+ * Copy the content of |buffer| into this object. This method assumes that
+ * canCopy() check already passed.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if successful
+ * false otherwise.
+ */
+ virtual bool copy(const std::shared_ptr<C2Buffer> &buffer) {
+ (void)buffer;
+ return false;
+ }
+
+protected:
+ /**
+ * canCopy() implementation for linear buffers.
+ */
+ bool canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const;
+
+ /**
+ * copy() implementation for linear buffers.
+ */
+ bool copyLinear(const std::shared_ptr<C2Buffer> &buffer);
+};
+
+/**
+ * MediaCodecBuffer implementation on top of local linear buffer. This cannot
+ * cross process boundary so asC2Buffer() returns only nullptr.
+ */
+class LocalLinearBuffer : public Codec2Buffer {
+public:
+ using Codec2Buffer::Codec2Buffer;
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override { return nullptr; }
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+};
+
+/**
+ * MediaCodecBuffer implementation to be used only as a dummy wrapper around a
+ * C2Buffer object.
+ */
+class DummyContainerBuffer : public Codec2Buffer {
+public:
+ DummyContainerBuffer(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer = nullptr);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+private:
+ std::shared_ptr<C2Buffer> mBufferRef;
+};
+
/**
* MediaCodecBuffer implementation wraps around C2LinearBlock.
*/
-class LinearBlockBuffer : public MediaCodecBuffer {
+class LinearBlockBuffer : public Codec2Buffer {
public:
- static sp<LinearBlockBuffer> allocate(
+ /**
+ * Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+ */
+ static sp<LinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
virtual ~LinearBlockBuffer() = default;
- C2ConstLinearBlock share();
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
private:
LinearBlockBuffer(
@@ -50,20 +133,27 @@
/**
* MediaCodecBuffer implementation wraps around C2ConstLinearBlock.
*/
-class ConstLinearBlockBuffer : public MediaCodecBuffer {
+class ConstLinearBlockBuffer : public Codec2Buffer {
public:
- static sp<ConstLinearBlockBuffer> allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block);
+ /**
+ * Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+ */
+ static sp<ConstLinearBlockBuffer> Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
virtual ~ConstLinearBlockBuffer() = default;
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+
private:
ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView &&readView);
+ C2ReadView &&readView,
+ const std::shared_ptr<C2Buffer> &buffer);
ConstLinearBlockBuffer() = delete;
C2ReadView mReadView;
+ std::shared_ptr<C2Buffer> mBufferRef;
};
} // 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/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 9538c3d..63c0697 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -37,7 +37,7 @@
// not to expose other methods to the controller whose connection wasn't accepted.
// But this would be enough for now because it's the same as existing
// MediaBrowser and MediaBrowserService.
- void connect(String callingPackage, IMediaSession2Callback callback);
+ void connect(IMediaSession2Callback caller, String callingPackage);
void release(IMediaSession2Callback caller);
void setVolumeTo(IMediaSession2Callback caller, int value, int flags);
@@ -52,21 +52,24 @@
void sendCustomCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args,
in ResultReceiver receiver);
- void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
- void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
+ void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void setRating(IMediaSession2Callback caller, String mediaId, in Bundle rating);
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Get library service specific
//////////////////////////////////////////////////////////////////////////////////////////////
- void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
- void getItem(IMediaSession2Callback callback, String mediaId);
- void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
+ // library service specific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ void getBrowserRoot(IMediaSession2Callback caller, in Bundle rootHints);
+ void getItem(IMediaSession2Callback caller, String mediaId);
+ void getChildren(IMediaSession2Callback caller, String parentId, int page, int pageSize,
in Bundle extras);
- void search(IMediaSession2Callback callback, String query, in Bundle extras);
- void getSearchResult(IMediaSession2Callback callback, String query, int page, int pageSize,
+ void search(IMediaSession2Callback caller, String query, in Bundle extras);
+ void getSearchResult(IMediaSession2Callback caller, String query, int page, int pageSize,
in Bundle extras);
+ void subscribe(IMediaSession2Callback caller, String parentId, in Bundle extras);
+ void unsubscribe(IMediaSession2Callback caller, String parentId);
}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index b3aa59c..9a0be7a 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -36,7 +36,7 @@
// TODO(jaewan): Handle when the playlist becomes too huge.
void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState,
- in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, int ratingType,
+ in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist,
in PendingIntent sessionActivity);
void onDisconnected();
@@ -49,8 +49,10 @@
//////////////////////////////////////////////////////////////////////////////////////////////
void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
void onItemLoaded(String mediaId, in Bundle result);
- void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
- void onSearchResultLoaded(String query, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
+ void onChildrenChanged(String rootMediaId, int childCount, in Bundle extras);
+ void onChildrenLoaded(String parentId, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
+ void onSearchResultChanged(String query, int itemCount, in Bundle extras);
+ void onSearchResultLoaded(String query, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 76da42b..c095187 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -20,7 +20,6 @@
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaItem2;
-import android.media.MediaSession2.CommandButton;
import android.media.SessionToken2;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
@@ -64,12 +63,36 @@
@Override
public void subscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.subscribe(getControllerStub(), parentId, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void unsubscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ public void unsubscribe_impl(String parentId) {
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.unsubscribe(getControllerStub(), parentId);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
@@ -170,17 +193,29 @@
});
}
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onChildrenLoaded(String parentId, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ mCallback.onChildrenLoaded(parentId, page, pageSize, result, extras);
});
}
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onSearchResultLoaded(query, page, pageSize, extras, result);
+ mCallback.onSearchResultChanged(query, itemCount, extras);
+ });
+ }
+
+ public void onSearchResultLoaded(String query, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onSearchResultLoaded(query, page, pageSize, result, extras);
+ });
+ }
+
+ public void onChildrenChanged(final String parentId, int childCount, final Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onChildrenChanged(parentId, childCount, extras);
});
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 5af4240..77db355 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -75,8 +75,6 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private int mRatingType;
- @GuardedBy("mLock")
private PendingIntent mSessionActivity;
@GuardedBy("mLock")
private CommandGroup mCommandGroup;
@@ -164,7 +162,7 @@
private void connectToSession(IMediaSession2 sessionBinder) {
try {
- sessionBinder.connect(mContext.getPackageName(), mSessionCallbackStub);
+ sessionBinder.connect(mSessionCallbackStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Failed to call connection request. Framework will retry"
+ " automatically");
@@ -286,11 +284,6 @@
}
@Override
- public int getRatingType_impl() {
- return mRatingType;
- }
-
- @Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
final IMediaSession2 binder = mSessionBinder;
@@ -403,9 +396,26 @@
// TODO(jaewan): Handle.
}
}
+
@Override
- public void setRating_impl(Rating2 rating) {
- // TODO(jaewan): Implement
+ public void setRating_impl(String mediaId, Rating2 rating) {
+ if (mediaId == null) {
+ throw new IllegalArgumentException("mediaId shouldn't be null");
+ }
+ if (rating == null) {
+ throw new IllegalArgumentException("rating shouldn't be null");
+ }
+
+ final IMediaSession2 binder = mSessionBinder;
+ if (binder != null) {
+ try {
+ binder.setRating(mSessionCallbackStub, mediaId, rating.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ // TODO(jaewan): Handle.
+ }
}
@Override
@@ -563,7 +573,7 @@
// Should be used without a lock to prevent potential deadlock.
void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
- final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
+ final PlaylistParams params, final List<MediaItem2> playlist,
final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -591,7 +601,6 @@
mPlaybackInfo = info;
mPlaylistParams = params;
mPlaylist = playlist;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 52db74e..b9d2fa4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,12 +26,12 @@
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider;
import android.os.Bundle;
+import android.text.TextUtils;
import com.android.media.MediaSession2Impl.BuilderBaseImpl;
@@ -68,10 +68,9 @@
implements MediaLibrarySessionProvider {
public MediaLibrarySessionImpl(Context context,
MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider,
- int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
+ PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, volumeProvider, ratingType, sessionActivity,
- callbackExecutor, callback);
+ super(context, player, id, volumeProvider, sessionActivity, callbackExecutor, callback);
// Don't put any extra initialization here. Here's the reason.
// System service will recognize this session inside of the super constructor and would
// connect to this session assuming that initialization is finished. However, if any
@@ -96,13 +95,36 @@
@Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
- Bundle options) {
- // TODO(jaewan): Implements
+ int childCount, Bundle extras) {
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, childCount,
+ extras);
}
@Override
- public void notifyChildrenChanged_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implements
+ public void notifyChildrenChanged_impl(String parentId, int childCount, Bundle extras) {
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(parentId, childCount, extras);
+ }
+
+ @Override
+ public void notifySearchResultChanged_impl(ControllerInfo controller, String query,
+ int itemCount, Bundle extras) {
+ ensureCallingThread();
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras);
}
}
@@ -117,7 +139,7 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
@@ -148,4 +170,4 @@
return mExtras;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index 852029a..e174d91 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -125,7 +125,7 @@
@Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
- playlist, int ratingType, PendingIntent sessionActivity) {
+ playlist, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
if (DEBUG) {
@@ -146,7 +146,7 @@
PlaybackState2.fromBundle(context, playbackState),
PlaybackInfoImpl.fromBundle(context, playbackInfo),
PlaylistParams.fromBundle(context, playlistParams),
- list, ratingType, sessionActivity);
+ list, sessionActivity);
}
@Override
@@ -244,8 +244,8 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -265,12 +265,29 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ browser.onChildrenLoaded(parentId, page, pageSize, result, extras);
}
@Override
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras)
+ throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onSearchResultChanged(query, itemCount, extras);
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -290,6 +307,22 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onSearchResultLoaded(query, page, pageSize, extras, result);
+ browser.onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onChildrenChanged(parentId, childCount, extras);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 4a9a729..a32ea0a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -38,7 +38,7 @@
import android.media.MediaLibraryService2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -62,6 +62,7 @@
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -84,8 +85,7 @@
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
- private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
- private final int mRatingType;
+ private final ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
private final PendingIntent mSessionActivity;
// mPlayer is set to null when the session is closed, and we shouldn't throw an exception
@@ -111,7 +111,7 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private MyPlaybackListener mListener;
+ private MyEventCallback mEventCallback;
/**
* Can be only called by the {@link Builder#build()}.
@@ -119,13 +119,13 @@
* @param context
* @param player
* @param id
- * @param callback
* @param volumeProvider
- * @param ratingType
* @param sessionActivity
+ * @param callbackExecutor
+ * @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerInterface player, String id,
- VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity,
+ VolumeProvider2 volumeProvider, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
mInstance = createInstance();
@@ -136,7 +136,6 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -225,19 +224,20 @@
}
private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
- PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+ final PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
- if (mPlayer != null && mListener != null) {
+ if (mPlayer != null && mEventCallback != null) {
// This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
}
mPlayer = player;
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
+ mEventCallback = new MyEventCallback(this, player);
+ player.registerEventCallback(mCallbackExecutor, mEventCallback);
mVolumeProvider = volumeProvider;
mPlaybackInfo = info;
}
mSessionStub.notifyPlaybackInfoChanged(info);
+ notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState());
}
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -293,7 +293,7 @@
synchronized (mLock) {
if (mPlayer != null) {
// close can be called multiple times
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
mPlayer = null;
}
}
@@ -522,32 +522,31 @@
}
@Override
- public void addPlaybackListener_impl(Executor executor, PlaybackListener listener) {
+ public void registerPlayerEventCallback_impl(Executor executor, EventCallback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor shouldn't be null");
}
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- if (PlaybackListenerHolder.contains(mListeners, listener)) {
- Log.w(TAG, "listener is already added. Ignoring.");
+ if (mCallbacks.get(callback) != null) {
+ Log.w(TAG, "callback is already added. Ignoring.");
return;
}
- mListeners.add(new PlaybackListenerHolder(executor, listener));
- executor.execute(() -> listener.onPlaybackChanged(getInstance().getPlaybackState()));
+ mCallbacks.put(callback, executor);
+ // TODO(jaewan): Double check if we need this.
+ final PlaybackState2 state = getInstance().getPlaybackState();
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
@Override
- public void removePlaybackListener_impl(PlaybackListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ public void unregisterPlayerEventCallback_impl(EventCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (idx >= 0) {
- mListeners.remove(idx);
- }
+ mCallbacks.remove(callback);
}
@Override
@@ -580,7 +579,7 @@
// 1. Allow calls from random threads for all methods.
// 2. Allow calls from random threads for all methods, except for the
// {@link #setPlayer()}.
- private void ensureCallingThread() {
+ void ensureCallingThread() {
// TODO(jaewan): Uncomment or remove
/*
if (mHandler.getLooper() != Looper.myLooper()) {
@@ -588,19 +587,35 @@
}*/
}
- private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
- List<PlaybackListenerHolder> listeners = new ArrayList<>();
+ private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
synchronized (mLock) {
- listeners.addAll(mListeners);
+ callbacks.putAll(mCallbacks);
}
- // Notify to listeners added directly to this session
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).postPlaybackChange(state);
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
// Notify to controllers as well.
mSessionStub.notifyPlaybackStateChangedNotLocked(state);
}
+ private void notifyErrorNotLocked(String mediaId, int what, int extra) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+ synchronized (mLock) {
+ callbacks.putAll(mCallbacks);
+ }
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onError(mediaId, what, extra));
+ }
+ // TODO(jaewan): Notify to controllers as well.
+ }
+
Context getContext() {
return mContext;
}
@@ -621,6 +636,10 @@
return mCallback;
}
+ MediaSession2Stub getSessionStub() {
+ return mSessionStub;
+ }
+
VolumeProvider2 getVolumeProvider() {
return mVolumeProvider;
}
@@ -631,33 +650,47 @@
}
}
- int getRatingType() {
- return mRatingType;
- }
-
PendingIntent getSessionActivity() {
return mSessionActivity;
}
- private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+ private static class MyEventCallback implements EventCallback {
private final WeakReference<MediaSession2Impl> mSession;
private final MediaPlayerInterface mPlayer;
- private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
+ private MyEventCallback(MediaSession2Impl session, MediaPlayerInterface player) {
mSession = new WeakReference<>(session);
mPlayer = player;
}
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
MediaSession2Impl session = mSession.get();
if (mPlayer != session.mInstance.getPlayer()) {
Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
new IllegalStateException());
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackStateChanged from player, state=" + state);
+ }
session.notifyPlaybackStateChangedNotLocked(state);
}
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ MediaSession2Impl session = mSession.get();
+ if (mPlayer != session.mInstance.getPlayer()) {
+ Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
+ new IllegalStateException());
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onError from player, mediaId=" + mediaId + ", what=" + what
+ + ", extra=" + extra);
+ }
+ session.notifyErrorNotLocked(mediaId, what, extra);
+ }
}
public static final class CommandImpl implements CommandProvider {
@@ -1167,7 +1200,6 @@
Executor mCallbackExecutor;
C mCallback;
VolumeProvider2 mVolumeProvider;
- int mRatingType;
PendingIntent mSessionActivity;
/**
@@ -1196,10 +1228,6 @@
mVolumeProvider = volumeProvider;
}
- public void setRatingType_impl(int type) {
- mRatingType = type;
- }
-
public void setSessionActivity_impl(PendingIntent pi) {
mSessionActivity = pi;
}
@@ -1239,7 +1267,7 @@
mCallback = new SessionCallback(mContext);
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 759d580..914f85e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -28,6 +28,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.PlaybackState2;
+import android.media.Rating2;
import android.media.VolumeProvider2;
import android.net.Uri;
import android.os.Binder;
@@ -47,6 +48,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
public class MediaSession2Stub extends IMediaSession2.Stub {
@@ -63,6 +66,8 @@
@GuardedBy("mLock")
private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<ControllerInfo, Set<String>> mSubscriptions = new ArrayMap<>();
public MediaSession2Stub(MediaSession2Impl session) {
mSession = new WeakReference<>(session);
@@ -76,11 +81,11 @@
mControllers.clear();
}
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder();
try {
// Should be used without a lock hold to prevent potential deadlock.
- callbackBinder.onDisconnected();
+ controllerBinder.onDisconnected();
} catch (RemoteException e) {
// Controller is gone. Should be fine because we're destroying.
}
@@ -117,12 +122,12 @@
//////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void connect(String callingPackage, final IMediaSession2Callback callback)
+ public void connect(final IMediaSession2Callback caller, String callingPackage)
throws RuntimeException {
final MediaSession2Impl sessionImpl = getSession();
final Context context = sessionImpl.getContext();
final ControllerInfo request = new ControllerInfo(context,
- Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
+ Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller);
sessionImpl.getCallbackExecutor().execute(() -> {
final MediaSession2Impl session = mSession.get();
if (session == null) {
@@ -134,7 +139,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);
@@ -155,11 +160,10 @@
// TODO(jaewan): Should we protect getting playback state?
final PlaybackState2 state = session.getInstance().getPlaybackState();
final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
- final Bundle playbackInfoBundle =
- ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+ final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
+ session.getPlaybackInfo().getProvider()).toBundle();
final PlaylistParams params = session.getInstance().getPlaylistParams();
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
- final int ratingType = session.getRatingType();
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist = session.getInstance().getPlaylist();
final List<Bundle> playlistBundle = new ArrayList<>();
@@ -182,9 +186,9 @@
return;
}
try {
- callback.onConnected(MediaSession2Stub.this,
+ caller.onConnected(MediaSession2Stub.this,
allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle,
- paramsBundle, playlistBundle, ratingType, sessionActivity);
+ paramsBundle, playlistBundle, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
@@ -194,7 +198,7 @@
Log.d(TAG, "Rejecting connection, request=" + request);
}
try {
- callback.onDisconnected();
+ caller.onDisconnected();
} catch (RemoteException e) {
// Controller may be died prematurely.
// Not an issue because we'll ignore it anyway.
@@ -210,6 +214,7 @@
if (DEBUG) {
Log.d(TAG, "releasing " + controllerInfo);
}
+ mSubscriptions.remove(controllerInfo);
}
}
@@ -389,7 +394,7 @@
@Override
public void prepareFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -403,13 +408,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromUri(controller, uri, extra);
+ session.getCallback().onPrepareFromUri(controller, uri, extras);
});
}
@Override
public void prepareFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -423,13 +428,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromSearch(controller, query, extra);
+ session.getCallback().onPrepareFromSearch(controller, query, extras);
});
}
@Override
public void prepareFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -443,13 +448,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPrepareFromMediaId(controller, mediaId, extras);
});
}
@Override
public void playFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -463,13 +468,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromUri(controller, uri, extra);
+ session.getCallback().onPlayFromUri(controller, uri, extras);
});
}
@Override
public void playFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -483,13 +488,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromSearch(controller, query, extra);
+ session.getCallback().onPlayFromSearch(controller, query, extras);
});
}
@Override
public void playFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -503,7 +508,28 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPlayFromMediaId(controller, mediaId, extras);
+ });
+ }
+
+ @Override
+ public void setRating(final IMediaSession2Callback caller, final String mediaId,
+ final Bundle ratingBundle) {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ Rating2 rating = Rating2Impl.fromBundle(session.getContext(), ratingBundle);
+ session.getCallback().onSetRating(controller, mediaId, rating);
});
}
@@ -621,7 +647,7 @@
try {
controllerImpl.getControllerBinder().onChildrenLoaded(
- parentId, page, pageSize, extras, bundleList);
+ parentId, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -704,7 +730,7 @@
try {
controllerImpl.getControllerBinder().onSearchResultLoaded(
- query, page, pageSize, extras, bundleList);
+ query, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -712,6 +738,56 @@
});
}
+ @Override
+ public void subscribe(final IMediaSession2Callback caller, final String parentId,
+ final Bundle option) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onSubscribed(controller, parentId, option);
+ synchronized (mLock) {
+ Set<String> subscription = mSubscriptions.get(controller);
+ if (subscription == null) {
+ subscription = new HashSet<>();
+ mSubscriptions.put(controller, subscription);
+ }
+ subscription.add(parentId);
+ }
+ });
+ }
+
+ @Override
+ public void unsubscribe(final IMediaSession2Callback caller, final String parentId) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "unsubscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onUnsubscribed(controller, parentId);
+ synchronized (mLock) {
+ mSubscriptions.remove(controller);
+ }
+ });
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// APIs for MediaSession2Impl
//////////////////////////////////////////////////////////////////////////////////////////////
@@ -731,11 +807,11 @@
public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
final Bundle bundle = state != null ? state.toBundle() : null;
- callbackBinder.onPlaybackStateChanged(bundle);
+ controllerBinder.onPlaybackStateChanged(bundle);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -746,7 +822,7 @@
public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
// TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
// is rejected. Handle the case.
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
List<Bundle> layoutBundles = new ArrayList<>();
@@ -756,7 +832,7 @@
layoutBundles.add(bundle);
}
}
- callbackBinder.onCustomLayoutChanged(layoutBundles);
+ controllerBinder.onCustomLayoutChanged(layoutBundles);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -778,10 +854,10 @@
}
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistChanged(bundleList);
+ controllerBinder.onPlaylistChanged(bundleList);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -792,10 +868,10 @@
public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistParamsChanged(params.toBundle());
+ controllerBinder.onPlaylistParamsChanged(params.toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -806,11 +882,11 @@
public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaybackInfoChanged(
- ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
+ controllerBinder.onPlaybackInfoChanged(((MediaController2Impl.PlaybackInfoImpl)
+ playbackInfo.getProvider()).toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -827,9 +903,9 @@
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
- if (getController(callbackBinder) == null) {
+ if (getController(controllerBinder) == null) {
throw new IllegalArgumentException("Controller is gone");
}
sendCustomCommandInternal(controller, command, args, receiver);
@@ -847,14 +923,64 @@
private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
Bundle commandBundle = command.toBundle();
- callbackBinder.sendCustomCommand(commandBundle, args, receiver);
+ controllerBinder.sendCustomCommand(commandBundle, args, receiver);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
}
}
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // APIs for MediaLibrarySessionImpl
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
+ Bundle extras) {
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onSearchResultChanged(query, itemCount, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
+ public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
+ int childCount, Bundle extras) {
+ // TODO(jaewan): Handle when controller is disconnected and no longer valid.
+ // Note: Commands may be sent while onConnected() is running. Should we also
+ // consider it as error?
+ notifyChildrenChangedInternalNotLocked(controller, parentId, childCount, extras);
+ }
+
+ public void notifyChildrenChangedNotLocked(String parentId, int childCount, Bundle extras) {
+ final List<ControllerInfo> controllers = getControllers();
+ for (int i = 0; i < controllers.size(); i++) {
+ notifyChildrenChangedInternalNotLocked(controllers.get(i), parentId, childCount,
+ extras);
+ }
+ }
+
+ public void notifyChildrenChangedInternalNotLocked(final ControllerInfo controller,
+ String parentId, int childCount, Bundle extras) {
+ // Ensure subscription
+ synchronized (mLock) {
+ Set<String> subscriptions = mSubscriptions.get(controller);
+ if (subscriptions == null || !subscriptions.contains(parentId)) {
+ return;
+ }
+ }
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onChildrenChanged(parentId, childCount, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle controller removed?
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 8773df4..aa5ac84 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -42,7 +42,7 @@
private static final boolean DEBUG = true; // TODO(jaewan): Change this.
private final MediaSessionService2 mInstance;
- private final PlaybackListener mListener = new SessionServicePlaybackListener();
+ private final EventCallback mCallback = new SessionServiceEventCallback();
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -94,7 +94,7 @@
+ ", but got " + mSession);
}
// TODO(jaewan): Uncomment here.
- // mSession.addPlaybackListener(mListener, mSession.getExecutor());
+ // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor());
}
@TokenType int getSessionType() {
@@ -135,9 +135,9 @@
mediaNotification.getNotification());
}
- private class SessionServicePlaybackListener implements PlaybackListener {
+ private class SessionServiceEventCallback implements EventCallback {
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
if (state == null) {
Log.w(TAG, "Ignoring null playback state");
return;
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 4241f85..0000000
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package com.android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.media.PlaybackState2;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
index 5eb1129..ee8d6d7 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -30,7 +30,6 @@
private static final String KEY_BUFFERED_POSITION =
"android.media.playbackstate2.buffered_position";
private static final String KEY_SPEED = "android.media.playbackstate2.speed";
- private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
@@ -42,11 +41,9 @@
private final float mSpeed;
private final long mBufferedPosition;
private final long mActiveItemId;
- private final CharSequence mErrorMessage;
public PlaybackState2Impl(Context context, PlaybackState2 instance, int state, long position,
- long updateTime, float speed, long bufferedPosition, long activeItemId,
- CharSequence error) {
+ long updateTime, float speed, long bufferedPosition, long activeItemId) {
mContext = context;
mInstance = instance;
mState = state;
@@ -55,7 +52,6 @@
mUpdateTime = updateTime;
mBufferedPosition = bufferedPosition;
mActiveItemId = activeItemId;
- mErrorMessage = error;
}
@Override
@@ -67,7 +63,6 @@
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", active item id=").append(mActiveItemId);
- bob.append(", error=").append(mErrorMessage);
bob.append("}");
return bob.toString();
}
@@ -93,11 +88,6 @@
}
@Override
- public CharSequence getErrorMessage_impl() {
- return mErrorMessage;
- }
-
- @Override
public long getLastPositionUpdateTime_impl() {
return mUpdateTime;
}
@@ -116,7 +106,6 @@
bundle.putFloat(KEY_SPEED, mSpeed);
bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
- bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
return bundle;
}
@@ -129,18 +118,15 @@
|| !bundle.containsKey(KEY_UPDATE_TIME)
|| !bundle.containsKey(KEY_SPEED)
|| !bundle.containsKey(KEY_BUFFERED_POSITION)
- || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
- || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
+ || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)) {
return null;
}
-
return new PlaybackState2(context,
bundle.getInt(KEY_STATE),
bundle.getLong(KEY_POSITION),
bundle.getLong(KEY_UPDATE_TIME),
bundle.getFloat(KEY_SPEED),
bundle.getLong(KEY_BUFFERED_POSITION),
- bundle.getLong(KEY_ACTIVE_ITEM_ID),
- bundle.getCharSequence(KEY_ERROR_MESSAGE));
+ bundle.getLong(KEY_ACTIVE_ITEM_ID));
}
}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 46812e7..994824d 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -44,7 +44,6 @@
import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
-import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaBrowser2Provider;
@@ -60,7 +59,6 @@
import android.media.update.MediaSessionService2Provider;
import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.media.update.PlaybackState2Provider;
-import android.media.update.SessionPlayer2Provider;
import android.media.update.SessionToken2Provider;
import android.media.update.StaticProvider;
import android.media.update.VideoView2Provider;
@@ -230,12 +228,6 @@
}
@Override
- public SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance) {
- // TODO(jaewan): Implement this
- return null;
- }
-
- @Override
public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
@@ -302,9 +294,9 @@
@Override
public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
int state, long position, long updateTime, float speed, long bufferedPosition,
- long activeItemId, CharSequence error) {
+ long activeItemId) {
return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
- bufferedPosition, activeItemId, error);
+ bufferedPosition, activeItemId);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index ea7e714..a8ce18b 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,9 @@
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 VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
+ private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener;
private VideoViewInterface mCurrentView;
private VideoTextureView mTextureView;
@@ -351,13 +346,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;
@@ -365,35 +359,13 @@
}
@Override
- public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
- mOnPreparedListener = l;
+ public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
+ mViewTypeChangedListener = l;
}
@Override
- public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
- mOnCompletionListener = l;
- }
-
- @Override
- public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
- mOnErrorListener = l;
- }
-
- @Override
- public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
- mOnInfoListener = l;
- }
-
- @Override
- public void setOnViewTypeChangedListener_impl(Executor executor,
- VideoView2.OnViewTypeChangedListener l) {
- mOnViewTypeChangedListener = l;
- }
-
- @Override
- public void setFullScreenRequestListener_impl(Executor executor,
- VideoView2.OnFullScreenRequestListener l) {
- mOnFullScreenRequestListener = l;
+ public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) {
+ mFullScreenRequestListener = l;
}
@Override
@@ -492,8 +464,8 @@
Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
}
mCurrentView = view;
- if (mOnViewTypeChangedListener != null) {
- mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
+ if (mViewTypeChangedListener != null) {
+ mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -845,9 +817,6 @@
updatePlaybackState();
extractSubtitleTracks();
- if (mOnPreparedListener != null) {
- mOnPreparedListener.onPrepared(mInstance);
- }
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
}
@@ -908,9 +877,6 @@
mTargetState = STATE_PLAYBACK_COMPLETED;
updatePlaybackState();
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
}
@@ -920,10 +886,6 @@
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 (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
extractSubtitleTracks();
}
@@ -944,47 +906,6 @@
if (mMediaControlView != null) {
mMediaControlView.setVisibility(View.GONE);
}
-
- /* If an error handler has been supplied, use it and finish. */
- if (mOnErrorListener != null) {
- if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
- return true;
- }
- }
-
- /* 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,8 +941,8 @@
mInstance.setSubtitleEnabled(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
- if (mOnFullScreenRequestListener != null) {
- mOnFullScreenRequestListener.onFullScreenRequest(
+ if (mFullScreenRequestListener != null) {
+ mFullScreenRequestListener.onFullScreenRequest(
mInstance,
args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
}
@@ -1033,7 +954,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/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index b60fde3..7e93232 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -21,16 +21,21 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.os.Process;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
@@ -68,8 +73,12 @@
// Browser specific callbacks
default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
default void onItemLoaded(String mediaId, MediaItem2 result) {}
- default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {}
+ default void onChildrenChanged(String parentId, int childCount, Bundle extras) {}
+ default void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
+ default void onSearchResultChanged(String query, int itemCount, Bundle extras) {}
+ default void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
}
@Test
@@ -83,7 +92,7 @@
public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
assertTrue(TestUtils.equals(param, rootHints));
assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
- assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
+ assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRAS, rootExtra));
latch.countDown();
}
};
@@ -141,23 +150,22 @@
final String parentId = MockMediaLibraryService2.PARENT_ID;
final int page = 4;
final int pageSize = 10;
- final Bundle options = new Bundle();
- options.putString(TAG, TAG);
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertEquals(parentId, parentIdOut);
assertEquals(page, pageOut);
assertEquals(pageSize, pageSizeOut);
- assertTrue(TestUtils.equals(options, optionsOut));
+ assertTrue(TestUtils.equals(extras, extrasOut));
assertNotNull(result);
int fromIndex = (page - 1) * pageSize;
- int toIndex = Math.min(page * pageSize,
- MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+ int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
// Compare the given results with originals.
for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
@@ -173,7 +181,7 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
- browser.getChildren(parentId, page, pageSize, options);
+ browser.getChildren(parentId, page, pageSize, extras);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -185,7 +193,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNotNull(result);
assertEquals(0, result.size());
latch.countDown();
@@ -206,7 +214,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNull(result);
latch.countDown();
}
@@ -218,6 +226,224 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testSearch() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latchForSearch = new CountDownLatch(1);
+ final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latchForSearch.countDown();
+ }
+
+ @Override
+ public void onSearchResultLoaded(String queryOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(
+ page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ assertEquals(
+ MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latchForGetSearchResult.countDown();
+ }
+ };
+
+ // Request the search.
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ // Get the search result.
+ browser.getSearchResult(query, page, pageSize, extras);
+ assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchTakesTime() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(
+ MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchEmptyResult() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(0, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+ final String testParentId = "testSubscribeId";
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId, testParentId);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.subscribe(testParentId, testExtras);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testUnsubscribe() throws InterruptedException {
+ final String testParentId = "testUnsubscribeId";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onUnsubscribed(ControllerInfo info, String parentId) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.unsubscribe(testParentId);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+ // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+ final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+ final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+ final int testChildrenCount = 101;
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId1, testParentId1);
+
+ final CountDownLatch latch = new CountDownLatch(3);
+ final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ final MockMediaLibraryService2 service = (MockMediaLibraryService2)
+ TestServiceRegistry.getInstance().getServiceInstance();
+ final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
+ session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
+ return super.onConnect(controller);
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ final MediaLibrarySession session = (MediaLibrarySession) mSession;
+ session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+ session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+ testExtras);
+ }
+ }
+ };
+ final TestBrowserCallbackInterface controllerCallbackProxy =
+ new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenChanged(String parentId, int childCount,
+ Bundle extras) {
+ switch ((int) latch.getCount()) {
+ case 3:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertNull(extras);
+ latch.countDown();
+ break;
+ case 2:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ break;
+ default:
+ // Unexpected call.
+ fail();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(sessionCallbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ final MediaBrowser2 browser = (MediaBrowser2) createController(
+ token, true, controllerCallbackProxy);
+ final MockMediaLibraryService2 service =
+ (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = service.getSession();
+ assertTrue(mSession instanceof MediaLibrarySession);
+ browser.subscribe(testParentId2, null);
+ // This ensures that onChildrenChanged() is only called for the expected reasons.
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
public static class TestBrowserCallback extends BrowserCallback
implements WaitForConnectionInterface {
private final TestControllerCallbackInterface mCallbackProxy;
@@ -287,12 +513,40 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {
- super.onChildrenLoaded(parentId, page, pageSize, options, result);
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onChildrenLoaded(parentId, page, pageSize, result, extras);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
((TestBrowserCallbackInterface) mCallbackProxy)
- .onChildrenLoaded(parentId, page, pageSize, options, result);
+ .onChildrenLoaded(parentId, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
+ super.onSearchResultChanged(query, itemCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultChanged(query, itemCount, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onSearchResultLoaded(query, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ super.onChildrenChanged(parentId, childCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onChildrenChanged(parentId, childCount, extras);
}
}
@@ -329,4 +583,4 @@
return mCallback;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 7bf0fd2..e162f1d 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -19,7 +19,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
@@ -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;
@@ -62,7 +60,6 @@
@FlakyTest
public class MediaController2Test extends MediaSession2TestBase {
private static final String TAG = "MediaController2Test";
- private static final int DEFAULT_RATING_TYPE = Rating2.RATING_5_STARS;
PendingIntent mIntent;
MediaSession2 mSession;
@@ -80,7 +77,6 @@
mPlayer = new MockPlayer(1);
mSession = new MediaSession2.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
- .setRatingType(DEFAULT_RATING_TYPE)
.setSessionActivity(mIntent)
.setId(TAG).build();
mController = createController(mSession.getToken());
@@ -212,11 +208,6 @@
}
@Test
- public void testGetRatingType() throws InterruptedException {
- assertEquals(DEFAULT_RATING_TYPE, mController.getRatingType());
- }
-
- @Test
public void testGetSessionActivity() throws InterruptedException {
PendingIntent sessionActivity = mController.getSessionActivity();
assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
@@ -391,7 +382,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -415,7 +406,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -439,7 +430,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -460,11 +451,12 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback(mContext) {
@Override
- public void onPrepareFromSearch(ControllerInfo controller, String query, Bundle extras) {
+ public void onPrepareFromSearch(ControllerInfo controller, String query,
+ Bundle extras) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -488,7 +480,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -512,7 +504,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -525,6 +517,34 @@
}
@Test
+ public void testSetRating() throws InterruptedException {
+ final int ratingType = Rating2.RATING_5_STARS;
+ final float ratingValue = 3.5f;
+ final Rating2 rating = Rating2.newStarRating(mContext, ratingType, ratingValue);
+ final String mediaId = "media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback(mContext) {
+ @Override
+ public void onSetRating(ControllerInfo controller, String mediaIdOut,
+ Rating2 ratingOut) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(mediaId, mediaIdOut);
+ assertEquals(rating, ratingOut);
+ latch.countDown();
+ }
+ };
+
+ try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testSetRating").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.setRating(mediaId, rating);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
public void testIsConnected() throws InterruptedException {
assertTrue(mController.isConnected());
sHandler.postAndSync(()->{
@@ -601,7 +621,6 @@
}
}
- @Ignore
@Test
public void testGetServiceToken() {
SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
@@ -612,20 +631,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);
@@ -639,13 +657,13 @@
if (Process.myUid() == controller.getUid()) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
- latch.countDown();;
+ latch.countDown();
}
return super.onConnect(controller);
}
};
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
@@ -657,7 +675,7 @@
// TODO(jaewan): Add equivalent tests again
/*
final CountDownLatch latch = new CountDownLatch(1);
- mController.addPlaybackListener((state) -> {
+ mController.registerPlayerEventCallback((state) -> {
assertNotNull(state);
assertEquals(PlaybackState.STATE_REWINDING, state.getState());
latch.countDown();
@@ -668,15 +686,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 +722,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 +761,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.
@@ -764,16 +774,19 @@
private void testNoInteraction() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- final PlaybackListener playbackListener = (state) -> {
- fail("Controller shouldn't be notified about change in session after the close.");
- latch.countDown();
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ fail("Controller shouldn't be notified about change in session after the close.");
+ latch.countDown();
+ }
};
// TODO(jaewan): Add equivalent tests again
/*
- mController.addPlaybackListener(playbackListener, sHandler);
+ mController.registerPlayerEventCallback(playbackListener, sHandler);
mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mController.removePlaybackListener(playbackListener);
+ mController.unregisterPlayerEventCallback(playbackListener);
*/
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index f5ac6aa..9de4ce7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -28,9 +28,8 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.media.AudioManager;
import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -49,7 +48,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -253,51 +251,67 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
- // TODO(jaewan): Re-enable test..
- @Ignore
@Test
- public void testPlaybackStateChangedListener() throws InterruptedException {
- final CountDownLatch latch = new CountDownLatch(2);
+ public void testRegisterEventCallback() throws InterruptedException {
+ final int testWhat = 1001;
final MockPlayer player = new MockPlayer(0);
- final PlaybackListener listener = (state) -> {
- assertEquals(sHandler.getLooper(), Looper.myLooper());
- assertNotNull(state);
- switch ((int) latch.getCount()) {
- case 2:
- assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
- break;
- case 1:
- assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
- break;
- case 0:
- fail();
+ final CountDownLatch playbackLatch = new CountDownLatch(3);
+ final CountDownLatch errorLatch = new CountDownLatch(1);
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ assertEquals(sHandler.getLooper(), Looper.myLooper());
+ switch ((int) playbackLatch.getCount()) {
+ case 3:
+ assertNull(state);
+ break;
+ case 2:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
+ break;
+ case 1:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+ break;
+ case 0:
+ fail();
+ }
+ playbackLatch.countDown();
}
- latch.countDown();
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ assertEquals(testWhat, what);
+ errorLatch.countDown();
+ }
};
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, listener);
- // When the player is set, listeners will be notified about the player's current state.
- mSession.setPlayer(player);
- });
+ // EventCallback will be notified with the mPlayer's playback state (null)
+ mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+ // When the player is set, EventCallback will be notified about the new player's state.
+ mSession.setPlayer(player);
+ // When the player is set, EventCallback will be notified about the new player's state.
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ player.notifyError(testWhat);
+ assertTrue(errorLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testBadPlayer() throws InterruptedException {
// TODO(jaewan): Add equivalent tests again
- final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
+ final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
final BadPlayer player = new BadPlayer(0);
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, (state) -> {
+ mSession.registerPlayerEventCallback(sHandlerExecutor, new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
// This will be called for every setPlayer() calls, but no more.
assertNull(state);
latch.countDown();
- });
- mSession.setPlayer(player);
- mSession.setPlayer(mPlayer);
+ }
});
+ mSession.setPlayer(player);
+ mSession.setPlayer(mPlayer);
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -308,7 +322,7 @@
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
+ public void unregisterEventCallback(@NonNull EventCallback listener) {
// No-op. This bad player will keep push notification to the listener that is previously
// registered by session.setPlayer().
}
@@ -461,7 +475,8 @@
}
@Override
- public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
+ public boolean onCommandRequest(ControllerInfo controllerInfo,
+ MediaSession2.Command command) {
assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
assertEquals(Process.myUid(), controllerInfo.getUid());
assertFalse(controllerInfo.isTrusted());
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index f5abfff..7106561 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -118,7 +118,7 @@
*/
public PlaybackState2 createPlaybackState(int state) {
return new PlaybackState2(mContext, state, 0, 0, 1.0f,
- 0, 0, null);
+ 0, 0);
}
final MediaController2 createController(SessionToken2 token) throws InterruptedException {
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/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 5fabebc..e1cce25 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -26,7 +26,6 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
-import android.os.Process;
import android.util.Log;
import java.io.FileDescriptor;
@@ -34,6 +33,8 @@
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
@@ -45,23 +46,32 @@
public static final String ID = "TestLibrary";
public static final String ROOT_ID = "rootId";
- public static final Bundle EXTRA = new Bundle();
+ public static final Bundle EXTRAS = new Bundle();
public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
public static final String PARENT_ID = "parent_id";
public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
public static final String PARENT_ID_ERROR = "parent_id_error";
- public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
- private static final int CHILDREN_COUNT = 100;
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+ public static final int CHILDREN_COUNT = 100;
+
+ public static final String SEARCH_QUERY = "search_query";
+ public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+ public static final int SEARCH_TIME_IN_MS = 5000;
+ public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+ public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+ public static final int SEARCH_RESULT_COUNT = 50;
+
private static final DataSourceDesc DATA_SOURCE_DESC =
new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
private static final String TAG = "MockMediaLibrarySvc2";
static {
- EXTRA.putString(ROOT_ID, ROOT_ID);
+ EXTRAS.putString(ROOT_ID, ROOT_ID);
}
@GuardedBy("MockMediaLibraryService2.class")
private static SessionToken2 sToken;
@@ -71,9 +81,15 @@
public MockMediaLibraryService2() {
super();
GET_CHILDREN_RESULT.clear();
- String mediaIdPrefix = "media_id_";
+ String getChildrenMediaIdPrefix = "get_children_media_id_";
for (int i = 0; i < CHILDREN_COUNT; i++) {
- GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+ GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+ }
+
+ SEARCH_RESULT.clear();
+ String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+ for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+ SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
}
}
@@ -133,7 +149,7 @@
@Override
public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
- return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
+ return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRAS);
}
@Override
@@ -147,7 +163,7 @@
@Override
public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
- int pageSize, Bundle options) {
+ int pageSize, Bundle extras) {
if (PARENT_ID.equals(parentId)) {
return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
} else if (PARENT_ID_ERROR.equals(parentId)) {
@@ -156,6 +172,47 @@
// Includes the case of PARENT_ID_NO_CHILDREN.
return new ArrayList<>();
}
+
+ @Override
+ public void onSearch(ControllerInfo controllerInfo, String query, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+ extras);
+ } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+ // Searching takes some time. Notify after 5 seconds.
+ Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ mSession.notifySearchResultChanged(
+ controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+ }
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+ } else {
+ // TODO: For the error case, how should we notify the browser?
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onLoadSearchResult(ControllerInfo controllerInfo,
+ String query, int page, int pageSize, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo controller, String parentId, Bundle extras) {
+ mCallbackProxy.onSubscribed(controller, parentId, extras);
+ }
+
+ @Override
+ public void onUnsubscribed(ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribed(controller, parentId);
+ }
}
private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
@@ -191,4 +248,4 @@
.build(),
0 /* Flags */);
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index 1faf0f4..ae31ce6 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -19,6 +19,7 @@
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@
public boolean mSetPlaylistCalled;
public boolean mSetPlaylistParamsCalled;
- public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+ public ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
public List<MediaItem2> mPlaylist;
public PlaylistParams mPlaylistParams;
@@ -146,23 +147,30 @@
}
@Override
- public void addPlaybackListener(@NonNull Executor executor,
- @NonNull PlaybackListener listener) {
- mListeners.add(new PlaybackListenerHolder(executor, listener));
+ public void registerEventCallback(@NonNull Executor executor,
+ @NonNull EventCallback callback) {
+ mCallbacks.put(callback, executor);
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
- int index = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (index >= 0) {
- mListeners.remove(index);
- }
+ public void unregisterEventCallback(@NonNull EventCallback callback) {
+ mCallbacks.remove(callback);
}
public void notifyPlaybackState(final PlaybackState2 state) {
mLastPlaybackState = state;
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).postPlaybackChange(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
+ }
+ }
+
+ public void notifyError(int what) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onError(null, what, 0));
}
}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 0f1644c..0000000
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 3800c28..a18ad8e 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -22,6 +22,7 @@
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
@@ -73,6 +74,9 @@
* Called when enclosing service is destroyed.
*/
public void onServiceDestroyed() { }
+
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extra) { }
+ public void onUnsubscribed(ControllerInfo info, String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a3ce1f6..9a30f71 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1099,26 +1099,17 @@
if (status != NO_ERROR) {
return status;
}
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");
AutoMutex lock(mLock);
- Vector<VolumeInterface *> volumeInterfaces;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface == NULL) {
- return BAD_VALUE;
- }
- volumeInterfaces.add(volumeInterface);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return BAD_VALUE;
}
-
- mStreamTypes[stream].volume = value;
-
- if (volumeInterfaces.size() == 0) {
- volumeInterfaces = getAllVolumeInterfaces_l();
- }
- for (size_t i = 0; i < volumeInterfaces.size(); i++) {
- volumeInterfaces[i]->setStreamVolume(stream, value);
- }
+ volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
@@ -1157,21 +1148,17 @@
if (status != NO_ERROR) {
return 0.0f;
}
-
- AutoMutex lock(mLock);
- float volume;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface != NULL) {
- volume = volumeInterface->streamVolume(stream);
- } else {
- volume = 0.0f;
- }
- } else {
- volume = streamVolume_l(stream);
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return 0.0f;
}
- return volume;
+ AutoMutex lock(mLock);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return 0.0f;
+ }
+
+ return volumeInterface->streamVolume(stream);
}
bool AudioFlinger::streamMute(audio_stream_type_t stream) const
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7c38bcc..ebd1b18 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -616,9 +616,6 @@
// no range check, AudioFlinger::mLock held
bool streamMute_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].mute; }
- // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
- float streamVolume_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
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..3134323 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),
@@ -1702,11 +1703,14 @@
readOutputParameters_l();
// ++ operator does not compile
- for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
+ for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
- mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+ mStreamTypes[stream].volume = 0.0f;
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
+ // Audio patch volume is always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -2157,6 +2161,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 +2364,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 +4159,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 +4388,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 +4431,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 +4799,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 +4810,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 +4920,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 +5380,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 +5997,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/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index cd2174d..78a184b 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -133,6 +133,9 @@
// Note: called after changeRefCount(-1);
void stop();
void close();
+ status_t openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
+ const sp<SwAudioOutputDescriptor>& output2,
+ audio_io_handle_t *ioHandle);
const sp<IOProfile> mProfile; // I/O profile this output derives from
audio_io_handle_t mIoHandle; // output handle
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 17fc272..caaa0f7 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -222,7 +222,7 @@
SwAudioOutputDescriptor::SwAudioOutputDescriptor(const sp<IOProfile>& profile,
AudioPolicyClientInterface *clientInterface)
: AudioOutputDescriptor(profile, clientInterface),
- mProfile(profile), mIoHandle(0), mLatency(0),
+ mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0),
mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
mOutput1(0), mOutput2(0), mDirectOpenCount(0),
mDirectClientSession(AUDIO_SESSION_NONE), mGlobalRefCount(0)
@@ -509,6 +509,30 @@
}
}
+status_t SwAudioOutputDescriptor::openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
+ const sp<SwAudioOutputDescriptor>& output2,
+ audio_io_handle_t *ioHandle)
+{
+ // open a duplicating output thread for the new output and the primary output
+ // Note: openDuplicateOutput() API expects the output handles in the reverse order from the
+ // numbering in SwAudioOutputDescriptor mOutput1 and mOutput2
+ *ioHandle = mClientInterface->openDuplicateOutput(output2->mIoHandle, output1->mIoHandle);
+ if (*ioHandle == AUDIO_IO_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
+
+ mId = AudioPort::getNextUniqueId();
+ mIoHandle = *ioHandle;
+ mOutput1 = output1;
+ mOutput2 = output2;
+ mSamplingRate = output2->mSamplingRate;
+ mFormat = output2->mFormat;
+ mChannelMask = output2->mChannelMask;
+ mLatency = output2->mLatency;
+
+ return NO_ERROR;
+}
+
// HwAudioOutputDescriptor implementation
HwAudioOutputDescriptor::HwAudioOutputDescriptor(const sp<AudioSourceDescriptor>& source,
AudioPolicyClientInterface *clientInterface)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 40e0199..57d9371 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,
@@ -3789,9 +3791,11 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output,
+ const sp<SwAudioOutputDescriptor>& outputDesc)
{
mOutputs.add(output, outputDesc);
+ applyStreamVolumes(outputDesc, AUDIO_DEVICE_NONE, 0 /* delayMs */, true /* force */);
updateMono(output); // update mono status when adding to output list
selectOutputForMusicEffects();
nextAudioPortGeneration();
@@ -3803,7 +3807,8 @@
selectOutputForMusicEffects();
}
-void AudioPolicyManager::addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input,
+ const sp<AudioInputDescriptor>& inputDesc)
{
mInputs.add(input, inputDesc);
nextAudioPortGeneration();
@@ -3953,27 +3958,16 @@
// outputs used by dynamic policy mixes
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
- // set initial stream volume for device
- applyStreamVolumes(desc, device, 0, true);
-
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
- duplicatedOutput =
- mpClientInterface->openDuplicateOutput(output,
- mPrimaryOutput->mIoHandle);
- if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
+ sp<SwAudioOutputDescriptor> dupOutputDesc =
+ new SwAudioOutputDescriptor(NULL, mpClientInterface);
+ status_t status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc,
+ &duplicatedOutput);
+ if (status == NO_ERROR) {
// add duplicated output descriptor
- sp<SwAudioOutputDescriptor> dupOutputDesc =
- new SwAudioOutputDescriptor(NULL, mpClientInterface);
- dupOutputDesc->mOutput1 = mPrimaryOutput;
- dupOutputDesc->mOutput2 = desc;
- dupOutputDesc->mSamplingRate = desc->mSamplingRate;
- dupOutputDesc->mFormat = desc->mFormat;
- dupOutputDesc->mChannelMask = desc->mChannelMask;
- dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput->mIoHandle, output);
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 7fe363d..c7dfe0f 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -399,11 +399,12 @@
while (pos + size > *totSize) {
*totSize += ((*totSize + 7) / 8) * 4;
}
- *param = (char *)realloc(*param, *totSize);
- if (*param == NULL) {
+ char *newParam = (char *)realloc(*param, *totSize);
+ if (newParam == NULL) {
ALOGE("%s realloc error for size %zu", __func__, *totSize);
return 0;
}
+ *param = newParam;
}
*curSize = pos + size;
return pos;
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index e06a81f..394701a 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -32,13 +32,15 @@
namespace android {
+using hardware::camera::common::V1_0::TorchModeStatus;
+
/////////////////////////////////////////////////////////////////////
// CameraFlashlight implementation begins
// used by camera service to control flashflight.
/////////////////////////////////////////////////////////////////////
CameraFlashlight::CameraFlashlight(sp<CameraProviderManager> providerManager,
- camera_module_callbacks_t* callbacks) :
+ CameraProviderManager::StatusListener* callbacks) :
mProviderManager(providerManager),
mCallbacks(callbacks),
mFlashlightMapInitialized(false) {
@@ -59,7 +61,7 @@
} else {
// Only HAL1 devices do not support setTorchMode
mFlashControl =
- new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
+ new CameraHardwareInterfaceFlashControl(mProviderManager, mCallbacks);
}
return OK;
@@ -119,7 +121,8 @@
}
int CameraFlashlight::getNumberOfCameras() {
- return mProviderManager->getAPI1CompatibleCameraCount();
+ size_t len = mProviderManager->getAPI1CompatibleCameraDeviceIds().size();
+ return static_cast<int>(len);
}
status_t CameraFlashlight::findFlashUnits() {
@@ -221,9 +224,8 @@
int numCameras = getNumberOfCameras();
for (int i = 0; i < numCameras; i++) {
if (hasFlashUnitLocked(String8::format("%d", i))) {
- mCallbacks->torch_mode_status_change(mCallbacks,
- String8::format("%d", i).string(),
- TORCH_MODE_STATUS_NOT_AVAILABLE);
+ mCallbacks->onTorchStatusChanged(
+ String8::format("%d", i), TorchModeStatus::NOT_AVAILABLE);
}
}
}
@@ -266,9 +268,8 @@
int numCameras = getNumberOfCameras();
for (int i = 0; i < numCameras; i++) {
if (hasFlashUnitLocked(String8::format("%d", i))) {
- mCallbacks->torch_mode_status_change(mCallbacks,
- String8::format("%d", i).string(),
- TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(
+ String8::format("%d", i), TorchModeStatus::AVAILABLE_OFF);
}
}
}
@@ -315,9 +316,9 @@
CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
- const camera_module_callbacks_t& callbacks) :
+ CameraProviderManager::StatusListener* callbacks) :
mProviderManager(manager),
- mCallbacks(&callbacks),
+ mCallbacks(callbacks),
mTorchEnabled(false) {
}
@@ -333,8 +334,7 @@
if (mCallbacks) {
ALOGV("%s: notify the framework that torch was turned off",
__FUNCTION__);
- mCallbacks->torch_mode_status_change(mCallbacks,
- mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(mCameraId, TorchModeStatus::AVAILABLE_OFF);
}
}
}
@@ -368,8 +368,7 @@
// disabling the torch mode of currently opened device
disconnectCameraDevice();
mTorchEnabled = false;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_OFF);
return OK;
}
@@ -379,8 +378,7 @@
}
mTorchEnabled = true;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
+ mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_ON);
return OK;
}
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index c86ee85..07ce829 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -19,7 +19,6 @@
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
-#include <hardware/camera_common.h>
#include <utils/KeyedVector.h>
#include <utils/SortedVector.h>
#include "common/CameraProviderManager.h"
@@ -55,7 +54,7 @@
class CameraFlashlight : public virtual VirtualLightRefBase {
public:
CameraFlashlight(sp<CameraProviderManager> providerManager,
- camera_module_callbacks_t* callbacks);
+ CameraProviderManager::StatusListener* callbacks);
virtual ~CameraFlashlight();
// Find all flash units. This must be called before other methods. All
@@ -99,7 +98,7 @@
sp<CameraProviderManager> mProviderManager;
- const camera_module_callbacks_t *mCallbacks;
+ CameraProviderManager::StatusListener* mCallbacks;
SortedVector<String8> mOpenedCameraIds;
// camera id -> if it has a flash unit
@@ -134,7 +133,7 @@
public:
CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
- const camera_module_callbacks_t& callbacks);
+ CameraProviderManager::StatusListener* callbacks);
virtual ~CameraHardwareInterfaceFlashControl();
// FlashControlBase
@@ -166,7 +165,7 @@
status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash, bool keepDeviceOpen);
sp<CameraProviderManager> mProviderManager;
- const camera_module_callbacks_t *mCallbacks;
+ CameraProviderManager::StatusListener* mCallbacks;
sp<CameraHardwareInterface> mDevice;
String8 mCameraId;
CameraParameters mParameters;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e7609ed..1df6b6a 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -67,6 +67,7 @@
#include "api1/Camera2Client.h"
#include "api2/CameraDeviceClient.h"
#include "utils/CameraTraces.h"
+#include "utils/TagMonitor.h"
namespace {
const char* kPermissionServiceName = "permission";
@@ -107,79 +108,13 @@
// ----------------------------------------------------------------------------
-extern "C" {
-static void camera_device_status_change(
- const struct camera_module_callbacks* callbacks,
- int camera_id,
- int new_status) {
- sp<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
- String8 id = String8::format("%d", camera_id);
-
- CameraDeviceStatus newStatus{CameraDeviceStatus::NOT_PRESENT};
- switch (new_status) {
- case CAMERA_DEVICE_STATUS_NOT_PRESENT:
- newStatus = CameraDeviceStatus::NOT_PRESENT;
- break;
- case CAMERA_DEVICE_STATUS_PRESENT:
- newStatus = CameraDeviceStatus::PRESENT;
- break;
- case CAMERA_DEVICE_STATUS_ENUMERATING:
- newStatus = CameraDeviceStatus::ENUMERATING;
- break;
- default:
- ALOGW("Unknown device status change to %d", new_status);
- break;
- }
- cs->onDeviceStatusChanged(id, newStatus);
-}
-
-static void torch_mode_status_change(
- const struct camera_module_callbacks* callbacks,
- const char* camera_id,
- int new_status) {
- if (!callbacks || !camera_id) {
- ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__,
- callbacks, camera_id);
- }
- sp<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
-
- TorchModeStatus status;
- switch (new_status) {
- case TORCH_MODE_STATUS_NOT_AVAILABLE:
- status = TorchModeStatus::NOT_AVAILABLE;
- break;
- case TORCH_MODE_STATUS_AVAILABLE_OFF:
- status = TorchModeStatus::AVAILABLE_OFF;
- break;
- case TORCH_MODE_STATUS_AVAILABLE_ON:
- status = TorchModeStatus::AVAILABLE_ON;
- break;
- default:
- ALOGE("Unknown torch status %d", new_status);
- return;
- }
-
- cs->onTorchStatusChanged(
- String8(camera_id),
- status);
-}
-} // extern "C"
-
-// ----------------------------------------------------------------------------
-
static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
- mNumberOfCameras(0), mNumberOfNormalCameras(0),
+ mNumberOfCameras(0),
mSoundRef(0), mInitialized(false) {
ALOGI("CameraService started (pid=%d)", getpid());
-
- this->camera_device_status_change = android::camera_device_status_change;
- this->torch_mode_status_change = android::torch_mode_status_change;
-
mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);
}
@@ -209,52 +144,42 @@
status_t CameraService::enumerateProviders() {
status_t res;
- Mutex::Autolock l(mServiceLock);
- if (nullptr == mCameraProviderManager.get()) {
- mCameraProviderManager = new CameraProviderManager();
- res = mCameraProviderManager->initialize(this);
- if (res != OK) {
- ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- }
+ std::vector<std::string> deviceIds;
+ {
+ Mutex::Autolock l(mServiceLock);
- mNumberOfCameras = mCameraProviderManager->getCameraCount();
- mNumberOfNormalCameras =
- mCameraProviderManager->getAPI1CompatibleCameraCount();
-
- // Setup vendor tags before we call get_camera_info the first time
- // because HAL might need to setup static vendor keys in get_camera_info
- // TODO: maybe put this into CameraProviderManager::initialize()?
- mCameraProviderManager->setUpVendorTags();
-
- if (nullptr == mFlashlight.get()) {
- mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
- }
-
- res = mFlashlight->findFlashUnits();
- if (res != OK) {
- ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
- }
-
- for (auto& cameraId : mCameraProviderManager->getCameraDeviceIds()) {
- String8 id8 = String8(cameraId.c_str());
- bool cameraFound = false;
- {
-
- Mutex::Autolock lock(mCameraStatesLock);
- auto iter = mCameraStates.find(id8);
- if (iter != mCameraStates.end()) {
- cameraFound = true;
+ if (nullptr == mCameraProviderManager.get()) {
+ mCameraProviderManager = new CameraProviderManager();
+ res = mCameraProviderManager->initialize(this);
+ if (res != OK) {
+ ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
}
}
- if (!cameraFound) {
- addStates(id8);
+
+ // Setup vendor tags before we call get_camera_info the first time
+ // because HAL might need to setup static vendor keys in get_camera_info
+ // TODO: maybe put this into CameraProviderManager::initialize()?
+ mCameraProviderManager->setUpVendorTags();
+
+ if (nullptr == mFlashlight.get()) {
+ mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
}
+ res = mFlashlight->findFlashUnits();
+ if (res != OK) {
+ ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
+ }
+
+ deviceIds = mCameraProviderManager->getCameraDeviceIds();
+ }
+
+
+ for (auto& cameraId : deviceIds) {
+ String8 id8 = String8(cameraId.c_str());
onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT);
}
@@ -291,6 +216,13 @@
enumerateProviders();
}
+void CameraService::updateCameraNumAndIds() {
+ Mutex::Autolock l(mServiceLock);
+ mNumberOfCameras = mCameraProviderManager->getCameraCount();
+ mNormalDeviceIds =
+ mCameraProviderManager->getAPI1CompatibleCameraDeviceIds();
+}
+
void CameraService::addStates(const String8 id) {
std::string cameraId(id.c_str());
hardware::camera::common::V1_0::CameraResourceCost cost;
@@ -313,10 +245,13 @@
if (mFlashlight->hasFlashUnit(id)) {
mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
}
+
+ updateCameraNumAndIds();
logDeviceAdded(id, "Device added");
}
void CameraService::removeStates(const String8 id) {
+ updateCameraNumAndIds();
if (mFlashlight->hasFlashUnit(id)) {
mTorchStatusMap.removeItem(id);
}
@@ -361,15 +296,16 @@
if (newStatus == StatusInternal::NOT_PRESENT) {
logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
newStatus));
+
+ // Set the device status to NOT_PRESENT, clients will no longer be able to connect
+ // to this device until the status changes
+ updateStatus(StatusInternal::NOT_PRESENT, id);
+
sp<BasicClient> clientToDisconnect;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
Mutex::Autolock lock(mServiceLock);
- // Set the device status to NOT_PRESENT, clients will no longer be able to connect
- // to this device until the status changes
- updateStatus(StatusInternal::NOT_PRESENT, id);
-
// Remove cached shim parameters
state->setShimParams(CameraParameters());
@@ -472,7 +408,7 @@
Mutex::Autolock l(mServiceLock);
switch (type) {
case CAMERA_TYPE_BACKWARD_COMPATIBLE:
- *numCameras = mNumberOfNormalCameras;
+ *numCameras = static_cast<int>(mNormalDeviceIds.size());
break;
case CAMERA_TYPE_ALL:
*numCameras = mNumberOfCameras;
@@ -502,7 +438,8 @@
}
Status ret = Status::ok();
- status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
+ status_t err = mCameraProviderManager->getCameraInfo(
+ cameraIdIntToStrLocked(cameraId), cameraInfo);
if (err != OK) {
ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Error retrieving camera info from device %d: %s (%d)", cameraId,
@@ -512,13 +449,19 @@
return ret;
}
-int CameraService::cameraIdToInt(const String8& cameraId) {
- int id;
- bool success = base::ParseInt(cameraId.string(), &id, 0);
- if (!success) {
- return -1;
+std::string CameraService::cameraIdIntToStrLocked(int cameraIdInt) {
+ if (cameraIdInt < 0 || cameraIdInt >= static_cast<int>(mNormalDeviceIds.size())) {
+ ALOGE("%s: input id %d invalid: valid range (0, %zu)",
+ __FUNCTION__, cameraIdInt, mNormalDeviceIds.size());
+ return std::string{};
}
- return id;
+
+ return mNormalDeviceIds[cameraIdInt];
+}
+
+String8 CameraService::cameraIdIntToStr(int cameraIdInt) {
+ Mutex::Autolock lock(mServiceLock);
+ return String8(cameraIdIntToStrLocked(cameraIdInt).c_str());
}
Status CameraService::getCameraCharacteristics(const String16& cameraId,
@@ -635,8 +578,8 @@
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
+ bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
@@ -646,8 +589,9 @@
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, getpid(), legacyMode);
+ *client = new CameraClient(cameraService, tmp, packageName,
+ api1CameraId, facing, clientPid, clientUid,
+ getpid(), legacyMode);
} else { // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
@@ -662,8 +606,10 @@
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, servicePid, legacyMode);
+ *client = new Camera2Client(cameraService, tmp, packageName,
+ cameraId, api1CameraId,
+ facing, clientPid, clientUid,
+ servicePid, legacyMode);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
@@ -685,8 +631,9 @@
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, servicePid, legacyMode);
+ *client = new CameraClient(cameraService, tmp, packageName,
+ api1CameraId, facing, clientPid, clientUid,
+ servicePid, legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
@@ -782,7 +729,8 @@
Status ret = Status::ok();
sp<Client> tmp = nullptr;
if (!(ret = connectHelper<ICameraClient,Client>(
- sp<ICameraClient>{nullptr}, id, static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
+ sp<ICameraClient>{nullptr}, id, cameraId,
+ static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
internalPackageName, uid, USE_CALLING_PID,
API_1, /*legacyMode*/ false, /*shimUpdateOnly*/ true,
/*out*/ tmp)
@@ -1235,7 +1183,7 @@
Status CameraService::connect(
const sp<ICameraClient>& cameraClient,
- int cameraId,
+ int api1CameraId,
const String16& clientPackageName,
int clientUid,
int clientPid,
@@ -1244,9 +1192,10 @@
ATRACE_CALL();
Status ret = Status::ok();
- String8 id = String8::format("%d", cameraId);
+
+ String8 id = cameraIdIntToStr(api1CameraId);
sp<Client> client = nullptr;
- ret = connectHelper<ICameraClient,Client>(cameraClient, id,
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
/*out*/client);
@@ -1263,18 +1212,18 @@
Status CameraService::connectLegacy(
const sp<ICameraClient>& cameraClient,
- int cameraId, int halVersion,
+ int api1CameraId, int halVersion,
const String16& clientPackageName,
int clientUid,
/*out*/
sp<ICamera>* device) {
ATRACE_CALL();
- String8 id = String8::format("%d", cameraId);
+ String8 id = cameraIdIntToStr(api1CameraId);
Status ret = Status::ok();
sp<Client> client = nullptr;
- ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion,
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId, halVersion,
clientPackageName, clientUid, USE_CALLING_PID, API_1,
/*legacyMode*/ true, /*shimUpdateOnly*/ false,
/*out*/client);
@@ -1302,6 +1251,7 @@
String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
+ /*api1CameraId*/-1,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
clientUid, USE_CALLING_PID, API_2,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
@@ -1319,8 +1269,8 @@
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
- apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+ int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
+ int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
binder::Status ret = binder::Status::ok();
@@ -1403,8 +1353,10 @@
}
sp<BasicClient> tmp = nullptr;
- if(!(ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
- clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
+ if(!(ret = makeClient(this, cameraCb, clientPackageName,
+ cameraId, api1CameraId, facing,
+ clientPid, clientUid, getpid(), legacyMode,
+ halVersion, deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk()) {
return ret;
}
@@ -1413,7 +1365,7 @@
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
- err = client->initialize(mCameraProviderManager);
+ err = client->initialize(mCameraProviderManager, mMonitorTags);
if (err != OK) {
ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
// Errors could be from the HAL module open call or from AppOpsManager
@@ -1782,8 +1734,6 @@
}
bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
- const int callingPid = getCallingPid();
- const int servicePid = getpid();
bool ret = false;
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1799,8 +1749,7 @@
mActiveClientManager.remove(i);
continue;
}
- if (remote == clientSp->getRemote() && (callingPid == servicePid ||
- callingPid == clientSp->getClientPid())) {
+ if (remote == clientSp->getRemote()) {
mActiveClientManager.remove(i);
evicted.push_back(clientSp);
@@ -2112,7 +2061,8 @@
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const String16& clientPackageName,
- const String8& cameraIdStr, int cameraFacing,
+ const String8& cameraIdStr,
+ int api1CameraId, int cameraFacing,
int clientPid, uid_t clientUid,
int servicePid) :
CameraService::BasicClient(cameraService,
@@ -2121,7 +2071,7 @@
cameraIdStr, cameraFacing,
clientPid, clientUid,
servicePid),
- mCameraId(CameraService::cameraIdToInt(cameraIdStr))
+ mCameraId(api1CameraId)
{
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d, id %d)", callingPid, mCameraId);
@@ -2676,7 +2626,10 @@
}
dprintf(fd, "\n== Service global info: ==\n\n");
dprintf(fd, "Number of camera devices: %d\n", mNumberOfCameras);
- dprintf(fd, "Number of normal camera devices: %d\n", mNumberOfNormalCameras);
+ dprintf(fd, "Number of normal camera devices: %zu\n", mNormalDeviceIds.size());
+ for (size_t i = 0; i < mNormalDeviceIds.size(); i++) {
+ dprintf(fd, " Device %zu maps to \"%s\"\n", i, mNormalDeviceIds[i].c_str());
+ }
String8 activeClientString = mActiveClientManager.toString();
dprintf(fd, "Active Camera Clients:\n%s", activeClientString.string());
dprintf(fd, "Allowed user IDs: %s\n", toString(mAllowedUsers).string());
@@ -2688,6 +2641,16 @@
dprintf(fd, "CameraStates in use, may be deadlocked\n");
}
+ int argSize = args.size();
+ for (int i = 0; i < argSize; i++) {
+ if (args[i] == TagMonitor::kMonitorOption) {
+ if (i + 1 < argSize) {
+ mMonitorTags = String8(args[i + 1]);
+ }
+ break;
+ }
+ }
+
for (auto& state : mCameraStates) {
String8 cameraId = state.first;
@@ -2815,7 +2778,7 @@
* While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
* binder driver
*/
-
+ // PID here is approximate and can be wrong.
logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
// check torch client
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 67db7ec..cbfc50b 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -62,7 +62,6 @@
public BinderService<CameraService>,
public virtual ::android::hardware::BnCameraService,
public virtual IBinder::DeathRecipient,
- public camera_module_callbacks_t,
public virtual CameraProviderManager::StatusListener
{
friend class BinderService<CameraService>;
@@ -205,7 +204,8 @@
class BasicClient : public virtual RefBase {
public:
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) = 0;
virtual binder::Status disconnect();
// because we can't virtually inherit IInterface, which breaks
@@ -333,6 +333,7 @@
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
const String8& cameraIdStr,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -551,7 +552,8 @@
// Eumerate all camera providers in the system
status_t enumerateProviders();
- // Add a new camera to camera and torch state lists or remove an unplugged one
+ // Add/remove a new camera to camera and torch state lists or remove an unplugged one
+ // Caller must not hold mServiceLock
void addStates(const String8 id);
void removeStates(const String8 id);
@@ -578,7 +580,7 @@
// Single implementation shared between the various connect calls
template<class CALLBACK, class CLIENT>
binder::Status connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int halVersion, const String16& clientPackageName,
+ int api1CameraId, int halVersion, const String16& clientPackageName,
int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device);
@@ -605,6 +607,9 @@
RingBuffer<String8> mEventLog;
Mutex mLogLock;
+ // The last monitored tags set by client
+ String8 mMonitorTags;
+
// Currently allowed user IDs
std::set<userid_t> mAllowedUsers;
@@ -640,9 +645,16 @@
void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc);
/**
- * Returns the integer corresponding to the given camera ID string, or -1 on failure.
+ * Returns the underlying camera Id string mapped to a camera id int
+ * Empty string is returned when the cameraIdInt is invalid.
*/
- static int cameraIdToInt(const String8& cameraId);
+ String8 cameraIdIntToStr(int cameraIdInt);
+
+ /**
+ * Returns the underlying camera Id string mapped to a camera id int
+ * Empty string is returned when the cameraIdInt is invalid.
+ */
+ std::string cameraIdIntToStrLocked(int cameraIdInt);
/**
* Remove a single client corresponding to the given camera id from the list of active clients.
@@ -710,8 +722,14 @@
*/
void dumpEventLog(int fd);
+ /**
+ * This method will acquire mServiceLock
+ */
+ void updateCameraNumAndIds();
+
int mNumberOfCameras;
- int mNumberOfNormalCameras;
+
+ std::vector<std::string> mNormalDeviceIds;
// sounds
MediaPlayer* newMediaPlayer(const char *file);
@@ -821,8 +839,8 @@
static binder::Status makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
+ bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client);
status_t checkCameraAccess(const String16& opPackageName);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 585d2eb..3578bba 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -49,16 +49,17 @@
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
- int cameraId,
+ const String8& cameraDeviceId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid,
bool legacyMode):
Camera2ClientBase(cameraService, cameraClient, clientPackageName,
- String8::format("%d", cameraId), cameraFacing,
+ cameraDeviceId, api1CameraId, cameraFacing,
clientPid, clientUid, servicePid),
- mParameters(cameraId, cameraFacing)
+ mParameters(api1CameraId, cameraFacing)
{
ATRACE_CALL();
@@ -68,8 +69,8 @@
mLegacyMode = legacyMode;
}
-status_t Camera2Client::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2Client::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
bool Camera2Client::isZslEnabledInStillTemplate() {
@@ -87,13 +88,13 @@
}
template<typename TProviderPtr>
-status_t Camera2Client::initializeImpl(TProviderPtr providerPtr)
+status_t Camera2Client::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags)
{
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5af74eb..44929c3 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -91,7 +91,8 @@
Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
- int cameraId,
+ const String8& cameraDeviceId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -100,7 +101,8 @@
virtual ~Camera2Client();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -223,7 +225,7 @@
status_t overrideVideoSnapshotSize(Parameters ¶ms);
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
bool isZslEnabledInStillTemplate();
};
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index e848a3f..f6d27ab 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -42,7 +42,7 @@
int clientPid, int clientUid,
int servicePid, bool legacyMode):
Client(cameraService, cameraClient, clientPackageName,
- String8::format("%d", cameraId), cameraFacing, clientPid,
+ String8::format("%d", cameraId), cameraId, cameraFacing, clientPid,
clientUid, servicePid)
{
int callingPid = getCallingPid();
@@ -62,7 +62,8 @@
LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
-status_t CameraClient::initialize(sp<CameraProviderManager> manager) {
+status_t CameraClient::initialize(sp<CameraProviderManager> manager,
+ const String8& /*monitorTags*/) {
int callingPid = getCallingPid();
status_t res;
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 7f93fef..1910536 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -72,7 +72,8 @@
bool legacyMode = false);
~CameraClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 050c3f7..b4c7e9d 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -759,12 +759,17 @@
focusingAreas.clear();
focusingAreas.add(Parameters::Area(0,0,0,0,0));
- camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
- if (!availableFocalLengths.count) return NO_INIT;
+ if (fastInfo.isExternalCamera) {
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, -1.0);
+ } else {
+ camera_metadata_ro_entry_t availableFocalLengths =
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
+ if (!availableFocalLengths.count) return NO_INIT;
- float minFocalLength = availableFocalLengths.data.f[0];
- params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
+ float minFocalLength = availableFocalLengths.data.f[0];
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
+ }
+
float horizFov, vertFov;
res = calculatePictureFovs(&horizFov, &vertFov);
@@ -1091,9 +1096,15 @@
focusDistanceCalibration.data.u8[0] !=
ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
+
+ camera_metadata_ro_entry_t hwLevel = staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
+ if (!hwLevel.count) return NO_INIT;
+ fastInfo.isExternalCamera =
+ hwLevel.data.u8[0] == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+
camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
- if (!availableFocalLengths.count) return NO_INIT;
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
+ if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
SortedVector<int32_t> availableFormats = getAvailableOutputFormats();
if (!availableFormats.size()) return NO_INIT;
@@ -1178,10 +1189,14 @@
// Find smallest (widest-angle) focal length to use as basis of still
// picture FOV reporting.
- fastInfo.minFocalLength = availableFocalLengths.data.f[0];
- for (size_t i = 1; i < availableFocalLengths.count; i++) {
- if (fastInfo.minFocalLength > availableFocalLengths.data.f[i]) {
- fastInfo.minFocalLength = availableFocalLengths.data.f[i];
+ if (fastInfo.isExternalCamera) {
+ fastInfo.minFocalLength = -1.0;
+ } else {
+ fastInfo.minFocalLength = availableFocalLengths.data.f[0];
+ for (size_t i = 1; i < availableFocalLengths.count; i++) {
+ if (fastInfo.minFocalLength > availableFocalLengths.data.f[i]) {
+ fastInfo.minFocalLength = availableFocalLengths.data.f[i];
+ }
}
}
@@ -2870,8 +2885,13 @@
if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
sc.width <= limit.width && sc.height <= limit.height) {
- Size sz = {sc.width, sc.height};
- sizes->push(sz);
+ int64_t minFrameDuration = getMinFrameDurationNs(
+ {sc.width, sc.height}, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ if (minFrameDuration > MAX_PREVIEW_RECORD_DURATION_NS) {
+ // Filter slow sizes from preview/record
+ continue;
+ }
+ sizes->push({sc.width, sc.height});
}
}
@@ -3081,6 +3101,16 @@
status_t Parameters::calculatePictureFovs(float *horizFov, float *vertFov)
const {
+ if (fastInfo.isExternalCamera) {
+ if (horizFov != NULL) {
+ *horizFov = -1.0;
+ }
+ if (vertFov != NULL) {
+ *vertFov = -1.0;
+ }
+ return OK;
+ }
+
camera_metadata_ro_entry_t sensorSize =
staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
if (!sensorSize.count) return NO_INIT;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 17e3d75..fe725fd 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -207,6 +207,11 @@
static const int32_t FPS_MARGIN = 1;
// Max FPS for default parameters
static const int32_t MAX_DEFAULT_FPS = 30;
+ // Minimum FPS for a size to be listed in supported preview/video sizes
+ // Set to slightly less than 30.0 to have some tolerance margin
+ static constexpr double MIN_PREVIEW_RECORD_FPS = 29.97;
+ // Maximum frame duration for a size to be listed in supported preview/video sizes
+ static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / MIN_PREVIEW_RECORD_FPS;
// Full static camera info, object owned by someone else, such as
// Camera2Device.
@@ -233,6 +238,7 @@
}
};
DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
+ bool isExternalCamera;
float minFocalLength;
bool useFlexibleYuv;
Size maxJpegSize;
@@ -380,6 +386,7 @@
Vector<Size> availablePreviewSizes;
Vector<Size> availableVideoSizes;
// Get size list (that are no larger than limit) from static metadata.
+ // This method filtered size with minFrameDuration < MAX_PREVIEW_RECORD_DURATION_NS
status_t getFilteredSizes(Size limit, Vector<Size> *sizes);
// Get max size (from the size array) that matches the given aspect ratio.
Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 4a72de0..efe3ca1 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -49,6 +49,7 @@
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -62,6 +63,8 @@
clientUid,
servicePid),
mRemoteCallback(remoteCallback) {
+ // We don't need it for API2 clients, but Camera2ClientBase requires it.
+ (void) api1CameraId;
}
// Interface used by CameraService
@@ -75,7 +78,8 @@
uid_t clientUid,
int servicePid) :
Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
- cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ cameraId, /*API1 camera ID*/ -1,
+ cameraFacing, clientPid, clientUid, servicePid),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0) {
@@ -84,16 +88,17 @@
ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
}
-status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template<typename TProviderPtr>
-status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr) {
+status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags) {
ATRACE_CALL();
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
@@ -654,14 +659,6 @@
return res;
if (!isStreamInfoValid) {
- // Streaming sharing is only supported for IMPLEMENTATION_DEFINED
- // formats.
- if (isShared && streamInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
- "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
isStreamInfoValid = true;
}
@@ -941,14 +938,6 @@
if (!res.isOk())
return res;
- // Stream sharing is only supported for IMPLEMENTATION_DEFINED
- // formats.
- if (outInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
- "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
streamInfos.push_back(outInfo);
newOutputs.push_back(surface);
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index f1cd00c..5aaf5aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -45,6 +45,7 @@
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -156,7 +157,8 @@
int servicePid);
virtual ~CameraDeviceClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -224,7 +226,7 @@
std::vector<int32_t> mSupportedPhysicalRequestKeys;
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
/** Utility members */
binder::Status checkPidStatus(const char* checkLocation);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 4ce82dc..459e45d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -48,15 +48,16 @@
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid):
TClientBase(cameraService, remoteCallback, clientPackageName,
- cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ cameraId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
- mDeviceActive(false)
+ mDeviceActive(false), mApi1CameraId(api1CameraId)
{
ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
String8(clientPackageName).string(), clientPid, clientUid);
@@ -79,13 +80,15 @@
}
template <typename TClientBase>
-status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template <typename TClientBase>
template <typename TProviderPtr>
-status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr) {
+status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr,
+ const String8& monitorTags) {
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %s", __FUNCTION__,
TClientBase::mCameraIdStr.string());
@@ -103,7 +106,7 @@
return NO_INIT;
}
- res = mDevice->initialize(providerPtr);
+ res = mDevice->initialize(providerPtr, monitorTags);
if (res != OK) {
ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
__FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
@@ -329,7 +332,7 @@
template <typename TClientBase>
int Camera2ClientBase<TClientBase>::getCameraId() const {
- return std::stoi(TClientBase::mCameraIdStr.string());
+ return mApi1CameraId;
}
template <typename TClientBase>
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index e898d5d..e74fbdf 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -49,13 +49,14 @@
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid);
virtual ~Camera2ClientBase();
- virtual status_t initialize(sp<CameraProviderManager> manager);
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
/**
@@ -140,9 +141,11 @@
bool mDeviceActive;
+ const int mApi1CameraId; // -1 if client is API2
+
private:
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index ad83c3d..0ba7403 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -60,7 +60,7 @@
*/
virtual metadata_vendor_id_t getVendorTagId() const = 0;
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) = 0;
virtual status_t disconnect() = 0;
virtual status_t dump(int fd, const Vector<String16> &args) = 0;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 70e7761..b37f004 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -20,11 +20,13 @@
#include "CameraProviderManager.h"
+#include <algorithm>
#include <chrono>
#include <inttypes.h>
#include <hidl/ServiceManagement.h>
#include <functional>
#include <camera_metadata_hidden.h>
+#include <android-base/parseint.h>
namespace android {
@@ -38,7 +40,7 @@
const std::string kExternalProviderName("external/0");
// Slash-separated list of provider types to consider for use via the old camera API
-const std::string kStandardProviderTypes("internal/legacy");
+const std::string kStandardProviderTypes("internal/legacy/external");
} // anonymous namespace
@@ -79,18 +81,7 @@
std::lock_guard<std::mutex> lock(mInterfaceMutex);
int count = 0;
for (auto& provider : mProviders) {
- count += provider->mUniqueDeviceCount;
- }
- return count;
-}
-
-int CameraProviderManager::getAPI1CompatibleCameraCount() const {
- std::lock_guard<std::mutex> lock(mInterfaceMutex);
- int count = 0;
- for (auto& provider : mProviders) {
- if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
- count += provider->mUniqueAPI1CompatibleCameraIds.size();
- }
+ count += provider->mUniqueCameraIds.size();
}
return count;
}
@@ -116,6 +107,24 @@
}
}
}
+
+ std::sort(deviceIds.begin(), deviceIds.end(),
+ [](const std::string& a, const std::string& b) -> bool {
+ uint32_t aUint = 0, bUint = 0;
+ bool aIsUint = base::ParseUint(a, &aUint);
+ bool bIsUint = base::ParseUint(b, &bUint);
+
+ // Uint device IDs first
+ if (aIsUint && bIsUint) {
+ return aUint < bUint;
+ } else if (aIsUint) {
+ return true;
+ } else if (bIsUint) {
+ return false;
+ }
+ // Simple string compare if both id are not uint
+ return a < b;
+ });
return deviceIds;
}
@@ -480,6 +489,8 @@
}
ALOGI("Connecting to new camera provider: %s, isRemote? %d",
mProviderName.c_str(), mInterface->isRemote());
+ // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
+ // before setCallback returns
hardware::Return<Status> status = mInterface->setCallback(this);
if (!status.isOk()) {
ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
@@ -536,17 +547,10 @@
}
}
- for (auto& device : mDevices) {
- mUniqueCameraIds.insert(device->mId);
- if (device->isAPI1Compatible()) {
- mUniqueAPI1CompatibleCameraIds.insert(device->mId);
- }
- }
- mUniqueDeviceCount = mUniqueCameraIds.size();
-
ALOGI("Camera provider %s ready with %zu camera devices",
mProviderName.c_str(), mDevices.size());
+ mInitialized = true;
return OK;
}
@@ -594,9 +598,15 @@
}
if (deviceInfo == nullptr) return BAD_VALUE;
deviceInfo->mStatus = initialStatus;
+ bool isAPI1Compatible = deviceInfo->isAPI1Compatible();
mDevices.push_back(std::move(deviceInfo));
+ mUniqueCameraIds.insert(id);
+ if (isAPI1Compatible) {
+ mUniqueAPI1CompatibleCameraIds.insert(id);
+ }
+
if (parsedId != nullptr) {
*parsedId = id;
}
@@ -606,6 +616,10 @@
void CameraProviderManager::ProviderInfo::removeDevice(std::string id) {
for (auto it = mDevices.begin(); it != mDevices.end(); it++) {
if ((*it)->mId == id) {
+ mUniqueCameraIds.erase(id);
+ if ((*it)->isAPI1Compatible()) {
+ mUniqueAPI1CompatibleCameraIds.erase(id);
+ }
mDevices.erase(it);
break;
}
@@ -671,6 +685,7 @@
CameraDeviceStatus newStatus) {
sp<StatusListener> listener;
std::string id;
+ bool initialized = false;
{
std::lock_guard<std::mutex> lock(mLock);
bool known = false;
@@ -697,9 +712,13 @@
removeDevice(id);
}
listener = mManager->getStatusListener();
+ initialized = mInitialized;
}
// Call without lock held to allow reentrancy into provider manager
- if (listener != nullptr) {
+ // Don't send the callback if providerInfo hasn't been initialized.
+ // CameraService will initialize device status after provider is
+ // initialized
+ if (listener != nullptr && initialized) {
listener->onDeviceStatusChanged(String8(id.c_str()), newStatus);
}
return hardware::Void();
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index d02abb0..bbe6789 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -18,7 +18,7 @@
#define ANDROID_SERVERS_CAMERA_CAMERAPROVIDER_H
#include <vector>
-#include <set>
+#include <unordered_set>
#include <string>
#include <mutex>
@@ -125,16 +125,14 @@
*/
int getCameraCount() const;
+ std::vector<std::string> getCameraDeviceIds() const;
+
/**
* Retrieve the number of API1 compatible cameras; these are internal and
* backwards-compatible. This is the set of cameras that will be
- * accessible via the old camera API, with IDs in range of
- * [0, getAPI1CompatibleCameraCount()-1]. This value is not expected to change dynamically.
+ * accessible via the old camera API.
+ * The return value may change dynamically due to external camera hotplug.
*/
- int getAPI1CompatibleCameraCount() const;
-
- std::vector<std::string> getCameraDeviceIds() const;
-
std::vector<std::string> getAPI1CompatibleCameraDeviceIds() const;
/**
@@ -314,9 +312,9 @@
static status_t setTorchMode(InterfaceT& interface, bool enabled);
};
std::vector<std::unique_ptr<DeviceInfo>> mDevices;
- std::set<std::string> mUniqueCameraIds;
+ std::unordered_set<std::string> mUniqueCameraIds;
int mUniqueDeviceCount;
- std::set<std::string> mUniqueAPI1CompatibleCameraIds;
+ std::unordered_set<std::string> mUniqueAPI1CompatibleCameraIds;
// HALv1-specific camera fields, including the actual device interface
struct DeviceInfo1 : public DeviceInfo {
@@ -366,6 +364,8 @@
CameraProviderManager *mManager;
+ bool mInitialized = false;
+
// Templated method to instantiate the right kind of DeviceInfo and call the
// right CameraProvider getCameraDeviceInterface_* method.
template<class DeviceInfoT>
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 04c2c5b..42bcb2d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -96,7 +96,7 @@
return mId;
}
-status_t Camera3Device::initialize(sp<CameraProviderManager> manager) {
+status_t Camera3Device::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -169,6 +169,10 @@
mInterface = new HalInterface(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
+ mTagMonitor.initialize(mVendorTagId);
+ if (!monitorTags.isEmpty()) {
+ mTagMonitor.parseTagsToMonitor(String8(monitorTags));
+ }
return initializeCommonLocked();
}
@@ -192,8 +196,6 @@
/** Create buffer manager */
mBufferManager = new Camera3BufferManager();
- mTagMonitor.initialize(mVendorTagId);
-
Vector<int32_t> sessionParamKeys;
camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
@@ -582,13 +584,12 @@
bool dumpTemplates = false;
String16 templatesOption("-t");
- String16 monitorOption("-m");
int n = args.size();
for (int i = 0; i < n; i++) {
if (args[i] == templatesOption) {
dumpTemplates = true;
}
- if (args[i] == monitorOption) {
+ if (args[i] == TagMonitor::kMonitorOption) {
if (i + 1 < n) {
String8 monitorTags = String8(args[i + 1]);
if (monitorTags == "off") {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7faa6e5..13b83ba 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -96,7 +96,7 @@
metadata_vendor_id_t getVendorTagId() const override { return mVendorTagId; }
// Transitions to idle state on success.
- status_t initialize(sp<CameraProviderManager> manager) override;
+ status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) override;
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
const CameraMetadata& info() const override;
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index dec97d7..c0a353f 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -33,6 +33,8 @@
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
{}
+const String16 TagMonitor::kMonitorOption = String16("-m");
+
const char* TagMonitor::k3aTags =
"android.control.aeMode, android.control.afMode, android.control.awbMode,"
"android.control.aeState, android.control.afState, android.control.awbState,"
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 7155314..2dece62 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -38,6 +38,10 @@
* buffer log that can be dumped at will. */
class TagMonitor {
public:
+
+ // Monitor argument
+ static const String16 kMonitorOption;
+
enum eventSource {
REQUEST,
RESULT