/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <arpa/inet.h>
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
#include <media/IMediaPlayer.h>
#include <media/IStreamSource.h>
#include <gui/IGraphicBufferProducer.h>
#include <utils/String8.h>
namespace android {
enum {
DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
SET_DATA_SOURCE_URL,
SET_DATA_SOURCE_FD,
SET_DATA_SOURCE_STREAM,
PREPARE_ASYNC,
START,
STOP,
IS_PLAYING,
PAUSE,
SEEK_TO,
GET_CURRENT_POSITION,
GET_DURATION,
RESET,
SET_AUDIO_STREAM_TYPE,
SET_LOOPING,
SET_VOLUME,
INVOKE,
SET_METADATA_FILTER,
GET_METADATA,
SET_AUX_EFFECT_SEND_LEVEL,
ATTACH_AUX_EFFECT,
SET_VIDEO_SURFACETEXTURE,
SET_PARAMETER,
GET_PARAMETER,
SET_RETRANSMIT_ENDPOINT,
GET_RETRANSMIT_ENDPOINT,
SET_NEXT_PLAYER,
};
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
public:
BpMediaPlayer(const sp<IBinder>& impl)
: BpInterface<IMediaPlayer>(impl)
{
}
// disconnect from media player service
void disconnect()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(DISCONNECT, data, &reply);
}
status_t setDataSource(const char* url,
const KeyedVector<String8, String8>* headers)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeCString(url);
if (headers == NULL) {
data.writeInt32(0);
} else {
// serialize the headers
data.writeInt32(headers->size());
for (size_t i = 0; i < headers->size(); ++i) {
data.writeString8(headers->keyAt(i));
data.writeString8(headers->valueAt(i));
}
}
remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
return reply.readInt32();
}
status_t setDataSource(int fd, int64_t offset, int64_t length) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFileDescriptor(fd);
data.writeInt64(offset);
data.writeInt64(length);
remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
return reply.readInt32();
}
status_t setDataSource(const sp<IStreamSource> &source) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeStrongBinder(source->asBinder());
remote()->transact(SET_DATA_SOURCE_STREAM, data, &reply);
return reply.readInt32();
}
// pass the buffered IGraphicBufferProducer to the media player service
status_t setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
sp<IBinder> b(bufferProducer->asBinder());
data.writeStrongBinder(b);
remote()->transact(SET_VIDEO_SURFACETEXTURE, data, &reply);
return reply.readInt32();
}
status_t prepareAsync()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(PREPARE_ASYNC, data, &reply);
return reply.readInt32();
}
status_t start()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(START, data, &reply);
return reply.readInt32();
}
status_t stop()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(STOP, data, &reply);
return reply.readInt32();
}
status_t isPlaying(bool* state)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(IS_PLAYING, data, &reply);
*state = reply.readInt32();
return reply.readInt32();
}
status_t pause()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(PAUSE, data, &reply);
return reply.readInt32();
}
status_t seekTo(int msec)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(msec);
remote()->transact(SEEK_TO, data, &reply);
return reply.readInt32();
}
status_t getCurrentPosition(int* msec)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(GET_CURRENT_POSITION, data, &reply);
*msec = reply.readInt32();
return reply.readInt32();
}
status_t getDuration(int* msec)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(GET_DURATION, data, &reply);
*msec = reply.readInt32();
return reply.readInt32();
}
status_t reset()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(RESET, data, &reply);
return reply.readInt32();
}
status_t setAudioStreamType(audio_stream_type_t stream)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32((int32_t) stream);
remote()->transact(SET_AUDIO_STREAM_TYPE, data, &reply);
return reply.readInt32();
}
status_t setLooping(int loop)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(loop);
remote()->transact(SET_LOOPING, data, &reply);
return reply.readInt32();
}
status_t setVolume(float leftVolume, float rightVolume)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFloat(leftVolume);
data.writeFloat(rightVolume);
remote()->transact(SET_VOLUME, data, &reply);
return reply.readInt32();
}
status_t invoke(const Parcel& request, Parcel *reply)
{
// Avoid doing any extra copy. The interface descriptor should
// have been set by MediaPlayer.java.
return remote()->transact(INVOKE, request, reply);
}
status_t setMetadataFilter(const Parcel& request)
{
Parcel reply;
// Avoid doing any extra copy of the request. The interface
// descriptor should have been set by MediaPlayer.java.
remote()->transact(SET_METADATA_FILTER, request, &reply);
return reply.readInt32();
}
status_t getMetadata(bool update_only, bool apply_filter, Parcel *reply)
{
Parcel request;
request.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
// TODO: Burning 2 ints for 2 boolean. Should probably use flags in an int here.
request.writeInt32(update_only);
request.writeInt32(apply_filter);
remote()->transact(GET_METADATA, request, reply);
return reply->readInt32();
}
status_t setAuxEffectSendLevel(float level)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFloat(level);
remote()->transact(SET_AUX_EFFECT_SEND_LEVEL, data, &reply);
return reply.readInt32();
}
status_t attachAuxEffect(int effectId)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(effectId);
remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
return reply.readInt32();
}
status_t setParameter(int key, const Parcel& request)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(key);
if (request.dataSize() > 0) {
data.appendFrom(const_cast<Parcel *>(&request), 0, request.dataSize());
}
remote()->transact(SET_PARAMETER, data, &reply);
return reply.readInt32();
}
status_t getParameter(int key, Parcel *reply)
{
Parcel data;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(key);
return remote()->transact(GET_PARAMETER, data, reply);
}
status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint)
{
Parcel data, reply;
status_t err;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
if (NULL != endpoint) {
data.writeInt32(sizeof(*endpoint));
data.write(endpoint, sizeof(*endpoint));
} else {
data.writeInt32(0);
}
err = remote()->transact(SET_RETRANSMIT_ENDPOINT, data, &reply);
if (OK != err) {
return err;
}
return reply.readInt32();
}
status_t setNextPlayer(const sp<IMediaPlayer>& player) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
sp<IBinder> b(player->asBinder());
data.writeStrongBinder(b);
remote()->transact(SET_NEXT_PLAYER, data, &reply);
return reply.readInt32();
}
status_t getRetransmitEndpoint(struct sockaddr_in* endpoint)
{
Parcel data, reply;
status_t err;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
err = remote()->transact(GET_RETRANSMIT_ENDPOINT, data, &reply);
if ((OK != err) || (OK != (err = reply.readInt32()))) {
return err;
}
data.read(endpoint, sizeof(*endpoint));
return err;
}
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
// ----------------------------------------------------------------------
status_t BnMediaPlayer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case DISCONNECT: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
disconnect();
return NO_ERROR;
} break;
case SET_DATA_SOURCE_URL: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
const char* url = data.readCString();
KeyedVector<String8, String8> headers;
int32_t numHeaders = data.readInt32();
for (int i = 0; i < numHeaders; ++i) {
String8 key = data.readString8();
String8 value = data.readString8();
headers.add(key, value);
}
reply->writeInt32(setDataSource(url, numHeaders > 0 ? &headers : NULL));
return NO_ERROR;
} break;
case SET_DATA_SOURCE_FD: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
int fd = data.readFileDescriptor();
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
reply->writeInt32(setDataSource(fd, offset, length));
return NO_ERROR;
}
case SET_DATA_SOURCE_STREAM: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
sp<IStreamSource> source =
interface_cast<IStreamSource>(data.readStrongBinder());
reply->writeInt32(setDataSource(source));
return NO_ERROR;
}
case SET_VIDEO_SURFACETEXTURE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
sp<IGraphicBufferProducer> bufferProducer =
interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
reply->writeInt32(setVideoSurfaceTexture(bufferProducer));
return NO_ERROR;
} break;
case PREPARE_ASYNC: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(prepareAsync());
return NO_ERROR;
} break;
case START: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(start());
return NO_ERROR;
} break;
case STOP: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(stop());
return NO_ERROR;
} break;
case IS_PLAYING: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
bool state;
status_t ret = isPlaying(&state);
reply->writeInt32(state);
reply->writeInt32(ret);
return NO_ERROR;
} break;
case PAUSE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(pause());
return NO_ERROR;
} break;
case SEEK_TO: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(seekTo(data.readInt32()));
return NO_ERROR;
} break;
case GET_CURRENT_POSITION: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
int msec;
status_t ret = getCurrentPosition(&msec);
reply->writeInt32(msec);
reply->writeInt32(ret);
return NO_ERROR;
} break;
case GET_DURATION: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
int msec;
status_t ret = getDuration(&msec);
reply->writeInt32(msec);
reply->writeInt32(ret);
return NO_ERROR;
} break;
case RESET: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(reset());
return NO_ERROR;
} break;
case SET_AUDIO_STREAM_TYPE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setAudioStreamType((audio_stream_type_t) data.readInt32()));
return NO_ERROR;
} break;
case SET_LOOPING: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setLooping(data.readInt32()));
return NO_ERROR;
} break;
case SET_VOLUME: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
float leftVolume = data.readFloat();
float rightVolume = data.readFloat();
reply->writeInt32(setVolume(leftVolume, rightVolume));
return NO_ERROR;
} break;
case INVOKE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
status_t result = invoke(data, reply);
return result;
} break;
case SET_METADATA_FILTER: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setMetadataFilter(data));
return NO_ERROR;
} break;
case GET_METADATA: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
bool update_only = static_cast<bool>(data.readInt32());
bool apply_filter = static_cast<bool>(data.readInt32());
const status_t retcode = getMetadata(update_only, apply_filter, reply);
reply->setDataPosition(0);
reply->writeInt32(retcode);
reply->setDataPosition(0);
return NO_ERROR;
} break;
case SET_AUX_EFFECT_SEND_LEVEL: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setAuxEffectSendLevel(data.readFloat()));
return NO_ERROR;
} break;
case ATTACH_AUX_EFFECT: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(attachAuxEffect(data.readInt32()));
return NO_ERROR;
} break;
case SET_PARAMETER: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
int key = data.readInt32();
Parcel request;
if (data.dataAvail() > 0) {
request.appendFrom(
const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
}
request.setDataPosition(0);
reply->writeInt32(setParameter(key, request));
return NO_ERROR;
} break;
case GET_PARAMETER: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
return getParameter(data.readInt32(), reply);
} break;
case SET_RETRANSMIT_ENDPOINT: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
struct sockaddr_in endpoint;
int amt = data.readInt32();
if (amt == sizeof(endpoint)) {
data.read(&endpoint, sizeof(struct sockaddr_in));
reply->writeInt32(setRetransmitEndpoint(&endpoint));
} else {
reply->writeInt32(setRetransmitEndpoint(NULL));
}
return NO_ERROR;
} break;
case GET_RETRANSMIT_ENDPOINT: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
struct sockaddr_in endpoint;
status_t res = getRetransmitEndpoint(&endpoint);
reply->writeInt32(res);
reply->write(&endpoint, sizeof(endpoint));
return NO_ERROR;
} break;
case SET_NEXT_PLAYER: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setNextPlayer(interface_cast<IMediaPlayer>(data.readStrongBinder())));
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------------
}; // namespace android