Merge change 7332

* changes:
  Squashed commit of the following:
This commit is contained in:
Android (Google) Code Review
2009-07-16 10:30:56 -07:00
81 changed files with 13327 additions and 14 deletions

View File

@@ -0,0 +1,66 @@
ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
stagefright.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright
LOCAL_C_INCLUDES:= \
frameworks/base/media/libstagefright \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/external/opencore/android
LOCAL_CFLAGS += -Wno-multichar
LOCAL_MODULE:= stagefright
include $(BUILD_EXECUTABLE)
################################################################################
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
record.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright
LOCAL_C_INCLUDES:= \
frameworks/base/media/libstagefright \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/external/opencore/android
LOCAL_CFLAGS += -Wno-multichar
LOCAL_MODULE:= record
include $(BUILD_EXECUTABLE)
################################################################################
# include $(CLEAR_VARS)
#
# LOCAL_SRC_FILES:= \
# play.cpp
#
# LOCAL_SHARED_LIBRARIES := \
# libstagefright
#
# LOCAL_C_INCLUDES:= \
# frameworks/base/media/libstagefright \
# $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
# $(TOP)/external/opencore/android
#
# LOCAL_CFLAGS += -Wno-multichar
#
# LOCAL_MODULE:= play
#
# include $(BUILD_EXECUTABLE)
endif

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2009 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_WAVEWRITER_H_
#define ANDROID_WAVEWRITER_H_
namespace android {
class WaveWriter {
public:
WaveWriter(const char *filename,
uint16_t num_channels, uint32_t sampling_rate)
: mFile(fopen(filename, "wb")),
mTotalBytes(0) {
fwrite("RIFFxxxxWAVEfmt \x10\x00\x00\x00\x01\x00", 1, 22, mFile);
write_u16(num_channels);
write_u32(sampling_rate);
write_u32(sampling_rate * num_channels * 2);
write_u16(num_channels * 2);
write_u16(16);
fwrite("dataxxxx", 1, 8, mFile);
}
~WaveWriter() {
fseek(mFile, 40, SEEK_SET);
write_u32(mTotalBytes);
fseek(mFile, 4, SEEK_SET);
write_u32(36 + mTotalBytes);
fclose(mFile);
mFile = NULL;
}
void Append(const void *data, size_t size) {
fwrite(data, 1, size, mFile);
mTotalBytes += size;
}
private:
void write_u16(uint16_t x) {
fputc(x & 0xff, mFile);
fputc(x >> 8, mFile);
}
void write_u32(uint32_t x) {
write_u16(x & 0xffff);
write_u16(x >> 16);
}
FILE *mFile;
size_t mTotalBytes;
};
} // namespace android
#endif // ANDROID_WAVEWRITER_H_

295
cmds/stagefright/play.cpp Normal file
View File

@@ -0,0 +1,295 @@
/*
* Copyright (C) 2009 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 <binder/ProcessState.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/TimedEventQueue.h>
#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXDecoder.h>
using namespace android;
struct NewPlayer {
NewPlayer();
~NewPlayer();
void setSource(const char *uri);
void start();
void pause();
void stop();
private:
struct PlayerEvent : public TimedEventQueue::Event {
PlayerEvent(NewPlayer *player,
void (NewPlayer::*method)(int64_t realtime_us))
: mPlayer(player),
mMethod(method) {
}
virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
(mPlayer->*mMethod)(realtime_us);
}
private:
NewPlayer *mPlayer;
void (NewPlayer::*mMethod)(int64_t realtime_us);
PlayerEvent(const PlayerEvent &);
PlayerEvent &operator=(const PlayerEvent &);
};
struct PlayVideoFrameEvent : public TimedEventQueue::Event {
PlayVideoFrameEvent(NewPlayer *player, MediaBuffer *buffer)
: mPlayer(player),
mBuffer(buffer) {
}
virtual ~PlayVideoFrameEvent() {
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
}
virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
mPlayer->onPlayVideoFrame(realtime_us, mBuffer);
mBuffer = NULL;
}
private:
NewPlayer *mPlayer;
MediaBuffer *mBuffer;
PlayVideoFrameEvent(const PlayVideoFrameEvent &);
PlayVideoFrameEvent &operator=(const PlayVideoFrameEvent &);
};
OMXClient mClient;
MPEG4Extractor *mExtractor;
MediaSource *mAudioSource;
OMXDecoder *mAudioDecoder;
MediaSource *mVideoSource;
OMXDecoder *mVideoDecoder;
int32_t mVideoWidth, mVideoHeight;
TimedEventQueue mQueue;
wp<TimedEventQueue::Event> mPlayVideoFrameEvent;
int64_t mMediaTimeUsStart;
int64_t mRealTimeUsStart;
void setAudioSource(MediaSource *source);
void setVideoSource(MediaSource *source);
int64_t approxRealTime(int64_t mediatime_us) const;
void onStart(int64_t realtime_us);
void onPause(int64_t realtime_us);
void onFetchVideoFrame(int64_t realtime_us);
void onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer);
static int64_t getMediaBufferTimeUs(MediaBuffer *buffer);
NewPlayer(const NewPlayer &);
NewPlayer &operator=(const NewPlayer &);
};
NewPlayer::NewPlayer()
: mExtractor(NULL),
mAudioSource(NULL),
mAudioDecoder(NULL),
mVideoSource(NULL),
mVideoDecoder(NULL),
mVideoWidth(0),
mVideoHeight(0) {
status_t err = mClient.connect();
assert(err == OK);
}
NewPlayer::~NewPlayer() {
stop();
mClient.disconnect();
}
void NewPlayer::setSource(const char *uri) {
stop();
mExtractor = new MPEG4Extractor(new MmapSource(uri));
int num_tracks;
status_t err = mExtractor->countTracks(&num_tracks);
assert(err == OK);
for (int i = 0; i < num_tracks; ++i) {
const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
assert(meta != NULL);
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
continue;
}
bool is_audio = false;
bool is_acceptable = false;
if (!strncasecmp(mime, "audio/", 6)) {
is_audio = true;
is_acceptable = (mAudioSource == NULL);
} else if (!strncasecmp(mime, "video/", 6)) {
is_acceptable = (mVideoSource == NULL);
}
if (!is_acceptable) {
continue;
}
MediaSource *source;
if (mExtractor->getTrack(i, &source) != OK) {
continue;
}
if (is_audio) {
setAudioSource(source);
} else {
setVideoSource(source);
}
}
}
void NewPlayer::setAudioSource(MediaSource *source) {
mAudioSource = source;
sp<MetaData> meta = source->getFormat();
mAudioDecoder = OMXDecoder::Create(&mClient, meta);
mAudioDecoder->setSource(source);
}
void NewPlayer::setVideoSource(MediaSource *source) {
mVideoSource = source;
sp<MetaData> meta = source->getFormat();
bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
assert(success);
success = meta->findInt32(kKeyHeight, &mVideoHeight);
assert(success);
mVideoDecoder = OMXDecoder::Create(&mClient, meta);
mVideoDecoder->setSource(source);
}
void NewPlayer::start() {
mQueue.start();
mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onStart));
}
void NewPlayer::pause() {
mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onPause));
}
void NewPlayer::stop() {
mQueue.stop();
delete mVideoDecoder;
mVideoDecoder = NULL;
delete mVideoSource;
mVideoSource = NULL;
mVideoWidth = mVideoHeight = 0;
delete mAudioDecoder;
mAudioDecoder = NULL;
delete mAudioSource;
mAudioSource = NULL;
delete mExtractor;
mExtractor = NULL;
}
int64_t NewPlayer::approxRealTime(int64_t mediatime_us) const {
return mRealTimeUsStart + (mediatime_us - mMediaTimeUsStart);
}
void NewPlayer::onStart(int64_t realtime_us) {
mRealTimeUsStart = TimedEventQueue::getRealTimeUs();
if (mVideoDecoder != NULL) {
mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
}
}
void NewPlayer::onFetchVideoFrame(int64_t realtime_us) {
MediaBuffer *buffer;
status_t err = mVideoDecoder->read(&buffer);
assert(err == OK);
int64_t mediatime_us = getMediaBufferTimeUs(buffer);
sp<TimedEventQueue::Event> event = new PlayVideoFrameEvent(this, buffer);
mPlayVideoFrameEvent = event;
mQueue.postTimedEvent(event, approxRealTime(mediatime_us));
}
// static
int64_t NewPlayer::getMediaBufferTimeUs(MediaBuffer *buffer) {
int32_t units, scale;
bool success =
buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
assert(success);
success =
buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
assert(success);
return (int64_t)units * 1000000 / scale;
}
void NewPlayer::onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer) {
LOGI("playing video frame (mediatime: %.2f sec)\n",
getMediaBufferTimeUs(buffer) / 1E6);
fflush(stdout);
buffer->release();
buffer = NULL;
mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
}
void NewPlayer::onPause(int64_t realtime_us) {
}
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
if (argc != 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
}
NewPlayer player;
player.setSource(argv[1]);
player.start();
sleep(10);
player.stop();
return 0;
}

181
cmds/stagefright/record.cpp Normal file
View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2009 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.
*/
#undef NDEBUG
#include <assert.h>
#include <binder/ProcessState.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXDecoder.h>
using namespace android;
class DummySource : public MediaSource {
public:
DummySource(int width, int height)
: mSize((width * height * 3) / 2) {
mGroup.add_buffer(new MediaBuffer(mSize));
}
virtual ::status_t getMaxSampleSize(size_t *max_size) {
*max_size = mSize;
return ::OK;
}
virtual ::status_t read(MediaBuffer **buffer) {
::status_t err = mGroup.acquire_buffer(buffer);
if (err != ::OK) {
return err;
}
char x = (char)((double)rand() / RAND_MAX * 255);
memset((*buffer)->data(), x, mSize);
(*buffer)->set_range(0, mSize);
return ::OK;
}
private:
MediaBufferGroup mGroup;
size_t mSize;
DummySource(const DummySource &);
DummySource &operator=(const DummySource &);
};
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
#if 1
if (argc != 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
}
MPEG4Extractor extractor(new MmapSource(argv[1]));
int num_tracks;
assert(extractor.countTracks(&num_tracks) == ::OK);
MediaSource *source = NULL;
sp<MetaData> meta;
for (int i = 0; i < num_tracks; ++i) {
meta = extractor.getTrackMetaData(i);
assert(meta.get() != NULL);
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
continue;
}
if (strncasecmp(mime, "video/", 6)) {
continue;
}
if (extractor.getTrack(i, &source) != ::OK) {
source = NULL;
continue;
}
break;
}
if (source == NULL) {
fprintf(stderr, "Unable to find a suitable video track.\n");
return 1;
}
OMXClient client;
assert(client.connect() == android::OK);
OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
decoder->setSource(source);
int width, height;
bool success = meta->findInt32(kKeyWidth, &width);
success = success && meta->findInt32(kKeyHeight, &height);
assert(success);
sp<MetaData> enc_meta = new MetaData;
// enc_meta->setCString(kKeyMIMEType, "video/3gpp");
enc_meta->setCString(kKeyMIMEType, "video/mp4v-es");
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
encoder->setSource(decoder);
// encoder->setSource(meta, new DummySource(width, height));
#if 1
MPEG4Writer writer("/sdcard/output.mp4");
writer.addSource(enc_meta, encoder);
writer.start();
sleep(120);
writer.stop();
#else
encoder->start();
MediaBuffer *buffer;
while (encoder->read(&buffer) == ::OK) {
printf("got an output frame of size %d\n", buffer->range_length());
buffer->release();
buffer = NULL;
}
encoder->stop();
#endif
delete encoder;
encoder = NULL;
delete decoder;
decoder = NULL;
client.disconnect();
delete source;
source = NULL;
#endif
#if 0
CameraSource *source = CameraSource::Create();
printf("source = %p\n", source);
for (int i = 0; i < 100; ++i) {
MediaBuffer *buffer;
status_t err = source->read(&buffer);
assert(err == OK);
printf("got a frame, data=%p, size=%d\n",
buffer->data(), buffer->range_length());
buffer->release();
buffer = NULL;
}
delete source;
source = NULL;
#endif
return 0;
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (C) 2009 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 <sys/time.h>
#undef NDEBUG
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/ESDS.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXDecoder.h>
#include "WaveWriter.h"
using namespace android;
////////////////////////////////////////////////////////////////////////////////
static bool convertToWav(
OMXClient *client, const sp<MetaData> &meta, MediaSource *source) {
printf("convertToWav\n");
OMXDecoder *decoder = OMXDecoder::Create(client, meta);
int32_t sampleRate;
bool success = meta->findInt32(kKeySampleRate, &sampleRate);
assert(success);
int32_t numChannels;
success = meta->findInt32(kKeyChannelCount, &numChannels);
assert(success);
const char *mime;
success = meta->findCString(kKeyMIMEType, &mime);
assert(success);
if (!strcasecmp("audio/3gpp", mime)) {
numChannels = 1; // XXX
}
WaveWriter writer("/sdcard/Music/shoutcast.wav", numChannels, sampleRate);
decoder->setSource(source);
for (int i = 0; i < 100; ++i) {
MediaBuffer *buffer;
::status_t err = decoder->read(&buffer);
if (err != ::OK) {
break;
}
writer.Append((const char *)buffer->data() + buffer->range_offset(),
buffer->range_length());
buffer->release();
buffer = NULL;
}
delete decoder;
decoder = NULL;
return true;
}
////////////////////////////////////////////////////////////////////////////////
static int64_t getNowUs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
}
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
if (argc > 1 && !strcmp(argv[1], "--list")) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
assert(service.get() != NULL);
sp<IOMX> omx = service->createOMX();
assert(omx.get() != NULL);
List<String8> list;
omx->list_nodes(&list);
for (List<String8>::iterator it = list.begin();
it != list.end(); ++it) {
printf("%s\n", (*it).string());
}
return 0;
}
#if 0
MediaPlayerImpl player(argv[1]);
player.play();
sleep(10000);
#else
DataSource::RegisterDefaultSniffers();
OMXClient client;
status_t err = client.connect();
MmapSource *dataSource = new MmapSource(argv[1]);
MediaExtractor *extractor = MediaExtractor::Create(dataSource);
dataSource = NULL;
int numTracks;
err = extractor->countTracks(&numTracks);
sp<MetaData> meta;
int i;
for (i = 0; i < numTracks; ++i) {
meta = extractor->getTrackMetaData(i);
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
if (!strncasecmp(mime, "video/", 6)) {
break;
}
}
OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
if (decoder != NULL) {
MediaSource *source;
err = extractor->getTrack(i, &source);
decoder->setSource(source);
decoder->start();
int64_t startTime = getNowUs();
int n = 0;
MediaBuffer *buffer;
while ((err = decoder->read(&buffer)) == OK) {
if ((++n % 16) == 0) {
printf(".");
fflush(stdout);
}
buffer->release();
buffer = NULL;
}
decoder->stop();
printf("\n");
int64_t delay = getNowUs() - startTime;
printf("avg. %.2f fps\n", n * 1E6 / delay);
delete decoder;
decoder = NULL;
delete source;
source = NULL;
}
delete extractor;
extractor = NULL;
client.disconnect();
#endif
return 0;
}

View File

@@ -29,6 +29,7 @@
namespace android {
class IMediaRecorder;
class IOMX;
class IMediaPlayerService: public IInterface
{
@@ -41,6 +42,7 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IOMX> createOMX() = 0;
};
// ----------------------------------------------------------------------------

176
include/media/IOMX.h Normal file
View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2009 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_IOMX_H_
#define ANDROID_IOMX_H_
#include <binder/IInterface.h>
#include <utils/List.h>
#include <utils/String8.h>
#include <OMX_Core.h>
#define IOMX_USES_SOCKETS 0
namespace android {
class IMemory;
class IOMXObserver;
class IOMX : public IInterface {
public:
DECLARE_META_INTERFACE(OMX);
typedef void *buffer_id;
typedef void *node_id;
#if IOMX_USES_SOCKETS
// If successful, returns a socket descriptor used for further
// communication. Caller assumes ownership of "*sd".
virtual status_t connect(int *sd) = 0;
#endif
virtual status_t list_nodes(List<String8> *list) = 0;
virtual status_t allocate_node(const char *name, node_id *node) = 0;
virtual status_t free_node(node_id node) = 0;
virtual status_t send_command(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
virtual status_t get_parameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) = 0;
virtual status_t set_parameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) = 0;
virtual status_t allocate_buffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) = 0;
virtual status_t allocate_buffer_with_backup(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) = 0;
virtual status_t free_buffer(
node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
#if !IOMX_USES_SOCKETS
virtual status_t observe_node(
node_id node, const sp<IOMXObserver> &observer) = 0;
virtual void fill_buffer(node_id node, buffer_id buffer) = 0;
virtual void empty_buffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) = 0;
#endif
};
struct omx_message {
enum {
EVENT,
EMPTY_BUFFER_DONE,
FILL_BUFFER_DONE,
#if IOMX_USES_SOCKETS
EMPTY_BUFFER,
FILL_BUFFER,
SEND_COMMAND,
DISCONNECT,
DISCONNECTED,
#endif
// reserved for OMXDecoder use.
START,
INITIAL_FILL_BUFFER,
// reserved for OMXObserver use.
QUIT_OBSERVER,
} type;
union {
// if type == EVENT
struct {
IOMX::node_id node;
OMX_EVENTTYPE event;
OMX_U32 data1;
OMX_U32 data2;
} event_data;
// if type == EMPTY_BUFFER_DONE || type == FILL_BUFFER
// || type == INITIAL_FILL_BUFFER
struct {
IOMX::node_id node;
IOMX::buffer_id buffer;
} buffer_data;
// if type == EMPTY_BUFFER || type == FILL_BUFFER_DONE
struct {
IOMX::node_id node;
IOMX::buffer_id buffer;
OMX_U32 range_offset;
OMX_U32 range_length;
OMX_U32 flags;
OMX_TICKS timestamp;
OMX_PTR platform_private; // ignored if type == EMPTY_BUFFER
} extended_buffer_data;
// if type == SEND_COMMAND
struct {
IOMX::node_id node;
OMX_COMMANDTYPE cmd;
OMX_S32 param;
} send_command_data;
} u;
};
class IOMXObserver : public IInterface {
public:
DECLARE_META_INTERFACE(OMXObserver);
virtual void on_message(const omx_message &msg) = 0;
};
////////////////////////////////////////////////////////////////////////////////
class BnOMX : public BnInterface<IOMX> {
public:
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
};
class BnOMXObserver : public BnInterface<IOMXObserver> {
public:
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
};
} // namespace android
#endif // ANDROID_IOMX_H_

View File

