- Implement StreamInformation for video size notification.
- Implement the XAVolumeItf for volume control
- Fix bug in GUID -> MPH hash.
- Fixed typo in GenericPlayer::pause() log
- Do not signal a discontinuity automatically when the ABQ is
cleared because clearing the queue doesn't imply there will
be a discontinuity in the data (e.g. the same data that was
cleared could be reenqueued)
- In "native-media" test app: add test code to exercise the
XAVolumeItf functionality.
Change-Id: I9f69f8cacbdce51b6d96d60141ec1d0f645df991
typedef XAresult (XAAPIENTRY *xaAndroidBufferQueueCallback)(
XAAndroidBufferQueueItf caller,/* input */
- void *pContext, /* input */
- const void *pBufferData, /* input */
+ void *pCallbackContext, /* input */
+ void *pBufferContext, /* input */
+ void *pBufferData, /* input */
XAuint32 dataSize, /* input */
XAuint32 dataUsed, /* input */
const XAAndroidBufferItem *pItems,/* input */
XAresult (*RegisterCallback) (
XAAndroidBufferQueueItf self,
xaAndroidBufferQueueCallback callback,
- void* pContext
+ void* pCallbackContext
);
XAresult (*Clear) (
XAresult (*Enqueue) (
XAAndroidBufferQueueItf self,
- const void *pData,
+ void *pBufferContext,
+ void *pData,
XAuint32 dataLength,
const XAAndroidBufferItem *pItems,
XAuint32 itemsLength
typedef SLresult (SLAPIENTRY *slAndroidBufferQueueCallback)(
SLAndroidBufferQueueItf caller,/* input */
- void *pContext, /* input */
- const void *pBufferData, /* input */
+ void *pCallbackContext, /* input */
+ void *pBufferContext, /* input */
+ void *pBufferData, /* input */
SLuint32 dataSize, /* input */
SLuint32 dataUsed, /* input */
const SLAndroidBufferItem *pItems,/* input */
SLresult (*RegisterCallback) (
SLAndroidBufferQueueItf self,
slAndroidBufferQueueCallback callback,
- void* pContext
+ void* pCallbackContext
);
SLresult (*Clear) (
SLresult (*Enqueue) (
SLAndroidBufferQueueItf self,
- const void *pData,
+ void *pBufferContext,
+ void *pData,
SLuint32 dataLength,
const SLAndroidBufferItem *pItems,
SLuint32 itemsLength
#define MPH_ANDROIDSIMPLEBUFFERQUEUE 49
// Android API level 10 extended interfaces
-#define MPH_ANDROIDBUFFERQUEUE 50
+#define MPH_ANDROIDBUFFERQUEUE 50 // used for SL and XA
// OpenMAX AL 1.0.1
#define MPH_XAENGINE 51
#define MPH_XAPLAY 52
#define MPH_XASTREAMINFORMATION 53
+#define MPH_XAVOLUME 54
// total number of interface IDs
-#define MPH_MAX 54
+#define MPH_MAX 55
#endif // !defined(__MPH_H)
[MPH_OBJECT] = 0,
[MPH_DYNAMICINTERFACEMANAGEMENT] = 1,
[MPH_XAPLAY] = 2,
- [MPH_ANDROIDBUFFERQUEUE] = 3,
+ [MPH_XASTREAMINFORMATION] = 3,
+ [MPH_XAVOLUME] = 4,
+ [MPH_ANDROIDBUFFERQUEUE] = 5,
// FIXME more TBD
#else
#include "MPH_to_MediaPlayer.h"
{ 0xb9c293e0, 0xf776, 0x11db, 0x80df, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
// XA_IID_STREAMINFORMATION
{ 0x3a628fe0, 0x1238, 0x11de, 0xad9f, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
+ // XA_IID_VOLUME
+ { 0x088ba520, 0xf777, 0x11db, 0xa5e3, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
// OpenMAX AL 1.0.1 Android API level 10 extended interfaces
// XA_IID_ANDROIDBUFFERQUEUE
const XAInterfaceID XA_IID_PLAY = (XAInterfaceID) &SL_IID_array[MPH_XAPLAY];
const XAInterfaceID XA_IID_STREAMINFORMATION =
(XAInterfaceID) &SL_IID_array[MPH_XASTREAMINFORMATION];
+const XAInterfaceID XA_IID_VOLUME = (XAInterfaceID) &SL_IID_array[MPH_XAVOLUME];
// OpenMAX AL 1.0.1 Android API level 10 extended interfaces
const XAInterfaceID XA_IID_ANDROIDBUFFERQUEUE =
//-----------------------------------------------------------------------------
// Callback associated with an SfPlayer of an SL ES AudioPlayer that gets its data
// from a URI or FD, for prepare and prefetch events
-static void sfplayer_handlePrefetchEvent(int event, int data1, void* user) {
+static void sfplayer_handlePrefetchEvent(int event, int data1, int data2, void* user) {
if (NULL == user) {
return;
}
//-----------------------------------------------------------------------------
-static void player_handleMediaPlayerEventNotifications(const int event, const int data1, void* user)
+static void player_handleMediaPlayerEventNotifications(int event, int data1, int data2, void* user)
{
if (NULL == user) {
return;
switch(event) {
- case android::GenericPlayer::kEventPrepared: {
+ case android::GenericPlayer::kEventPrepared: {
if (PLAYER_SUCCESS == data1) {
object_lock_exclusive(&mp->mObject);
SL_LOGV("Received AVPlayer::kEventPrepared from AVPlayer for CMediaPlayer %p", mp);
mp->mAndroidObjState = ANDROID_READY;
object_unlock_exclusive(&mp->mObject);
}
+ break;
+ }
+
+ case android::GenericPlayer::kEventHasVideoSize: {
+ SL_LOGV("Received AVPlayer::kEventHasVideoSize (%d,%d) for CMediaPlayer %p",
+ data1, data2, mp);
+
+ object_lock_exclusive(&mp->mObject);
+
+ // remove an existing video info entry (here we only have one video stream)
+ for(size_t i=0 ; i < mp->mStreamInfo.mStreamInfoTable.size() ; i++) {
+ if (XA_DOMAINTYPE_VIDEO == mp->mStreamInfo.mStreamInfoTable.itemAt(i).domain) {
+ mp->mStreamInfo.mStreamInfoTable.removeAt(i);
+ break;
+ }
+ }
+ // update the stream information with a new video info entry
+ StreamInfo streamInfo;
+ streamInfo.domain = XA_DOMAINTYPE_VIDEO;
+ streamInfo.videoInfo.codecId = 0;// unknown, we don't have that info FIXME
+ streamInfo.videoInfo.width = (XAuint32)data1;
+ streamInfo.videoInfo.height = (XAuint32)data2;
+ streamInfo.videoInfo.bitRate = 0;// unknown, we don't have that info FIXME
+ streamInfo.videoInfo.duration = XA_TIME_UNKNOWN;
+ StreamInfo &contInfo = mp->mStreamInfo.mStreamInfoTable.editItemAt(0);
+ contInfo.containerInfo.numStreams = 1;
+ ssize_t index = mp->mStreamInfo.mStreamInfoTable.add(streamInfo);
+
+ xaStreamEventChangeCallback callback = mp->mStreamInfo.mCallback;
+ void* callbackPContext = mp->mStreamInfo.mContext;
+
+ object_unlock_exclusive(&mp->mObject);
+
+ // notify (outside of lock) that the stream information has been updated
+ if ((NULL != callback) && (index >= 0)) {
+ (*callback)(&mp->mStreamInfo.mItf, XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/,
+ 1 /*streamIndex, only one stream supported here, 0 is reserved*/,
+ NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/,
+ callbackPContext /*pContext*/);
}
break;
+ }
default:
SL_LOGE("Received unknown event %d, data %d from AVPlayer", event, data1);
mp->mStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE;
mp->mSessionId = android::AudioSystem::newAudioSessionId();
- mp->mAndroidAudioLevels.mAmplFromVolLevel = 1.0f;
- mp->mAndroidAudioLevels.mAmplFromStereoPos[0] = 1.0f;
- mp->mAndroidAudioLevels.mAmplFromStereoPos[1] = 1.0f;
- mp->mAndroidAudioLevels.mAmplFromDirectLevel = 1.0f; // matches initial mDirectLevel value
- mp->mAndroidAudioLevels.mAuxSendLevel = 0;
mp->mDirectLevel = 0; // no attenuation
return result;
return result;
}
+
+//-----------------------------------------------------------------------------
+/**
+ * pre-condition: avp != NULL, pVolItf != NULL
+ */
+XAresult android_Player_volumeUpdate(android::GenericPlayer *avp, IVolume *pVolItf)
+{
+ XAresult result = XA_RESULT_SUCCESS;
+
+ avp->updateVolume((bool)pVolItf->mMute, (bool)pVolItf->mEnableStereoPosition,
+ pVolItf->mStereoPosition, pVolItf->mLevel);
+
+ return result;
+}
+
//-----------------------------------------------------------------------------
/**
* pre-condition: avp != NULL
extern XAresult android_Player_getDuration(IPlay *pPlayItf, SLmillisecond *pDurMsec);
+/**
+ * pre-condition: avp != NULL, pVolItf != NULL
+ */
+extern XAresult android_Player_volumeUpdate(android::GenericPlayer *avp, IVolume *pVolItf);
+
/**************************************************************************************************
* Playback control and events
****************************/
int32_t val;
if (msg->findInt32(PLAYEREVENT_PREFETCHSTATUSCHANGE, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHSTATUSCHANGE, val);
- mNotifyClient(kEventPrefetchStatusChange, val, mNotifyUser);
+ mNotifyClient(kEventPrefetchStatusChange, val, 0, mNotifyUser);
}
else if (msg->findInt32(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHFILLLEVELUPDATE, val);
- mNotifyClient(kEventPrefetchFillLevelUpdate, val, mNotifyUser);
+ mNotifyClient(kEventPrefetchFillLevelUpdate, val, 0, mNotifyUser);
}
else if (msg->findInt32(PLAYEREVENT_ENDOFSTREAM, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_ENDOFSTREAM, val);
- mNotifyClient(kEventEndOfStream, val, mNotifyUser);
+ mNotifyClient(kEventEndOfStream, val, 0, mNotifyUser);
}
else {
GenericPlayer::onNotify(msg);
namespace android {
//--------------------------------------------------------------------------------------------------
-MediaPlayerNotificationClient::MediaPlayerNotificationClient() :
+MediaPlayerNotificationClient::MediaPlayerNotificationClient(GenericMediaPlayer* gmp) :
+ mGenericMediaPlayer(gmp),
mPlayerPrepared(false)
{
void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2) {
SL_LOGI("MediaPlayerNotificationClient::notify(msg=%d, ext1=%d, ext2=%d)", msg, ext1, ext2);
- if (msg == MEDIA_PREPARED) {
+ switch (msg) {
+ case MEDIA_PREPARED:
mPlayerPrepared = true;
mPlayerPreparedCondition.signal();
+ break;
+
+ case MEDIA_SET_VIDEO_SIZE:
+ mGenericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE,
+ (int32_t)ext1, (int32_t)ext2, true /*async*/);
+ break;
+
+ default: { }
}
}
CHECK(mMediaPlayerService.get() != NULL);
- mPlayerClient = new MediaPlayerNotificationClient();
+ mPlayerClient = new MediaPlayerNotificationClient(this);
}
GenericMediaPlayer::~GenericMediaPlayer() {
mPlayer->pause();
mStateFlags &= ~kFlagPlaying;
}
+}
+
+
+void GenericMediaPlayer::onVolumeUpdate() {
+ // use settings lock to read the volume settings
+ Mutex::Autolock _l(mSettingsLock);
+ if (this->mAndroidAudioLevels.mMute) {
+ mPlayer->setVolume(0.0f, 0.0f);
+ } else {
+ mPlayer->setVolume(mAndroidAudioLevels.mFinalVolume[0],
+ mAndroidAudioLevels.mFinalVolume[1]);
+ }
}
//--------------------------------------------------------------------------------------------------
namespace android {
+class GenericMediaPlayer;
class MediaPlayerNotificationClient : public BnMediaPlayerClient
{
public:
- MediaPlayerNotificationClient();
+ MediaPlayerNotificationClient(GenericMediaPlayer* gmp);
virtual ~MediaPlayerNotificationClient();
// IMediaPlayerClient implementation
private:
Mutex mLock;
+ GenericMediaPlayer* mGenericMediaPlayer;
Condition mPlayerPreparedCondition;
bool mPlayerPrepared;
};
virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
protected:
+ friend class MediaPlayerNotificationClient;
// Async event handlers (called from GenericPlayer's event loop)
virtual void onPrepare();
virtual void onPlay();
virtual void onPause();
+ virtual void onVolumeUpdate();
bool mHasVideo;
mNotifyUser(NULL),
mStateFlags(0),
mLooperPriority(PRIORITY_DEFAULT),
- mPlaybackParams(*params)
+ mPlaybackParams(*params),
+ mChannelCount(1)
{
SL_LOGI("GenericPlayer::GenericPlayer()");
mLooper = new android::ALooper();
+ mAndroidAudioLevels.mMute = false;
+ mAndroidAudioLevels.mFinalVolume[0] = 1.0f;
+ mAndroidAudioLevels.mFinalVolume[1] = 1.0f;
}
void GenericPlayer::pause() {
- SL_LOGI("GenericPlayer::prepare()");
+ SL_LOGI("GenericPlayer::pause()");
sp<AMessage> msg = new AMessage(kWhatPause, id());
msg->post();
}
*msec = -1;
}
+//--------------------------------------------------
+void GenericPlayer::updateVolume(bool mute, bool useStereoPos,
+ XApermille stereoPos, XAmillibel volume) {
+
+ // compute amplification as the combination of volume level and stereo position
+ float leftVol = 1.0f, rightVol = 1.0f;
+ // amplification from volume level
+ leftVol *= sles_to_android_amplification(volume);
+ rightVol = leftVol;
+
+ // amplification from direct level (changed in SLEffectSendtItf and SLAndroidEffectSendItf)
+ // FIXME use calculation below when supporting effects
+ //leftVol *= mAndroidAudioLevels.mAmplFromDirectLevel;
+ //rightVol *= mAndroidAudioLevels.mAmplFromDirectLevel;
+
+ // amplification from stereo position
+ if (useStereoPos) {
+ // panning law depends on number of channels of content: stereo panning vs 2ch. balance
+ if (1 == mChannelCount) {
+ // stereo panning
+ double theta = (1000 + stereoPos) * M_PI_4 / 1000.0f; // 0 <= theta <= Pi/2
+ leftVol *= cos(theta);
+ rightVol *= sin(theta);
+ } else {
+ // stereo balance
+ if (stereoPos > 0) {
+ leftVol *= (1000 - stereoPos) / 1000.0f;
+ rightVol *= 1.0f;
+ } else {
+ leftVol *= 1.0f;
+ rightVol *= (1000 + stereoPos) / 1000.0f;
+ }
+ }
+ }
+
+ {
+ Mutex::Autolock _l(mSettingsLock);
+ mAndroidAudioLevels.mMute = mute;
+ mAndroidAudioLevels.mFinalVolume[0] = leftVol;
+ mAndroidAudioLevels.mFinalVolume[1] = rightVol;
+ }
+
+ // send a message for the volume to be updated by the object which implements the volume
+ (new AMessage(kWhatVolumeUpdate, id()))->post();
+}
+
//--------------------------------------------------
/*
}
+void GenericPlayer::notify(const char* event, int data1, int data2, bool async) {
+ sp<AMessage> msg = new AMessage(kWhatNotif, id());
+ msg->setRect(event, 0, 0, (int32_t)data1, (int32_t)data2);
+ if (async) {
+ msg->post();
+ } else {
+ this->onNotify(msg);
+ }
+}
+
+
//--------------------------------------------------
// AHandler implementation
void GenericPlayer::onMessageReceived(const sp<AMessage> &msg) {
onLoop(msg);
break;
+ case kWhatVolumeUpdate:
+ onVolumeUpdate();
+ break;
+
default:
TRESPASS();
}
return;
}
- int32_t val;
- if (msg->findInt32(PLAYEREVENT_PREPARED, &val)) {
- SL_LOGV("GenericPlayer notifying %s = %d", PLAYEREVENT_PREPARED, val);
- mNotifyClient(kEventPrepared, val, mNotifyUser);
+ int32_t val1, val2;
+ if (msg->findInt32(PLAYEREVENT_PREPARED, &val1)) {
+ SL_LOGV("GenericPlayer notifying %s = %d", PLAYEREVENT_PREPARED, val1);
+ mNotifyClient(kEventPrepared, val1, 0, mNotifyUser);
+ } else if (msg->findRect(PLAYEREVENT_VIDEO_SIZE_UPDATE, &val1, &val2, &val1, &val2)) {
+ SL_LOGD("GenericPlayer notifying %s = %d, %d", PLAYEREVENT_VIDEO_SIZE_UPDATE, val1, val2);
+ mNotifyClient(kEventHasVideoSize, val1, val2, mNotifyUser);
}
}
SL_LOGV("GenericPlayer::onLoop");
}
+
+void GenericPlayer::onVolumeUpdate() {
+
+}
+
} // namespace android
public:
enum {
- kEventPrepared = 'prep'
+ kEventPrepared = 'prep',
+ kEventHasVideoSize = 'vsiz',
};
GenericPlayer(const AudioPlayback_Parameters* params);
virtual void getDurationMsec(int* msec); // -1 if unknown
+ void updateVolume(bool mute, bool useStereoPos, XApermille stereoPos, XAmillibel volume);
+
protected:
+ Mutex mSettingsLock;
void resetDataLocator();
DataLocator2 mDataLocator;
int mDataLocatorType;
enum {
- kWhatPrepare = 'prep',
- kWhatNotif = 'noti',
- kWhatPlay = 'play',
- kWhatPause = 'paus',
- kWhatSeek = 'seek',
- kWhatLoop = 'loop',
+ kWhatPrepare = 'prep',
+ kWhatNotif = 'noti',
+ kWhatPlay = 'play',
+ kWhatPause = 'paus',
+ kWhatSeek = 'seek',
+ kWhatLoop = 'loop',
+ kWhatVolumeUpdate = 'volu'
};
// Send a notification to one of the event listeners
- virtual void notify(const char* event, int data, bool async);
+ virtual void notify(const char* event, int data1, bool async);
+ virtual void notify(const char* event, int data1, int data2, bool async);
// AHandler implementation
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onPause();
virtual void onSeek(const sp<AMessage> &msg);
virtual void onLoop(const sp<AMessage> &msg);
+ virtual void onVolumeUpdate();
// Event notification from GenericPlayer to OpenSL ES / OpenMAX AL framework
notif_cbf_t mNotifyClient;
AudioPlayback_Parameters mPlaybackParams;
+ AndroidAudioLevels mAndroidAudioLevels;
+ int mChannelCount; // this is used for the panning law, and is not exposed outside of the object
+
private:
DISALLOW_EVIL_CONSTRUCTORS(GenericPlayer);
};
int32_t val;
if (msg->findInt32(PLAYEREVENT_PREFETCHSTATUSCHANGE, &val)) {
SL_LOGV("\tSfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHSTATUSCHANGE, val);
- mNotifyClient(kEventPrefetchStatusChange, val, mNotifyUser);
+ mNotifyClient(kEventPrefetchStatusChange, val, 0, mNotifyUser);
}
if (msg->findInt32(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, &val)) {
SL_LOGV("\tSfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHFILLLEVELUPDATE, val);
- mNotifyClient(kEventPrefetchFillLevelUpdate, val, mNotifyUser);
+ mNotifyClient(kEventPrefetchFillLevelUpdate, val, 0, mNotifyUser);
}
if (msg->findInt32(PLAYEREVENT_ENDOFSTREAM, &val)) {
SL_LOGV("\tSfPlayer notifying %s = %d", PLAYEREVENT_ENDOFSTREAM, val);
- mNotifyClient(kEventEndOfStream, val, mNotifyUser);
+ mNotifyClient(kEventEndOfStream, val, 0, mNotifyUser);
}
if (msg->findInt32(PLAYEREVENT_PREPARED, &val)) {
SL_LOGV("\tSfPlayer notifying %s = %d", PLAYEREVENT_PREPARED, val);
- mNotifyClient(GenericMediaPlayer::kEventPrepared, val, mNotifyUser);
+ mNotifyClient(GenericMediaPlayer::kEventPrepared, val, 0, mNotifyUser);
}
if (msg->findInt32(PLAYEREVENT_NEW_AUDIOTRACK, &val)) {
SL_LOGV("\tSfPlayer notifying %s", PLAYEREVENT_NEW_AUDIOTRACK);
- mNotifyClient(kEventNewAudioTrack, val, mNotifyUser);
+ mNotifyClient(kEventNewAudioTrack, val, 0, mNotifyUser);
}
}
// oldFront was only initialized in the code path where callback is initialized
// so no need to check if it's valid
(*callback)(&mAndroidBufferQueue->mItf, callbackPContext,
+ (void *)oldFront->mBufferContext, /* pBufferContext */
(void *)oldFront->mDataBuffer,/* pBufferData */
oldFront->mDataSize, /* dataSize */
// here a buffer is only dequeued when fully consumed
void StreamPlayer::appClear_l() {
- // the user of StreamPlayer has cleared its AndroidBufferQueue: a discontinuity is expected
- Mutex::Autolock _l(mAppProxyLock);
- if (mAppProxy != 0) {
- mAppProxy->receivedCmd_l(IStreamListener::DISCONTINUITY);
- }
+ // the user of StreamPlayer has cleared its AndroidBufferQueue:
+ // there's no clear() for the shared memory queue, so this is a no-op
}
* Structure to maintain the set of audio levels about a player
*/
typedef struct AndroidAudioLevels_struct {
-/** send level to aux effect, there's a single aux bus, so there's a single level */
- SLmillibel mAuxSendLevel;
/**
- * Amplification (can be attenuation) factor derived for the VolumeLevel
+ * Is this player muted
*/
- float mAmplFromVolLevel;
+ bool mMute;
/**
- * Left/right amplification (can be attenuations) factors derived for the StereoPosition
+ * Send level to aux effect, there's a single aux bus, so there's a single level
*/
- float mAmplFromStereoPos[STEREO_CHANNELS];
+ // FIXME not used yet, will be used when supporting effects in OpenMAX AL
+ //SLmillibel mAuxSendLevel;
/**
* Attenuation factor derived from direct level
*/
- float mAmplFromDirectLevel;
+ // FIXME not used yet, will be used when supporting effects in OpenMAX AL
+ //float mAmplFromDirectLevel;
+ /**
+ * Android Left/Right volume
+ * The final volume of an Android AudioTrack or MediaPlayer is a stereo amplification
+ * (or attenuation) represented as a float from 0.0f to 1.0f
+ */
+ float mFinalVolume[STEREO_CHANNELS];
} AndroidAudioLevels;
/**
* Event notification callback from Android to SL ES framework
*/
-typedef void (*notif_cbf_t)(int event, int data1, void* notifUser);
+typedef void (*notif_cbf_t)(int event, int data1, int data2, void* notifUser);
/**
* Audio data push callback from Android objects to SL ES framework
#define PLAYEREVENT_PREFETCHFILLLEVELUPDATE "pflu"
#define PLAYEREVENT_ENDOFSTREAM "eos"
#define PLAYEREVENT_NEW_AUDIOTRACK "nwat"
+#define PLAYEREVENT_VIDEO_SIZE_UPDATE "vsiz"
/**
int IID_to_MPH(const SLInterfaceID iid)
{
-#define MAX_HASH_VALUE 115
+#define MAX_HASH_VALUE 139
static const unsigned char asso_values[] =
{
- 5, 116, 116, 2, 116, 116, 17, 116, 116, 17,
- 116, 22, 116, 116, 116, 51, 116, 116, 116, 116,
- 116, 116, 2, 116, 116, 46, 116, 116, 116, 116,
- 116, 17, 20, 116, 116, 116, 116, 116, 116, 116,
- 116, 116, 51, 20, 46, 116, 31, 116, 116, 116,
- 116, 116, 116, 36, 116, 2, 116, 116, 2, 116,
- 116, 61, 116, 2, 55, 116, 116, 116, 116, 16,
- 116, 26, 116, 116, 116, 116, 116, 116, 116, 116,
- 21, 116, 116, 116, 116, 116, 26, 116, 116, 116,
- 11, 116, 116, 116, 116, 116, 40, 21, 116, 60,
- 116, 116, 116, 116, 116, 116, 16, 116, 116, 116,
- 116, 116, 1, 116, 116, 116, 116, 116, 116, 116,
- 116, 11, 55, 6, 116, 116, 116, 6, 45, 116,
- 116, 1, 116, 116, 116, 116, 116, 1, 116, 116,
- 116, 1, 116, 116, 116, 116, 116, 116, 116, 116,
- 116, 60, 116, 116, 116, 116, 116, 116, 116, 116,
- 10, 116, 116, 116, 116, 116, 116, 116, 116, 116,
- 50, 116, 116, 116, 45, 116, 116, 116, 60, 116,
- 45, 116, 116, 116, 116, 55, 116, 116, 116, 116,
- 116, 116, 15, 116, 35, 116, 45, 5, 116, 50,
- 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
- 116, 116, 40, 116, 116, 50, 116, 116, 116, 116,
- 116, 25, 116, 116, 15, 40, 116, 116, 10, 116,
- 116, 116, 116, 116, 116, 10, 0, 116, 116, 15,
- 30, 116, 116, 116, 116, 116, 55, 116, 116, 116,
- 116, 116, 116, 116, 0, 116
+ 5, 140, 140, 7, 140, 140, 27, 140, 17, 17,
+ 140, 22, 140, 140, 140, 51, 140, 140, 140, 140,
+ 140, 140, 2, 140, 140, 46, 140, 140, 140, 140,
+ 140, 17, 20, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 51, 20, 46, 140, 31, 140, 140, 140,
+ 140, 140, 140, 36, 140, 2, 140, 140, 2, 140,
+ 140, 61, 140, 2, 55, 140, 140, 140, 140, 16,
+ 140, 26, 140, 140, 140, 140, 140, 140, 140, 140,
+ 21, 140, 140, 140, 140, 140, 26, 140, 140, 140,
+ 11, 140, 140, 140, 140, 140, 40, 21, 140, 60,
+ 140, 140, 140, 140, 140, 140, 16, 140, 140, 140,
+ 140, 140, 1, 140, 140, 140, 140, 140, 140, 140,
+ 140, 11, 55, 6, 140, 140, 140, 6, 45, 140,
+ 140, 1, 140, 140, 140, 140, 140, 1, 140, 140,
+ 140, 1, 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 60, 140, 140, 140, 140, 140, 140, 140, 140,
+ 10, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 50, 140, 140, 140, 45, 140, 140, 140, 60, 140,
+ 45, 140, 140, 140, 140, 55, 140, 140, 140, 140,
+ 140, 140, 15, 140, 35, 140, 45, 5, 140, 50,
+ 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 40, 140, 140, 50, 140, 140, 140, 140,
+ 140, 25, 140, 140, 15, 40, 140, 140, 10, 140,
+ 140, 140, 140, 140, 140, 10, 0, 140, 140, 15,
+ 30, 140, 140, 140, 140, 140, 55, 140, 140, 140,
+ 140, 140, 140, 140, 0, 140
};
static const signed char hash_to_MPH[] = {
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
MPH_NULL,
-1,
-1,
-1,
MPH_BUFFERQUEUE,
MPH_3DMACROSCOPIC,
- MPH_BASSBOOST,
+ MPH_XAVOLUME,
-1,
-1,
MPH_3DLOCATION,
-1,
MPH_MIDIMESSAGE,
MPH_ANDROIDBUFFERQUEUE,
- MPH_MIDIMUTESOLO,
+ MPH_BASSBOOST,
-1,
-1,
MPH_SEEK,
MPH_OBJECT,
- -1,
+ MPH_MIDIMUTESOLO,
-1,
-1,
MPH_PITCH,
if (&SL_IID_array[0] <= iid && &SL_IID_array[MPH_MAX] > iid)
return iid - &SL_IID_array[0];
if (NULL != iid) {
- unsigned key = asso_values[((unsigned char *)iid)[3]] +
- asso_values[((unsigned char *)iid)[0]];
+ static const unsigned len = sizeof(struct SLInterfaceID_);
+ unsigned key = len +
+ asso_values[((unsigned char *)iid)[3]] + asso_values[((unsigned char *)iid)[0]];
if (key <= MAX_HASH_VALUE) {
int MPH = hash_to_MPH[key];
if (MPH >= 0) {
// This file is automagically generated by mphtogen, do not edit
-1, 3, -1, 2, 5, 4, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, 3, 4, 5, 16, 6, -1, -1, -1, -1, 17, 7, -1, 1, 18, 8, -1, -1, 19, 20,
-1, 10, 11, -1, -1, -1, -1, 9, -1, 0, -1, 21, 2, 23, 12, 22, 13, -1, 14, -1,
- -1, 24, 25, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, 24, 25, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, 3, -1, -1, 4, -1, -1, 1, 5, -1, -1, -1, -1, 6,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1,
- -1, -1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
8, -1, -1, -1, -1, -1, 6, -1, 7, 5, -1, -1, 9, 1, -1, -1, 2, 3, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
2, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, 2, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, 2, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, -1, 2, 3, 4
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, -1,
-1, 3, 4, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, 3, 4, 5, 19, 6, -1, -1, -1, -1, 20, 7, -1, 1, 21, 8, -1, -1, 22, 23,
-1, 10, 11, 12, 15, 14, 13, 9, -1, 0, -1, 24, 2, 26, 16, 25, -1, -1, 17, -1,
- -1, 27, 28, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, 27, 28, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, -1, -1, 1, -1, -1, -1, -1, 4, 5,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, -1, -1, -1, -1, 6, -1, -1, -1, -1,
- -1, 7, 10, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ -1, 7, 10, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
// This file is automagically generated by mphtogen, do not edit
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
{MPH_DYNAMICINTERFACEMANAGEMENT, INTERFACE_IMPLICIT,
offsetof(CMediaPlayer, mDynamicInterfaceManagement)},
{MPH_XAPLAY, INTERFACE_IMPLICIT, offsetof(CMediaPlayer, mPlay)},
+ {MPH_XASTREAMINFORMATION, INTERFACE_EXPLICIT, offsetof(CMediaPlayer, mStreamInfo)},
+ {MPH_XAVOLUME, INTERFACE_IMPLICIT, offsetof(CMediaPlayer, mVolume)},
#ifdef ANDROID
{MPH_ANDROIDBUFFERQUEUE, INTERFACE_EXPLICIT, offsetof(CMediaPlayer, mAndroidBufferQueue)},
#endif
typedef struct CMediaPlayer_struct {
IObject mObject;
-#define INTERFACES_MediaPlayer 4
+#define INTERFACES_MediaPlayer 6
XAuint8 mInterfaceStates2[INTERFACES_MediaPlayer - INTERFACES_Default];
IDynamicInterfaceManagement mDynamicInterfaceManagement;
IDynamicSource mDynamicSource;
IPlay mPlay;
IStreamInformation mStreamInfo;
+ IVolume mVolume;
#ifdef ANDROID
IAndroidBufferQueue mAndroidBufferQueue;
#endif
int mSessionId;
/** identifies the Android stream type playback will occur on */
int mStreamType;
- AndroidAudioLevels mAndroidAudioLevels;
#endif
} CMediaPlayer;
XAImageStreamInformation imageInfo;
XATimedTextStreamInformation textInfo;
XAMIDIStreamInformation midiInfo;
+ XAVendorStreamInformation vendorInfo;
};
} StreamInfo;
+
+// FIXME a terrible hack until OpenMAX AL spec is updated
+#define XA_DOMAINTYPE_CONTAINER 0
"XAENGINE",
"XAPLAY",
"XASTREAMINFORMATION",
+ "XAVOLUME",
};
thiz->mBufferArray[i].mDataBuffer = NULL;
thiz->mBufferArray[i].mDataSize = 0;
thiz->mBufferArray[i].mDataSizeConsumed = 0;
+ thiz->mBufferArray[i].mBufferContext = NULL;
switch (thiz->mBufferType) {
case kAndroidBufferTypeMpeg2Ts:
thiz->mBufferArray[i].mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
SLresult IAndroidBufferQueue_Enqueue(SLAndroidBufferQueueItf self,
- const void *pData,
+ void *pBufferContext,
+ void *pData,
SLuint32 dataLength,
const SLAndroidBufferItem *pItems,
SLuint32 itemsLength)
oldRear->mDataBuffer = pData;
oldRear->mDataSize = dataLength;
oldRear->mDataSizeConsumed = 0;
+ oldRear->mBufferContext = pBufferContext;
thiz->mRear = newRear;
++thiz->mState.count;
setItems(pItems, itemsLength, thiz->mBufferType, oldRear);
thiz->mAndroidBufferQueue.mBufferArray[i].mDataBuffer = NULL;
thiz->mAndroidBufferQueue.mBufferArray[i].mDataSize = 0;
thiz->mAndroidBufferQueue.mBufferArray[i].mDataSizeConsumed = 0;
+ thiz->mAndroidBufferQueue.mBufferArray[i].mBufferContext = NULL;
switch (thiz->mAndroidBufferQueue.mBufferType) {
case kAndroidBufferTypeMpeg2Ts:
thiz->mAndroidBufferQueue.mBufferArray[i].mItems.mTsCmdData.
if (XA_CONTAINERTYPE_MPEG_TS ==
thiz->mDataSource.mFormat.mMIME.containerType) {
thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
+
+ // Set the container type for the StreamInformation interface
+ XAMediaContainerInformation *containerInfo =
+ (XAMediaContainerInformation*)
+ // always storing container info at index 0, as per spec
+ &(thiz->mStreamInfo.mStreamInfoTable.itemAt(0).
+ containerInfo);
+ containerInfo->containerType = XA_CONTAINERTYPE_MPEG_TS;
+ // there are no streams at this stage
+ containerInfo->numStreams = 0;
+
} else {
thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
SL_LOGE("Invalid buffer type in Android Buffer Queue");
thiz->mAndroidBufferQueue.mBufferArray[i].mDataBuffer = NULL;
thiz->mAndroidBufferQueue.mBufferArray[i].mDataSize = 0;
thiz->mAndroidBufferQueue.mBufferArray[i].mDataSizeConsumed = 0;
+ thiz->mAndroidBufferQueue.mBufferArray[i].mBufferContext = NULL;
switch (thiz->mAndroidBufferQueue.mBufferType) {
case kAndroidBufferTypeMpeg2Ts:
thiz->mAndroidBufferQueue.mBufferArray[i].mItems.mTsCmdData.
#ifdef ANDROID
IStreamInformation *thiz = (IStreamInformation *) self;
interface_lock_exclusive(thiz);
+ // always storing container info at index 0, as per spec
info = (XAMediaContainerInformation*)&(thiz->mStreamInfoTable.itemAt(0).containerInfo);
interface_unlock_exclusive(thiz);
// even though the pointer to the media container info is returned, the values aren't set
IStreamInformation *thiz = (IStreamInformation *) self;
interface_lock_exclusive(thiz);
+
XAuint32 nbStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
// streams in the container are numbered 1..nbStreams
if (streamIndex <= nbStreams) {
streamIndex, nbStreams);
result = XA_RESULT_PARAMETER_INVALID;
}
+
interface_unlock_exclusive(thiz);
}
#endif
{
XA_ENTER_INTERFACE
- SL_LOGE("unsupported XAStreamInformationItf function");
- result = XA_RESULT_CONTENT_UNSUPPORTED;
+ if (NULL == info) {
+ result = XA_RESULT_PARAMETER_INVALID;
+ } else {
+
+#ifndef ANDROID
+ result = XA_RESULT_FEATURE_UNSUPPORTED;
+#else
+
+ IStreamInformation *thiz = (IStreamInformation *) self;
+
+ interface_lock_exclusive(thiz);
+
+ XAuint32 nbStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
+ // streams in the container are numbered 1..nbStreams
+ if (streamIndex <= nbStreams) {
+ result = XA_RESULT_SUCCESS;
+ const StreamInfo& streamInfo = thiz->mStreamInfoTable.itemAt((size_t)streamIndex);
+
+ switch (streamInfo.domain) {
+ case XA_DOMAINTYPE_CONTAINER:
+ *(XAMediaContainerInformation *)info = streamInfo.containerInfo;
+ break;
+ case XA_DOMAINTYPE_AUDIO:
+ *(XAAudioStreamInformation *)info = streamInfo.audioInfo;
+ break;
+ case XA_DOMAINTYPE_VIDEO:
+ *(XAVideoStreamInformation *)info = streamInfo.videoInfo;
+ break;
+ case XA_DOMAINTYPE_IMAGE:
+ *(XAImageStreamInformation *)info = streamInfo.imageInfo;
+ break;
+ case XA_DOMAINTYPE_TIMEDTEXT:
+ *(XATimedTextStreamInformation *)info = streamInfo.textInfo;
+ break;
+ case XA_DOMAINTYPE_MIDI:
+ *(XAMIDIStreamInformation *)info = streamInfo.midiInfo;
+ break;
+ case XA_DOMAINTYPE_VENDOR:
+ *(XAVendorStreamInformation *)info = streamInfo.vendorInfo;
+ break;
+ default:
+ SL_LOGE("StreamInformation::QueryStreamInformation index %lu has unknown domain %lu", streamIndex, streamInfo.domain);
+ result = XA_RESULT_INTERNAL_ERROR;
+ break;
+ }
+
+ } else {
+ SL_LOGE("Querying stream type for stream %ld, only %ld streams available",
+ streamIndex, nbStreams);
+ result = XA_RESULT_PARAMETER_INVALID;
+ }
+
+ interface_unlock_exclusive(thiz);
+#endif
+
+ }
XA_LEAVE_INTERFACE
}
XA_LEAVE_INTERFACE;
}
- SL_LOGE("unsupported XAStreamInformationItf function");
- result = XA_RESULT_CONTENT_UNSUPPORTED;
- *numStreams = 0;
- activeStreams = NULL;
+ IStreamInformation *thiz = (IStreamInformation *) self;
+
+ interface_lock_exclusive(thiz);
+
+ result = XA_RESULT_SUCCESS;
+ *numStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
+ activeStreams = thiz->mActiveStreams;
+
+ interface_unlock_exclusive(thiz);
XA_LEAVE_INTERFACE
}
IStreamInformation_SetActiveStream
};
+
void IStreamInformation_init(void *self)
{
+ SL_LOGV("IStreamInformation_init\n");
IStreamInformation *thiz = (IStreamInformation *) self;
thiz->mItf = &IStreamInformation_Itf;
thiz->mCallback = NULL;
thiz->mContext = NULL;
+ for (int i=0 ; i < NB_SUPPORTED_STREAMS ; i++) {
+ thiz->mActiveStreams[i] = XA_BOOLEAN_FALSE;
+ }
+
#ifdef ANDROID
+ // placement new constructor for C++ field within C struct
+ (void) new (&thiz->mStreamInfoTable) android::Vector<StreamInfo>();
// initialize container info
StreamInfo contInf;
- contInf.domain = XA_DOMAINTYPE_UNKNOWN; // there should really be a domain for the container!
+ contInf.domain = XA_DOMAINTYPE_CONTAINER;
contInf.containerInfo.containerType = XA_CONTAINERTYPE_UNSPECIFIED;
contInf.containerInfo.mediaDuration = XA_TIME_UNKNOWN;
+ // FIXME shouldn't this be 1 ?
contInf.containerInfo.numStreams = 0;
+ // always storing container info at index 0, as per spec: here, the table was still empty
thiz->mStreamInfoTable.add(contInf);
#endif
}
+
+
+void IStreamInformation_deinit(void *self) {
+#ifdef ANDROID
+ IStreamInformation *thiz = (IStreamInformation *) self;
+ // explicit destructor
+ thiz->mStreamInfoTable.~Vector<StreamInfo>();
+#endif
+}
IObject *mThis;
} IXAEngine;
+#define NB_SUPPORTED_STREAMS 1 // only one (video) stream supported in this implementation
typedef struct {
const struct XAStreamInformationItf_ *mItf;
IObject *mThis;
xaStreamEventChangeCallback mCallback;
void *mContext;
+ XAboolean mActiveStreams[NB_SUPPORTED_STREAMS];
#ifdef ANDROID
android::Vector<StreamInfo> mStreamInfoTable;
#endif
// MIDI
SL_LOGD("[ FIXME: gain update on an SL_OBJECTID_MIDIPLAYER to be implemented ]");
break;
+ case XA_OBJECTID_MEDIAPLAYER: {
+#ifdef ANDROID
+ attributes &= ~ATTR_GAIN; // no need to process asynchronously also
+ CMediaPlayer *mp = (CMediaPlayer *) thiz;
+ android::GenericPlayer* avp = mp->mAVPlayer.get();
+ if (avp != NULL) {
+ android_Player_volumeUpdate(avp, &mp->mVolume);
+ }
+#endif
+ break;
+ }
default:
break;
}
IVibra_init(void *),
IVirtualizer_init(void *),
IVisualization_init(void *),
- IVolume_init(void *);
+ IVolume_init(void *),
+ IStreamInformation_init(void*);
extern void
I3DGrouping_deinit(void *),
IObject_deinit(void *),
IPresetReverb_deinit(void *),
IThreadSync_deinit(void *),
- IVirtualizer_deinit(void *);
+ IVirtualizer_deinit(void *),
+ IStreamInformation_deinit(void *);
extern bool
IAndroidEffectCapabilities_Expose(void *),
#define IAndroidEffectCapabilities_deinit NULL
#define IAndroidEffectCapabilities_Expose NULL
#define IAndroidBufferQueue_init NULL
+#define IStreamInformation_init NULL
#endif
#ifndef USE_OUTPUTMIXEXT
// OpenMAX AL 1.0.1 interfaces
{ /* MPH_XAENGINE */ IXAEngine_init, NULL, NULL, NULL, NULL },
{ /* MPH_XAPLAY */ IPlay_init, NULL, NULL, NULL, NULL },
+ { /* MPH_XASTREAMINFORMATION */ IStreamInformation_init, NULL, IStreamInformation_deinit,
+ NULL, NULL },
+ { /* MPH_XAVOLUME, */ IVolume_init, NULL, NULL, NULL, NULL },
};
SLuint32 mDataSize;
SLuint32 mDataSizeConsumed;
AdvancedBufferItems mItems;
+ const void *mBufferContext;
} AdvancedBufferHeader;
#endif
#include <jni.h>
#include <pthread.h>
#include <string.h>
+#define LOG_NDEBUG 0
#define LOG_TAG "NativeMedia"
#include <utils/Log.h>
static XAObjectItf playerObj = NULL;
static XAPlayItf playerPlayItf = NULL;
static XAAndroidBufferQueueItf playerBQItf = NULL;
+static XAStreamInformationItf playerStreamInfoItf = NULL;
+static XAVolumeItf playerVolItf;
// number of required interfaces for the MediaPlayer creation
-#define NB_MAXAL_INTERFACES 2 // XAAndroidBufferQueueItf and XAPlayItf
+#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
// cached surface where the video display happens
#if NO_NATIVE_WINDOW
// AndroidBufferQueueItf callback for an audio player
XAresult AndroidBufferQueueCallback(
XAAndroidBufferQueueItf caller,
- void *pContext, /* input */
- const void *pBufferData, /* input */
+ void *pCallbackContext, /* input */
+ void *pBufferContext, /* input */
+ void *pBufferData, /* input */
XAuint32 dataSize, /* input */
XAuint32 dataUsed, /* input */
const XAAndroidBufferItem *pItems,/* input */
XAuint32 itemsLength /* input */)
{
// assert(BUFFER_SIZE <= dataSize);
+ if (pBufferData == NULL) {
+ // this is the case when our buffer with the EOS message has been consumed
+ return XA_RESULT_SUCCESS;
+ }
+
+#if 0
+ // sample code to use the XAVolumeItf
+ XAAndroidBufferQueueState state;
+ (*caller)->GetState(caller, &state);
+ switch (state.index) {
+ case 300:
+ (*playerVolItf)->SetVolumeLevel(playerVolItf, -600); // -6dB
+ LOGV("setting volume to -6dB");
+ break;
+ case 400:
+ (*playerVolItf)->SetVolumeLevel(playerVolItf, -1200); // -12dB
+ LOGV("setting volume to -12dB");
+ break;
+ case 500:
+ (*playerVolItf)->SetVolumeLevel(playerVolItf, 0); // full volume
+ LOGV("setting volume to 0dB (full volume)");
+ break;
+ case 600:
+ (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_TRUE); // mute
+ LOGV("muting player");
+ break;
+ case 700:
+ (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_FALSE); // unmute
+ LOGV("unmuting player");
+ break;
+ case 800:
+ (*playerVolItf)->SetStereoPosition(playerVolItf, -1000);
+ (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_TRUE);
+ LOGV("pan sound to the left (hard-left)");
+ break;
+ case 900:
+ (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_FALSE);
+ LOGV("disabling stereo position");
+ break;
+ default:
+ break;
+ }
+#endif
+
size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE, file);
- if (nbRead > 0) {
- (*caller)->Enqueue(caller,
+ if ((nbRead > 0) && (NULL != pBufferData)) {
+ (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
pBufferData /*pData*/,
nbRead /*dataLength*/,
NULL /*pMsg*/,
msgEos.itemSize = 0;
// EOS message has no parameters, so the total size of the message is the size of the key
// plus the size if itemSize, both XAuint32
- (*caller)->Enqueue(caller, NULL /*pData*/, 0 /*dataLength*/,
- &msgEos /*pMsg*/,
- sizeof(XAuint32)*2 /*msgLength*/);
+ (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
+ NULL /*pData*/, 0 /*dataLength*/,
+ &msgEos /*pMsg*/,
+ sizeof(XAuint32)*2 /*msgLength*/);
reachedEof = 1;
}
}
+void StreamChangeCallback (XAStreamInformationItf caller,
+ XAuint32 eventId,
+ XAuint32 streamIndex,
+ void * pEventData,
+ void * pContext )
+{
+ if (XA_STREAMCBEVENT_PROPERTYCHANGE == eventId) {
+ LOGD("StreamChangeCallback called for stream %lu", streamIndex);
+
+ XAuint32 domain;
+ if (XA_RESULT_SUCCESS == (*caller)->QueryStreamType(caller, streamIndex, &domain)) {
+ if (XA_DOMAINTYPE_VIDEO == domain) {
+ XAVideoStreamInformation videoInfo;
+ if (XA_RESULT_SUCCESS == (*caller)->QueryStreamInformation(caller, streamIndex,
+ &videoInfo)) {
+ LOGI("Found video size %lu x %lu", videoInfo.width, videoInfo.height);
+ }
+ }
+ }
+ }
+}
+
+
// create the engine and output mix objects
void Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
{
XADataSink imageVideoSink = {&loc_nd, NULL};
// declare interfaces to use
- XAboolean required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
- XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY, XA_IID_ANDROIDBUFFERQUEUE};
+ XAboolean required[NB_MAXAL_INTERFACES]
+ = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
+ XAInterfaceID iidArray[NB_MAXAL_INTERFACES]
+ = {XA_IID_PLAY, XA_IID_ANDROIDBUFFERQUEUE, XA_IID_STREAMINFORMATION};
+
// create media player
res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
assert(XA_RESULT_SUCCESS == res);
+ // get the stream information interface (for video size)
+ res = (*playerObj)->GetInterface(playerObj, XA_IID_STREAMINFORMATION, &playerStreamInfoItf);
+ assert(XA_RESULT_SUCCESS == res);
+
+ // get the volume interface
+ res = (*playerObj)->GetInterface(playerObj, XA_IID_VOLUME, &playerVolItf);
+ assert(XA_RESULT_SUCCESS == res);
+
// get the Android buffer queue interface
res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUE, &playerBQItf);
assert(XA_RESULT_SUCCESS == res);
// register the callback from which OpenMAX AL can retrieve the data to play
res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
+ assert(XA_RESULT_SUCCESS == res);
+
+ // we want to be notified of the video size once it's found, so we register a callback for that
+ res = (*playerStreamInfoItf)->RegisterStreamChangeCallback(playerStreamInfoItf,
+ StreamChangeCallback, NULL);
/* Fill our cache */
if (fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file) <= 0) {
we don't want to starve the player */
int i;
for (i=0 ; i < NB_BUFFERS ; i++) {
- res = (*playerBQItf)->Enqueue(playerBQItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
+ res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
+ dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
assert(XA_RESULT_SUCCESS == res);
}
-
// prepare the player
res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
assert(XA_RESULT_SUCCESS == res);
+ // set the volume
+ res = (*playerVolItf)->SetVolumeLevel(playerVolItf, 0);//-300);
+ assert(XA_RESULT_SUCCESS == res);
+
+ // start the playback
res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
assert(XA_RESULT_SUCCESS == res);
#define NB_BUFFERS 16
#define MPEG2_TS_BLOCK_SIZE 188
-#define BUFFER_SIZE 20*MPEG2_TS_BLOCK_SIZE
+#define BUFFER_SIZE (20*MPEG2_TS_BLOCK_SIZE)
+#define DISCONTINUITY_MAGIC 1977
/* Where we store the data to play */
char dataCache[BUFFER_SIZE * NB_BUFFERS];
FILE *file;
/* Has the app reached the end of the file */
bool reachedEof = false;
+/* Special discontinuity buffer context */
+int myDiscBufferContext = DISCONTINUITY_MAGIC;
//-----------------------------------------------------------------
//* Exits the application if an error is encountered */
/* AndroidBufferQueueItf callback for an audio player */
SLresult AndroidBufferQueueCallback(
SLAndroidBufferQueueItf caller,
- void *pContext, /* input */
- const void *pBufferData, /* input */
- SLuint32 dataSize, /* input */
- SLuint32 dataUsed, /* input */
- const SLAndroidBufferItem *pItems,/* input */
- SLuint32 itemsLength /* input */)
+ void *pCallbackContext, /* input */
+ void *pBufferContext, /* input */
+ void *pBufferData, /* input */
+ SLuint32 dataSize, /* input */
+ SLuint32 dataUsed, /* input */
+ const SLAndroidBufferItem *pItems, /* input */
+ SLuint32 itemsLength /* input */)
{
// assert(BUFFER_SIZE <= dataSize);
msgDiscontinuity.itemSize = 0;
// message has no parameters, so the total size of the message is the size of the key
// plus the size if itemSize, both SLuint32
- (*caller)->Enqueue(caller, NULL /*pData*/, 0 /*dataLength*/,
+ (*caller)->Enqueue(caller, (void*)&myDiscBufferContext /*pBufferContext*/,
+ NULL /*pData*/, 0 /*dataLength*/,
&msgDiscontinuity /*pMsg*/,
sizeof(SLuint32)*2 /*msgLength*/);
size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE*(NB_BUFFERS/2), file);
if (nbRead == BUFFER_SIZE*(NB_BUFFERS/2)) {
for (int i=0 ; i < NB_BUFFERS/2 ; i++) {
- SLresult res = (*caller)->Enqueue(caller, dataCache + i*BUFFER_SIZE,
+ SLresult res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
+ dataCache + i*BUFFER_SIZE,
BUFFER_SIZE, NULL, 0);
CheckErr(res);
}
// pBufferData can be null if the last consumed buffer contained only a command
// just like we do for signalling DISCONTINUITY (above) or EOS (below)
+ if ((pBufferContext != NULL) && (*((int*)pBufferContext) == DISCONTINUITY_MAGIC)) {
+ fprintf(stdout, "Successfully detected my discontinuity buffer having been consumed\n");
+ }
if (pBufferData != NULL) {
size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE, file);
if (nbRead > 0) {
- (*caller)->Enqueue(caller,
+ (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
pBufferData /*pData*/,
nbRead /*dataLength*/,
NULL /*pMsg*/,
msgEos.itemSize = 0;
// EOS message has no parameters, so the total size of the message is the size of the key
// plus the size if itemSize, both SLuint32
- (*caller)->Enqueue(caller, NULL /*pData*/, 0 /*dataLength*/,
+ (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
+ NULL /*pData*/, 0 /*dataLength*/,
&msgEos /*pMsg*/,
sizeof(SLuint32)*2 /*msgLength*/);
reachedEof = true;
/* Enqueue the content of our cache before starting to play,
* we don't want to starve the player */
for (int i=0 ; i < NB_BUFFERS ; i++) {
- res = (*abqItf)->Enqueue(abqItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
+ res = (*abqItf)->Enqueue(abqItf, NULL /*pBufferContext*/,
+ dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
CheckErr(res);
}
all : IID_to_MPH.c
-install :
+install : IID_to_MPH.c
cp IID_to_MPH.c ../../src/autogen
CFLAGS = -I../../include -g -DNDEBUG
part7.c : part23in.c hash.sed
# was 55p
- sed -n '/return asso_values/p' < part23in.c | sed -f hash.sed >> $@
+ sed -n '/return.*asso_values/p' < part23in.c | sed -f hash.sed >> $@
# part8.c is human_generated
if (&SL_IID_array[0] <= iid && &SL_IID_array[MPH_MAX] > iid)
return iid - &SL_IID_array[0];
if (NULL != iid) {
+ static const unsigned len = sizeof(struct SLInterfaceID_);