This is a cherry-pick of I6ab07d89b2eeb0650e634b8c3b7a0b36aba4e7dd with merge conflicts addressed by hand and additional changes made in response to code review feedback. Move in the direction of a more publishable API for configuring a media player for retransmission. It used to be that we used a custom invoke and a modified URL (prefixed with aahTX://). There are many issues with this technique and it was never meant to stand the test of time. This CL gets rid of all that. A new (but currently hidden) method was introduced to the java level MediaPlayer API, called setRetransmitTarget(InetSocketAddress), which allows an app writer to set the retransmit target. For now, this method needs to be called before a call to setDataSource (which is pretty unusual for the MediaPlayer API) because this mid level code uses this as a cue to instantiate an aahTX player instead of relying on the data source to select a player. When retranmit functionality becomes part of the existing android player implemenation, this set-retrans-before-set-data-source behavior can go away, along with the aahTX player itself. Change-Id: I3b46c5227bbf69acb2f3cc4f93cfccad9777be98 Signed-off-by: John Grossman <johngro@google.com>
503 lines
16 KiB
C++
503 lines
16 KiB
C++
/*
|
|
**
|
|
** 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/ISurfaceTexture.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,
|
|
};
|
|
|
|
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 ISurfaceTexture to the media player service
|
|
status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
|
|
sp<IBinder> b(surfaceTexture->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();
|
|
}
|
|
};
|
|
|
|
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<ISurfaceTexture> surfaceTexture =
|
|
interface_cast<ISurfaceTexture>(data.readStrongBinder());
|
|
reply->writeInt32(setVideoSurfaceTexture(surfaceTexture));
|
|
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;
|
|
default:
|
|
return BBinder::onTransact(code, data, reply, flags);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
}; // namespace android
|