@@ -32,7 +32,8 @@ class Parcel;
enum player_type {
PV_PLAYER = 1,
SONIVOX_PLAYER = 2,
VORBIS_PLAYER = 3
VORBIS_PLAYER = 3,
STAGEFRIGHT_PLAYER = 4
};
@@ -51,6 +52,9 @@ public:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
typedef void (*AudioCallback)(
AudioSink *audioSink, void *buffer, size_t size, void *cookie);
virtual ~AudioSink() {}
virtual bool ready() const = 0; // audio output is open and ready
virtual bool realtime() const = 0; // audio output is real-time output
@@ -60,7 +64,17 @@ public:
virtual ssize_t frameSize() const = 0;
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
virtual status_t open(uint32_t sampleRate, int channelCount, int format=AudioSystem::PCM_16_BIT, int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT) = 0;
// If no callback is specified, use the "write" API below to submit
// audio data. Otherwise return a full buffer of audio data on each
// callback.
virtual status_t open(
uint32_t sampleRate, int channelCount,
int format=AudioSystem::PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
AudioCallback cb = NULL,
void *cookie = NULL) = 0;
virtual void start() = 0;
virtual ssize_t write(const void* buffer, size_t size) = 0;
virtual void stop() = 0;

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2009 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 AUDIO_PLAYER_H_
#define AUDIO_PLAYER_H_
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/TimeSource.h>
#include <utils/threads.h>
namespace android {
class MediaSource;
class AudioTrack;
class AudioPlayer : public TimeSource {
public:
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
~AudioPlayer();
// Caller retains ownership of "source".
void setSource(MediaSource *source);
// Return time in us.
virtual int64_t getRealTimeUs();
void start();
void pause();
void resume();
void stop();
// Returns the timestamp of the last buffer played (in us).
int64_t getMediaTimeUs();
// Returns true iff a mapping is established, i.e. the AudioPlayer
// has played at least one frame of audio.
bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
status_t seekTo(int64_t time_us);
private:
MediaSource *mSource;
AudioTrack *mAudioTrack;
MediaBuffer *mInputBuffer;
int mSampleRate;
int64_t mLatencyUs;
size_t mFrameSize;
Mutex mLock;
int64_t mNumFramesPlayed;
int64_t mPositionTimeMediaUs;
int64_t mPositionTimeRealUs;
bool mSeeking;
int64_t mSeekTimeUs;
bool mStarted;
sp<MediaPlayerBase::AudioSink> mAudioSink;
static void AudioCallback(int event, void *user, void *info);
void AudioCallback(int event, void *info);
static void AudioSinkCallback(
MediaPlayerBase::AudioSink *audioSink,
void *data, size_t size, void *me);
void fillBuffer(void *data, size_t size);
int64_t getRealTimeUsLocked() const;
AudioPlayer(const AudioPlayer &);
AudioPlayer &operator=(const AudioPlayer &);
};
} // namespace android
#endif // AUDIO_PLAYER_H_

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2009 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 AUDIO_SOURCE_H_
#define AUDIO_SOURCE_H_
#include <media/stagefright/MediaSource.h>
namespace android {
class AudioRecord;
class AudioSource {
public:
AudioSource(int inputSource);
virtual ~AudioSource();
status_t initCheck() const;
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
private:
AudioRecord *mRecord;
status_t mInitCheck;
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
};
} // namespace android
#endif // AUDIO_SOURCE_H_

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2009 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 CACHING_DATASOURCE_H_
#define CACHING_DATASOURCE_H_
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/threads.h>
namespace android {
class CachingDataSource : public DataSource {
public:
// Assumes ownership of "source".
CachingDataSource(DataSource *source, size_t pageSize, int numPages);
virtual ~CachingDataSource();
status_t InitCheck() const;
virtual ssize_t read_at(off_t offset, void *data, size_t size);
private:
struct Page {
Page *mPrev, *mNext;
off_t mOffset;
size_t mLength;
void *mData;
};
DataSource *mSource;
void *mData;
size_t mPageSize;
Page *mFirst, *mLast;
Page *allocate_page();
Mutex mLock;
CachingDataSource(const CachingDataSource &);
CachingDataSource &operator=(const CachingDataSource &);
};
} // namespace android
#endif // CACHING_DATASOURCE_H_

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2009 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 CAMERA_SOURCE_H_
#define CAMERA_SOURCE_H_
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaSource.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class ICamera;
class ICameraClient;
class IMemory;
class CameraSource : public MediaSource,
public MediaBufferObserver {
public:
static CameraSource *Create();
virtual ~CameraSource();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
virtual void signalBufferReturned(MediaBuffer *buffer);
private:
CameraSource(const sp<ICamera> &camera, const sp<ICameraClient> &client);
sp<ICamera> mCamera;
sp<ICameraClient> mCameraClient;
Mutex mLock;
Condition mFrameAvailableCondition;
List<sp<IMemory> > mFrames;
int mNumFrames;
bool mStarted;
CameraSource(const CameraSource &);
CameraSource &operator=(const CameraSource &);
};
} // namespace android
#endif // CAMERA_SOURCE_H_

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2009 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 DATA_SOURCE_H_
#define DATA_SOURCE_H_
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/threads.h>
namespace android {
class String8;
class DataSource {
public:
DataSource() {}
virtual ~DataSource() {}
virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0;
// May return ERROR_UNSUPPORTED.
virtual status_t getSize(off_t *size);
////////////////////////////////////////////////////////////////////////////
bool sniff(String8 *mimeType, float *confidence);
typedef bool (*SnifferFunc)(
DataSource *source, String8 *mimeType, float *confidence);
static void RegisterSniffer(SnifferFunc func);
static void RegisterDefaultSniffers();
private:
static Mutex gSnifferMutex;
static List<SnifferFunc> gSniffers;
DataSource(const DataSource &);
DataSource &operator=(const DataSource &);
};
} // namespace android
#endif // DATA_SOURCE_H_

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2009 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 ESDS_H_
#define ESDS_H_
#include <stdint.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
class ESDS {
public:
ESDS(const void *data, size_t size);
~ESDS();
status_t InitCheck() const;
status_t getCodecSpecificInfo(const void **data, size_t *size) const;
private:
enum {
kTag_ESDescriptor = 0x03,
kTag_DecoderConfigDescriptor = 0x04,
kTag_DecoderSpecificInfo = 0x05
};
uint8_t *mData;
size_t mSize;
status_t mInitCheck;
size_t mDecoderSpecificOffset;
size_t mDecoderSpecificLength;
status_t skipDescriptorHeader(
size_t offset, size_t size,
uint8_t *tag, size_t *data_offset, size_t *data_size) const;
status_t parse();
status_t parseESDescriptor(size_t offset, size_t size);
status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
ESDS(const ESDS &);
ESDS &operator=(const ESDS &);
};
} // namespace android
#endif // ESDS_H_

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2009 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 FILE_SOURCE_H_
#define FILE_SOURCE_H_
#include <stdio.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/threads.h>
namespace android {
class FileSource : public DataSource {
public:
FileSource(const char *filename);
virtual ~FileSource();
status_t InitCheck() const;
virtual ssize_t read_at(off_t offset, void *data, size_t size);
private:
FILE *mFile;
Mutex mLock;
FileSource(const FileSource &);
FileSource &operator=(const FileSource &);
};
} // namespace android
#endif // FILE_SOURCE_H_

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2009 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 HTTP_DATASOURCE_H_
#define HTTP_DATASOURCE_H_
#include <media/stagefright/DataSource.h>
#include <media/stagefright/HTTPStream.h>
namespace android {
class HTTPDataSource : public DataSource {
public:
HTTPDataSource(const char *host, int port, const char *path);
HTTPDataSource(const char *uri);
virtual ~HTTPDataSource();
// XXXandih
status_t InitCheck() const { return OK; }
virtual ssize_t read_at(off_t offset, void *data, size_t size);
private:
enum {
kBufferSize = 64 * 1024
};
HTTPStream mHttp;
char *mHost;
int mPort;
char *mPath;
void *mBuffer;
size_t mBufferLength;
off_t mBufferOffset;
HTTPDataSource(const HTTPDataSource &);
HTTPDataSource &operator=(const HTTPDataSource &);
};
} // namespace android
#endif // HTTP_DATASOURCE_H_

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2009 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 HTTP_STREAM_H_
#define HTTP_STREAM_H_
#include <sys/types.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/string.h>
#include <utils/KeyedVector.h>
namespace android {
class HTTPStream {
public:
HTTPStream();
~HTTPStream();
status_t connect(const char *server, int port = 80);
status_t disconnect();
status_t send(const char *data, size_t size);
// Assumes data is a '\0' terminated string.
status_t send(const char *data);
// Receive up to "size" bytes of data.
ssize_t receive(void *data, size_t size);
status_t receive_header(int *http_status);
// The header key used to retrieve the status line.
static const char *kStatusKey;
bool find_header_value(
const string &key, string *value) const;
private:
enum State {
READY,
CONNECTED
};
State mState;
int mSocket;
KeyedVector<string, string> mHeaders;
// Receive a line of data terminated by CRLF, line will be '\0' terminated
// _excluding_ the termianting CRLF.
status_t receive_line(char *line, size_t size);
HTTPStream(const HTTPStream &);
HTTPStream &operator=(const HTTPStream &);
};
} // namespace android
#endif // HTTP_STREAM_H_

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2009 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 MP3_EXTRACTOR_H_
#define MP3_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
namespace android {
class DataSource;
class String8;
class MP3Extractor : public MediaExtractor {
public:
// Extractor assumes ownership of "source".
MP3Extractor(DataSource *source);
~MP3Extractor();
status_t countTracks(int *num_tracks);
status_t getTrack(int index, MediaSource **source);
sp<MetaData> getTrackMetaData(int index);
private:
DataSource *mDataSource;
off_t mFirstFramePos;
sp<MetaData> mMeta;
uint32_t mFixedHeader;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);
};
bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence);
} // namespace android
#endif // MP3_EXTRACTOR_H_

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2009 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 MPEG4_EXTRACTOR_H_
#define MPEG4_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
namespace android {
class DataSource;
class SampleTable;
class String8;
class MPEG4Extractor : public MediaExtractor {
public:
// Extractor assumes ownership of "source".
MPEG4Extractor(DataSource *source);
~MPEG4Extractor();
status_t countTracks(int *num_tracks);
status_t getTrack(int index, MediaSource **source);
sp<MetaData> getTrackMetaData(int index);
private:
struct Track {
Track *next;
sp<MetaData> meta;
uint32_t timescale;
SampleTable *sampleTable;
};
DataSource *mDataSource;
bool mHaveMetadata;
Track *mFirstTrack, *mLastTrack;
uint32_t mHandlerType;
status_t readMetaData();
status_t parseChunk(off_t *offset, int depth);
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);
};
bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence);
} // namespace android
#endif // MPEG4_EXTRACTOR_H_

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2009 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 MPEG4_WRITER_H_
#define MPEG4_WRITER_H_
#include <stdio.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class MediaBuffer;
class MediaSource;
class MetaData;
class MPEG4Writer {
public:
MPEG4Writer(const char *filename);
~MPEG4Writer();
// Caller retains ownership of both meta and source.
void addSource(const sp<MetaData> &meta, MediaSource *source);
void start();
void stop();
void beginBox(const char *fourcc);
void writeInt8(int8_t x);
void writeInt16(int16_t x);
void writeInt32(int32_t x);
void writeInt64(int64_t x);
void writeCString(const char *s);
void writeFourcc(const char *fourcc);
void write(const void *data, size_t size);
void endBox();
private:
class Track;
FILE *mFile;
off_t mOffset;
off_t mMdatOffset;
Mutex mLock;
List<Track *> mTracks;
List<off_t> mBoxes;
off_t addSample(MediaBuffer *buffer);
MPEG4Writer(const MPEG4Writer &);
MPEG4Writer &operator=(const MPEG4Writer &);
};
} // namespace android
#endif // MPEG4_WRITER_H_

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2009 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 MEDIA_BUFFER_H_
#define MEDIA_BUFFER_H_
#include <pthread.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
namespace android {
class MediaBuffer;
class MediaBufferObserver;
class MetaData;
class MediaBufferObserver {
public:
MediaBufferObserver() {}
virtual ~MediaBufferObserver() {}
virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
private:
MediaBufferObserver(const MediaBufferObserver &);
MediaBufferObserver &operator=(const MediaBufferObserver &);
};
class MediaBuffer {
public:
// The underlying data remains the responsibility of the caller!
MediaBuffer(void *data, size_t size);
MediaBuffer(size_t size);
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
void release();
// Increments the reference count.
void add_ref();
void *data() const;
size_t size() const;
size_t range_offset() const;
size_t range_length() const;
void set_range(size_t offset, size_t length);
sp<MetaData> meta_data();
// Clears meta data and resets the range to the full extent.
void reset();
void setObserver(MediaBufferObserver *group);
// Returns a clone of this MediaBuffer increasing its reference count.
// The clone references the same data but has its own range and
// MetaData.
MediaBuffer *clone();
protected:
virtual ~MediaBuffer();
private:
friend class MediaBufferGroup;
friend class OMXDecoder;
// For use by OMXDecoder, reference count must be 1, drop reference
// count to 0 without signalling the observer.
void claim();
MediaBufferObserver *mObserver;
MediaBuffer *mNextBuffer;
int mRefCount;
void *mData;
size_t mSize, mRangeOffset, mRangeLength;
bool mOwnsData;
sp<MetaData> mMetaData;
MediaBuffer *mOriginal;
void setNextBuffer(MediaBuffer *buffer);
MediaBuffer *nextBuffer();
int refcount() const;
MediaBuffer(const MediaBuffer &);
MediaBuffer &operator=(const MediaBuffer &);
};
} // namespace android
#endif // MEDIA_BUFFER_H_

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2009 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 MEDIA_BUFFER_GROUP_H_
#define MEDIA_BUFFER_GROUP_H_
#include <utils/Errors.h>
#include <utils/threads.h>
namespace android {
class MediaBuffer;
class MetaData;
class MediaBufferGroup : public MediaBufferObserver {
public:
MediaBufferGroup();
~MediaBufferGroup();
void add_buffer(MediaBuffer *buffer);
// Blocks until a buffer is available and returns it to the caller,
// the returned buffer will have a reference count of 1.
status_t acquire_buffer(MediaBuffer **buffer);
protected:
virtual void signalBufferReturned(MediaBuffer *buffer);
private:
friend class MediaBuffer;
Mutex mLock;
Condition mCondition;
MediaBuffer *mFirstBuffer, *mLastBuffer;
MediaBufferGroup(const MediaBufferGroup &);
MediaBufferGroup &operator=(const MediaBufferGroup &);
};
} // namespace android
#endif // MEDIA_BUFFER_GROUP_H_

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2009 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 MEDIA_ERRORS_H_
#define MEDIA_ERRORS_H_
#include <utils/Errors.h>
namespace android {
enum {
MEDIA_ERROR_BASE = -1000,
ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
ERROR_NOT_CONNECTED = MEDIA_ERROR_BASE - 1,
ERROR_UNKNOWN_HOST = MEDIA_ERROR_BASE - 2,
ERROR_CANNOT_CONNECT = MEDIA_ERROR_BASE - 3,
ERROR_IO = MEDIA_ERROR_BASE - 4,
ERROR_CONNECTION_LOST = MEDIA_ERROR_BASE - 5,
ERROR_MALFORMED = MEDIA_ERROR_BASE - 7,
ERROR_OUT_OF_RANGE = MEDIA_ERROR_BASE - 8,
ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9,
ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10,
ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11,
};
} // namespace android
#endif // MEDIA_ERRORS_H_

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2009 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 MEDIA_EXTRACTOR_H_
#define MEDIA_EXTRACTOR_H_
#include <utils/RefBase.h>
namespace android {
class DataSource;
class MediaSource;
class MetaData;
class MediaExtractor {
public:
static MediaExtractor *Create(DataSource *source, const char *mime = NULL);
virtual ~MediaExtractor() {}
virtual status_t countTracks(int *num_tracks) = 0;
virtual status_t getTrack(int index, MediaSource **source) = 0;
virtual sp<MetaData> getTrackMetaData(int index) = 0;
protected:
MediaExtractor() {}
private:
MediaExtractor(const MediaExtractor &);
MediaExtractor &operator=(const MediaExtractor &);
};
} // namespace android
#endif // MEDIA_EXTRACTOR_H_

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2009 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 MEDIA_PLAYER_IMPL_H_
#define MEDIA_PLAYER_IMPL_H_
#include <pthread.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/OMXClient.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class AudioPlayer;
class ISurface;
class MediaExtractor;
class MediaBuffer;
class MediaSource;
class MemoryHeapPmem;
class MetaData;
class OMXDecoder;
class Surface;
class TimeSource;
class VideoRenderer;
class MediaPlayerImpl {
public:
MediaPlayerImpl(const char *uri);
status_t initCheck() const;
// Assumes ownership of "fd".
MediaPlayerImpl(int fd, int64_t offset, int64_t length);
~MediaPlayerImpl();
void play();
void pause();
bool isPlaying() const;
void setSurface(const sp<Surface> &surface);
void setISurface(const sp<ISurface> &isurface);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
int32_t getWidth() const { return mVideoWidth; }
int32_t getHeight() const { return mVideoHeight; }
int64_t getDuration();
int64_t getPosition();
status_t seekTo(int64_t time);
private:
status_t mInitCheck;
OMXClient mClient;
MediaExtractor *mExtractor;
TimeSource *mTimeSource;
MediaSource *mAudioSource;
OMXDecoder *mAudioDecoder;
AudioPlayer *mAudioPlayer;
MediaSource *mVideoSource;
MediaSource *mVideoDecoder;
int32_t mVideoWidth, mVideoHeight;
int64_t mVideoPosition;
int64_t mDuration;
bool mPlaying;
bool mPaused;
int64_t mTimeSourceDeltaUs;
sp<Surface> mSurface;
sp<ISurface> mISurface;
VideoRenderer *mRenderer;
sp<MediaPlayerBase::AudioSink> mAudioSink;
Mutex mLock;
pthread_t mVideoThread;
bool mSeeking;
int64_t mSeekTimeUs;
size_t mFrameSize;
bool mUseSoftwareColorConversion;
void init();
static void *VideoWrapper(void *me);
void videoEntry();
void setAudioSource(MediaSource *source);
void setVideoSource(MediaSource *source);
MediaSource *makeShoutcastSource(const char *path);
void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
void populateISurface();
void depopulateISurface();
void sendFrameToISurface(MediaBuffer *buffer);
void stop();
MediaPlayerImpl(const MediaPlayerImpl &);
MediaPlayerImpl &operator=(const MediaPlayerImpl &);
};
} // namespace android
#endif // MEDIA_PLAYER_IMPL_H_

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2009 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 MEDIA_SOURCE_H_
#define MEDIA_SOURCE_H_
#include <sys/types.h>
#include <utils/RefBase.h>
namespace android {
class MediaBuffer;
class MetaData;
struct MediaSource {
MediaSource();
virtual ~MediaSource();
// To be called before any other methods on this object, except
// getFormat().
virtual status_t start(MetaData *params = NULL) = 0;
// Any blocking read call returns immediately with a result of NO_INIT.
// It is an error to call any methods other than start after this call
// returns. Any buffers the object may be holding onto at the time of
// the stop() call are released.
// Also, it is imperative that any buffers output by this object and
// held onto by callers be released before a call to stop() !!!
virtual status_t stop() = 0;
// Returns the format of the data output by this media source.
virtual sp<MetaData> getFormat() = 0;
struct ReadOptions;
// Returns a new buffer of data. Call blocks until a
// buffer is available, an error is encountered of the end of the stream
// is reached.
// End of stream is signalled by a result of ERROR_END_OF_STREAM.
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
// Options that modify read() behaviour. The default is to
// a) not request a seek
// b) not be late, i.e. lateness_us = 0
struct ReadOptions {
ReadOptions();
// Reset everything back to defaults.
void reset();
void setSeekTo(int64_t time_us);
void clearSeekTo();
bool getSeekTo(int64_t *time_us) const;
void setLateBy(int64_t lateness_us);
int64_t getLateBy() const;
private:
enum Options {
kSeekTo_Option = 1,
};
uint32_t mOptions;
int64_t mSeekTimeUs;
int64_t mLatenessUs;
};
private:
MediaSource(const MediaSource &);
MediaSource &operator=(const MediaSource &);
};
} // namespace android
#endif // MEDIA_SOURCE_H_

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2009 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 META_DATA_H_
#define META_DATA_H_
#include <sys/types.h>
#include <stdint.h>
#include <utils/RefBase.h>
#include <utils/KeyedVector.h>
namespace android {
enum {
kKeyMIMEType = 'mime',
kKeyWidth = 'widt',
kKeyHeight = 'heig',
kKeyChannelCount = '#chn',
kKeySampleRate = 'srte',
kKeyBitRate = 'brte',
kKeyESDS = 'esds',
kKeyAVCC = 'avcc',
kKeyTimeUnits = '#tim',
kKeyTimeScale = 'scal',
kKeyNeedsNALFraming = 'NALf',
kKeyIsSyncFrame = 'sync',
kKeyDuration = 'dura',
kKeyColorFormat = 'colf',
kKeyPlatformPrivate = 'priv',
kKeyDecoderComponent = 'decC',
};
enum {
kTypeESDS = 'esds',
kTypeAVCC = 'avcc',
};
class MetaData : public RefBase {
public:
MetaData();
MetaData(const MetaData &from);
enum Type {
TYPE_NONE = 'none',
TYPE_C_STRING = 'cstr',
TYPE_INT32 = 'in32',
TYPE_FLOAT = 'floa',
TYPE_POINTER = 'ptr ',
};
void clear();
bool remove(uint32_t key);
bool setCString(uint32_t key, const char *value);
bool setInt32(uint32_t key, int32_t value);
bool setFloat(uint32_t key, float value);
bool setPointer(uint32_t key, void *value);
bool findCString(uint32_t key, const char **value);
bool findInt32(uint32_t key, int32_t *value);
bool findFloat(uint32_t key, float *value);
bool findPointer(uint32_t key, void **value);
bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
bool findData(uint32_t key, uint32_t *type,
const void **data, size_t *size) const;
protected:
virtual ~MetaData();
private:
struct typed_data {
typed_data();
~typed_data();
typed_data(const MetaData::typed_data &);
typed_data &operator=(const MetaData::typed_data &);
void clear();
void setData(uint32_t type, const void *data, size_t size);
void getData(uint32_t *type, const void **data, size_t *size) const;
private:
uint32_t mType;
size_t mSize;
union {
void *ext_data;
float reservoir;
} u;
bool usesReservoir() const {
return mSize <= sizeof(u.reservoir);
}
void allocateStorage(size_t size);
void freeStorage();
void *storage() {
return usesReservoir() ? &u.reservoir : u.ext_data;
}
const void *storage() const {
return usesReservoir() ? &u.reservoir : u.ext_data;
}
};
KeyedVector<uint32_t, typed_data> mItems;
// MetaData &operator=(const MetaData &);
};
} // namespace android
#endif // META_DATA_H_

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2009 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 MMAP_SOURCE_H_
#define MMAP_SOURCE_H_
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
class MmapSource : public DataSource {
public:
MmapSource(const char *filename);
// Assumes ownership of "fd".
MmapSource(int fd, int64_t offset, int64_t length);
virtual ~MmapSource();
status_t InitCheck() const;
virtual ssize_t read_at(off_t offset, void *data, size_t size);
virtual status_t getSize(off_t *size);
private:
int mFd;
void *mBase;
size_t mSize;
MmapSource(const MmapSource &);
MmapSource &operator=(const MmapSource &);
};
} // namespace android
#endif // MMAP_SOURCE_H_

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2009 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 OMX_CLIENT_H_
#define OMX_CLIENT_H_
#include <media/IOMX.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/threads.h>
namespace android {
class OMXObserver {
public:
OMXObserver();
virtual ~OMXObserver();
void postMessage(const omx_message &msg);
protected:
virtual void onOMXMessage(const omx_message &msg) = 0;
private:
friend class OMXClient;
pthread_t mThread;
Mutex mLock;
Condition mQueueNotEmpty;
List<omx_message> mQueue;
void start();
void stop();
static void *ThreadWrapper(void *me);
void threadEntry();
OMXObserver(const OMXObserver &);
OMXObserver &operator=(const OMXObserver &);
};
class OMXClient;
class OMXClientReflector : public BnOMXObserver {
public:
OMXClientReflector(OMXClient *client);
virtual void on_message(const omx_message &msg);
void reset();
private:
OMXClient *mClient;
OMXClientReflector(const OMXClientReflector &);
OMXClientReflector &operator=(const OMXClientReflector &);
};
class OMXClient {
public:
friend class OMXClientReflector;
OMXClient();
~OMXClient();
status_t connect();
void disconnect();
sp<IOMX> interface() {
return mOMX;
}
status_t registerObserver(IOMX::node_id node, OMXObserver *observer);
void unregisterObserver(IOMX::node_id node);
status_t fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer);
status_t emptyBuffer(
IOMX::node_id node, IOMX::buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp);
status_t send_command(
IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
private:
sp<IOMX> mOMX;
int mSock;
Mutex mLock;
pthread_t mThread;
KeyedVector<IOMX::node_id, OMXObserver *> mObservers;
sp<OMXClientReflector> mReflector;
#if IOMX_USES_SOCKETS
static void *ThreadWrapper(void *me);
void threadEntry();
#endif
bool onOMXMessage(const omx_message &msg);
OMXClient(const OMXClient &);
OMXClient &operator=(const OMXClient &);
};
} // namespace android
#endif // OMX_CLIENT_H_

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2009 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 OMX_DECODER_H_
#define OMX_DECODER_H_
#include <binder/MemoryDealer.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/OMXClient.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/threads.h>
namespace android {
class OMXMediaBuffer;
class OMXDecoder : public MediaSource,
public OMXObserver,
public MediaBufferObserver {
public:
static OMXDecoder *Create(
OMXClient *client, const sp<MetaData> &data);
static OMXDecoder *CreateEncoder(
OMXClient *client, const sp<MetaData> &data);
virtual ~OMXDecoder();
// Caller retains ownership of "source".
void setSource(MediaSource *source);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
void addCodecSpecificData(const void *data, size_t size);
// from OMXObserver
virtual void onOMXMessage(const omx_message &msg);
// from MediaBufferObserver
virtual void signalBufferReturned(MediaBuffer *buffer);
private:
enum {
kPortIndexInput = 0,
kPortIndexOutput = 1
};
enum PortStatus {
kPortStatusActive = 0,
kPortStatusDisabled = 1,
kPortStatusShutdown = 2,
kPortStatusFlushing = 3
};
OMXClient *mClient;
sp<IOMX> mOMX;
IOMX::node_id mNode;
char *mComponentName;
bool mIsMP3;
MediaSource *mSource;
sp<MetaData> mOutputFormat;
Mutex mLock;
Condition mOutputBufferAvailable;
List<MediaBuffer *> mOutputBuffers;
struct CodecSpecificData {
void *data;
size_t size;
};
List<CodecSpecificData> mCodecSpecificData;
List<CodecSpecificData>::iterator mCodecSpecificDataIterator;
volatile OMX_STATETYPE mState;
OMX_U32 mPortStatusMask;
bool mShutdownInitiated;
typedef List<IOMX::buffer_id> BufferList;
Vector<BufferList> mBuffers;
KeyedVector<IOMX::buffer_id, sp<IMemory> > mBufferMap;
KeyedVector<IOMX::buffer_id, OMXMediaBuffer *> mMediaBufferMap;
sp<MemoryDealer> mDealer;
bool mSeeking;
int64_t mSeekTimeUs;
bool mStarted;
status_t mErrorCondition;
bool mReachedEndOfInput;
OMXDecoder(OMXClient *client, IOMX::node_id node,
const char *mime, const char *codec);
void setPortStatus(OMX_U32 port_index, PortStatus status);
PortStatus getPortStatus(OMX_U32 port_index) const;
void allocateBuffers(OMX_U32 port_index);
void setAMRFormat();
void setAACFormat();
void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
void setup();
void dumpPortDefinition(OMX_U32 port_index);
void onStart();
void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
void onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data);
void onEventPortSettingsChanged(OMX_U32 port_index);
void onStateChanged(OMX_STATETYPE to);
void onEmptyBufferDone(IOMX::buffer_id buffer);
void onFillBufferDone(const omx_message &msg);
void onRealEmptyBufferDone(IOMX::buffer_id buffer);
void onRealFillBufferDone(const omx_message &msg);
void initiateShutdown();
void freeInputBuffer(IOMX::buffer_id buffer);
void freeOutputBuffer(IOMX::buffer_id buffer);
void postStart();
void postEmptyBufferDone(IOMX::buffer_id buffer);
void postInitialFillBuffer(IOMX::buffer_id buffer);
OMXDecoder(const OMXDecoder &);
OMXDecoder &operator=(const OMXDecoder &);
};
} // namespace android
#endif // OMX_DECODER_H_

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2009 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 QCOM_HARDWARE_RENDERER_H_
#define QCOM_HARDWARE_RENDERER_H_
#include <media/stagefright/VideoRenderer.h>
#include <utils/RefBase.h>
namespace android {
class ISurface;
class MemoryHeapPmem;
class QComHardwareRenderer : public VideoRenderer {
public:
QComHardwareRenderer(
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight);
virtual ~QComHardwareRenderer();
virtual void render(
const void *data, size_t size, void *platformPrivate);
private:
sp<ISurface> mISurface;
size_t mDisplayWidth, mDisplayHeight;
size_t mDecodedWidth, mDecodedHeight;
size_t mFrameSize;
sp<MemoryHeapPmem> mMemoryHeap;
bool getOffset(void *platformPrivate, size_t *offset);
void publishBuffers(uint32_t pmem_fd);
QComHardwareRenderer(const QComHardwareRenderer &);
QComHardwareRenderer &operator=(const QComHardwareRenderer &);
};
} // namespace android
#endif // QCOM_HARDWARE_RENDERER_H_

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2009 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 SAMPLE_TABLE_H_
#define SAMPLE_TABLE_H_
#include <sys/types.h>
#include <stdint.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/threads.h>
namespace android {
class DataSource;
class SampleTable {
public:
// Caller retains ownership of "source".
SampleTable(DataSource *source);
~SampleTable();
// type can be 'stco' or 'co64'.
status_t setChunkOffsetParams(
uint32_t type, off_t data_offset, off_t data_size);
status_t setSampleToChunkParams(off_t data_offset, off_t data_size);
// type can be 'stsz' or 'stz2'.
status_t setSampleSizeParams(
uint32_t type, off_t data_offset, off_t data_size);
status_t setTimeToSampleParams(off_t data_offset, off_t data_size);
status_t setSyncSampleParams(off_t data_offset, off_t data_size);
////////////////////////////////////////////////////////////////////////////
uint32_t countChunkOffsets() const;
status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
status_t getChunkForSample(
uint32_t sample_index, uint32_t *chunk_index,
uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
uint32_t countSamples() const;
status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
status_t getSampleOffsetAndSize(
uint32_t sample_index, off_t *offset, size_t *size);
status_t getMaxSampleSize(size_t *size);
status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
enum {
kSyncSample_Flag = 1
};
status_t findClosestSample(
uint32_t req_time, uint32_t *sample_index, uint32_t flags);
status_t findClosestSyncSample(
uint32_t start_sample_index, uint32_t *sample_index);
private:
DataSource *mDataSource;
Mutex mLock;
off_t mChunkOffsetOffset;
uint32_t mChunkOffsetType;
uint32_t mNumChunkOffsets;
off_t mSampleToChunkOffset;
uint32_t mNumSampleToChunkOffsets;
off_t mSampleSizeOffset;
uint32_t mSampleSizeFieldSize;
uint32_t mDefaultSampleSize;
uint32_t mNumSampleSizes;
uint32_t mTimeToSampleCount;
uint32_t *mTimeToSample;
off_t mSyncSampleOffset;
uint32_t mNumSyncSamples;
SampleTable(const SampleTable &);
SampleTable &operator=(const SampleTable &);
};
} // namespace android
#endif // SAMPLE_TABLE_H_

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2009 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 SHOUTCAST_SOURCE_H_
#define SHOUTCAST_SOURCE_H_
#include <sys/types.h>
#include <media/stagefright/MediaSource.h>
namespace android {
class HTTPStream;
class MediaBufferGroup;
class ShoutcastSource : public MediaSource {
public:
// Assumes ownership of "http".
ShoutcastSource(HTTPStream *http);
virtual ~ShoutcastSource();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
private:
HTTPStream *mHttp;
size_t mMetaDataOffset;
size_t mBytesUntilMetaData;
MediaBufferGroup *mGroup;
bool mStarted;
ShoutcastSource(const ShoutcastSource &);
ShoutcastSource &operator= (const ShoutcastSource &);
};
} // namespace android
#endif // SHOUTCAST_SOURCE_H_

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2009 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 SOFTWARE_RENDERER_H_
#define SOFTWARE_RENDERER_H_
#include <media/stagefright/VideoRenderer.h>
#include <utils/RefBase.h>
namespace android {
class ISurface;
class MemoryHeapBase;
class SoftwareRenderer : public VideoRenderer {
public:
SoftwareRenderer(
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight);
virtual ~SoftwareRenderer();
virtual void render(
const void *data, size_t size, void *platformPrivate);
private:
sp<ISurface> mISurface;
size_t mDisplayWidth, mDisplayHeight;
size_t mDecodedWidth, mDecodedHeight;
size_t mFrameSize;
sp<MemoryHeapBase> mMemoryHeap;
int mIndex;
SoftwareRenderer(const SoftwareRenderer &);
SoftwareRenderer &operator=(const SoftwareRenderer &);
};
} // namespace android
#endif // SOFTWARE_RENDERER_H_

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2009 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 SURFACE_RENDERER_H_
#define SURFACE_RENDERER_H_
#include <media/stagefright/VideoRenderer.h>
#include <utils/RefBase.h>
namespace android {
class Surface;
class SurfaceRenderer : public VideoRenderer {
public:
SurfaceRenderer(
const sp<Surface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight);
virtual ~SurfaceRenderer();
virtual void render(
const void *data, size_t size, void *platformPrivate);
private:
sp<Surface> mSurface;
size_t mDisplayWidth, mDisplayHeight;
size_t mDecodedWidth, mDecodedHeight;
SurfaceRenderer(const SurfaceRenderer &);
SurfaceRenderer &operator=(const SurfaceRenderer &);
};
} // namespace android
#endif // SURFACE_RENDERER_H_

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2009 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 TIME_SOURCE_H_
#define TIME_SOURCE_H_
namespace android {
class TimeSource {
public:
TimeSource() {}
virtual ~TimeSource() {}
virtual int64_t getRealTimeUs() = 0;
private:
TimeSource(const TimeSource &);
TimeSource &operator=(const TimeSource &);
};
class SystemTimeSource : public TimeSource {
public:
SystemTimeSource();
virtual int64_t getRealTimeUs();
private:
static int64_t GetSystemTimeUs();
int64_t mStartTimeUs;
};
} // namespace android
#endif // TIME_SOURCE_H_

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2009 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 TIMED_EVENT_QUEUE_H_
#define TIMED_EVENT_QUEUE_H_
#include <pthread.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
struct TimedEventQueue {
struct Event : public RefBase {
Event() {}
virtual ~Event() {}
protected:
virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
private:
friend class TimedEventQueue;
Event(const Event &);
Event &operator=(const Event &);
};
TimedEventQueue();
~TimedEventQueue();
// Start executing the event loop.
void start();
// Stop executing the event loop, if flush is false, any pending
// events are discarded, otherwise the queue will stop (and this call
// return) once all pending events have been handled.
void stop(bool flush = false);
// Posts an event to the front of the queue (after all events that
// have previously been posted to the front but before timed events).
void postEvent(const sp<Event> &event);
void postEventToBack(const sp<Event> &event);
// It is an error to post an event with a negative delay.
void postEventWithDelay(const sp<Event> &event, int64_t delay_us);
// If the event is to be posted at a time that has already passed,
// it will fire as soon as possible.
void postTimedEvent(const sp<Event> &event, int64_t realtime_us);
// Returns true iff event is currently in the queue and has been
// successfully cancelled. In this case the event will have been
// removed from the queue and won't fire.
bool cancelEvent(const sp<Event> &event);
static int64_t getRealTimeUs();
private:
struct QueueItem {
sp<Event> event;
int64_t realtime_us;
};
struct StopEvent : public TimedEventQueue::Event {
virtual void fire(TimedEventQueue *queue, int64_t now_us) {
queue->mStopped = true;
}
};
pthread_t mThread;
List<QueueItem> mQueue;
Mutex mLock;
Condition mQueueNotEmptyCondition;
Condition mQueueHeadChangedCondition;
bool mRunning;
bool mStopped;
static void *ThreadWrapper(void *me);
void threadEntry();
TimedEventQueue(const TimedEventQueue &);
TimedEventQueue &operator=(const TimedEventQueue &);
};
} // namespace android
#endif // TIMED_EVENT_QUEUE_H_

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2009 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 UTILS_H_
#define UTILS_H_
#include <stdint.h>
namespace android {
#define FOURCC(c1, c2, c3, c4) \
(c1 << 24 | c2 << 16 | c3 << 8 | c4)
uint16_t U16_AT(const uint8_t *ptr);
uint32_t U32_AT(const uint8_t *ptr);
uint64_t U64_AT(const uint8_t *ptr);
uint64_t ntoh64(uint64_t x);
uint64_t hton64(uint64_t x);
} // namespace android
#endif // UTILS_H_

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2009 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 VIDEO_RENDERER_H_
#define VIDEO_RENDERER_H_
#include <sys/types.h>
namespace android {
class VideoRenderer {
public:
virtual ~VideoRenderer() {}
virtual void render(
const void *data, size_t size, void *platformPrivate) = 0;
protected:
VideoRenderer() {}
VideoRenderer(const VideoRenderer &);
VideoRenderer &operator=(const VideoRenderer &);
};
} // namespace android
#endif // VIDEO_RENDERER_H_

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2009 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 STRING_H_
#define STRING_H_
#include <utils/String8.h>
namespace android {
class string {
public:
typedef size_t size_type;
static size_type npos;
string();
string(const char *s);
string(const char *s, size_t length);
string(const string &from, size_type start, size_type length = npos);
const char *c_str() const;
size_type size() const;
void clear();
void erase(size_type from, size_type length);
size_type find(char c) const;
bool operator<(const string &other) const;
bool operator==(const string &other) const;
string &operator+=(char c);
private:
String8 mString;
};
} // namespace android
#endif // STRING_H_

View File

@@ -18,7 +18,8 @@ LOCAL_SRC_FILES:= \
IMediaMetadataRetriever.cpp \
mediametadataretriever.cpp \
ToneGenerator.cpp \
JetPlayer.cpp
JetPlayer.cpp \
IOMX.cpp
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox
@@ -34,6 +35,7 @@ LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_C_INCLUDES := \
$(call include-path-for, graphics corecg)
$(call include-path-for, graphics corecg) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
include $(BUILD_SHARED_LIBRARY)

View File

@@ -17,12 +17,14 @@
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <utils/Errors.h> // for status_t
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/IOMX.h>
#include <utils/Errors.h> // for status_t
namespace android {
@@ -33,6 +35,7 @@ enum {
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
CREATE_OMX,
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -110,6 +113,13 @@ public:
*pFormat = reply.readInt32();
return interface_cast<IMemory>(reply.readStrongBinder());
}
virtual sp<IOMX> createOMX() {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
remote()->transact(CREATE_OMX, data, &reply);
return interface_cast<IOMX>(reply.readStrongBinder());
}
};
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
@@ -182,6 +192,12 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(retriever->asBinder());
return NO_ERROR;
} break;
case CREATE_OMX: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IOMX> omx = createOMX();
reply->writeStrongBinder(omx->asBinder());
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}

561
media/libmedia/IOMX.cpp Normal file
View File

@@ -0,0 +1,561 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "IOMX"
#include <utils/Log.h>
#include <binder/IMemory.h>
#include <binder/Parcel.h>
#include <media/IOMX.h>
namespace android {
enum {
CONNECT = IBinder::FIRST_CALL_TRANSACTION,
LIST_NODES,
ALLOCATE_NODE,
FREE_NODE,
SEND_COMMAND,
GET_PARAMETER,
SET_PARAMETER,
USE_BUFFER,
ALLOC_BUFFER,
ALLOC_BUFFER_WITH_BACKUP,
FREE_BUFFER,
OBSERVE_NODE,
FILL_BUFFER,
EMPTY_BUFFER,
OBSERVER_ON_MSG,
};
static void *readVoidStar(const Parcel *parcel) {
// FIX if sizeof(void *) != sizeof(int32)
return (void *)parcel->readInt32();
}
static void writeVoidStar(void *x, Parcel *parcel) {
// FIX if sizeof(void *) != sizeof(int32)
parcel->writeInt32((int32_t)x);
}
class BpOMX : public BpInterface<IOMX> {
public:
BpOMX(const sp<IBinder> &impl)
: BpInterface<IOMX>(impl) {
}
#if IOMX_USES_SOCKETS
virtual status_t connect(int *sd) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
remote()->transact(CONNECT, data, &reply);
status_t err = reply.readInt32();
if (err == OK) {
*sd = dup(reply.readFileDescriptor());
} else {
*sd = -1;
}
return reply.readInt32();
}
#endif
virtual status_t list_nodes(List<String8> *list) {
list->clear();
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
remote()->transact(LIST_NODES, data, &reply);
int32_t n = reply.readInt32();
for (int32_t i = 0; i < n; ++i) {
String8 s = reply.readString8();
list->push_back(s);
}
return OK;
}
virtual status_t allocate_node(const char *name, node_id *node) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeCString(name);
remote()->transact(ALLOCATE_NODE, data, &reply);
status_t err = reply.readInt32();
if (err == OK) {
*node = readVoidStar(&reply);
} else {
*node = 0;
}
return err;
}
virtual status_t free_node(node_id node) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
remote()->transact(FREE_NODE, data, &reply);
return reply.readInt32();
}
virtual status_t send_command(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(cmd);
data.writeInt32(param);
remote()->transact(SEND_COMMAND, data, &reply);
return reply.readInt32();
}
virtual status_t get_parameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(index);
data.writeInt32(size);
data.write(params, size);
remote()->transact(GET_PARAMETER, data, &reply);
status_t err = reply.readInt32();
if (err != OK) {
return err;
}
reply.read(params, size);
return OK;
}
virtual status_t set_parameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(index);
data.writeInt32(size);
data.write(params, size);
remote()->transact(SET_PARAMETER, data, &reply);
return reply.readInt32();
}
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(port_index);
data.writeStrongBinder(params->asBinder());
remote()->transact(USE_BUFFER, data, &reply);
status_t err = reply.readInt32();
if (err != OK) {
*buffer = 0;
return err;
}
*buffer = readVoidStar(&reply);
return err;
}
virtual status_t allocate_buffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(port_index);
data.writeInt32(size);
remote()->transact(ALLOC_BUFFER, data, &reply);
status_t err = reply.readInt32();
if (err != OK) {
*buffer = 0;
return err;
}
*buffer = readVoidStar(&reply);
return err;
}
virtual status_t allocate_buffer_with_backup(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(port_index);
data.writeStrongBinder(params->asBinder());
remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
status_t err = reply.readInt32();
if (err != OK) {
*buffer = 0;
return err;
}
*buffer = readVoidStar(&reply);
return err;
}
virtual status_t free_buffer(
node_id node, OMX_U32 port_index, buffer_id buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeInt32(port_index);
writeVoidStar(buffer, &data);
remote()->transact(FREE_BUFFER, data, &reply);
return reply.readInt32();
}
#if !IOMX_USES_SOCKETS
virtual status_t observe_node(
node_id node, const sp<IOMXObserver> &observer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
data.writeStrongBinder(observer->asBinder());
remote()->transact(OBSERVE_NODE, data, &reply);
return reply.readInt32();
}
virtual void fill_buffer(node_id node, buffer_id buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
writeVoidStar(buffer, &data);
remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual void empty_buffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
writeVoidStar(node, &data);
writeVoidStar(buffer, &data);
data.writeInt32(range_offset);
data.writeInt32(range_length);
data.writeInt32(flags);
data.writeInt64(timestamp);
remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
}
#endif
};
IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
////////////////////////////////////////////////////////////////////////////////
#define CHECK_INTERFACE(interface, data, reply) \
do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
LOGW("Call incorrectly routed to " #interface); \
return PERMISSION_DENIED; \
} } while (0)
status_t BnOMX::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
#if IOMX_USES_SOCKETS
case CONNECT:
{
CHECK_INTERFACE(IOMX, data, reply);
int s;
status_t err = connect(&s);
reply->writeInt32(err);
if (err == OK) {
assert(s >= 0);
reply->writeDupFileDescriptor(s);
close(s);
s = -1;
} else {
assert(s == -1);
}
return NO_ERROR;
}
#endif
case LIST_NODES:
{
CHECK_INTERFACE(IOMX, data, reply);
List<String8> list;
list_nodes(&list);
reply->writeInt32(list.size());
for (List<String8>::iterator it = list.begin();
it != list.end(); ++it) {
reply->writeString8(*it);
}
return NO_ERROR;
}
case ALLOCATE_NODE:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node;
status_t err = allocate_node(data.readCString(), &node);
reply->writeInt32(err);
if (err == OK) {
writeVoidStar(node, reply);
}
return NO_ERROR;
}
case FREE_NODE:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
reply->writeInt32(free_node(node));
return NO_ERROR;
}
case SEND_COMMAND:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_COMMANDTYPE cmd =
static_cast<OMX_COMMANDTYPE>(data.readInt32());
OMX_S32 param = data.readInt32();
reply->writeInt32(send_command(node, cmd, param));
return NO_ERROR;
}
case GET_PARAMETER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
size_t size = data.readInt32();
// XXX I am not happy with this but Parcel::readInplace didn't work.
void *params = malloc(size);
data.read(params, size);
status_t err = get_parameter(node, index, params, size);
reply->writeInt32(err);
if (err == OK) {
reply->write(params, size);
}
free(params);
params = NULL;
return NO_ERROR;
}
case SET_PARAMETER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
size_t size = data.readInt32();
void *params = const_cast<void *>(data.readInplace(size));
reply->writeInt32(set_parameter(node, index, params, size));
return NO_ERROR;
}
case USE_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_U32 port_index = data.readInt32();
sp<IMemory> params =
interface_cast<IMemory>(data.readStrongBinder());
buffer_id buffer;
status_t err = use_buffer(node, port_index, params, &buffer);
reply->writeInt32(err);
if (err == OK) {
writeVoidStar(buffer, reply);
}
return NO_ERROR;
}
case ALLOC_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_U32 port_index = data.readInt32();
size_t size = data.readInt32();
buffer_id buffer;
status_t err = allocate_buffer(node, port_index, size, &buffer);
reply->writeInt32(err);
if (err == OK) {
writeVoidStar(buffer, reply);
}
return NO_ERROR;
}
case ALLOC_BUFFER_WITH_BACKUP:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_U32 port_index = data.readInt32();
sp<IMemory> params =
interface_cast<IMemory>(data.readStrongBinder());
buffer_id buffer;
status_t err = allocate_buffer_with_backup(
node, port_index, params, &buffer);
reply->writeInt32(err);
if (err == OK) {
writeVoidStar(buffer, reply);
}
return NO_ERROR;
}
case FREE_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
OMX_U32 port_index = data.readInt32();
buffer_id buffer = readVoidStar(&data);
reply->writeInt32(free_buffer(node, port_index, buffer));
return NO_ERROR;
}
#if !IOMX_USES_SOCKETS
case OBSERVE_NODE:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
sp<IOMXObserver> observer =
interface_cast<IOMXObserver>(data.readStrongBinder());
reply->writeInt32(observe_node(node, observer));
return NO_ERROR;
}
case FILL_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
buffer_id buffer = readVoidStar(&data);
fill_buffer(node, buffer);
return NO_ERROR;
}
case EMPTY_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
node_id node = readVoidStar(&data);
buffer_id buffer = readVoidStar(&data);
OMX_U32 range_offset = data.readInt32();
OMX_U32 range_length = data.readInt32();
OMX_U32 flags = data.readInt32();
OMX_TICKS timestamp = data.readInt64();
empty_buffer(
node, buffer, range_offset, range_length,
flags, timestamp);
return NO_ERROR;
}
#endif
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
////////////////////////////////////////////////////////////////////////////////
class BpOMXObserver : public BpInterface<IOMXObserver> {
public:
BpOMXObserver(const sp<IBinder> &impl)
: BpInterface<IOMXObserver>(impl) {
}
virtual void on_message(const omx_message &msg) {
Parcel data, reply;
data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
data.write(&msg, sizeof(msg));
remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
}
};
IMPLEMENT_META_INTERFACE(OMXObserver, "android.hardware.IOMXObserver");
status_t BnOMXObserver::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
case OBSERVER_ON_MSG:
{
CHECK_INTERFACE(IOMXObserver, data, reply);
omx_message msg;
data.read(&msg, sizeof(msg));
// XXX Could use readInplace maybe?
on_message(msg);
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
} // namespace android

View File

@@ -29,9 +29,22 @@ LOCAL_SHARED_LIBRARIES := \
libandroid_runtime
LOCAL_C_INCLUDES := external/tremor/Tremor \
$(call include-path-for, graphics corecg)
$(call include-path-for, graphics corecg) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
LOCAL_MODULE:= libmediaplayerservice
ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
LOCAL_SRC_FILES += StagefrightPlayer.cpp
LOCAL_SHARED_LIBRARIES += \
libstagefright \
libstagefright_omx
LOCAL_C_INCLUDES += $(TOP)/frameworks/base/media/libstagefright/omx
LOCAL_CFLAGS += -DBUILD_WITH_STAGEFRIGHT -DUSE_STAGEFRIGHT
endif
include $(BUILD_SHARED_LIBRARY)

View File

@@ -58,6 +58,15 @@
#include "MidiFile.h"
#include "VorbisPlayer.h"
#include <media/PVPlayer.h>
#if USE_STAGEFRIGHT
#include "StagefrightPlayer.h"
#endif
#ifdef BUILD_WITH_STAGEFRIGHT
#include <OMX.h>
#else
#include <media/IOMX.h>
#endif
/* desktop Linux needs a little help with gettid() */
#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -186,6 +195,10 @@ typedef struct {
const player_type playertype;
} extmap;
extmap FILE_EXTS [] = {
#if USE_STAGEFRIGHT
{".mp4", STAGEFRIGHT_PLAYER},
{".3gp", STAGEFRIGHT_PLAYER},
#endif
{".mid", SONIVOX_PLAYER},
{".midi", SONIVOX_PLAYER},
{".smf", SONIVOX_PLAYER},
@@ -271,6 +284,14 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie
return c;
}
sp<IOMX> MediaPlayerService::createOMX() {
#ifdef BUILD_WITH_STAGEFRIGHT
return new OMX;
#else
return NULL;
#endif
}
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
@@ -577,6 +598,7 @@ void MediaPlayerService::Client::disconnect()
p = mPlayer;
}
mClient.clear();
mPlayer.clear();
// clear the notification to prevent callbacks to dead client
@@ -624,13 +646,16 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length)
EAS_Shutdown(easdata);
}
#if USE_STAGEFRIGHT
return STAGEFRIGHT_PLAYER;
#endif
// Fall through to PV
return PV_PLAYER;
}
static player_type getPlayerType(const char* url)
{
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -643,6 +668,10 @@ static player_type getPlayerType(const char* url)
}
}
#if USE_STAGEFRIGHT
return STAGEFRIGHT_PLAYER;
#endif
// Fall through to PV
return PV_PLAYER;
}
@@ -666,6 +695,17 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
LOGV(" create VorbisPlayer");
p = new VorbisPlayer();
break;
#if USE_STAGEFRIGHT
case STAGEFRIGHT_PLAYER:
LOGV(" create StagefrightPlayer");
p = new StagefrightPlayer;
break;
#else
case STAGEFRIGHT_PLAYER:
LOG_ALWAYS_FATAL(
"Should not be here, stagefright player not enabled.");
break;
#endif
}
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
@@ -1136,7 +1176,8 @@ Exit:
#undef LOG_TAG
#define LOG_TAG "AudioSink"
MediaPlayerService::AudioOutput::AudioOutput()
{
: mCallback(NULL),
mCallbackCookie(NULL) {
mTrack = 0;
mStreamType = AudioSystem::MUSIC;
mLeftVolume = 1.0;
@@ -1206,8 +1247,13 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const
return mMsecsPerFrame;
}
status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, int format, int bufferCount,
AudioCallback cb, void *cookie)
{
mCallback = cb;
mCallbackCookie = cookie;
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1228,7 +1274,17 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC
}
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount);
AudioTrack *t;
if (mCallback != NULL) {
t = new AudioTrack(
mStreamType, sampleRate, format, channelCount, frameCount,
0 /* flags */, CallbackWrapper, this);
} else {
t = new AudioTrack(
mStreamType, sampleRate, format, channelCount, frameCount);
}
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
LOGE("Unable to create audio track");
delete t;
@@ -1254,6 +1310,8 @@ void MediaPlayerService::AudioOutput::start()
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) return mTrack->write(buffer, size);
return NO_INIT;
@@ -1294,6 +1352,20 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
}
}
// static
void MediaPlayerService::AudioOutput::CallbackWrapper(
int event, void *cookie, void *info) {
if (event != AudioTrack::EVENT_MORE_DATA) {
return;
}
AudioOutput *me = (AudioOutput *)cookie;
AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
(*me->mCallback)(
me, buffer->raw, buffer->size, me->mCallbackCookie);
}
#undef LOG_TAG
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const char* name) :
@@ -1314,8 +1386,14 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const
return mMsecsPerFrame;
}
status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
status_t MediaPlayerService::AudioCache::open(
uint32_t sampleRate, int channelCount, int format, int bufferCount,
AudioCallback cb, void *cookie)
{
if (cb != NULL) {
return UNKNOWN_ERROR; // TODO: implement this.
}
LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
if (mHeap->getHeapID() < 0) return NO_INIT;
mSampleRate = sampleRate;

View File

@@ -35,6 +35,7 @@ typedef int32_t MetadataType;
class IMediaRecorder;
class IMediaMetadataRetriever;
class IOMX;
#define CALLBACK_ANTAGONIZER 0
#if CALLBACK_ANTAGONIZER
@@ -75,7 +76,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const;
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4);
virtual status_t open(
uint32_t sampleRate, int channelCount,
int format, int bufferCount,
AudioCallback cb, void *cookie);
virtual void start();
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop();
@@ -90,8 +96,12 @@ class MediaPlayerService : public BnMediaPlayerService
static int getMinBufferCount();
private:
static void setMinBufferCount();
static void CallbackWrapper(
int event, void *me, void *info);
AudioTrack* mTrack;
AudioCallback mCallback;
void * mCallbackCookie;
int mStreamType;
float mLeftVolume;
float mRightVolume;
@@ -119,7 +129,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1);
virtual status_t open(
uint32_t sampleRate, int channelCount, int format,
int bufferCount = 1,
AudioCallback cb = NULL, void *cookie = NULL);
virtual void start() {}
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop() {}
@@ -166,6 +181,7 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IOMX> createOMX();
virtual status_t dump(int fd, const Vector<String16>& args);

View File

@@ -0,0 +1,208 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "StagefrightPlayer"
#include <utils/Log.h>
#include "StagefrightPlayer.h"
#include <media/stagefright/MediaPlayerImpl.h>
namespace android {
StagefrightPlayer::StagefrightPlayer()
: mPlayer(NULL) {
LOGV("StagefrightPlayer");
}
StagefrightPlayer::~StagefrightPlayer() {
LOGV("~StagefrightPlayer");
reset();
LOGV("~StagefrightPlayer done.");
}
status_t StagefrightPlayer::initCheck() {
LOGV("initCheck");
return OK;
}
status_t StagefrightPlayer::setDataSource(const char *url) {
LOGV("setDataSource('%s')", url);
reset();
mPlayer = new MediaPlayerImpl(url);
status_t err = mPlayer->initCheck();
if (err != OK) {
delete mPlayer;
mPlayer = NULL;
} else {
mPlayer->setAudioSink(mAudioSink);
}
return err;
}
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
reset();
mPlayer = new MediaPlayerImpl(fd, offset, length);
status_t err = mPlayer->initCheck();
if (err != OK) {
delete mPlayer;
mPlayer = NULL;
} else {
mPlayer->setAudioSink(mAudioSink);
}
return err;
}
status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
LOGV("setVideoSurface");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->setISurface(surface);
return OK;
}
status_t StagefrightPlayer::prepare() {
LOGV("prepare");
if (mPlayer == NULL) {
return NO_INIT;
}
sendEvent(
MEDIA_SET_VIDEO_SIZE,
mPlayer->getWidth(), mPlayer->getHeight());
return OK;
}
status_t StagefrightPlayer::prepareAsync() {
LOGV("prepareAsync");
status_t err = prepare();
if (err != OK) {
return err;
}
sendEvent(MEDIA_PREPARED);
return OK;
}
status_t StagefrightPlayer::start() {
LOGV("start");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->play();
return OK;
}
status_t StagefrightPlayer::stop() {
LOGV("stop");
if (mPlayer == NULL) {
return NO_INIT;
}
reset();
return OK;
}
status_t StagefrightPlayer::pause() {
LOGV("pause");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->pause();
return OK;
}
bool StagefrightPlayer::isPlaying() {
LOGV("isPlaying");
return mPlayer != NULL && mPlayer->isPlaying();
}
status_t StagefrightPlayer::seekTo(int msec) {
LOGV("seekTo");
if (mPlayer == NULL) {
return NO_INIT;
}
status_t err = mPlayer->seekTo((int64_t)msec * 1000);
sendEvent(MEDIA_SEEK_COMPLETE);
return err;
}
status_t StagefrightPlayer::getCurrentPosition(int *msec) {
LOGV("getCurrentPosition");
if (mPlayer == NULL) {
return NO_INIT;
}
*msec = mPlayer->getPosition() / 1000;
return OK;
}
status_t StagefrightPlayer::getDuration(int *msec) {
LOGV("getDuration");
if (mPlayer == NULL) {
return NO_INIT;
}
*msec = mPlayer->getDuration() / 1000;
return OK;
}
status_t StagefrightPlayer::reset() {
LOGV("reset");
delete mPlayer;
mPlayer = NULL;
return OK;
}
status_t StagefrightPlayer::setLooping(int loop) {
LOGV("setLooping");
return UNKNOWN_ERROR;
}
player_type StagefrightPlayer::playerType() {
LOGV("playerType");
return STAGEFRIGHT_PLAYER;
}
status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
return INVALID_OPERATION;
}
void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
MediaPlayerInterface::setAudioSink(audioSink);
if (mPlayer != NULL) {
mPlayer->setAudioSink(audioSink);
}
}
} // namespace android

View File

@@ -0,0 +1,60 @@
/*
**
** Copyright 2009, 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_STAGEFRIGHTPLAYER_H
#define ANDROID_STAGEFRIGHTPLAYER_H
#include <media/MediaPlayerInterface.h>
namespace android {
class MediaPlayerImpl;
class StagefrightPlayer : public MediaPlayerInterface {
public:
StagefrightPlayer();
virtual ~StagefrightPlayer();
virtual status_t initCheck();
virtual status_t setDataSource(const char *url);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurface(const sp<ISurface> &surface);
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
virtual status_t pause();
virtual bool isPlaying();
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType();
virtual status_t invoke(const Parcel &request, Parcel *reply);
virtual void setAudioSink(const sp<AudioSink> &audioSink);
private:
MediaPlayerImpl *mPlayer;
StagefrightPlayer(const StagefrightPlayer &);
StagefrightPlayer &operator=(const StagefrightPlayer &);
};
} // namespace android
#endif // ANDROID_STAGEFRIGHTPLAYER_H

View File

@@ -0,0 +1,56 @@
ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
CachingDataSource.cpp \
DataSource.cpp \
FileSource.cpp \
HTTPDataSource.cpp \
HTTPStream.cpp \
MP3Extractor.cpp \
MPEG4Extractor.cpp \
MPEG4Writer.cpp \
MediaBuffer.cpp \
MediaBufferGroup.cpp \
MediaExtractor.cpp \
MediaPlayerImpl.cpp \
MediaSource.cpp \
MetaData.cpp \
MmapSource.cpp \
QComHardwareRenderer.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
SoftwareRenderer.cpp \
SurfaceRenderer.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
Utils.cpp \
AudioPlayer.cpp \
ESDS.cpp \
OMXClient.cpp \
OMXDecoder.cpp \
string.cpp
LOCAL_C_INCLUDES:= \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/external/opencore/android
LOCAL_SHARED_LIBRARIES := \
libbinder \
libmedia \
libutils \
libcutils \
libui
LOCAL_CFLAGS += -Wno-multichar
LOCAL_PRELINK_MODULE:= false
LOCAL_MODULE:= libstagefright
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
endif

View File

@@ -0,0 +1,285 @@
/*
* Copyright (C) 2009 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.
*/
#undef NDEBUG
#include <assert.h>
#define LOG_TAG "AudioPlayer"
#include <utils/Log.h>
#include <media/AudioTrack.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
namespace android {
AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
: mSource(NULL),
mAudioTrack(NULL),
mInputBuffer(NULL),
mSampleRate(0),
mLatencyUs(0),
mFrameSize(0),
mNumFramesPlayed(0),
mPositionTimeMediaUs(-1),
mPositionTimeRealUs(-1),
mSeeking(false),
mStarted(false),
mAudioSink(audioSink) {
}
AudioPlayer::~AudioPlayer() {
if (mStarted) {
stop();
}
}
void AudioPlayer::setSource(MediaSource *source) {
assert(mSource == NULL);
mSource = source;
}
void AudioPlayer::start() {
assert(!mStarted);
assert(mSource != NULL);
status_t err = mSource->start();
assert(err == OK);
sp<MetaData> format = mSource->getFormat();
const char *mime;
bool success = format->findCString(kKeyMIMEType, &mime);
assert(success);
assert(!strcasecmp(mime, "audio/raw"));
success = format->findInt32(kKeySampleRate, &mSampleRate);
assert(success);
int32_t numChannels;
success = format->findInt32(kKeyChannelCount, &numChannels);
assert(success);
if (mAudioSink.get() != NULL) {
status_t err = mAudioSink->open(
mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
DEFAULT_AUDIOSINK_BUFFERCOUNT,
&AudioPlayer::AudioSinkCallback, this);
assert(err == OK);
mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
mFrameSize = mAudioSink->frameSize();
mAudioSink->start();
} else {
mAudioTrack = new AudioTrack(
AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
numChannels, 8192, 0, &AudioCallback, this, 0);
assert(mAudioTrack->initCheck() == OK);
mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
mFrameSize = mAudioTrack->frameSize();
mAudioTrack->start();
}
mStarted = true;
}
void AudioPlayer::pause() {
assert(mStarted);
if (mAudioSink.get() != NULL) {
mAudioSink->pause();
} else {
mAudioTrack->stop();
}
}
void AudioPlayer::resume() {
assert(mStarted);
if (mAudioSink.get() != NULL) {
mAudioSink->start();
} else {
mAudioTrack->start();
}
}
void AudioPlayer::stop() {
assert(mStarted);
if (mAudioSink.get() != NULL) {
mAudioSink->stop();
} else {
mAudioTrack->stop();
delete mAudioTrack;
mAudioTrack = NULL;
}
// Make sure to release any buffer we hold onto so that the
// source is able to stop().
if (mInputBuffer != NULL) {
LOGI("AudioPlayer releasing input buffer.");
mInputBuffer->release();
mInputBuffer = NULL;
}
mSource->stop();
mNumFramesPlayed = 0;
mPositionTimeMediaUs = -1;
mPositionTimeRealUs = -1;
mSeeking = false;
mStarted = false;
}
// static
void AudioPlayer::AudioCallback(int event, void *user, void *info) {
static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
}
// static
void AudioPlayer::AudioSinkCallback(
MediaPlayerBase::AudioSink *audioSink,
void *buffer, size_t size, void *cookie) {
AudioPlayer *me = (AudioPlayer *)cookie;
me->fillBuffer(buffer, size);
}
void AudioPlayer::AudioCallback(int event, void *info) {
if (event != AudioTrack::EVENT_MORE_DATA) {
return;
}
AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
fillBuffer(buffer->raw, buffer->size);
}
void AudioPlayer::fillBuffer(void *data, size_t size) {
if (mNumFramesPlayed == 0) {
LOGI("AudioCallback");
}
size_t size_done = 0;
size_t size_remaining = size;
while (size_remaining > 0) {
MediaSource::ReadOptions options;
{
Mutex::Autolock autoLock(mLock);
if (mSeeking) {
options.setSeekTo(mSeekTimeUs);
if (mInputBuffer != NULL) {
mInputBuffer->release();
mInputBuffer = NULL;
}
mSeeking = false;
}
}
if (mInputBuffer == NULL) {
status_t err = mSource->read(&mInputBuffer, &options);
assert((err == OK && mInputBuffer != NULL)
|| (err != OK && mInputBuffer == NULL));
if (err != OK) {
memset((char *)data + size_done, 0, size_remaining);
break;
}
int32_t units, scale;
bool success =
mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
success = success &&
mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
assert(success);
Mutex::Autolock autoLock(mLock);
mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
mPositionTimeRealUs =
((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX
}
if (mInputBuffer->range_length() == 0) {
mInputBuffer->release();
mInputBuffer = NULL;
continue;
}
size_t copy = size_remaining;
if (copy > mInputBuffer->range_length()) {
copy = mInputBuffer->range_length();
}
memcpy((char *)data + size_done,
(const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
copy);
mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
mInputBuffer->range_length() - copy);
size_done += copy;
size_remaining -= copy;
}
Mutex::Autolock autoLock(mLock);
mNumFramesPlayed += size / mFrameSize;
}
int64_t AudioPlayer::getRealTimeUs() {
Mutex::Autolock autoLock(mLock);
return getRealTimeUsLocked();
}
int64_t AudioPlayer::getRealTimeUsLocked() const {
return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
}
int64_t AudioPlayer::getMediaTimeUs() {
Mutex::Autolock autoLock(mLock);
return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
}
bool AudioPlayer::getMediaTimeMapping(
int64_t *realtime_us, int64_t *mediatime_us) {
Mutex::Autolock autoLock(mLock);
*realtime_us = mPositionTimeRealUs;
*mediatime_us = mPositionTimeMediaUs;
return mPositionTimeRealUs != -1 || mPositionTimeMediaUs != -1;
}
status_t AudioPlayer::seekTo(int64_t time_us) {
Mutex::Autolock autoLock(mLock);
mSeeking = true;
mSeekTimeUs = time_us;
return OK;
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2009 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 <media/stagefright/CachingDataSource.h>
#undef NDEBUG
#include <assert.h>
#include <stdlib.h>
#include <string.h>
namespace android {
CachingDataSource::CachingDataSource(
DataSource *source, size_t pageSize, int numPages)
: mSource(source),
mData(malloc(pageSize * numPages)),
mPageSize(pageSize),
mFirst(NULL),
mLast(NULL) {
for (int i = 0; i < numPages; ++i) {
Page *page = new Page;
page->mPrev = mLast;
page->mNext = NULL;
if (mLast == NULL) {
mFirst = page;
} else {
mLast->mNext = page;
}
mLast = page;
page->mOffset = -1;
page->mLength = 0;
page->mData = (char *)mData + mPageSize * i;
}
}
CachingDataSource::~CachingDataSource() {
Page *page = mFirst;
while (page != NULL) {
Page *next = page->mNext;
delete page;
page = next;
}
mFirst = mLast = NULL;
free(mData);
mData = NULL;
delete mSource;
mSource = NULL;
}
status_t CachingDataSource::InitCheck() const {
return OK;
}
ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
size_t total = 0;
while (size > 0) {
Page *page = mFirst;
while (page != NULL) {
if (page->mOffset >= 0 && offset >= page->mOffset
&& offset < page->mOffset + page->mLength) {
break;
}
page = page->mNext;
}
if (page == NULL) {
page = allocate_page();
page->mOffset = offset - offset % mPageSize;
ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize);
if (n < 0) {
page->mLength = 0;
} else {
page->mLength = (size_t)n;
}
mFirst->mPrev = page;
page->mNext = mFirst;
page->mPrev = NULL;
mFirst = page;
if (n < 0) {
return n;
}
if (offset >= page->mOffset + page->mLength) {
break;
}
} else {
// Move "page" to the front in LRU order.
if (page->mNext != NULL) {
page->mNext->mPrev = page->mPrev;
} else {
mLast = page->mPrev;
}
if (page->mPrev != NULL) {
page->mPrev->mNext = page->mNext;
} else {
mFirst = page->mNext;
}
mFirst->mPrev = page;
page->mNext = mFirst;
page->mPrev = NULL;
mFirst = page;
}
size_t copy = page->mLength - (offset - page->mOffset);
if (copy > size) {
copy = size;
}
memcpy(data,(const char *)page->mData + (offset - page->mOffset),
copy);
total += copy;
if (page->mLength < mPageSize) {
// This was the final page. There is no more data beyond it.
break;
}
offset += copy;
size -= copy;
data = (char *)data + copy;
}
return total;
}
CachingDataSource::Page *CachingDataSource::allocate_page() {
// The last page is the least recently used, i.e. oldest.
Page *page = mLast;
page->mPrev->mNext = NULL;
mLast = page->mPrev;
page->mPrev = NULL;
return page;
}
} // namespace android

View File

@@ -0,0 +1,226 @@
/*
* Copyright (C) 2009 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 <sys/time.h>
#undef NDEBUG
#include <assert.h>
#include <OMX_Component.h>
#include <binder/IServiceManager.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <ui/ICameraClient.h>
#include <ui/ICameraService.h>
#include <ui/Overlay.h>
#include <utils/String16.h>
namespace android {
class CameraBuffer : public MediaBuffer {
public:
CameraBuffer(const sp<IMemory> &frame)
: MediaBuffer(frame->pointer(), frame->size()),
mFrame(frame) {
}
sp<IMemory> releaseFrame() {
sp<IMemory> frame = mFrame;
mFrame.clear();
return frame;
}
private:
sp<IMemory> mFrame;
};
class CameraSourceClient : public BnCameraClient {
public:
CameraSourceClient()
: mSource(NULL) {
}
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
assert(mSource != NULL);
mSource->notifyCallback(msgType, ext1, ext2);
}
virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {
assert(mSource != NULL);
mSource->dataCallback(msgType, data);
}
void setCameraSource(CameraSource *source) {
mSource = source;
}
private:
CameraSource *mSource;
};
class DummySurface : public BnSurface {
public:
DummySurface() {}
virtual status_t registerBuffers(const BufferHeap &buffers) {
return OK;
}
virtual void postBuffer(ssize_t offset) {
}
virtual void unregisterBuffers() {
}
virtual sp<OverlayRef> createOverlay(
uint32_t w, uint32_t h, int32_t format) {
return NULL;
}
};
// static
CameraSource *CameraSource::Create() {
sp<IServiceManager> sm = defaultServiceManager();
sp<ICameraService> service =
interface_cast<ICameraService>(
sm->getService(String16("media.camera")));
sp<CameraSourceClient> client = new CameraSourceClient;
sp<ICamera> camera = service->connect(client);
CameraSource *source = new CameraSource(camera, client);
client->setCameraSource(source);
return source;
}
CameraSource::CameraSource(
const sp<ICamera> &camera, const sp<ICameraClient> &client)
: mCamera(camera),
mCameraClient(client),
mNumFrames(0),
mStarted(false) {
printf("params: \"%s\"\n", mCamera->getParameters().string());
}
CameraSource::~CameraSource() {
if (mStarted) {
stop();
}
mCamera->disconnect();
}
status_t CameraSource::start(MetaData *) {
assert(!mStarted);
status_t err = mCamera->lock();
assert(err == OK);
err = mCamera->setPreviewDisplay(new DummySurface);
assert(err == OK);
mCamera->setPreviewCallbackFlag(1);
mCamera->startPreview();
assert(err == OK);
mStarted = true;
return OK;
}
status_t CameraSource::stop() {
assert(mStarted);
mCamera->stopPreview();
mCamera->unlock();
mStarted = false;
return OK;
}
sp<MetaData> CameraSource::getFormat() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, "video/raw");
meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
meta->setInt32(kKeyWidth, 480);
meta->setInt32(kKeyHeight, 320);
return meta;
}
status_t CameraSource::read(
MediaBuffer **buffer, const ReadOptions *options) {
assert(mStarted);
*buffer = NULL;
int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) {
return ERROR_UNSUPPORTED;
}
sp<IMemory> frame;
{
Mutex::Autolock autoLock(mLock);
while (mFrames.empty()) {
mFrameAvailableCondition.wait(mLock);
}
frame = *mFrames.begin();
mFrames.erase(mFrames.begin());
}
int count = mNumFrames++;
*buffer = new CameraBuffer(frame);
(*buffer)->meta_data()->clear();
(*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
(*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
(*buffer)->add_ref();
(*buffer)->setObserver(this);
return OK;
}
void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2);
}
void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
Mutex::Autolock autoLock(mLock);
mFrames.push_back(data);
mFrameAvailableCondition.signal();
}
void CameraSource::signalBufferReturned(MediaBuffer *_buffer) {
CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer);
mCamera->releaseRecordingFrame(buffer->releaseFrame());
buffer->setObserver(NULL);
buffer->release();
buffer = NULL;
}
} // namespace android

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2009 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 <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MP3Extractor.h>
#include <media/stagefright/MPEG4Extractor.h>
#include <utils/String8.h>
namespace android {
status_t DataSource::getSize(off_t *size) {
*size = 0;
return ERROR_UNSUPPORTED;
}
////////////////////////////////////////////////////////////////////////////////
Mutex DataSource::gSnifferMutex;
List<DataSource::SnifferFunc> DataSource::gSniffers;
bool DataSource::sniff(String8 *mimeType, float *confidence) {
*mimeType = "";
*confidence = 0.0f;
Mutex::Autolock autoLock(gSnifferMutex);
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
String8 newMimeType;
float newConfidence;
if ((*it)(this, &newMimeType, &newConfidence)) {
if (newConfidence > *confidence) {
*mimeType = newMimeType;
*confidence = newConfidence;
}
}
}
return *confidence > 0.0;
}
// static
void DataSource::RegisterSniffer(SnifferFunc func) {
Mutex::Autolock autoLock(gSnifferMutex);
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
if (*it == func) {
return;
}
}
gSniffers.push_back(func);
}
// static
void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffMPEG4);
}
} // namespace android

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) 2009 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 <media/stagefright/ESDS.h>
#include <string.h>
namespace android {
ESDS::ESDS(const void *data, size_t size)
: mData(new uint8_t[size]),
mSize(size),
mInitCheck(NO_INIT),
mDecoderSpecificOffset(0),
mDecoderSpecificLength(0) {
memcpy(mData, data, size);
mInitCheck = parse();
}
ESDS::~ESDS() {
delete[] mData;
mData = NULL;
}
status_t ESDS::InitCheck() const {
return mInitCheck;
}
status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
if (mInitCheck != OK) {
return mInitCheck;
}
*data = &mData[mDecoderSpecificOffset];
*size = mDecoderSpecificLength;
return OK;
}
status_t ESDS::skipDescriptorHeader(
size_t offset, size_t size,
uint8_t *tag, size_t *data_offset, size_t *data_size) const {
if (size == 0) {
return ERROR_MALFORMED;
}
*tag = mData[offset++];
--size;
*data_size = 0;
bool more;
do {
if (size == 0) {
return ERROR_MALFORMED;
}
uint8_t x = mData[offset++];
--size;
*data_size = (*data_size << 7) | (x & 0x7f);
more = (x & 0x80) != 0;
}
while (more);
if (*data_size > size) {
return ERROR_MALFORMED;
}
*data_offset = offset;
return OK;
}
status_t ESDS::parse() {
uint8_t tag;
size_t data_offset;
size_t data_size;
status_t err =
skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
if (err != OK) {
return err;
}
if (tag != kTag_ESDescriptor) {
return ERROR_MALFORMED;
}
return parseESDescriptor(data_offset, data_size);
}
status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
if (size < 3) {
return ERROR_MALFORMED;
}
offset += 2; // skip ES_ID
size -= 2;
unsigned streamDependenceFlag = mData[offset] & 0x80;
unsigned URL_Flag = mData[offset] & 0x40;
unsigned OCRstreamFlag = mData[offset] & 0x20;
++offset;
--size;
if (streamDependenceFlag) {
offset += 2;
size -= 2;
}
if (URL_Flag) {
if (offset >= size) {
return ERROR_MALFORMED;
}
unsigned URLlength = mData[offset];
offset += URLlength + 1;
size -= URLlength + 1;
}
if (OCRstreamFlag) {
offset += 2;
size -= 2;
}
if (offset >= size) {
return ERROR_MALFORMED;
}
uint8_t tag;
size_t sub_offset, sub_size;
status_t err = skipDescriptorHeader(
offset, size, &tag, &sub_offset, &sub_size);
if (err != OK) {
return err;
}
if (tag != kTag_DecoderConfigDescriptor) {
return ERROR_MALFORMED;
}
err = parseDecoderConfigDescriptor(sub_offset, sub_size);
return err;
}
status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
if (size < 13) {
return ERROR_MALFORMED;
}
offset += 13;
size -= 13;
if (size == 0) {
mDecoderSpecificOffset = 0;
mDecoderSpecificLength = 0;
return OK;
}
uint8_t tag;
size_t sub_offset, sub_size;
status_t err = skipDescriptorHeader(
offset, size, &tag, &sub_offset, &sub_size);
if (err != OK) {
return err;
}
if (tag != kTag_DecoderSpecificInfo) {
return ERROR_MALFORMED;
}
mDecoderSpecificOffset = sub_offset;
mDecoderSpecificLength = sub_size;
return OK;
}
} // namespace android

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009 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 <media/stagefright/FileSource.h>
#undef NDEBUG
#include <assert.h>
namespace android {
FileSource::FileSource(const char *filename)
: mFile(fopen(filename, "rb")) {
}
FileSource::~FileSource() {
if (mFile != NULL) {
fclose(mFile);
mFile = NULL;
}
}
status_t FileSource::InitCheck() const {
return mFile != NULL ? OK : NO_INIT;
}
ssize_t FileSource::read_at(off_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
int err = fseeko(mFile, offset, SEEK_SET);
assert(err != -1);
ssize_t result = fread(data, 1, size, mFile);
return result;
}
} // namespace android

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2009 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.
*/
#undef NDEBUG
#include <assert.h>
#include <stdlib.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/string.h>
namespace android {
HTTPDataSource::HTTPDataSource(const char *uri)
: mHost(NULL),
mPort(0),
mPath(NULL),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
assert(!strncasecmp("http://", uri, 7));
string host;
string path;
int port;
char *slash = strchr(uri + 7, '/');
if (slash == NULL) {
host = uri + 7;
path = "/";
} else {
host = string(uri + 7, slash - (uri + 7));
path = slash;
}
char *colon = strchr(host.c_str(), ':');
if (colon == NULL) {
port = 80;
} else {
char *end;
long tmp = strtol(colon + 1, &end, 10);
assert(end > colon + 1);
assert(tmp > 0 && tmp < 65536);
port = tmp;
host = string(host, 0, colon - host.c_str());
}
LOGI("Connecting to host '%s', port %d, path '%s'",
host.c_str(), port, path.c_str());
mHost = strdup(host.c_str());
mPort = port;
mPath = strdup(path.c_str());
status_t err = mHttp.connect(mHost, mPort);
assert(err == OK);
}
HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
: mHost(strdup(host)),
mPort(port),
mPath(strdup(path)),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
status_t err = mHttp.connect(mHost, mPort);
assert(err == OK);
}
HTTPDataSource::~HTTPDataSource() {
mHttp.disconnect();
free(mBuffer);
mBuffer = NULL;
free(mPath);
mPath = NULL;
}
ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
size_t copy = num_bytes_available;
if (copy > size) {
copy = size;
}
memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
return copy;
}
mBufferOffset = offset;
mBufferLength = 0;
char host[128];
sprintf(host, "Host: %s\r\n", mHost);
char range[128];
sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
mBufferOffset, mBufferOffset + kBufferSize - 1);
int http_status;
status_t err;
int attempt = 1;
for (;;) {
if ((err = mHttp.send("GET ")) != OK
|| (err = mHttp.send(mPath)) != OK
|| (err = mHttp.send(" HTTP/1.1\r\n")) != OK
|| (err = mHttp.send(host)) != OK
|| (err = mHttp.send(range)) != OK
|| (err = mHttp.send("\r\n")) != OK
|| (err = mHttp.receive_header(&http_status)) != OK) {
if (attempt == 3) {
return err;
}
mHttp.connect(mHost, mPort);
++attempt;
} else {
break;
}
}
if ((http_status / 100) != 2) {
return UNKNOWN_ERROR;
}
string value;
if (!mHttp.find_header_value("Content-Length", &value)) {
return UNKNOWN_ERROR;
}
char *end;
unsigned long contentLength = strtoul(value.c_str(), &end, 10);
ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
if (num_bytes_received <= 0) {
return num_bytes_received;
}
mBufferLength = (size_t)num_bytes_received;
size_t copy = mBufferLength;
if (copy > size) {
copy = size;
}
memcpy(data, mBuffer, copy);
return copy;
}
} // namespace android

View File

@@ -0,0 +1,285 @@
/*
* Copyright (C) 2009 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 <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <media/stagefright/HTTPStream.h>
namespace android {
// static
const char *HTTPStream::kStatusKey = ":status:";
HTTPStream::HTTPStream()
: mState(READY),
mSocket(-1) {
}
HTTPStream::~HTTPStream() {
disconnect();
}
status_t HTTPStream::connect(const char *server, int port) {
status_t err = OK;
if (mState == CONNECTED) {
return ERROR_ALREADY_CONNECTED;
}
assert(mSocket == -1);
mSocket = socket(AF_INET, SOCK_STREAM, 0);
if (mSocket < 0) {
return UNKNOWN_ERROR;
}
struct hostent *ent = gethostbyname(server);
if (ent == NULL) {
err = ERROR_UNKNOWN_HOST;
goto exit1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
err = ERROR_CANNOT_CONNECT;
goto exit1;
}
mState = CONNECTED;
return OK;
exit1:
close(mSocket);
mSocket = -1;
return err;
}
status_t HTTPStream::disconnect() {
if (mState != CONNECTED) {
return ERROR_NOT_CONNECTED;
}
assert(mSocket >= 0);
close(mSocket);
mSocket = -1;
mState = READY;
return OK;
}
status_t HTTPStream::send(const char *data, size_t size) {
if (mState != CONNECTED) {
return ERROR_NOT_CONNECTED;
}
while (size > 0) {
ssize_t n = ::send(mSocket, data, size, 0);
if (n < 0) {
if (errno == EINTR) {
continue;
}
disconnect();
return ERROR_IO;
} else if (n == 0) {
disconnect();
return ERROR_CONNECTION_LOST;
}
size -= (size_t)n;
data += (size_t)n;
}
return OK;
}
status_t HTTPStream::send(const char *data) {
return send(data, strlen(data));
}
status_t HTTPStream::receive_line(char *line, size_t size) {
if (mState != CONNECTED) {
return ERROR_NOT_CONNECTED;
}
bool saw_CR = false;
size_t length = 0;
for (;;) {
char c;
ssize_t n = recv(mSocket, &c, 1, 0);
if (n < 0) {
if (errno == EINTR) {
continue;
}
disconnect();
return ERROR_IO;
} else if (n == 0) {
disconnect();
return ERROR_CONNECTION_LOST;
}
if (saw_CR && c == '\n') {
// We have a complete line.
line[length - 1] = '\0';
return OK;
}
saw_CR = (c == '\r');
assert(length + 1 < size);
line[length++] = c;
}
}
status_t HTTPStream::receive_header(int *http_status) {
*http_status = -1;
mHeaders.clear();
char line[256];
status_t err = receive_line(line, sizeof(line));
if (err != OK) {
return err;
}
mHeaders.add(string(kStatusKey), string(line));
char *spacePos = strchr(line, ' ');
if (spacePos == NULL) {
// Malformed response?
return UNKNOWN_ERROR;
}
char *status_start = spacePos + 1;
char *status_end = status_start;
while (isdigit(*status_end)) {
++status_end;
}
if (status_end == status_start) {
// Malformed response, status missing?
return UNKNOWN_ERROR;
}
memmove(line, status_start, status_end - status_start);
line[status_end - status_start] = '\0';
long tmp = strtol(line, NULL, 10);
if (tmp < 0 || tmp > 999) {
return UNKNOWN_ERROR;
}
*http_status = (int)tmp;
for (;;) {
err = receive_line(line, sizeof(line));
if (err != OK) {
return err;
}
if (*line == '\0') {
// Empty line signals the end of the header.
break;
}
// puts(line);
char *colonPos = strchr(line, ':');
if (colonPos == NULL) {
mHeaders.add(string(line), string());
} else {
char *end_of_key = colonPos;
while (end_of_key > line && isspace(end_of_key[-1])) {
--end_of_key;
}
char *start_of_value = colonPos + 1;
while (isspace(*start_of_value)) {
++start_of_value;
}
*end_of_key = '\0';
mHeaders.add(string(line), string(start_of_value));
}
}
return OK;
}
ssize_t HTTPStream::receive(void *data, size_t size) {
size_t total = 0;
while (total < size) {
ssize_t n = recv(mSocket, (char *)data + total, size - total, 0);
if (n < 0) {
if (errno == EINTR) {
continue;
}
disconnect();
return ERROR_IO;
} else if (n == 0) {
disconnect();
return ERROR_CONNECTION_LOST;
}
total += (size_t)n;
}
return (ssize_t)total;
}
bool HTTPStream::find_header_value(const string &key, string *value) const {
ssize_t index = mHeaders.indexOfKey(key);
if (index < 0) {
value->clear();
return false;
}
*value = mHeaders.valueAt(index);
return true;
}
} // namespace android

View File

@@ -0,0 +1,522 @@
/*
* Copyright (C) 2009 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 "MP3Extractor"
#include <utils/Log.h>
#undef NDEBUG
#include <assert.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MP3Extractor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
namespace android {
static bool get_mp3_frame_size(
uint32_t header, size_t *frame_size,
int *out_sampling_rate = NULL, int *out_channels = NULL,
int *out_bitrate = NULL) {
*frame_size = 0;
if (out_sampling_rate) {
*out_sampling_rate = 0;
}
if (out_channels) {
*out_channels = 0;
}
if (out_bitrate) {
*out_bitrate = 0;
}
if ((header & 0xffe00000) != 0xffe00000) {
return false;
}
unsigned version = (header >> 19) & 3;
if (version == 0x01) {
return false;
}
unsigned layer = (header >> 17) & 3;
if (layer == 0x00) {
return false;
}
unsigned protection = (header >> 16) & 1;
unsigned bitrate_index = (header >> 12) & 0x0f;
if (bitrate_index == 0 || bitrate_index == 0x0f) {
// Disallow "free" bitrate.
return false;
}
unsigned sampling_rate_index = (header >> 10) & 3;
if (sampling_rate_index == 3) {
return false;
}
static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
int sampling_rate = kSamplingRateV1[sampling_rate_index];
if (version == 2 /* V2 */) {
sampling_rate /= 2;
} else if (version == 0 /* V2.5 */) {
sampling_rate /= 4;
}
unsigned padding = (header >> 9) & 1;
if (layer == 3) {
// layer I
static const int kBitrateV1[] = {
32, 64, 96, 128, 160, 192, 224, 256,
288, 320, 352, 384, 416, 448
};
static const int kBitrateV2[] = {
32, 48, 56, 64, 80, 96, 112, 128,
144, 160, 176, 192, 224, 256
};
int bitrate =
(version == 3 /* V1 */)
? kBitrateV1[bitrate_index - 1]
: kBitrateV2[bitrate_index - 1];
if (out_bitrate) {
*out_bitrate = bitrate;
}
*frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
} else {
// layer II or III
static const int kBitrateV1L2[] = {
32, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, 384
};
static const int kBitrateV1L3[] = {
32, 40, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320
};
static const int kBitrateV2[] = {
8, 16, 24, 32, 40, 48, 56, 64,
80, 96, 112, 128, 144, 160
};
int bitrate;
if (version == 3 /* V1 */) {
bitrate = (layer == 2 /* L2 */)
? kBitrateV1L2[bitrate_index - 1]
: kBitrateV1L3[bitrate_index - 1];
} else {
// V2 (or 2.5)
bitrate = kBitrateV2[bitrate_index - 1];
}
if (out_bitrate) {
*out_bitrate = bitrate;
}
*frame_size = 144000 * bitrate / sampling_rate + padding;
}
if (out_sampling_rate) {
*out_sampling_rate = sampling_rate;
}
if (out_channels) {
int channel_mode = (header >> 6) & 3;
*out_channels = (channel_mode == 3) ? 1 : 2;
}
return true;
}
static bool Resync(
DataSource *source, uint32_t match_header,
off_t *inout_pos, uint32_t *out_header) {
// Everything must match except for
// protection, bitrate, padding, private bits and mode extension.
const uint32_t kMask = 0xfffe0ccf;
const size_t kMaxFrameSize = 4096;
uint8_t *buffer = new uint8_t[kMaxFrameSize];
off_t pos = *inout_pos;
size_t buffer_offset = kMaxFrameSize;
size_t buffer_length = kMaxFrameSize;
bool valid = false;
do {
if (buffer_offset + 3 >= buffer_length) {
if (buffer_length < kMaxFrameSize) {
break;
}
pos += buffer_length;
if (pos >= *inout_pos + 128 * 1024) {
// Don't scan forever.
LOGV("giving up at offset %ld", pos);
break;
}
memmove(buffer, &buffer[buffer_offset], buffer_length - buffer_offset);
buffer_length = buffer_length - buffer_offset;
buffer_offset = 0;
ssize_t n = source->read_at(
pos, &buffer[buffer_length], kMaxFrameSize - buffer_length);
if (n <= 0) {
break;
}
buffer_length += (size_t)n;
continue;
}
uint32_t header = U32_AT(&buffer[buffer_offset]);
if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
++buffer_offset;
continue;
}
size_t frame_size;
int sample_rate, num_channels, bitrate;
if (get_mp3_frame_size(header, &frame_size,
&sample_rate, &num_channels, &bitrate)) {
LOGV("found possible 1st frame at %ld", pos + buffer_offset);
// We found what looks like a valid frame,
// now find its successors.
off_t test_pos = pos + buffer_offset + frame_size;
valid = true;
for (int j = 0; j < 3; ++j) {
uint8_t tmp[4];
if (source->read_at(test_pos, tmp, 4) < 4) {
valid = false;
break;
}
uint32_t test_header = U32_AT(tmp);
LOGV("subsequent header is %08x", test_header);
if ((test_header & kMask) != (header & kMask)) {
valid = false;
break;
}
size_t test_frame_size;
if (!get_mp3_frame_size(test_header, &test_frame_size)) {
valid = false;
break;
}
LOGV("found subsequent frame #%d at %ld", j + 2, test_pos);
test_pos += test_frame_size;
}
}
if (valid) {
*inout_pos = pos + buffer_offset;
if (out_header != NULL) {
*out_header = header;
}
} else {
LOGV("no dice, no valid sequence of frames found.");
}
++buffer_offset;
} while (!valid);
delete[] buffer;
buffer = NULL;
return valid;
}
class MP3Source : public MediaSource {
public:
MP3Source(
const sp<MetaData> &meta, DataSource *source,
off_t first_frame_pos, uint32_t fixed_header);
virtual ~MP3Source();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
private:
sp<MetaData> mMeta;
DataSource *mDataSource;
off_t mFirstFramePos;
uint32_t mFixedHeader;
off_t mCurrentPos;
int64_t mCurrentTimeUs;
bool mStarted;
MediaBufferGroup *mGroup;
MP3Source(const MP3Source &);
MP3Source &operator=(const MP3Source &);
};
MP3Extractor::MP3Extractor(DataSource *source)
: mDataSource(source),
mFirstFramePos(-1),
mFixedHeader(0) {
off_t pos = 0;
uint32_t header;
bool success = Resync(mDataSource, 0, &pos, &header);
assert(success);
if (success) {
mFirstFramePos = pos;
mFixedHeader = header;
size_t frame_size;
int sample_rate;
int num_channels;
int bitrate;
get_mp3_frame_size(
header, &frame_size, &sample_rate, &num_channels, &bitrate);
mMeta = new MetaData;
mMeta->setCString(kKeyMIMEType, "audio/mpeg");
mMeta->setInt32(kKeySampleRate, sample_rate);
mMeta->setInt32(kKeyBitRate, bitrate);
mMeta->setInt32(kKeyChannelCount, num_channels);
off_t fileSize;
if (mDataSource->getSize(&fileSize) == OK) {
mMeta->setInt32(
kKeyDuration,
8 * (fileSize - mFirstFramePos) / bitrate);
mMeta->setInt32(kKeyTimeScale, 1000);
}
}
}
MP3Extractor::~MP3Extractor() {
delete mDataSource;
mDataSource = NULL;
}
status_t MP3Extractor::countTracks(int *num_tracks) {
*num_tracks = mFirstFramePos < 0 ? 0 : 1;
return OK;
}
status_t MP3Extractor::getTrack(int index, MediaSource **source) {
if (mFirstFramePos < 0 || index != 0) {
return ERROR_OUT_OF_RANGE;
}
*source = new MP3Source(
mMeta, mDataSource, mFirstFramePos, mFixedHeader);
return OK;
}
sp<MetaData> MP3Extractor::getTrackMetaData(int index) {
if (mFirstFramePos < 0 || index != 0) {
return NULL;
}
return mMeta;
}
////////////////////////////////////////////////////////////////////////////////
MP3Source::MP3Source(
const sp<MetaData> &meta, DataSource *source,
off_t first_frame_pos, uint32_t fixed_header)
: mMeta(meta),
mDataSource(source),
mFirstFramePos(first_frame_pos),
mFixedHeader(fixed_header),
mCurrentPos(0),
mCurrentTimeUs(0),
mStarted(false),
mGroup(NULL) {
}
MP3Source::~MP3Source() {
if (mStarted) {
stop();
}
}
status_t MP3Source::start(MetaData *) {
assert(!mStarted);
mGroup = new MediaBufferGroup;
const size_t kMaxFrameSize = 32768;
mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
mCurrentPos = mFirstFramePos;
mCurrentTimeUs = 0;
mStarted = true;
return OK;
}
status_t MP3Source::stop() {
assert(mStarted);
delete mGroup;
mGroup = NULL;
mStarted = false;
return OK;
}
sp<MetaData> MP3Source::getFormat() {
return mMeta;
}
status_t MP3Source::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
if (options != NULL && options->getSeekTo(&seekTimeUs)) {
int32_t bitrate;
if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
// bitrate is in kbits/sec.
LOGI("no bitrate");
return ERROR_UNSUPPORTED;
}
mCurrentTimeUs = seekTimeUs;
mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 1000000 * 125;
}
MediaBuffer *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
}
size_t frame_size;
for (;;) {
ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4);
if (n < 4) {
buffer->release();
buffer = NULL;
return ERROR_END_OF_STREAM;
}
uint32_t header = U32_AT((const uint8_t *)buffer->data());
if (get_mp3_frame_size(header, &frame_size)) {
break;
}
// Lost sync.
LOGW("lost sync!\n");
off_t pos = mCurrentPos;
if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
LOGE("Unable to resync. Signalling end of stream.");
buffer->release();
buffer = NULL;
return ERROR_END_OF_STREAM;
}
mCurrentPos = pos;
// Try again with the new position.
}
assert(frame_size <= buffer->size());
ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size);
if (n < (ssize_t)frame_size) {
buffer->release();
buffer = NULL;
return ERROR_END_OF_STREAM;
}
buffer->set_range(0, frame_size);
buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
mCurrentPos += frame_size;
mCurrentTimeUs += 1152 * 1000000 / 44100;
*out = buffer;
return OK;
}
bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence) {
off_t pos = 0;
uint32_t header;
if (!Resync(source, 0, &pos, &header)) {
return false;
}
*mimeType = "audio/mpeg";
*confidence = 0.3f;
return true;
}
} // namespace android

View File

@@ -0,0 +1,937 @@
/*
* Copyright (C) 2009 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 "MPEG4Extractor"
#include <utils/Log.h>
#include <arpa/inet.h>
#undef NDEBUG
#include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
namespace android {
class MPEG4Source : public MediaSource {
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
MPEG4Source(const sp<MetaData> &format, DataSource *dataSource,
SampleTable *sampleTable);
virtual ~MPEG4Source();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
private:
sp<MetaData> mFormat;
DataSource *mDataSource;
int32_t mTimescale;
SampleTable *mSampleTable;
uint32_t mCurrentSampleIndex;
bool mIsAVC;
bool mStarted;
MediaBufferGroup *mGroup;
MediaBuffer *mBuffer;
size_t mBufferOffset;
size_t mBufferSizeRemaining;
bool mNeedsNALFraming;
MPEG4Source(const MPEG4Source &);
MPEG4Source &operator=(const MPEG4Source &);
};
static void hexdump(const void *_data, size_t size) {
const uint8_t *data = (const uint8_t *)_data;
size_t offset = 0;
while (offset < size) {
printf("0x%04x ", offset);
size_t n = size - offset;
if (n > 16) {
n = 16;
}
for (size_t i = 0; i < 16; ++i) {
if (i == 8) {
printf(" ");
}
if (offset + i < size) {
printf("%02x ", data[offset + i]);
} else {
printf(" ");
}
}
printf(" ");
for (size_t i = 0; i < n; ++i) {
if (isprint(data[offset + i])) {
printf("%c", data[offset + i]);
} else {
printf(".");
}
}
printf("\n");
offset += 16;
}
}
static const char *const FourCC2MIME(uint32_t fourcc) {
switch (fourcc) {
case FOURCC('m', 'p', '4', 'a'):
return "audio/mp4a-latm";
case FOURCC('s', 'a', 'm', 'r'):
return "audio/3gpp";
case FOURCC('m', 'p', '4', 'v'):
return "video/mp4v-es";
case FOURCC('s', '2', '6', '3'):
return "video/3gpp";
case FOURCC('a', 'v', 'c', '1'):
return "video/avc";
default:
assert(!"should not be here.");
return NULL;
}
}
MPEG4Extractor::MPEG4Extractor(DataSource *source)
: mDataSource(source),
mHaveMetadata(false),
mFirstTrack(NULL),
mLastTrack(NULL) {
}
MPEG4Extractor::~MPEG4Extractor() {
Track *track = mFirstTrack;
while (track) {
Track *next = track->next;
delete track->sampleTable;
track->sampleTable = NULL;
delete track;
track = next;
}
mFirstTrack = mLastTrack = NULL;
delete mDataSource;
mDataSource = NULL;
}
status_t MPEG4Extractor::countTracks(int *num_tracks) {
status_t err;
if ((err = readMetaData()) != OK) {
return err;
}
*num_tracks = 0;
Track *track = mFirstTrack;
while (track) {
++*num_tracks;
track = track->next;
}
return OK;
}
sp<MetaData> MPEG4Extractor::getTrackMetaData(int index) {
if (index < 0) {
return NULL;
}
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
}
Track *track = mFirstTrack;
while (index > 0) {
if (track == NULL) {
return NULL;
}
track = track->next;
--index;
}
return track->meta;
}
status_t MPEG4Extractor::readMetaData() {
if (mHaveMetadata) {
return OK;
}
off_t offset = 0;
status_t err;
while ((err = parseChunk(&offset, 0)) == OK) {
}
if (mHaveMetadata) {
return OK;
}
return err;
}
static void MakeFourCCString(uint32_t x, char *s) {
s[0] = x >> 24;
s[1] = (x >> 16) & 0xff;
s[2] = (x >> 8) & 0xff;
s[3] = x & 0xff;
s[4] = '\0';
}
status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
uint32_t hdr[2];
if (mDataSource->read_at(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);
uint32_t chunk_type = ntohl(hdr[1]);
off_t data_offset = *offset + 8;
if (chunk_size == 1) {
if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) {
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size);
data_offset += 8;
}
char chunk[5];
MakeFourCCString(chunk_type, chunk);
#if 0
static const char kWhitespace[] = " ";
const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
char buffer[256];
if (chunk_size <= sizeof(buffer)) {
if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) {
return ERROR_IO;
}
hexdump(buffer, chunk_size);
}
#endif
off_t chunk_data_size = *offset + chunk_size - data_offset;
switch(chunk_type) {
case FOURCC('m', 'o', 'o', 'v'):
case FOURCC('t', 'r', 'a', 'k'):
case FOURCC('m', 'd', 'i', 'a'):
case FOURCC('m', 'i', 'n', 'f'):
case FOURCC('d', 'i', 'n', 'f'):
case FOURCC('s', 't', 'b', 'l'):
case FOURCC('m', 'v', 'e', 'x'):
case FOURCC('m', 'o', 'o', 'f'):
case FOURCC('t', 'r', 'a', 'f'):
case FOURCC('m', 'f', 'r', 'a'):
case FOURCC('s', 'k', 'i' ,'p'):
{
off_t stop_offset = *offset + chunk_size;
*offset = data_offset;
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
assert(*offset == stop_offset);
if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mHaveMetadata = true;
return UNKNOWN_ERROR; // Return a dummy error.
}
break;
}
case FOURCC('t', 'k', 'h', 'd'):
{
assert(chunk_data_size >= 4);
uint8_t version;
if (mDataSource->read_at(data_offset, &version, 1) < 1) {
return ERROR_IO;
}
uint64_t ctime, mtime, duration;
int32_t id;
uint32_t width, height;
if (version == 1) {
if (chunk_data_size != 36 + 60) {
return ERROR_MALFORMED;
}
uint8_t buffer[36 + 60];
if (mDataSource->read_at(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
ctime = U64_AT(&buffer[4]);
mtime = U64_AT(&buffer[12]);
id = U32_AT(&buffer[20]);
duration = U64_AT(&buffer[28]);
width = U32_AT(&buffer[88]);
height = U32_AT(&buffer[92]);
} else if (version == 0) {
if (chunk_data_size != 24 + 60) {
return ERROR_MALFORMED;
}
uint8_t buffer[24 + 60];
if (mDataSource->read_at(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
ctime = U32_AT(&buffer[4]);
mtime = U32_AT(&buffer[8]);
id = U32_AT(&buffer[12]);
duration = U32_AT(&buffer[20]);
width = U32_AT(&buffer[76]);
height = U32_AT(&buffer[80]);
}
Track *track = new Track;
track->next = NULL;
if (mLastTrack) {
mLastTrack->next = track;
} else {
mFirstTrack = track;
}
mLastTrack = track;
track->meta = new MetaData;
track->timescale = 0;
track->sampleTable = new SampleTable(mDataSource);
track->meta->setCString(kKeyMIMEType, "application/octet-stream");
*offset += chunk_size;
break;
}
case FOURCC('m', 'd', 'h', 'd'):
{
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
uint8_t version;
if (mDataSource->read_at(
data_offset, &version, sizeof(version))
< (ssize_t)sizeof(version)) {
return ERROR_IO;
}
off_t timescale_offset;
if (version == 1) {
timescale_offset = data_offset + 4 + 16;
} else if (version == 0) {
timescale_offset = data_offset + 4 + 8;
} else {
return ERROR_IO;
}
uint32_t timescale;
if (mDataSource->read_at(
timescale_offset, &timescale, sizeof(timescale))
< (ssize_t)sizeof(timescale)) {
return ERROR_IO;
}
mLastTrack->timescale = ntohl(timescale);
mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
int64_t duration;
if (version == 1) {
if (mDataSource->read_at(
timescale_offset + 4, &duration, sizeof(duration))
< (ssize_t)sizeof(duration)) {
return ERROR_IO;
}
duration = ntoh64(duration);
} else {
int32_t duration32;
if (mDataSource->read_at(
timescale_offset + 4, &duration32, sizeof(duration32))
< (ssize_t)sizeof(duration32)) {
return ERROR_IO;
}
duration = ntohl(duration32);
}
mLastTrack->meta->setInt32(kKeyDuration, duration);
*offset += chunk_size;
break;
}
case FOURCC('h', 'd', 'l', 'r'):
{
if (chunk_data_size < 25) {
return ERROR_MALFORMED;
}
uint8_t buffer[24];
if (mDataSource->read_at(data_offset, buffer, 24) < 24) {
return ERROR_IO;
}
if (U32_AT(buffer) != 0) {
// Should be version 0, flags 0.
return ERROR_MALFORMED;
}
if (U32_AT(&buffer[4]) != 0) {
// pre_defined should be 0.
return ERROR_MALFORMED;
}
mHandlerType = U32_AT(&buffer[8]);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'd'):
{
if (chunk_data_size < 8) {
return ERROR_MALFORMED;
}
uint8_t buffer[8];
assert(chunk_data_size >= (off_t)sizeof(buffer));
if (mDataSource->read_at(
data_offset, buffer, 8) < 8) {
return ERROR_IO;
}
if (U32_AT(buffer) != 0) {
// Should be version 0, flags 0.
return ERROR_MALFORMED;
}
uint32_t entry_count = U32_AT(&buffer[4]);
if (entry_count > 1) {
// For now we only support a single type of media per track.
return ERROR_UNSUPPORTED;
}
off_t stop_offset = *offset + chunk_size;
*offset = data_offset + 8;
for (uint32_t i = 0; i < entry_count; ++i) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
assert(*offset == stop_offset);
break;
}
case FOURCC('m', 'p', '4', 'a'):
case FOURCC('s', 'a', 'm', 'r'):
{
if (mHandlerType != FOURCC('s', 'o', 'u', 'n')) {
return ERROR_MALFORMED;
}
uint8_t buffer[8 + 20];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
// Basic AudioSampleEntry size.
return ERROR_MALFORMED;
}
if (mDataSource->read_at(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t num_channels = U16_AT(&buffer[16]);
if (!strcasecmp("audio/3gpp", FourCC2MIME(chunk_type))) {
// AMR audio is always mono.
num_channels = 1;
}
uint16_t sample_size = U16_AT(&buffer[18]);
uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
printf("*** coding='%s' %d channels, size %d, rate %d\n",
chunk, num_channels, sample_size, sample_rate);
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
off_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
assert(*offset == stop_offset);
break;
}
case FOURCC('m', 'p', '4', 'v'):
case FOURCC('s', '2', '6', '3'):
case FOURCC('a', 'v', 'c', '1'):
{
if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
return ERROR_MALFORMED;
}
uint8_t buffer[78];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
// Basic VideoSampleEntry size.
return ERROR_MALFORMED;
}
if (mDataSource->read_at(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t width = U16_AT(&buffer[6 + 18]);
uint16_t height = U16_AT(&buffer[6 + 20]);
printf("*** coding='%s' width=%d height=%d\n",
chunk, width, height);
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyWidth, width);
mLastTrack->meta->setInt32(kKeyHeight, height);
off_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
assert(*offset == stop_offset);
break;
}
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'c'):
{
status_t err =
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 't', 's'):
{
status_t err =
mLastTrack->sampleTable->setTimeToSampleParams(
data_offset, chunk_data_size);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 's'):
{
status_t err =
mLastTrack->sampleTable->setSyncSampleParams(
data_offset, chunk_data_size);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('e', 's', 'd', 's'):
{
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
uint8_t buffer[256];
if (chunk_data_size > (off_t)sizeof(buffer)) {
return ERROR_BUFFER_TOO_SMALL;
}
if (mDataSource->read_at(
data_offset, buffer, chunk_data_size) < chunk_data_size) {
return ERROR_IO;
}
if (U32_AT(buffer) != 0) {
// Should be version 0, flags 0.
return ERROR_MALFORMED;
}
mLastTrack->meta->setData(
kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
*offset += chunk_size;
break;
}
case FOURCC('a', 'v', 'c', 'C'):
{
char buffer[256];
if (chunk_data_size > (off_t)sizeof(buffer)) {
return ERROR_BUFFER_TOO_SMALL;
}
if (mDataSource->read_at(
data_offset, buffer, chunk_data_size) < chunk_data_size) {
return ERROR_IO;
}
mLastTrack->meta->setData(
kKeyAVCC, kTypeAVCC, buffer, chunk_data_size);
*offset += chunk_size;
break;
}
default:
{
*offset += chunk_size;
break;
}
}
return OK;
}
status_t MPEG4Extractor::getTrack(int index, MediaSource **source) {
*source = NULL;
if (index < 0) {
return ERROR_OUT_OF_RANGE;
}
status_t err;
if ((err = readMetaData()) != OK) {
return err;
}
Track *track = mFirstTrack;
while (index > 0) {
if (track == NULL) {
return ERROR_OUT_OF_RANGE;
}
track = track->next;
--index;
}
*source = new MPEG4Source(
track->meta, mDataSource, track->sampleTable);
return OK;
}
////////////////////////////////////////////////////////////////////////////////
MPEG4Source::MPEG4Source(
const sp<MetaData> &format,
DataSource *dataSource, SampleTable *sampleTable)
: mFormat(format),
mDataSource(dataSource),
mTimescale(0),
mSampleTable(sampleTable),
mCurrentSampleIndex(0),
mIsAVC(false),
mStarted(false),
mGroup(NULL),
mBuffer(NULL),
mBufferOffset(0),
mBufferSizeRemaining(0),
mNeedsNALFraming(false) {
const char *mime;
bool success = mFormat->findCString(kKeyMIMEType, &mime);
assert(success);
success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
assert(success);
mIsAVC = !strcasecmp(mime, "video/avc");
}
MPEG4Source::~MPEG4Source() {
if (mStarted) {
stop();
}
}
status_t MPEG4Source::start(MetaData *params) {
assert(!mStarted);
int32_t val;
if (mIsAVC && params && params->findInt32(kKeyNeedsNALFraming, &val)
&& val != 0) {
mNeedsNALFraming = true;
} else {
mNeedsNALFraming = false;
}
mGroup = new MediaBufferGroup;
size_t max_size;
status_t err = mSampleTable->getMaxSampleSize(&max_size);
assert(err == OK);
// Add padding for de-framing of AVC content just in case.
mGroup->add_buffer(new MediaBuffer(max_size + 2));
mStarted = true;
return OK;
}
status_t MPEG4Source::stop() {
assert(mStarted);
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
delete mGroup;
mGroup = NULL;
mStarted = false;
mCurrentSampleIndex = 0;
return OK;
}
sp<MetaData> MPEG4Source::getFormat() {
return mFormat;
}
status_t MPEG4Source::read(
MediaBuffer **out, const ReadOptions *options) {
assert(mStarted);
*out = NULL;
int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) {
uint32_t sampleIndex;
status_t err = mSampleTable->findClosestSample(
seekTimeUs * mTimescale / 1000000,
&sampleIndex, SampleTable::kSyncSample_Flag);
if (err != OK) {
return err;
}
mCurrentSampleIndex = sampleIndex;
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
// fall through
}
if (mBuffer == NULL) {
off_t offset;
size_t size;
status_t err = mSampleTable->getSampleOffsetAndSize(
mCurrentSampleIndex, &offset, &size);
if (err != OK) {
return err;
}
uint32_t dts;
err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
if (err != OK) {
return err;
}
err = mGroup->acquire_buffer(&mBuffer);
if (err != OK) {
assert(mBuffer == NULL);
return err;
}
assert(mBuffer->size() + 2 >= size);
ssize_t num_bytes_read =
mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return err;
}
mBuffer->set_range(2, size);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
++mCurrentSampleIndex;
mBufferOffset = 2;
mBufferSizeRemaining = size;
}
if (!mIsAVC) {
*out = mBuffer;
mBuffer = NULL;
return OK;
}
uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset;
assert(mBufferSizeRemaining >= 2);
size_t nal_length = (data[0] << 8) | data[1];
assert(mBufferSizeRemaining >= 2 + nal_length);
if (mNeedsNALFraming) {
// Insert marker.
data[-2] = data[-1] = data[0] = 0;
data[1] = 1;
mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
} else {
mBuffer->set_range(mBufferOffset + 2, nal_length);
}
mBufferOffset += nal_length + 2;
mBufferSizeRemaining -= nal_length + 2;
if (mBufferSizeRemaining > 0) {
*out = mBuffer->clone();
} else {
*out = mBuffer;
mBuffer = NULL;
}
return OK;
}
bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence) {
uint8_t header[8];
ssize_t n = source->read_at(4, header, sizeof(header));
if (n < (ssize_t)sizeof(header)) {
return false;
}
if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
|| !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) {
*mimeType = "video/mp4";
*confidence = 0.1;
return true;
}
return false;
}
} // namespace android

View File

@@ -0,0 +1,641 @@
/*
* Copyright (C) 2009 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>
#undef NDEBUG
#include <assert.h>
#include <ctype.h>
#include <pthread.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/Utils.h>
namespace android {
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source);
~Track();
void start();
void stop();
int64_t getDuration() const;
void writeTrackHeader(int32_t trackID);
private:
MPEG4Writer *mOwner;
sp<MetaData> mMeta;
MediaSource *mSource;
volatile bool mDone;
pthread_t mThread;
struct SampleInfo {
size_t size;
off_t offset;
int64_t timestamp;
};
List<SampleInfo> mSampleInfos;
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
static void *ThreadWrapper(void *me);
void threadEntry();
Track(const Track &);
Track &operator=(const Track &);
};
MPEG4Writer::MPEG4Writer(const char *filename)
: mFile(fopen(filename, "wb")),
mOffset(0),
mMdatOffset(0) {
assert(mFile != NULL);
}
MPEG4Writer::~MPEG4Writer() {
stop();
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
delete *it;
}
mTracks.clear();
}
void MPEG4Writer::addSource(const sp<MetaData> &meta, MediaSource *source) {
Track *track = new Track(this, meta, source);
mTracks.push_back(track);
}
void MPEG4Writer::start() {
if (mFile == NULL) {
return;
}
beginBox("ftyp");
writeFourcc("isom");
writeInt32(0);
writeFourcc("isom");
endBox();
mMdatOffset = mOffset;
write("\x00\x00\x00\x01mdat????????", 16);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
(*it)->start();
}
}
void MPEG4Writer::stop() {
if (mFile == NULL) {
return;
}
int64_t max_duration = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
(*it)->stop();
int64_t duration = (*it)->getDuration();
if (duration > max_duration) {
max_duration = duration;
}
}
// Fix up the size of the 'mdat' chunk.
fseek(mFile, mMdatOffset + 8, SEEK_SET);
int64_t size = mOffset - mMdatOffset;
size = hton64(size);
fwrite(&size, 1, 8, mFile);
fseek(mFile, mOffset, SEEK_SET);
time_t now = time(NULL);
beginBox("moov");
beginBox("mvhd");
writeInt32(0); // version=0, flags=0
writeInt32(now); // creation time
writeInt32(now); // modification time
writeInt32(1000); // timescale
writeInt32(max_duration);
writeInt32(0x10000); // rate
writeInt16(0x100); // volume
writeInt16(0); // reserved
writeInt32(0); // reserved
writeInt32(0); // reserved
writeInt32(0x10000); // matrix
writeInt32(0);
writeInt32(0);
writeInt32(0);
writeInt32(0x10000);
writeInt32(0);
writeInt32(0);
writeInt32(0);
writeInt32(0x40000000);
writeInt32(0); // predefined
writeInt32(0); // predefined
writeInt32(0); // predefined
writeInt32(0); // predefined
writeInt32(0); // predefined
writeInt32(0); // predefined
writeInt32(mTracks.size() + 1); // nextTrackID
endBox(); // mvhd
int32_t id = 1;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it, ++id) {
(*it)->writeTrackHeader(id);
}
endBox(); // moov
assert(mBoxes.empty());
fclose(mFile);
mFile = NULL;
}
off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
Mutex::Autolock autoLock(mLock);
off_t old_offset = mOffset;
fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
1, buffer->range_length(), mFile);
mOffset += buffer->range_length();
return old_offset;
}
void MPEG4Writer::beginBox(const char *fourcc) {
assert(strlen(fourcc) == 4);
mBoxes.push_back(mOffset);
writeInt32(0);
writeFourcc(fourcc);
}
void MPEG4Writer::endBox() {
assert(!mBoxes.empty());
off_t offset = *--mBoxes.end();
mBoxes.erase(--mBoxes.end());
fseek(mFile, offset, SEEK_SET);
writeInt32(mOffset - offset);
mOffset -= 4;
fseek(mFile, mOffset, SEEK_SET);
}
void MPEG4Writer::writeInt8(int8_t x) {
fwrite(&x, 1, 1, mFile);
++mOffset;
}
void MPEG4Writer::writeInt16(int16_t x) {
x = htons(x);
fwrite(&x, 1, 2, mFile);
mOffset += 2;
}
void MPEG4Writer::writeInt32(int32_t x) {
x = htonl(x);
fwrite(&x, 1, 4, mFile);
mOffset += 4;
}
void MPEG4Writer::writeInt64(int64_t x) {
x = hton64(x);
fwrite(&x, 1, 8, mFile);
mOffset += 8;
}
void MPEG4Writer::writeCString(const char *s) {
size_t n = strlen(s);
fwrite(s, 1, n + 1, mFile);
mOffset += n + 1;
}
void MPEG4Writer::writeFourcc(const char *s) {
assert(strlen(s) == 4);
fwrite(s, 1, 4, mFile);
mOffset += 4;
}
void MPEG4Writer::write(const void *data, size_t size) {
fwrite(data, 1, size, mFile);
mOffset += size;
}
////////////////////////////////////////////////////////////////////////////////
MPEG4Writer::Track::Track(
MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source)
: mOwner(owner),
mMeta(meta),
mSource(source),
mDone(false),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0) {
}
MPEG4Writer::Track::~Track() {
stop();
if (mCodecSpecificData != NULL) {
free(mCodecSpecificData);
mCodecSpecificData = NULL;
}
}
void MPEG4Writer::Track::start() {
mSource->start();
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
mDone = false;
int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
assert(err == 0);
pthread_attr_destroy(&attr);
}
void MPEG4Writer::Track::stop() {
if (mDone) {
return;
}
mDone = true;
void *dummy;
pthread_join(mThread, &dummy);
mSource->stop();
}
// static
void *MPEG4Writer::Track::ThreadWrapper(void *me) {
Track *track = static_cast<Track *>(me);
track->threadEntry();
return NULL;
}
void MPEG4Writer::Track::threadEntry() {
bool is_mpeg4 = false;
sp<MetaData> meta = mSource->getFormat();
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
is_mpeg4 = !strcasecmp(mime, "video/mp4v-es");
MediaBuffer *buffer;
while (!mDone && mSource->read(&buffer) == OK) {
if (buffer->range_length() == 0) {
buffer->release();
buffer = NULL;
continue;
}
if (mCodecSpecificData == NULL && is_mpeg4) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
const size_t size = buffer->range_length();
size_t offset = 0;
while (offset + 3 < size) {
if (data[offset] == 0x00 && data[offset + 1] == 0x00
&& data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
break;
}
++offset;
}
assert(offset + 3 < size);
mCodecSpecificDataSize = offset;
mCodecSpecificData = malloc(offset);
memcpy(mCodecSpecificData, data, offset);
buffer->set_range(buffer->range_offset() + offset, size - offset);
}
off_t offset = mOwner->addSample(buffer);
SampleInfo info;
info.size = buffer->range_length();
info.offset = offset;
int32_t units, scale;
bool success =
buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
assert(success);
success =
buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
assert(success);
info.timestamp = (int64_t)units * 1000 / scale;
mSampleInfos.push_back(info);
buffer->release();
buffer = NULL;
}
}
int64_t MPEG4Writer::Track::getDuration() const {
return 10000; // XXX
}
void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
const char *mime;
bool success = mMeta->findCString(kKeyMIMEType, &mime);
assert(success);
bool is_audio = !strncasecmp(mime, "audio/", 6);
time_t now = time(NULL);
mOwner->beginBox("trak");
mOwner->beginBox("tkhd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(now); // creation time
mOwner->writeInt32(now); // modification time
mOwner->writeInt32(trackID);
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(getDuration());
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // layer
mOwner->writeInt16(0); // alternate group
mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
mOwner->writeInt16(0); // reserved
mOwner->writeInt32(0x10000); // matrix
mOwner->writeInt32(0);
mOwner->writeInt32(0);
mOwner->writeInt32(0);
mOwner->writeInt32(0x10000);
mOwner->writeInt32(0);
mOwner->writeInt32(0);
mOwner->writeInt32(0);
mOwner->writeInt32(0x40000000);
if (is_audio) {
mOwner->writeInt32(0);
mOwner->writeInt32(0);
} else {
int32_t width, height;
bool success = mMeta->findInt32(kKeyWidth, &width);
success = success && mMeta->findInt32(kKeyHeight, &height);
assert(success);
mOwner->writeInt32(width);
mOwner->writeInt32(height);
}
mOwner->endBox(); // tkhd
mOwner->beginBox("mdia");
mOwner->beginBox("mdhd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(now); // creation time
mOwner->writeInt32(now); // modification time
mOwner->writeInt32(1000); // timescale
mOwner->writeInt32(getDuration());
mOwner->writeInt16(0); // language code XXX
mOwner->writeInt16(0); // predefined
mOwner->endBox();
mOwner->beginBox("hdlr");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(0); // predefined
mOwner->writeFourcc(is_audio ? "soun" : "vide");
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeCString(""); // name
mOwner->endBox();
mOwner->beginBox("minf");
mOwner->beginBox("dinf");
mOwner->beginBox("dref");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(1);
mOwner->beginBox("url ");
mOwner->writeInt32(1); // version=0, flags=1
mOwner->endBox(); // url
mOwner->endBox(); // dref
mOwner->endBox(); // dinf
if (is_audio) {
mOwner->beginBox("smhd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt16(0); // balance
mOwner->writeInt16(0); // reserved
mOwner->endBox();
} else {
mOwner->beginBox("vmhd");
mOwner->writeInt32(0x00000001); // version=0, flags=1
mOwner->writeInt16(0); // graphics mode
mOwner->writeInt16(0); // opcolor
mOwner->writeInt16(0);
mOwner->writeInt16(0);
mOwner->endBox();
}
mOwner->endBox(); // minf
mOwner->beginBox("stbl");
mOwner->beginBox("stsd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(1); // entry count
if (is_audio) {
mOwner->beginBox("xxxx"); // audio format XXX
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // reserved
mOwner->writeInt16(0); // data ref index
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(2); // channel count
mOwner->writeInt16(16); // sample size
mOwner->writeInt16(0); // predefined
mOwner->writeInt16(0); // reserved
int32_t samplerate;
bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
assert(success);
mOwner->writeInt32(samplerate << 16);
mOwner->endBox();
} else {
if (!strcasecmp("video/mp4v-es", mime)) {
mOwner->beginBox("mp4v");
} else if (!strcasecmp("video/3gpp", mime)) {
mOwner->beginBox("s263");
} else {
assert(!"should not be here, unknown mime type.");
}
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // reserved
mOwner->writeInt16(0); // data ref index
mOwner->writeInt16(0); // predefined
mOwner->writeInt16(0); // reserved
mOwner->writeInt32(0); // predefined
mOwner->writeInt32(0); // predefined
mOwner->writeInt32(0); // predefined
int32_t width, height;
bool success = mMeta->findInt32(kKeyWidth, &width);
success = success && mMeta->findInt32(kKeyHeight, &height);
assert(success);
mOwner->writeInt16(width);
mOwner->writeInt16(height);
mOwner->writeInt32(0x480000); // horiz resolution
mOwner->writeInt32(0x480000); // vert resolution
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(1); // frame count
mOwner->write(" ", 32);
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
assert(23 + mCodecSpecificDataSize < 128);
if (!strcasecmp("video/mp4v-es", mime)) {
mOwner->beginBox("esds");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt8(0x03); // ES_DescrTag
mOwner->writeInt8(23 + mCodecSpecificDataSize);
mOwner->writeInt16(0x0000); // ES_ID
mOwner->writeInt8(0x1f);
mOwner->writeInt8(0x04); // DecoderConfigDescrTag
mOwner->writeInt8(15 + mCodecSpecificDataSize);
mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
mOwner->writeInt8(0x11); // streamType VisualStream
static const uint8_t kData[] = {
0x01, 0x77, 0x00,
0x00, 0x03, 0xe8, 0x00,
0x00, 0x03, 0xe8, 0x00
};
mOwner->write(kData, sizeof(kData));
mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
mOwner->writeInt8(mCodecSpecificDataSize);
mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
static const uint8_t kData2[] = {
0x06, // SLConfigDescriptorTag
0x01,
0x02
};
mOwner->write(kData2, sizeof(kData2));
mOwner->endBox(); // esds
} else if (!strcasecmp("video/3gpp", mime)) {
mOwner->beginBox("d263");
mOwner->writeInt32(0); // vendor
mOwner->writeInt8(0); // decoder version
mOwner->writeInt8(10); // level: 10
mOwner->writeInt8(0); // profile: 0
mOwner->endBox(); // d263
}
mOwner->endBox(); // mp4v or s263
}
mOwner->endBox(); // stsd
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSampleInfos.size() - 1);
List<SampleInfo>::iterator it = mSampleInfos.begin();
int64_t last = (*it).timestamp;
++it;
while (it != mSampleInfos.end()) {
mOwner->writeInt32(1);
mOwner->writeInt32((*it).timestamp - last);
last = (*it).timestamp;
++it;
}
mOwner->endBox(); // stts
mOwner->beginBox("stsz");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(0); // default sample size
mOwner->writeInt32(mSampleInfos.size());
for (List<SampleInfo>::iterator it = mSampleInfos.begin();
it != mSampleInfos.end(); ++it) {
mOwner->writeInt32((*it).size);
}
mOwner->endBox(); // stsz
mOwner->beginBox("stsc");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSampleInfos.size());
int32_t n = 1;
for (List<SampleInfo>::iterator it = mSampleInfos.begin();
it != mSampleInfos.end(); ++it, ++n) {
mOwner->writeInt32(n);
mOwner->writeInt32(1);
mOwner->writeInt32(1);
}
mOwner->endBox(); // stsc
mOwner->beginBox("co64");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSampleInfos.size());
for (List<SampleInfo>::iterator it = mSampleInfos.begin();
it != mSampleInfos.end(); ++it, ++n) {
mOwner->writeInt64((*it).offset);
}
mOwner->endBox(); // co64
mOwner->endBox(); // stbl
mOwner->endBox(); // mdia
mOwner->endBox(); // trak
}
} // namespace android

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2009 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 "MediaBuffer"
#include <utils/Log.h>
#undef NDEBUG
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
namespace android {
// XXX make this truly atomic.
static int atomic_add(int *value, int delta) {
int prev_value = *value;
*value += delta;
return prev_value;
}
MediaBuffer::MediaBuffer(void *data, size_t size)
: mObserver(NULL),
mNextBuffer(NULL),
mRefCount(0),
mData(data),
mSize(size),
mRangeOffset(0),
mRangeLength(size),
mOwnsData(false),
mMetaData(new MetaData),
mOriginal(NULL) {
}
MediaBuffer::MediaBuffer(size_t size)
: mObserver(NULL),
mNextBuffer(NULL),
mRefCount(0),
mData(malloc(size)),
mSize(size),
mRangeOffset(0),
mRangeLength(size),
mOwnsData(true),
mMetaData(new MetaData),
mOriginal(NULL) {
}
void MediaBuffer::release() {
if (mObserver == NULL) {
assert(mRefCount == 0);
delete this;
return;
}
int prevCount = atomic_add(&mRefCount, -1);
if (prevCount == 1) {
if (mObserver == NULL) {
delete this;
return;
}
mObserver->signalBufferReturned(this);
}
assert(prevCount > 0);
}
void MediaBuffer::claim() {
assert(mObserver != NULL);
assert(mRefCount == 1);
mRefCount = 0;
}
void MediaBuffer::add_ref() {
atomic_add(&mRefCount, 1);
}
void *MediaBuffer::data() const {
return mData;
}
size_t MediaBuffer::size() const {
return mSize;
}
size_t MediaBuffer::range_offset() const {
return mRangeOffset;
}
size_t MediaBuffer::range_length() const {
return mRangeLength;
}
void MediaBuffer::set_range(size_t offset, size_t length) {
if (offset < 0 || offset + length > mSize) {
LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
}
assert(offset >= 0 && offset + length <= mSize);
mRangeOffset = offset;
mRangeLength = length;
}
sp<MetaData> MediaBuffer::meta_data() {
return mMetaData;
}
void MediaBuffer::reset() {
mMetaData->clear();
set_range(0, mSize);
}
MediaBuffer::~MediaBuffer() {
assert(mObserver == NULL);
if (mOwnsData && mData != NULL) {
free(mData);
mData = NULL;
}
if (mOriginal != NULL) {
mOriginal->release();
mOriginal = NULL;
}
}
void MediaBuffer::setObserver(MediaBufferObserver *observer) {
assert(observer == NULL || mObserver == NULL);
mObserver = observer;
}
void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
mNextBuffer = buffer;
}
MediaBuffer *MediaBuffer::nextBuffer() {
return mNextBuffer;
}
int MediaBuffer::refcount() const {
return mRefCount;
}
MediaBuffer *MediaBuffer::clone() {
MediaBuffer *buffer = new MediaBuffer(mData, mSize);
buffer->set_range(mRangeOffset, mRangeLength);
buffer->mMetaData = new MetaData(*mMetaData.get());
add_ref();
buffer->mOriginal = this;
return buffer;
}
} // namespace android

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2009 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 "MediaBufferGroup"
#include <utils/Log.h>
#undef NDEBUG
#include <assert.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
namespace android {
MediaBufferGroup::MediaBufferGroup()
: mFirstBuffer(NULL),
mLastBuffer(NULL) {
}
MediaBufferGroup::~MediaBufferGroup() {
MediaBuffer *next;
for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
buffer = next) {
next = buffer->nextBuffer();
assert(buffer->refcount() == 0);
buffer->setObserver(NULL);
buffer->release();
}
}
void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
Mutex::Autolock autoLock(mLock);
buffer->setObserver(this);
if (mLastBuffer) {
mLastBuffer->setNextBuffer(buffer);
} else {
mFirstBuffer = buffer;
}
mLastBuffer = buffer;
}
status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
Mutex::Autolock autoLock(mLock);
for (;;) {
for (MediaBuffer *buffer = mFirstBuffer;
buffer != NULL; buffer = buffer->nextBuffer()) {
if (buffer->refcount() == 0) {
buffer->add_ref();
buffer->reset();
*out = buffer;
goto exit;
}
}
// All buffers are in use. Block until one of them is returned to us.
mCondition.wait(mLock);
}
exit:
return OK;
}
void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
Mutex::Autolock autoLock(mLock);
mCondition.signal();
}
} // namespace android

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2009 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 "MediaExtractor"
#include <utils/Log.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MP3Extractor.h>
#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MediaExtractor.h>
#include <utils/String8.h>
namespace android {
// static
MediaExtractor *MediaExtractor::Create(DataSource *source, const char *mime) {
String8 tmp;
if (mime == NULL) {
float confidence;
if (!source->sniff(&tmp, &confidence)) {
LOGE("FAILED to autodetect media content.");
return NULL;
}
mime = tmp.string();
LOGI("Autodetected media content as '%s' with confidence %.2f",
mime, confidence);
}
if (!strcasecmp(mime, "video/mp4") || !strcasecmp(mime, "audio/mp4")) {
return new MPEG4Extractor(source);
} else if (!strcasecmp(mime, "audio/mpeg")) {
return new MP3Extractor(source);
}
return NULL;
}
} // namespace android

View File

@@ -0,0 +1,693 @@
/*
* Copyright (C) 2009 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 "MediaPlayerImpl"
#include "utils/Log.h"
#undef NDEBUG
#include <assert.h>
#include <OMX_Component.h>
#include <unistd.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/CachingDataSource.h>
// #include <media/stagefright/CameraSource.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXDecoder.h>
#include <media/stagefright/QComHardwareRenderer.h>
#include <media/stagefright/ShoutcastSource.h>
#include <media/stagefright/SoftwareRenderer.h>
#include <media/stagefright/SurfaceRenderer.h>
#include <media/stagefright/TimeSource.h>
#include <ui/PixelFormat.h>
#include <ui/Surface.h>
namespace android {
MediaPlayerImpl::MediaPlayerImpl(const char *uri)
: mInitCheck(NO_INIT),
mExtractor(NULL),
mTimeSource(NULL),
mAudioSource(NULL),
mAudioDecoder(NULL),
mAudioPlayer(NULL),
mVideoSource(NULL),
mVideoDecoder(NULL),
mVideoWidth(0),
mVideoHeight(0),
mVideoPosition(0),
mDuration(0),
mPlaying(false),
mPaused(false),
mRenderer(NULL),
mSeeking(false),
mFrameSize(0),
mUseSoftwareColorConversion(false) {
LOGI("MediaPlayerImpl(%s)", uri);
DataSource::RegisterDefaultSniffers();
status_t err = mClient.connect();
if (err != OK) {
LOGE("Failed to connect to OMXClient.");
return;
}
if (!strncasecmp("shoutcast://", uri, 12)) {
setAudioSource(makeShoutcastSource(uri));
#if 0
} else if (!strncasecmp("camera:", uri, 7)) {
mVideoWidth = 480;
mVideoHeight = 320;
mVideoDecoder = CameraSource::Create();
#endif
} else {
DataSource *source = NULL;
if (!strncasecmp("file://", uri, 7)) {
source = new MmapSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7)) {
source = new HTTPDataSource(uri);
source = new CachingDataSource(source, 64 * 1024, 10);
} else {
// Assume it's a filename.
source = new MmapSource(uri);
}
mExtractor = MediaExtractor::Create(source);
if (mExtractor == NULL) {
return;
}
}
init();
mInitCheck = OK;
}
MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
: mInitCheck(NO_INIT),
mExtractor(NULL),
mTimeSource(NULL),
mAudioSource(NULL),
mAudioDecoder(NULL),
mAudioPlayer(NULL),
mVideoSource(NULL),
mVideoDecoder(NULL),
mVideoWidth(0),
mVideoHeight(0),
mVideoPosition(0),
mDuration(0),
mPlaying(false),
mPaused(false),
mRenderer(NULL),
mSeeking(false),
mFrameSize(0),
mUseSoftwareColorConversion(false) {
LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
DataSource::RegisterDefaultSniffers();
status_t err = mClient.connect();
if (err != OK) {
LOGE("Failed to connect to OMXClient.");
return;
}
mExtractor = MediaExtractor::Create(
new MmapSource(fd, offset, length));
if (mExtractor == NULL) {
return;
}
init();
mInitCheck = OK;
}
status_t MediaPlayerImpl::initCheck() const {
return mInitCheck;
}
MediaPlayerImpl::~MediaPlayerImpl() {
stop();
setSurface(NULL);
LOGV("Shutting down audio.");
delete mAudioDecoder;
mAudioDecoder = NULL;
delete mAudioSource;
mAudioSource = NULL;
LOGV("Shutting down video.");
delete mVideoDecoder;
mVideoDecoder = NULL;
delete mVideoSource;
mVideoSource = NULL;
delete mExtractor;
mExtractor = NULL;
if (mInitCheck == OK) {
mClient.disconnect();
}
LOGV("~MediaPlayerImpl done.");
}
void MediaPlayerImpl::play() {
LOGI("play");
if (mPlaying) {
if (mPaused) {
if (mAudioSource != NULL) {
mAudioPlayer->resume();
}
mPaused = false;
}
return;
}
mPlaying = true;
if (mAudioSource != NULL) {
mAudioPlayer = new AudioPlayer(mAudioSink);
mAudioPlayer->setSource(mAudioDecoder);
mAudioPlayer->start();
mTimeSource = mAudioPlayer;
} else {
mTimeSource = new SystemTimeSource;
}
if (mVideoDecoder != NULL) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&mVideoThread, &attr, VideoWrapper, this);
pthread_attr_destroy(&attr);
}
}
void MediaPlayerImpl::pause() {
if (!mPlaying || mPaused) {
return;
}
if (mAudioSource != NULL) {
mAudioPlayer->pause();
}
mPaused = true;
}
void MediaPlayerImpl::stop() {
if (!mPlaying) {
return;
}
mPlaying = false;
if (mVideoDecoder != NULL) {
void *dummy;
pthread_join(mVideoThread, &dummy);
}
if (mAudioSource != NULL) {
mAudioPlayer->stop();
delete mAudioPlayer;
mAudioPlayer = NULL;
} else {
delete mTimeSource;
}
mTimeSource = NULL;
}
// static
void *MediaPlayerImpl::VideoWrapper(void *me) {
((MediaPlayerImpl *)me)->videoEntry();
return NULL;
}
void MediaPlayerImpl::videoEntry() {
bool firstFrame = true;
bool eof = false;
status_t err = mVideoDecoder->start();
assert(err == OK);
while (mPlaying) {
MediaBuffer *buffer;
MediaSource::ReadOptions options;
bool seeking = false;
{
Mutex::Autolock autoLock(mLock);
if (mSeeking) {
LOGI("seek-options to %lld", mSeekTimeUs);
options.setSeekTo(mSeekTimeUs);
mSeeking = false;
seeking = true;
eof = false;
}
}
if (eof || mPaused) {
usleep(100000);
continue;
}
status_t err = mVideoDecoder->read(&buffer, &options);
assert((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
if (err == ERROR_END_OF_STREAM || err != OK) {
eof = true;
continue;
}
if (buffer->range_length() == 0) {
// The final buffer is empty.
buffer->release();
continue;
}
int32_t units, scale;
bool success =
buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
assert(success);
success =
buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
assert(success);
int64_t pts_us = (int64_t)units * 1000000 / scale;
{
Mutex::Autolock autoLock(mLock);
mVideoPosition = pts_us;
}
if (seeking && mAudioPlayer != NULL) {
// Now that we know where exactly video seeked (taking sync-samples
// into account), we will seek the audio track to the same time.
mAudioPlayer->seekTo(pts_us);
}
if (firstFrame || seeking) {
mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
firstFrame = false;
}
displayOrDiscardFrame(buffer, pts_us);
}
mVideoDecoder->stop();
}
void MediaPlayerImpl::displayOrDiscardFrame(
MediaBuffer *buffer, int64_t pts_us) {
for (;;) {
if (!mPlaying || mPaused) {
buffer->release();
buffer = NULL;
return;
}
int64_t realtime_us, mediatime_us;
if (mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
mTimeSourceDeltaUs = realtime_us - mediatime_us;
}
int64_t now_us = mTimeSource->getRealTimeUs();
now_us -= mTimeSourceDeltaUs;
int64_t delay_us = pts_us - now_us;
if (delay_us < -15000) {
// We're late.
LOGI("we're late by %lld ms, dropping a frame\n",
-delay_us / 1000);
buffer->release();
buffer = NULL;
return;
} else if (delay_us > 100000) {
LOGI("we're much too early (by %lld ms)\n",
delay_us / 1000);
usleep(100000);
continue;
} else if (delay_us > 0) {
usleep(delay_us);
}
break;
}
{
Mutex::Autolock autoLock(mLock);
if (mRenderer != NULL) {
sendFrameToISurface(buffer);
}
}
buffer->release();
buffer = NULL;
}
void MediaPlayerImpl::init() {
if (mExtractor != NULL) {
int num_tracks;
assert(mExtractor->countTracks(&num_tracks) == OK);
mDuration = 0;
for (int i = 0; i < num_tracks; ++i) {
const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
assert(meta != NULL);
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
continue;
}
bool is_audio = false;
bool is_acceptable = false;
if (!strncasecmp(mime, "audio/", 6)) {
is_audio = true;
is_acceptable = (mAudioSource == NULL);
} else if (!strncasecmp(mime, "video/", 6)) {
is_acceptable = (mVideoSource == NULL);
}
if (!is_acceptable) {
continue;
}
MediaSource *source;
if (mExtractor->getTrack(i, &source) != OK) {
continue;
}
int32_t units, scale;
if (meta->findInt32(kKeyDuration, &units)
&& meta->findInt32(kKeyTimeScale, &scale)) {
int64_t duration_us = (int64_t)units * 1000000 / scale;
if (duration_us > mDuration) {
mDuration = duration_us;
}
}
if (is_audio) {
setAudioSource(source);
} else {
setVideoSource(source);
}
}
}
}
void MediaPlayerImpl::setAudioSource(MediaSource *source) {
mAudioSource = source;
sp<MetaData> meta = source->getFormat();
mAudioDecoder = OMXDecoder::Create(&mClient, meta);
mAudioDecoder->setSource(source);
}
void MediaPlayerImpl::setVideoSource(MediaSource *source) {
LOGI("setVideoSource");
mVideoSource = source;
sp<MetaData> meta = source->getFormat();
bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
assert(success);
success = meta->findInt32(kKeyHeight, &mVideoHeight);
assert(success);
mVideoDecoder = OMXDecoder::Create(&mClient, meta);
((OMXDecoder *)mVideoDecoder)->setSource(source);
if (mISurface.get() != NULL || mSurface.get() != NULL) {
depopulateISurface();
populateISurface();
}
}
void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
LOGI("setSurface %p", surface.get());
Mutex::Autolock autoLock(mLock);
depopulateISurface();
mSurface = surface;
mISurface = NULL;
if (mSurface.get() != NULL) {
populateISurface();
}
}
void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
LOGI("setISurface %p", isurface.get());
Mutex::Autolock autoLock(mLock);
depopulateISurface();
mSurface = NULL;
mISurface = isurface;
if (mISurface.get() != NULL) {
populateISurface();
}
}
MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
if (strncasecmp(uri, "shoutcast://", 12)) {
return NULL;
}
string host;
string path;
int port;
char *slash = strchr(uri + 12, '/');
if (slash == NULL) {
host = uri + 12;
path = "/";
} else {
host = string(uri + 12, slash - (uri + 12));
path = slash;
}
char *colon = strchr(host.c_str(), ':');
if (colon == NULL) {
port = 80;
} else {
char *end;
long tmp = strtol(colon + 1, &end, 10);
assert(end > colon + 1);
assert(tmp > 0 && tmp < 65536);
port = tmp;
host = string(host, 0, colon - host.c_str());
}
LOGI("Connecting to host '%s', port %d, path '%s'",
host.c_str(), port, path.c_str());
HTTPStream *http = new HTTPStream;
int http_status;
for (;;) {
status_t err = http->connect(host.c_str(), port);
assert(err == OK);
err = http->send("GET ");
err = http->send(path.c_str());
err = http->send(" HTTP/1.1\r\n");
err = http->send("Host: ");
err = http->send(host.c_str());
err = http->send("\r\n");
err = http->send("Icy-MetaData: 1\r\n\r\n");
assert(OK == http->receive_header(&http_status));
if (http_status == 301 || http_status == 302) {
string location;
assert(http->find_header_value("Location", &location));
assert(string(location, 0, 7) == "http://");
location.erase(0, 7);
string::size_type slashPos = location.find('/');
if (slashPos == string::npos) {
slashPos = location.size();
location += '/';
}
http->disconnect();
LOGI("Redirecting to %s\n", location.c_str());
host = string(location, 0, slashPos);
string::size_type colonPos = host.find(':');
if (colonPos != string::npos) {
const char *start = host.c_str() + colonPos + 1;
char *end;
long tmp = strtol(start, &end, 10);
assert(end > start && *end == '\0');
port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
} else {
port = 80;
}
path = string(location, slashPos);
continue;
}
break;
}
if (http_status != 200) {
LOGE("Connection failed: http_status = %d", http_status);
return NULL;
}
MediaSource *source = new ShoutcastSource(http);
return source;
}
bool MediaPlayerImpl::isPlaying() const {
return mPlaying && !mPaused;
}
int64_t MediaPlayerImpl::getDuration() {
return mDuration;
}
int64_t MediaPlayerImpl::getPosition() {
int64_t position = 0;
if (mVideoSource != NULL) {
Mutex::Autolock autoLock(mLock);
position = mVideoPosition;
} else if (mAudioPlayer != NULL) {
position = mAudioPlayer->getMediaTimeUs();
}
return position;
}
status_t MediaPlayerImpl::seekTo(int64_t time) {
LOGI("seekTo %lld", time);
if (mPaused) {
return UNKNOWN_ERROR;
}
if (mVideoSource == NULL && mAudioPlayer != NULL) {
mAudioPlayer->seekTo(time);
} else {
Mutex::Autolock autoLock(mLock);
mSeekTimeUs = time;
mSeeking = true;
}
return OK;
}
void MediaPlayerImpl::populateISurface() {
if (mVideoSource == NULL) {
return;
}
sp<MetaData> meta = mVideoDecoder->getFormat();
int32_t format;
const char *component;
int32_t decodedWidth, decodedHeight;
bool success = meta->findInt32(kKeyColorFormat, &format);
success = success && meta->findCString(kKeyDecoderComponent, &component);
success = success && meta->findInt32(kKeyWidth, &decodedWidth);
success = success && meta->findInt32(kKeyHeight, &decodedHeight);
assert(success);
if (mSurface.get() != NULL) {
mRenderer =
new SurfaceRenderer(
mSurface, mVideoWidth, mVideoHeight,
decodedWidth, decodedHeight);
} else if (format == OMX_COLOR_FormatYUV420Planar
&& !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) {
mRenderer =
new QComHardwareRenderer(
mISurface, mVideoWidth, mVideoHeight,
decodedWidth, decodedHeight);
} else {
LOGW("Using software renderer.");
mRenderer = new SoftwareRenderer(
mISurface, mVideoWidth, mVideoHeight,
decodedWidth, decodedHeight);
}
}
void MediaPlayerImpl::depopulateISurface() {
delete mRenderer;
mRenderer = NULL;
}
void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
void *platformPrivate;
if (!buffer->meta_data()->findPointer(
kKeyPlatformPrivate, &platformPrivate)) {
platformPrivate = NULL;
}
mRenderer->render(
(const uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length(),
platformPrivate);
}
void MediaPlayerImpl::setAudioSink(
const sp<MediaPlayerBase::AudioSink> &audioSink) {
LOGI("setAudioSink %p", audioSink.get());
mAudioSink = audioSink;
}
} // namespace android

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2009 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 <media/stagefright/MediaSource.h>
namespace android {
MediaSource::MediaSource() {}
MediaSource::~MediaSource() {}
////////////////////////////////////////////////////////////////////////////////
MediaSource::ReadOptions::ReadOptions() {
reset();
}
void MediaSource::ReadOptions::reset() {
mOptions = 0;
mSeekTimeUs = 0;
mLatenessUs = 0;
}
void MediaSource::ReadOptions::setSeekTo(int64_t time_us) {
mOptions |= kSeekTo_Option;
mSeekTimeUs = time_us;
}
void MediaSource::ReadOptions::clearSeekTo() {
mOptions &= ~kSeekTo_Option;
mSeekTimeUs = 0;
}
bool MediaSource::ReadOptions::getSeekTo(int64_t *time_us) const {
*time_us = mSeekTimeUs;
return (mOptions & kSeekTo_Option) != 0;
}
void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
mLatenessUs = lateness_us;
}
int64_t MediaSource::ReadOptions::getLateBy() const {
return mLatenessUs;
}
} // namespace android

View File

@@ -0,0 +1,232 @@
/*
* Copyright (C) 2009 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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include <media/stagefright/MetaData.h>
namespace android {
MetaData::MetaData() {
}
MetaData::MetaData(const MetaData &from)
: RefBase(),
mItems(from.mItems) {
}
MetaData::~MetaData() {
clear();
}
void MetaData::clear() {
mItems.clear();
}
bool MetaData::remove(uint32_t key) {
ssize_t i = mItems.indexOfKey(key);
if (i < 0) {
return false;
}
mItems.removeItemsAt(i);
return true;
}
bool MetaData::setCString(uint32_t key, const char *value) {
return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
}
bool MetaData::setInt32(uint32_t key, int32_t value) {
return setData(key, TYPE_INT32, &value, sizeof(value));
}
bool MetaData::setFloat(uint32_t key, float value) {
return setData(key, TYPE_FLOAT, &value, sizeof(value));
}
bool MetaData::setPointer(uint32_t key, void *value) {
return setData(key, TYPE_POINTER, &value, sizeof(value));
}
bool MetaData::findCString(uint32_t key, const char **value) {
uint32_t type;
const void *data;
size_t size;
if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
return false;
}
*value = (const char *)data;
return true;
}
bool MetaData::findInt32(uint32_t key, int32_t *value) {
uint32_t type;
const void *data;
size_t size;
if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
return false;
}
assert(size == sizeof(*value));
*value = *(int32_t *)data;
return true;
}
bool MetaData::findFloat(uint32_t key, float *value) {
uint32_t type;
const void *data;
size_t size;
if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
return false;
}
assert(size == sizeof(*value));
*value = *(float *)data;
return true;
}
bool MetaData::findPointer(uint32_t key, void **value) {
uint32_t type;
const void *data;
size_t size;
if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
return false;
}
assert(size == sizeof(*value));
*value = *(void **)data;
return true;
}
bool MetaData::setData(
uint32_t key, uint32_t type, const void *data, size_t size) {
bool overwrote_existing = true;
ssize_t i = mItems.indexOfKey(key);
if (i < 0) {
typed_data item;
i = mItems.add(key, item);
overwrote_existing = false;
}
typed_data &item = mItems.editValueAt(i);
item.setData(type, data, size);
return overwrote_existing;
}
bool MetaData::findData(uint32_t key, uint32_t *type,
const void **data, size_t *size) const {
ssize_t i = mItems.indexOfKey(key);
if (i < 0) {
return false;
}
const typed_data &item = mItems.valueAt(i);
item.getData(type, data, size);
return true;
}
MetaData::typed_data::typed_data()
: mType(0),
mSize(0) {
}
MetaData::typed_data::~typed_data() {
clear();
}
MetaData::typed_data::typed_data(const typed_data &from)
: mType(from.mType),
mSize(0) {
allocateStorage(from.mSize);
memcpy(storage(), from.storage(), mSize);
}
MetaData::typed_data &MetaData::typed_data::operator=(
const MetaData::typed_data &from) {
if (this != &from) {
clear();
mType = from.mType;
allocateStorage(from.mSize);
memcpy(storage(), from.storage(), mSize);
}
return *this;
}
void MetaData::typed_data::clear() {
freeStorage();
mType = 0;
}
void MetaData::typed_data::setData(
uint32_t type, const void *data, size_t size) {
clear();
mType = type;
allocateStorage(size);
memcpy(storage(), data, size);
}
void MetaData::typed_data::getData(
uint32_t *type, const void **data, size_t *size) const {
*type = mType;
*size = mSize;
*data = storage();
}
void MetaData::typed_data::allocateStorage(size_t size) {
mSize = size;
if (usesReservoir()) {
return;
}
u.ext_data = malloc(mSize);
}
void MetaData::typed_data::freeStorage() {
if (!usesReservoir()) {
if (u.ext_data) {
free(u.ext_data);
}
}
mSize = 0;
}
} // namespace android

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2009 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 "MmapSource"
#include <utils/Log.h>
#include <sys/mman.h>
#undef NDEBUG
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <media/stagefright/MmapSource.h>
namespace android {
MmapSource::MmapSource(const char *filename)
: mFd(open(filename, O_RDONLY)),
mBase(NULL),
mSize(0) {
LOGV("MmapSource '%s'", filename);
assert(mFd >= 0);
off_t size = lseek(mFd, 0, SEEK_END);
mSize = (size_t)size;
mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
if (mBase == (void *)-1) {
mBase = NULL;
close(mFd);
mFd = -1;
}
}
MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
: mFd(fd),
mBase(NULL),
mSize(length) {
LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
assert(fd >= 0);
mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
if (mBase == (void *)-1) {
mBase = NULL;
close(mFd);
mFd = -1;
}
}
MmapSource::~MmapSource() {
if (mFd != -1) {
munmap(mBase, mSize);
mBase = NULL;
mSize = 0;
close(mFd);
mFd = -1;
}
}
status_t MmapSource::InitCheck() const {
return mFd == -1 ? NO_INIT : OK;
}
ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) {
LOGV("read_at offset:%ld data:%p size:%d", offset, data, size);
assert(offset >= 0);
size_t avail = 0;
if (offset >= 0 && offset < (off_t)mSize) {
avail = mSize - offset;
}
if (size > avail) {
size = avail;
}
memcpy(data, (const uint8_t *)mBase + offset, size);
return (ssize_t)size;
}
status_t MmapSource::getSize(off_t *size) {
*size = mSize;
return OK;
}
} // namespace android

View File

@@ -0,0 +1,369 @@
/*
* Copyright (C) 2009 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 "OMXClient"
#include <utils/Log.h>
#include <sys/socket.h>
#undef NDEBUG
#include <assert.h>
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
#include <media/IOMX.h>
#include <media/stagefright/OMXClient.h>
namespace android {
OMXClient::OMXClient()
: mSock(-1) {
}
OMXClient::~OMXClient() {
disconnect();
}
status_t OMXClient::connect() {
Mutex::Autolock autoLock(mLock);
if (mSock >= 0) {
return UNKNOWN_ERROR;
}
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
assert(service.get() != NULL);
mOMX = service->createOMX();
assert(mOMX.get() != NULL);
#if IOMX_USES_SOCKETS
status_t result = mOMX->connect(&mSock);
if (result != OK) {
mSock = -1;
mOMX = NULL;
return result;
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
assert(err == 0);
pthread_attr_destroy(&attr);
#else
mReflector = new OMXClientReflector(this);
#endif
return OK;
}
void OMXClient::disconnect() {
{
Mutex::Autolock autoLock(mLock);
if (mSock < 0) {
return;
}
assert(mObservers.isEmpty());
}
#if IOMX_USES_SOCKETS
omx_message msg;
msg.type = omx_message::DISCONNECT;
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
void *dummy;
pthread_join(mThread, &dummy);
#else
mReflector->reset();
mReflector.clear();
#endif
}
#if IOMX_USES_SOCKETS
// static
void *OMXClient::ThreadWrapper(void *me) {
((OMXClient *)me)->threadEntry();
return NULL;
}
void OMXClient::threadEntry() {
bool done = false;
while (!done) {
omx_message msg;
ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
if (n <= 0) {
break;
}
done = onOMXMessage(msg);
}
Mutex::Autolock autoLock(mLock);
close(mSock);
mSock = -1;
}
#endif
status_t OMXClient::fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer) {
#if !IOMX_USES_SOCKETS
mOMX->fill_buffer(node, buffer);
#else
if (mSock < 0) {
return UNKNOWN_ERROR;
}
omx_message msg;
msg.type = omx_message::FILL_BUFFER;
msg.u.buffer_data.node = node;
msg.u.buffer_data.buffer = buffer;
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OK;
}
status_t OMXClient::emptyBuffer(
IOMX::node_id node, IOMX::buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) {
#if !IOMX_USES_SOCKETS
mOMX->empty_buffer(
node, buffer, range_offset, range_length, flags, timestamp);
#else
if (mSock < 0) {
return UNKNOWN_ERROR;
}
// XXX I don't like all this copying...
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER;
msg.u.extended_buffer_data.node = node;
msg.u.extended_buffer_data.buffer = buffer;
msg.u.extended_buffer_data.range_offset = range_offset;
msg.u.extended_buffer_data.range_length = range_length;
msg.u.extended_buffer_data.flags = flags;
msg.u.extended_buffer_data.timestamp = timestamp;
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OK;
}
status_t OMXClient::send_command(
IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
#if !IOMX_USES_SOCKETS
return mOMX->send_command(node, cmd, param);
#else
if (mSock < 0) {
return UNKNOWN_ERROR;
}
omx_message msg;
msg.type = omx_message::SEND_COMMAND;
msg.u.send_command_data.node = node;
msg.u.send_command_data.cmd = cmd;
msg.u.send_command_data.param = param;
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OK;
}
status_t OMXClient::registerObserver(
IOMX::node_id node, OMXObserver *observer) {
Mutex::Autolock autoLock(&mLock);
ssize_t index = mObservers.indexOfKey(node);
if (index >= 0) {
return UNKNOWN_ERROR;
}
mObservers.add(node, observer);
observer->start();
#if !IOMX_USES_SOCKETS
mOMX->observe_node(node, mReflector);
#endif
return OK;
}
void OMXClient::unregisterObserver(IOMX::node_id node) {
Mutex::Autolock autoLock(mLock);
ssize_t index = mObservers.indexOfKey(node);
assert(index >= 0);
if (index < 0) {
return;
}
OMXObserver *observer = mObservers.valueAt(index);
observer->stop();
mObservers.removeItemsAt(index);
}
bool OMXClient::onOMXMessage(const omx_message &msg) {
bool done = false;
switch (msg.type) {
case omx_message::EVENT:
{
LOGV("OnEvent node:%p event:%d data1:%ld data2:%ld",
msg.u.event_data.node,
msg.u.event_data.event,
msg.u.event_data.data1,
msg.u.event_data.data2);
break;
}
case omx_message::FILL_BUFFER_DONE:
{
LOGV("FillBufferDone %p", msg.u.extended_buffer_data.buffer);
break;
}
case omx_message::EMPTY_BUFFER_DONE:
{
LOGV("EmptyBufferDone %p", msg.u.buffer_data.buffer);
break;
}
#if IOMX_USES_SOCKETS
case omx_message::DISCONNECTED:
{
LOGV("Disconnected");
done = true;
break;
}
#endif
default:
LOGE("received unknown omx_message type %d", msg.type);
break;
}
Mutex::Autolock autoLock(mLock);
ssize_t index = mObservers.indexOfKey(msg.u.buffer_data.node);
if (index >= 0) {
mObservers.editValueAt(index)->postMessage(msg);
}
return done;
}
////////////////////////////////////////////////////////////////////////////////
OMXObserver::OMXObserver() {
}
OMXObserver::~OMXObserver() {
}
void OMXObserver::start() {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
assert(err == 0);
pthread_attr_destroy(&attr);
}
void OMXObserver::stop() {
omx_message msg;
msg.type = omx_message::QUIT_OBSERVER;
postMessage(msg);
void *dummy;
pthread_join(mThread, &dummy);
}
void OMXObserver::postMessage(const omx_message &msg) {
Mutex::Autolock autoLock(mLock);
mQueue.push_back(msg);
mQueueNotEmpty.signal();
}
// static
void *OMXObserver::ThreadWrapper(void *me) {
static_cast<OMXObserver *>(me)->threadEntry();
return NULL;
}
void OMXObserver::threadEntry() {
for (;;) {
omx_message msg;
{
Mutex::Autolock autoLock(mLock);
while (mQueue.empty()) {
mQueueNotEmpty.wait(mLock);
}
msg = *mQueue.begin();
mQueue.erase(mQueue.begin());
}
if (msg.type == omx_message::QUIT_OBSERVER) {
break;
}
onOMXMessage(msg);
}
}
////////////////////////////////////////////////////////////////////////////////
OMXClientReflector::OMXClientReflector(OMXClient *client)
: mClient(client) {
}
void OMXClientReflector::on_message(const omx_message &msg) {
if (mClient != NULL) {
mClient->onOMXMessage(msg);
}
}
void OMXClientReflector::reset() {
mClient = NULL;
}
} // namespace android

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2009 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.
*/
#undef NDEBUG
#include <assert.h>
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryHeapPmem.h>
#include <media/stagefright/QComHardwareRenderer.h>
#include <ui/ISurface.h>
namespace android {
////////////////////////////////////////////////////////////////////////////////
typedef struct PLATFORM_PRIVATE_ENTRY
{
/* Entry type */
uint32_t type;
/* Pointer to platform specific entry */
void *entry;
} PLATFORM_PRIVATE_ENTRY;
typedef struct PLATFORM_PRIVATE_LIST
{
/* Number of entries */
uint32_t nEntries;
/* Pointer to array of platform specific entries *
* Contiguous block of PLATFORM_PRIVATE_ENTRY elements */
PLATFORM_PRIVATE_ENTRY *entryList;
} PLATFORM_PRIVATE_LIST;
// data structures for tunneling buffers
typedef struct PLATFORM_PRIVATE_PMEM_INFO
{
/* pmem file descriptor */
uint32_t pmem_fd;
uint32_t offset;
} PLATFORM_PRIVATE_PMEM_INFO;
#define PLATFORM_PRIVATE_PMEM 1
QComHardwareRenderer::QComHardwareRenderer(
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight)
: mISurface(surface),
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
mDecodedWidth(decodedWidth),
mDecodedHeight(decodedHeight),
mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
assert(mISurface.get() != NULL);
assert(mDecodedWidth > 0);
assert(mDecodedHeight > 0);
}
QComHardwareRenderer::~QComHardwareRenderer() {
mISurface->unregisterBuffers();
}
void QComHardwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
size_t offset;
if (!getOffset(platformPrivate, &offset)) {
return;
}
mISurface->postBuffer(offset);
}
bool QComHardwareRenderer::getOffset(void *platformPrivate, size_t *offset) {
*offset = 0;
PLATFORM_PRIVATE_LIST *list = (PLATFORM_PRIVATE_LIST *)platformPrivate;
for (uint32_t i = 0; i < list->nEntries; ++i) {
if (list->entryList[i].type != PLATFORM_PRIVATE_PMEM) {
continue;
}
PLATFORM_PRIVATE_PMEM_INFO *info =
(PLATFORM_PRIVATE_PMEM_INFO *)list->entryList[i].entry;
if (info != NULL) {
if (mMemoryHeap.get() == NULL) {
publishBuffers(info->pmem_fd);
}
if (mMemoryHeap.get() == NULL) {
return false;
}
*offset = info->offset;
return true;
}
}
return false;
}
void QComHardwareRenderer::publishBuffers(uint32_t pmem_fd) {
sp<MemoryHeapBase> master =
reinterpret_cast<MemoryHeapBase *>(pmem_fd);
master->setDevice("/dev/pmem");
mMemoryHeap = new MemoryHeapPmem(master, 0);
mMemoryHeap->slap();
ISurface::BufferHeap bufferHeap(
mDisplayWidth, mDisplayHeight,
mDecodedWidth, mDecodedHeight,
PIXEL_FORMAT_YCbCr_420_SP,
mMemoryHeap);
status_t err = mISurface->registerBuffers(bufferHeap);
assert(err == OK);
}
} // namespace android

View File

@@ -0,0 +1,598 @@
/*
* Copyright (C) 2009 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 "SampleTable"
#include <utils/Log.h>
#include <arpa/inet.h>
#include <assert.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
namespace android {
static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
SampleTable::SampleTable(DataSource *source)
: mDataSource(source),
mChunkOffsetOffset(-1),
mChunkOffsetType(0),
mNumChunkOffsets(0),
mSampleToChunkOffset(-1),
mNumSampleToChunkOffsets(0),
mSampleSizeOffset(-1),
mSampleSizeFieldSize(0),
mDefaultSampleSize(0),
mNumSampleSizes(0),
mTimeToSampleCount(0),
mTimeToSample(NULL),
mSyncSampleOffset(-1),
mNumSyncSamples(0) {
}
SampleTable::~SampleTable() {
delete[] mTimeToSample;
mTimeToSample = NULL;
}
status_t SampleTable::setChunkOffsetParams(
uint32_t type, off_t data_offset, off_t data_size) {
if (mChunkOffsetOffset >= 0) {
return ERROR_MALFORMED;
}
assert(type == kChunkOffsetType32 || type == kChunkOffsetType64);
mChunkOffsetOffset = data_offset;
mChunkOffsetType = type;
if (data_size < 8) {
return ERROR_MALFORMED;
}
uint8_t header[8];
if (mDataSource->read_at(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
if (U32_AT(header) != 0) {
// Expected version = 0, flags = 0.
return ERROR_MALFORMED;
}
mNumChunkOffsets = U32_AT(&header[4]);
if (mChunkOffsetType == kChunkOffsetType32) {
if (data_size < 8 + mNumChunkOffsets * 4) {
return ERROR_MALFORMED;
}
} else {
if (data_size < 8 + mNumChunkOffsets * 8) {
return ERROR_MALFORMED;
}
}
return OK;
}
status_t SampleTable::setSampleToChunkParams(
off_t data_offset, off_t data_size) {
if (mSampleToChunkOffset >= 0) {
return ERROR_MALFORMED;
}
mSampleToChunkOffset = data_offset;
if (data_size < 8) {
return ERROR_MALFORMED;
}
uint8_t header[8];
if (mDataSource->read_at(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
if (U32_AT(header) != 0) {
// Expected version = 0, flags = 0.
return ERROR_MALFORMED;
}
mNumSampleToChunkOffsets = U32_AT(&header[4]);
if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
return ERROR_MALFORMED;
}
return OK;
}
status_t SampleTable::setSampleSizeParams(
uint32_t type, off_t data_offset, off_t data_size) {
if (mSampleSizeOffset >= 0) {
return ERROR_MALFORMED;
}
assert(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
mSampleSizeOffset = data_offset;
if (data_size < 12) {
return ERROR_MALFORMED;
}
uint8_t header[12];
if (mDataSource->read_at(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
if (U32_AT(header) != 0) {
// Expected version = 0, flags = 0.
return ERROR_MALFORMED;
}
mDefaultSampleSize = U32_AT(&header[4]);
mNumSampleSizes = U32_AT(&header[8]);
if (type == kSampleSizeType32) {
mSampleSizeFieldSize = 32;
if (mDefaultSampleSize != 0) {
return OK;
}
if (data_size < 12 + mNumSampleSizes * 4) {
return ERROR_MALFORMED;
}
} else {
if ((mDefaultSampleSize & 0xffffff00) != 0) {
// The high 24 bits are reserved and must be 0.
return ERROR_MALFORMED;
}
mSampleSizeFieldSize = mDefaultSampleSize & 0xf;
mDefaultSampleSize = 0;
if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
&& mSampleSizeFieldSize != 16) {
return ERROR_MALFORMED;
}
if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
return ERROR_MALFORMED;
}
}
return OK;
}
status_t SampleTable::setTimeToSampleParams(
off_t data_offset, off_t data_size) {
if (mTimeToSample != NULL || data_size < 8) {
return ERROR_MALFORMED;
}
uint8_t header[8];
if (mDataSource->read_at(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
if (U32_AT(header) != 0) {
// Expected version = 0, flags = 0.
return ERROR_MALFORMED;
}
mTimeToSampleCount = U32_AT(&header[4]);
mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
if (mDataSource->read_at(
data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
return ERROR_IO;
}
for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
mTimeToSample[i] = ntohl(mTimeToSample[i]);
}
return OK;
}
status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
if (mSyncSampleOffset >= 0 || data_size < 8) {
return ERROR_MALFORMED;
}
mSyncSampleOffset = data_offset;
uint8_t header[8];
if (mDataSource->read_at(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
if (U32_AT(header) != 0) {
// Expected version = 0, flags = 0.
return ERROR_MALFORMED;
}
mNumSyncSamples = U32_AT(&header[4]);
if (mNumSyncSamples < 2) {
LOGW("Table of sync samples is empty or has only a single entry!");
}
return OK;
}
uint32_t SampleTable::countChunkOffsets() const {
return mNumChunkOffsets;
}
status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
*offset = 0;
if (mChunkOffsetOffset < 0) {
return ERROR_MALFORMED;
}
if (chunk_index >= mNumChunkOffsets) {
return ERROR_OUT_OF_RANGE;
}
if (mChunkOffsetType == kChunkOffsetType32) {
uint32_t offset32;
if (mDataSource->read_at(
mChunkOffsetOffset + 8 + 4 * chunk_index,
&offset32,
sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
return ERROR_IO;
}
*offset = ntohl(offset32);
} else {
assert(mChunkOffsetOffset == kChunkOffsetType64);
uint64_t offset64;
if (mDataSource->read_at(
mChunkOffsetOffset + 8 + 8 * chunk_index,
&offset64,
sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
return ERROR_IO;
}
*offset = ntoh64(offset64);
}
return OK;
}
status_t SampleTable::getChunkForSample(
uint32_t sample_index,
uint32_t *chunk_index,
uint32_t *chunk_relative_sample_index,
uint32_t *desc_index) {
*chunk_index = 0;
*chunk_relative_sample_index = 0;
*desc_index = 0;
if (mSampleToChunkOffset < 0) {
return ERROR_MALFORMED;
}
if (sample_index >= countSamples()) {
return ERROR_END_OF_STREAM;
}
uint32_t first_chunk = 0;
uint32_t samples_per_chunk = 0;
uint32_t chunk_desc_index = 0;
uint32_t index = 0;
while (index < mNumSampleToChunkOffsets) {
uint8_t buffer[12];
if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12,
buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint32_t stop_chunk = U32_AT(buffer);
if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
break;
}
sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
first_chunk = stop_chunk;
samples_per_chunk = U32_AT(&buffer[4]);
chunk_desc_index = U32_AT(&buffer[8]);
++index;
}
*chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
*chunk_relative_sample_index = sample_index % samples_per_chunk;
*desc_index = chunk_desc_index;
return OK;
}
uint32_t SampleTable::countSamples() const {
return mNumSampleSizes;
}
status_t SampleTable::getSampleSize(
uint32_t sample_index, size_t *sample_size) {
*sample_size = 0;
if (mSampleSizeOffset < 0) {
return ERROR_MALFORMED;
}
if (sample_index >= mNumSampleSizes) {
return ERROR_OUT_OF_RANGE;
}
if (mDefaultSampleSize > 0) {
*sample_size = mDefaultSampleSize;
return OK;
}
switch (mSampleSizeFieldSize) {
case 32:
{
if (mDataSource->read_at(
mSampleSizeOffset + 12 + 4 * sample_index,
sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
return ERROR_IO;
}
*sample_size = ntohl(*sample_size);
break;
}
case 16:
{
uint16_t x;
if (mDataSource->read_at(
mSampleSizeOffset + 12 + 2 * sample_index,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
}
*sample_size = ntohs(x);
break;
}
case 8:
{
uint8_t x;
if (mDataSource->read_at(
mSampleSizeOffset + 12 + sample_index,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
}
*sample_size = x;
break;
}
default:
{
assert(mSampleSizeFieldSize == 4);
uint8_t x;
if (mDataSource->read_at(
mSampleSizeOffset + 12 + sample_index / 2,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
}
*sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
break;
}
}
return OK;
}
status_t SampleTable::getSampleOffsetAndSize(
uint32_t sample_index, off_t *offset, size_t *size) {
Mutex::Autolock autoLock(mLock);
*offset = 0;
*size = 0;
uint32_t chunk_index;
uint32_t chunk_relative_sample_index;
uint32_t desc_index;
status_t err = getChunkForSample(
sample_index, &chunk_index, &chunk_relative_sample_index,
&desc_index);
if (err != OK) {
return err;
}
err = getChunkOffset(chunk_index, offset);
if (err != OK) {
return err;
}
for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
size_t sample_size;
err = getSampleSize(sample_index - j - 1, &sample_size);
if (err != OK) {
return err;
}
*offset += sample_size;
}
err = getSampleSize(sample_index, size);
if (err != OK) {
return err;
}
return OK;
}
status_t SampleTable::getMaxSampleSize(size_t *max_size) {
Mutex::Autolock autoLock(mLock);
*max_size = 0;
for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
size_t sample_size;
status_t err = getSampleSize(i, &sample_size);
if (err != OK) {
return err;
}
if (sample_size > *max_size) {
*max_size = sample_size;
}
}
return OK;
}
status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
// XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
Mutex::Autolock autoLock(mLock);
if (sample_index >= mNumSampleSizes) {
return ERROR_OUT_OF_RANGE;
}
uint32_t cur_sample = 0;
*time = 0;
for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
uint32_t n = mTimeToSample[2 * i];
uint32_t delta = mTimeToSample[2 * i + 1];
if (sample_index < cur_sample + n) {
*time += delta * (sample_index - cur_sample);
return OK;
}
*time += delta * n;
cur_sample += n;
}
return ERROR_OUT_OF_RANGE;
}
status_t SampleTable::findClosestSample(
uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
Mutex::Autolock autoLock(mLock);
uint32_t cur_sample = 0;
uint32_t time = 0;
for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
uint32_t n = mTimeToSample[2 * i];
uint32_t delta = mTimeToSample[2 * i + 1];
if (req_time < time + n * delta) {
int j = (req_time - time) / delta;
*sample_index = cur_sample + j;
if (flags & kSyncSample_Flag) {
return findClosestSyncSample(*sample_index, sample_index);
}
return OK;
}
time += delta * n;
cur_sample += n;
}
return ERROR_OUT_OF_RANGE;
}
status_t SampleTable::findClosestSyncSample(
uint32_t start_sample_index, uint32_t *sample_index) {
*sample_index = 0;
if (mSyncSampleOffset < 0) {
// All samples are sync-samples.
*sample_index = start_sample_index;
return OK;
}
uint32_t x;
uint32_t left = 0;
uint32_t right = mNumSyncSamples;
while (left < right) {
uint32_t mid = (left + right) / 2;
if (mDataSource->read_at(
mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
return ERROR_IO;
}
x = ntohl(x);
if (x < (start_sample_index + 1)) {
left = mid + 1;
} else if (x > (start_sample_index + 1)) {
right = mid;
} else {
break;
}
}
#if 1
// Make sure we return a sample at or _after_ the requested one.
// Seeking to a particular time in a media source containing audio and
// video will most likely be able to sync fairly close to the requested
// time in the audio track but may only be able to seek a fair distance
// from the requested time in the video track.
// If we seek the video track to a time earlier than the audio track,
// we'll cause the video track to be late for quite a while, the decoder
// trying to catch up.
// If we seek the video track to a time later than the audio track,
// audio will start playing fine while no video will be output for a
// while, the video decoder will not stress the system.
if (mDataSource->read_at(
mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) {
return ERROR_IO;
}
x = ntohl(x);
assert((x - 1) >= start_sample_index);
#endif
*sample_index = x - 1;
return OK;
}
} // namespace android

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2009 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 <stdlib.h>
#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/ShoutcastSource.h>
#include <media/stagefright/string.h>
namespace android {
ShoutcastSource::ShoutcastSource(HTTPStream *http)
: mHttp(http),
mMetaDataOffset(0),
mBytesUntilMetaData(0),
mGroup(NULL),
mStarted(false) {
string metaint;
if (mHttp->find_header_value("icy-metaint", &metaint)) {
char *end;
const char *start = metaint.c_str();
mMetaDataOffset = strtol(start, &end, 10);
assert(end > start && *end == '\0');
assert(mMetaDataOffset > 0);
mBytesUntilMetaData = mMetaDataOffset;
}
}
ShoutcastSource::~ShoutcastSource() {
if (mStarted) {
stop();
}
delete mHttp;
mHttp = NULL;
}
status_t ShoutcastSource::start(MetaData *) {
assert(!mStarted);
mGroup = new MediaBufferGroup;
mGroup->add_buffer(new MediaBuffer(4096)); // XXX
mStarted = true;
return OK;
}
status_t ShoutcastSource::stop() {
assert(mStarted);
delete mGroup;
mGroup = NULL;
mStarted = false;
return OK;
}
sp<MetaData> ShoutcastSource::getFormat() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, "audio/mpeg");
meta->setInt32(kKeySampleRate, 44100);
meta->setInt32(kKeyChannelCount, 2); // XXX
return meta;
}
status_t ShoutcastSource::read(
MediaBuffer **out, const ReadOptions *options) {
assert(mStarted);
*out = NULL;
int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) {
return ERROR_UNSUPPORTED;
}
MediaBuffer *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
}
*out = buffer;
size_t num_bytes = buffer->size();
if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) {
num_bytes = mBytesUntilMetaData;
}
ssize_t n = mHttp->receive(buffer->data(), num_bytes);
if (n <= 0) {
return (status_t)n;
}
buffer->set_range(0, n);
mBytesUntilMetaData -= (size_t)n;
if (mBytesUntilMetaData == 0) {
unsigned char num_16_byte_blocks = 0;
n = mHttp->receive((char *)&num_16_byte_blocks, 1);
assert(n == 1);
char meta[255 * 16];
size_t meta_size = num_16_byte_blocks * 16;
size_t meta_length = 0;
while (meta_length < meta_size) {
n = mHttp->receive(&meta[meta_length], meta_size - meta_length);
if (n <= 0) {
return (status_t)n;
}
meta_length += (size_t) n;
}
while (meta_length > 0 && meta[meta_length - 1] == '\0') {
--meta_length;
}
if (meta_length > 0) {
// Technically we should probably attach this meta data to the
// next buffer. XXX
buffer->meta_data()->setData('shou', 'shou', meta, meta_length);
}
mBytesUntilMetaData = mMetaDataOffset;
}
return OK;
}
} // namespace android

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2009 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 "SoftwareRenderer"
#include <utils/Log.h>
#undef NDEBUG
#include <assert.h>
#include <binder/MemoryHeapBase.h>
#include <media/stagefright/SoftwareRenderer.h>
#include <ui/ISurface.h>
namespace android {
#define QCOM_YUV 0
SoftwareRenderer::SoftwareRenderer(
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight)
: mISurface(surface),
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
mDecodedWidth(decodedWidth),
mDecodedHeight(decodedHeight),
mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
mIndex(0) {
assert(mISurface.get() != NULL);
assert(mDecodedWidth > 0);
assert(mDecodedHeight > 0);
assert(mMemoryHeap->heapID() >= 0);
ISurface::BufferHeap bufferHeap(
mDisplayWidth, mDisplayHeight,
mDecodedWidth, mDecodedHeight,
PIXEL_FORMAT_RGB_565,
mMemoryHeap);
status_t err = mISurface->registerBuffers(bufferHeap);
assert(err == OK);
}
SoftwareRenderer::~SoftwareRenderer() {
mISurface->unregisterBuffers();
}
void SoftwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
assert(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
static const signed kClipMin = -278;
static const signed kClipMax = 535;
static uint8_t kClip[kClipMax - kClipMin + 1];
static uint8_t *kAdjustedClip = &kClip[-kClipMin];
static bool clipInitialized = false;
if (!clipInitialized) {
for (signed i = kClipMin; i <= kClipMax; ++i) {
kClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
}
clipInitialized = true;
}
size_t offset = mIndex * mFrameSize;
void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
uint32_t *dst_ptr = (uint32_t *)dst;
const uint8_t *src_y = (const uint8_t *)data;
const uint8_t *src_u =
(const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
#if !QCOM_YUV
const uint8_t *src_v =
(const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
#endif
for (size_t y = 0; y < mDecodedHeight; ++y) {
for (size_t x = 0; x < mDecodedWidth; x += 2) {
// B = 1.164 * (Y - 16) + 2.018 * (U - 128)
// G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
// R = 1.164 * (Y - 16) + 1.596 * (V - 128)
// B = 298/256 * (Y - 16) + 517/256 * (U - 128)
// G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
// R = .................. + 409/256 * (V - 128)
// min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
// min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
// min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
// max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
// max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
// max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
// clip range -278 .. 535
signed y1 = (signed)src_y[x] - 16;
signed y2 = (signed)src_y[x + 1] - 16;
#if QCOM_YUV
signed u = (signed)src_u[x & ~1] - 128;
signed v = (signed)src_u[(x & ~1) + 1] - 128;
#else
signed u = (signed)src_u[x / 2] - 128;
signed v = (signed)src_v[x / 2] - 128;
#endif
signed u_b = u * 517;
signed u_g = -u * 100;
signed v_g = -v * 208;
signed v_r = v * 409;
signed tmp1 = y1 * 298;
signed b1 = (tmp1 + u_b) / 256;
signed g1 = (tmp1 + v_g + u_g) / 256;
signed r1 = (tmp1 + v_r) / 256;
signed tmp2 = y2 * 298;
signed b2 = (tmp2 + u_b) / 256;
signed g2 = (tmp2 + v_g + u_g) / 256;
signed r2 = (tmp2 + v_r) / 256;
uint32_t rgb1 =
((kAdjustedClip[r1] >> 3) << 11)
| ((kAdjustedClip[g1] >> 2) << 5)
| (kAdjustedClip[b1] >> 3);
uint32_t rgb2 =
((kAdjustedClip[r2] >> 3) << 11)
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
}
src_y += mDecodedWidth;
if (y & 1) {
#if QCOM_YUV
src_u += mDecodedWidth;
#else
src_u += mDecodedWidth / 2;
src_v += mDecodedWidth / 2;
#endif
}
dst_ptr += mDecodedWidth / 2;
}
mISurface->postBuffer(offset);
mIndex = 1 - mIndex;
}
} // namespace android

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2009 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 "SurfaceRenderer"
#include <utils/Log.h>
#undef NDEBUG
#include <assert.h>
#include <media/stagefright/SurfaceRenderer.h>
#include <ui/Surface.h>
namespace android {
SurfaceRenderer::SurfaceRenderer(
const sp<Surface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight)
: mSurface(surface),
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
mDecodedWidth(decodedWidth),
mDecodedHeight(decodedHeight) {
}
SurfaceRenderer::~SurfaceRenderer() {
}
void SurfaceRenderer::render(
const void *data, size_t size, void *platformPrivate) {
Surface::SurfaceInfo info;
status_t err = mSurface->lock(&info);
if (err != OK) {
return;
}
const uint8_t *src = (const uint8_t *)data;
uint8_t *dst = (uint8_t *)info.bits;
for (size_t i = 0; i < mDisplayHeight; ++i) {
memcpy(dst, src, mDisplayWidth);
src += mDecodedWidth;
dst += mDisplayWidth;
}
src += (mDecodedHeight - mDisplayHeight) * mDecodedWidth;
for (size_t i = 0; i < (mDisplayHeight + 1) / 2; ++i) {
memcpy(dst, src, (mDisplayWidth + 1) & ~1);
src += (mDecodedWidth + 1) & ~1;
dst += (mDisplayWidth + 1) & ~1;
}
mSurface->unlockAndPost();
}
} // namespace android

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2009 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 <sys/time.h>
#include <media/stagefright/TimeSource.h>
namespace android {
SystemTimeSource::SystemTimeSource()
: mStartTimeUs(GetSystemTimeUs()) {
}
int64_t SystemTimeSource::getRealTimeUs() {
return GetSystemTimeUs() - mStartTimeUs;
}
// static
int64_t SystemTimeSource::GetSystemTimeUs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}
} // namespace android

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2009 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.
*/
#undef __STRICT_ANSI__
#define __STDINT_LIMITS
#include <stdint.h>
#define LOG_TAG "TimedEventQueue"
#include <utils/Log.h>
#include <sys/time.h>
#undef NDEBUG
#include <assert.h>
#include <media/stagefright/TimedEventQueue.h>
namespace android {
TimedEventQueue::TimedEventQueue()
: mRunning(false),
mStopped(false) {
}
TimedEventQueue::~TimedEventQueue() {
stop();
}
void TimedEventQueue::start() {
if (mRunning) {
return;
}
mStopped = false;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
mRunning = true;
}
void TimedEventQueue::stop(bool flush) {
if (!mRunning) {
return;
}
if (flush) {
postEventToBack(new StopEvent);
} else {
postTimedEvent(new StopEvent, INT64_MIN);
}
void *dummy;
pthread_join(mThread, &dummy);
mQueue.clear();
mRunning = false;
}
void TimedEventQueue::postEvent(const sp<Event> &event) {
// Reserve an earlier timeslot an INT64_MIN to be able to post
// the StopEvent to the absolute head of the queue.
postTimedEvent(event, INT64_MIN + 1);
}
void TimedEventQueue::postEventToBack(const sp<Event> &event) {
postTimedEvent(event, INT64_MAX);
}
void TimedEventQueue::postEventWithDelay(
const sp<Event> &event, int64_t delay_us) {
assert(delay_us >= 0);
postTimedEvent(event, getRealTimeUs() + delay_us);
}
void TimedEventQueue::postTimedEvent(
const sp<Event> &event, int64_t realtime_us) {
Mutex::Autolock autoLock(mLock);
List<QueueItem>::iterator it = mQueue.begin();
while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
++it;
}
QueueItem item;
item.event = event;
item.realtime_us = realtime_us;
if (it == mQueue.begin()) {
mQueueHeadChangedCondition.signal();
}
mQueue.insert(it, item);
mQueueNotEmptyCondition.signal();
}
bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
Mutex::Autolock autoLock(mLock);
List<QueueItem>::iterator it = mQueue.begin();
while (it != mQueue.end() && (*it).event != event) {
++it;
}
if (it == mQueue.end()) {
return false;
}
if (it == mQueue.begin()) {
mQueueHeadChangedCondition.signal();
}
mQueue.erase(it);
return true;
}
// static
int64_t TimedEventQueue::getRealTimeUs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}
// static
void *TimedEventQueue::ThreadWrapper(void *me) {
static_cast<TimedEventQueue *>(me)->threadEntry();
return NULL;
}
void TimedEventQueue::threadEntry() {
for (;;) {
int64_t now_us;
sp<Event> event;
{
Mutex::Autolock autoLock(mLock);
if (mStopped) {
break;
}
while (mQueue.empty()) {
mQueueNotEmptyCondition.wait(mLock);
}
List<QueueItem>::iterator it;
for (;;) {
it = mQueue.begin();
now_us = getRealTimeUs();
int64_t when_us = (*it).realtime_us;
int64_t delay_us;
if (when_us < 0 || when_us == INT64_MAX) {
delay_us = 0;
} else {
delay_us = when_us - now_us;
}
if (delay_us <= 0) {
break;
}
status_t err = mQueueHeadChangedCondition.waitRelative(
mLock, delay_us * 1000);
if (err == -ETIMEDOUT) {
now_us = getRealTimeUs();
break;
}
}
event = (*it).event;
mQueue.erase(it);
}
// Fire event with the lock NOT held.
event->fire(this, now_us);
}
}
} // namespace android

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2009 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 <media/stagefright/Utils.h>
namespace android {
uint16_t U16_AT(const uint8_t *ptr) {
return ptr[0] << 8 | ptr[1];
}
uint32_t U32_AT(const uint8_t *ptr) {
return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
}
uint64_t U64_AT(const uint8_t *ptr) {
return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
}
// XXX warning: these won't work on big-endian host.
uint64_t ntoh64(uint64_t x) {
return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
}
uint64_t hton64(uint64_t x) {
return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
}
} // namespace android

View File

@@ -0,0 +1,27 @@
ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# Set up the OpenCore variables.
include external/opencore/Config.mk
LOCAL_C_INCLUDES := $(PV_INCLUDES)
LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
LOCAL_SRC_FILES:= \
OMX.cpp
LOCAL_SHARED_LIBRARIES := \
libbinder \
libmedia \
libutils \
libui \
libopencore_common
LOCAL_PRELINK_MODULE:= false
LOCAL_MODULE:= libstagefright_omx
include $(BUILD_SHARED_LIBRARY)
endif

View File

@@ -0,0 +1,621 @@
/*
* Copyright (C) 2009 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 "OMX"
#include <utils/Log.h>
#include <sys/socket.h>
#undef NDEBUG
#include <assert.h>
#include "OMX.h"
#include "pv_omxcore.h"
#include <binder/IMemory.h>
#include <OMX_Component.h>
namespace android {
class NodeMeta {
public:
NodeMeta(OMX *owner)
: mOwner(owner),
mHandle(NULL) {
}
OMX *owner() const {
return mOwner;
}
void setHandle(OMX_HANDLETYPE handle) {
assert(mHandle == NULL);
mHandle = handle;
}
OMX_HANDLETYPE handle() const {
return mHandle;
}
void setObserver(const sp<IOMXObserver> &observer) {
mObserver = observer;
}
sp<IOMXObserver> observer() {
return mObserver;
}
private:
OMX *mOwner;
OMX_HANDLETYPE mHandle;
sp<IOMXObserver> mObserver;
NodeMeta(const NodeMeta &);
NodeMeta &operator=(const NodeMeta &);
};
class BufferMeta {
public:
BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
: mOwner(owner),
mMem(mem),
mIsBackup(is_backup) {
}
BufferMeta(OMX *owner, size_t size)
: mOwner(owner),
mSize(size),
mIsBackup(false) {
}
void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
if (!mIsBackup) {
return;
}
memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
header->pBuffer + header->nOffset,
header->nFilledLen);
}
void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
if (!mIsBackup) {
return;
}
memcpy(header->pBuffer + header->nOffset,
(const OMX_U8 *)mMem->pointer() + header->nOffset,
header->nFilledLen);
}
private:
OMX *mOwner;
sp<IMemory> mMem;
size_t mSize;
bool mIsBackup;
BufferMeta(const BufferMeta &);
BufferMeta &operator=(const BufferMeta &);
};
// static
OMX_CALLBACKTYPE OMX::kCallbacks = {
&OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};
// static
OMX_ERRORTYPE OMX::OnEvent(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData) {
NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData);
}
// static
OMX_ERRORTYPE OMX::OnEmptyBufferDone(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
return meta->owner()->OnEmptyBufferDone(meta, pBuffer);
}
// static
OMX_ERRORTYPE OMX::OnFillBufferDone(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
return meta->owner()->OnFillBufferDone(meta, pBuffer);
}
OMX::OMX()
#if IOMX_USES_SOCKETS
: mSock(-1)
#endif
{
}
OMX::~OMX() {
#if IOMX_USES_SOCKETS
assert(mSock < 0);
#endif
}
#if IOMX_USES_SOCKETS
status_t OMX::connect(int *sd) {
Mutex::Autolock autoLock(mLock);
if (mSock >= 0) {
return UNKNOWN_ERROR;
}
int sockets[2];
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
return UNKNOWN_ERROR;
}
mSock = sockets[0];
*sd = sockets[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
assert(err == 0);
pthread_attr_destroy(&attr);
return OK;
}
// static
void *OMX::ThreadWrapper(void *me) {
((OMX *)me)->threadEntry();
return NULL;
}
void OMX::threadEntry() {
bool done = false;
while (!done) {
omx_message msg;
ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
if (n <= 0) {
break;
}
Mutex::Autolock autoLock(mLock);
switch (msg.type) {
case omx_message::FILL_BUFFER:
{
OMX_BUFFERHEADERTYPE *header =
static_cast<OMX_BUFFERHEADERTYPE *>(
msg.u.buffer_data.buffer);
header->nFilledLen = 0;
header->nOffset = 0;
header->nFlags = 0;
NodeMeta *node_meta = static_cast<NodeMeta *>(
msg.u.buffer_data.node);
LOGV("FillThisBuffer buffer=%p", header);
OMX_ERRORTYPE err =
OMX_FillThisBuffer(node_meta->handle(), header);
assert(err == OMX_ErrorNone);
break;
}
case omx_message::EMPTY_BUFFER:
{
OMX_BUFFERHEADERTYPE *header =
static_cast<OMX_BUFFERHEADERTYPE *>(
msg.u.extended_buffer_data.buffer);
header->nFilledLen = msg.u.extended_buffer_data.range_length;
header->nOffset = msg.u.extended_buffer_data.range_offset;
header->nFlags = msg.u.extended_buffer_data.flags;
header->nTimeStamp = msg.u.extended_buffer_data.timestamp;
BufferMeta *buffer_meta =
static_cast<BufferMeta *>(header->pAppPrivate);
buffer_meta->CopyToOMX(header);
NodeMeta *node_meta = static_cast<NodeMeta *>(
msg.u.extended_buffer_data.node);
LOGV("EmptyThisBuffer buffer=%p", header);
OMX_ERRORTYPE err =
OMX_EmptyThisBuffer(node_meta->handle(), header);
assert(err == OMX_ErrorNone);
break;
}
case omx_message::SEND_COMMAND:
{
NodeMeta *node_meta = static_cast<NodeMeta *>(
msg.u.send_command_data.node);
OMX_ERRORTYPE err =
OMX_SendCommand(
node_meta->handle(), msg.u.send_command_data.cmd,
msg.u.send_command_data.param, NULL);
assert(err == OMX_ErrorNone);
break;
}
case omx_message::DISCONNECT:
{
omx_message msg;
msg.type = omx_message::DISCONNECTED;
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
done = true;
break;
}
default:
LOGE("received unknown omx_message type %d", msg.type);
break;
}
}
Mutex::Autolock autoLock(mLock);
close(mSock);
mSock = -1;
}
#endif
status_t OMX::list_nodes(List<String8> *list) {
OMX_MasterInit(); // XXX Put this somewhere else.
list->clear();
OMX_U32 index = 0;
char componentName[256];
while (OMX_MasterComponentNameEnum(componentName, sizeof(componentName), index)
== OMX_ErrorNone) {
list->push_back(String8(componentName));
++index;
}
return OK;
}
status_t OMX::allocate_node(const char *name, node_id *node) {
Mutex::Autolock autoLock(mLock);
*node = 0;
OMX_MasterInit(); // XXX Put this somewhere else.
NodeMeta *meta = new NodeMeta(this);
OMX_HANDLETYPE handle;
OMX_ERRORTYPE err = OMX_MasterGetHandle(
&handle, const_cast<char *>(name), meta, &kCallbacks);
if (err != OMX_ErrorNone) {
LOGE("FAILED to allocate omx component '%s'", name);
delete meta;
meta = NULL;
return UNKNOWN_ERROR;
}
meta->setHandle(handle);
*node = meta;
return OK;
}
status_t OMX::free_node(node_id node) {
Mutex::Autolock autoLock(mLock);
NodeMeta *meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle());
delete meta;
meta = NULL;
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
status_t OMX::send_command(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
Mutex::Autolock autoLock(mLock);
#if IOMX_USES_SOCKETS
if (mSock < 0) {
return UNKNOWN_ERROR;
}
#endif
NodeMeta *meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL);
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
status_t OMX::get_parameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
Mutex::Autolock autoLock(mLock);
NodeMeta *meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params);
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
status_t OMX::set_parameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
Mutex::Autolock autoLock(mLock);
NodeMeta *meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_SetParameter(meta->handle(), index, const_cast<void *>(params));
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
status_t OMX::use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) {
Mutex::Autolock autoLock(mLock);
BufferMeta *buffer_meta = new BufferMeta(this, params);
OMX_BUFFERHEADERTYPE *header;
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta,
params->size(), static_cast<OMX_U8 *>(params->pointer()));
if (err != OMX_ErrorNone) {
LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
delete buffer_meta;
buffer_meta = NULL;
*buffer = 0;
return UNKNOWN_ERROR;
}
*buffer = header;
return OK;
}
status_t OMX::allocate_buffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) {
Mutex::Autolock autoLock(mLock);
BufferMeta *buffer_meta = new BufferMeta(this, size);
OMX_BUFFERHEADERTYPE *header;
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_AllocateBuffer(node_meta->handle(), &header, port_index,
buffer_meta, size);
if (err != OMX_ErrorNone) {
delete buffer_meta;
buffer_meta = NULL;
*buffer = 0;
return UNKNOWN_ERROR;
}
*buffer = header;
return OK;
}
status_t OMX::allocate_buffer_with_backup(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer) {
Mutex::Autolock autoLock(mLock);
BufferMeta *buffer_meta = new BufferMeta(this, params, true);
OMX_BUFFERHEADERTYPE *header;
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_AllocateBuffer(
node_meta->handle(), &header, port_index, buffer_meta,
params->size());
if (err != OMX_ErrorNone) {
delete buffer_meta;
buffer_meta = NULL;
*buffer = 0;
return UNKNOWN_ERROR;
}
*buffer = header;
return OK;
}
status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_FreeBuffer(node_meta->handle(), port_index, header);
delete buffer_meta;
buffer_meta = NULL;
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
OMX_ERRORTYPE OMX::OnEvent(
NodeMeta *meta,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData) {
LOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
omx_message msg;
msg.type = omx_message::EVENT;
msg.u.event_data.node = meta;
msg.u.event_data.event = eEvent;
msg.u.event_data.data1 = nData1;
msg.u.event_data.data2 = nData2;
#if !IOMX_USES_SOCKETS
sp<IOMXObserver> observer = meta->observer();
if (observer.get() != NULL) {
observer->on_message(msg);
}
#else
assert(mSock >= 0);
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OMX_ErrorNone;
}
OMX_ERRORTYPE OMX::OnEmptyBufferDone(
NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
LOGV("OnEmptyBufferDone buffer=%p", pBuffer);
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER_DONE;
msg.u.buffer_data.node = meta;
msg.u.buffer_data.buffer = pBuffer;
#if !IOMX_USES_SOCKETS
sp<IOMXObserver> observer = meta->observer();
if (observer.get() != NULL) {
observer->on_message(msg);
}
#else
assert(mSock >= 0);
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OMX_ErrorNone;
}
OMX_ERRORTYPE OMX::OnFillBufferDone(
NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
LOGV("OnFillBufferDone buffer=%p", pBuffer);
BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate);
buffer_meta->CopyFromOMX(pBuffer);
omx_message msg;
msg.type = omx_message::FILL_BUFFER_DONE;
msg.u.extended_buffer_data.node = meta;
msg.u.extended_buffer_data.buffer = pBuffer;
msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
msg.u.extended_buffer_data.flags = pBuffer->nFlags;
msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
#if !IOMX_USES_SOCKETS
sp<IOMXObserver> observer = meta->observer();
if (observer.get() != NULL) {
observer->on_message(msg);
}
#else
assert(mSock >= 0);
ssize_t n = send(mSock, &msg, sizeof(msg), 0);
assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
#endif
return OMX_ErrorNone;
}
#if !IOMX_USES_SOCKETS
status_t OMX::observe_node(
node_id node, const sp<IOMXObserver> &observer) {
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
node_meta->setObserver(observer);
return OK;
}
void OMX::fill_buffer(node_id node, buffer_id buffer) {
OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
header->nFilledLen = 0;
header->nOffset = 0;
header->nFlags = 0;
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_FillThisBuffer(node_meta->handle(), header);
assert(err == OMX_ErrorNone);
}
void OMX::empty_buffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) {
OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
header->nFilledLen = range_length;
header->nOffset = range_offset;
header->nFlags = flags;
header->nTimeStamp = timestamp;
BufferMeta *buffer_meta =
static_cast<BufferMeta *>(header->pAppPrivate);
buffer_meta->CopyToOMX(header);
NodeMeta *node_meta = static_cast<NodeMeta *>(node);
OMX_ERRORTYPE err =
OMX_EmptyThisBuffer(node_meta->handle(), header);
assert(err == OMX_ErrorNone);
}
#endif
} // namespace android

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2009 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_OMX_H_
#define ANDROID_OMX_H_
#include <pthread.h>
#include <media/IOMX.h>
#include <utils/threads.h>
namespace android {
class NodeMeta;
class OMX : public BnOMX {
public:
OMX();
virtual ~OMX();
#if IOMX_USES_SOCKETS
virtual status_t connect(int *sd);
#endif
virtual status_t list_nodes(List<String8> *list);
virtual status_t allocate_node(const char *name, node_id *node);
virtual status_t free_node(node_id node);
virtual status_t send_command(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
virtual status_t get_parameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size);
virtual status_t set_parameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size);
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer);
virtual status_t allocate_buffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer);
virtual status_t allocate_buffer_with_backup(
node_id node, OMX_U32 port_index, const sp<IMemory> &params,
buffer_id *buffer);
virtual status_t free_buffer(
node_id node, OMX_U32 port_index, buffer_id buffer);
#if !IOMX_USES_SOCKETS
virtual status_t observe_node(
node_id node, const sp<IOMXObserver> &observer);
virtual void fill_buffer(node_id node, buffer_id buffer);
virtual void empty_buffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp);
#endif
private:
static OMX_CALLBACKTYPE kCallbacks;
#if IOMX_USES_SOCKETS
int mSock;
pthread_t mThread;
static void *ThreadWrapper(void *me);
void threadEntry();
#endif
Mutex mLock;
static OMX_ERRORTYPE OnEvent(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
static OMX_ERRORTYPE OnEmptyBufferDone(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
static OMX_ERRORTYPE OnFillBufferDone(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE OnEvent(
NodeMeta *meta,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
OMX_ERRORTYPE OnEmptyBufferDone(
NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
OMX_ERRORTYPE OnFillBufferDone(
NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
OMX(const OMX &);
OMX &operator=(const OMX &);
};
} // namespace android
#endif // ANDROID_OMX_H_

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2009 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 <media/stagefright/string.h>
namespace android {
// static
string::size_type string::npos = (string::size_type)-1;
string::string() {
}
string::string(const char *s, size_t length)
: mString(s, length) {
}
string::string(const string &from, size_type start, size_type length)
: mString(from.c_str() + start, length) {
}
string::string(const char *s)
: mString(s) {
}
const char *string::c_str() const {
return mString.string();
}
string::size_type string::size() const {
return mString.length();
}
void string::clear() {
mString = String8();
}
string::size_type string::find(char c) const {
char s[2];
s[0] = c;
s[1] = '\0';
ssize_t index = mString.find(s);
return index < 0 ? npos : (size_type)index;
}
bool string::operator<(const string &other) const {
return mString < other.mString;
}
bool string::operator==(const string &other) const {
return mString == other.mString;
}
string &string::operator+=(char c) {
mString.append(&c, 1);
return *this;
}
void string::erase(size_t from, size_t length) {
String8 s(mString.string(), from);
s.append(mString.string() + from + length);
mString = s;
}
} // namespace android