Merge change 7332
* changes: Squashed commit of the following:
This commit is contained in:
66
cmds/stagefright/Android.mk
Normal file
66
cmds/stagefright/Android.mk
Normal 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
|
||||
71
cmds/stagefright/WaveWriter.h
Normal file
71
cmds/stagefright/WaveWriter.h
Normal 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
295
cmds/stagefright/play.cpp
Normal 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
181
cmds/stagefright/record.cpp
Normal 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;
|
||||
}
|
||||
|
||||
200
cmds/stagefright/stagefright.cpp
Normal file
200
cmds/stagefright/stagefright.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
176
include/media/IOMX.h
Normal 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> ¶ms,
|
||||
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> ¶ms,
|
||||
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_
|
||||
@@ -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;
|
||||
|
||||
98
include/media/stagefright/AudioPlayer.h
Normal file
98
include/media/stagefright/AudioPlayer.h
Normal 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_
|
||||
51
include/media/stagefright/AudioSource.h
Normal file
51
include/media/stagefright/AudioSource.h
Normal 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_
|
||||
60
include/media/stagefright/CachingDataSource.h
Normal file
60
include/media/stagefright/CachingDataSource.h
Normal 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_
|
||||
72
include/media/stagefright/CameraSource.h
Normal file
72
include/media/stagefright/CameraSource.h
Normal 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_
|
||||
61
include/media/stagefright/DataSource.h
Normal file
61
include/media/stagefright/DataSource.h
Normal 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_
|
||||
64
include/media/stagefright/ESDS.h
Normal file
64
include/media/stagefright/ESDS.h
Normal 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_
|
||||
49
include/media/stagefright/FileSource.h
Normal file
49
include/media/stagefright/FileSource.h
Normal 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_
|
||||
|
||||
59
include/media/stagefright/HTTPDataSource.h
Normal file
59
include/media/stagefright/HTTPDataSource.h
Normal 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_
|
||||
|
||||
74
include/media/stagefright/HTTPStream.h
Normal file
74
include/media/stagefright/HTTPStream.h
Normal 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_
|
||||
53
include/media/stagefright/MP3Extractor.h
Normal file
53
include/media/stagefright/MP3Extractor.h
Normal 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_
|
||||
65
include/media/stagefright/MPEG4Extractor.h
Normal file
65
include/media/stagefright/MPEG4Extractor.h
Normal 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_
|
||||
73
include/media/stagefright/MPEG4Writer.h
Normal file
73
include/media/stagefright/MPEG4Writer.h
Normal 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_
|
||||
113
include/media/stagefright/MediaBuffer.h
Normal file
113
include/media/stagefright/MediaBuffer.h
Normal 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_
|
||||
57
include/media/stagefright/MediaBufferGroup.h
Normal file
57
include/media/stagefright/MediaBufferGroup.h
Normal 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_
|
||||
43
include/media/stagefright/MediaErrors.h
Normal file
43
include/media/stagefright/MediaErrors.h
Normal 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_
|
||||
49
include/media/stagefright/MediaExtractor.h
Normal file
49
include/media/stagefright/MediaExtractor.h
Normal 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_
|
||||
132
include/media/stagefright/MediaPlayerImpl.h
Normal file
132
include/media/stagefright/MediaPlayerImpl.h
Normal 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_
|
||||
91
include/media/stagefright/MediaSource.h
Normal file
91
include/media/stagefright/MediaSource.h
Normal 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_
|
||||
132
include/media/stagefright/MetaData.h
Normal file
132
include/media/stagefright/MetaData.h
Normal 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_
|
||||
52
include/media/stagefright/MmapSource.h
Normal file
52
include/media/stagefright/MmapSource.h
Normal 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_
|
||||
|
||||
124
include/media/stagefright/OMXClient.h
Normal file
124
include/media/stagefright/OMXClient.h
Normal 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_
|
||||
158
include/media/stagefright/OMXDecoder.h
Normal file
158
include/media/stagefright/OMXDecoder.h
Normal 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_
|
||||
57
include/media/stagefright/QComHardwareRenderer.h
Normal file
57
include/media/stagefright/QComHardwareRenderer.h
Normal 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_
|
||||
107
include/media/stagefright/SampleTable.h
Normal file
107
include/media/stagefright/SampleTable.h
Normal 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_
|
||||
59
include/media/stagefright/ShoutcastSource.h
Normal file
59
include/media/stagefright/ShoutcastSource.h
Normal 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_
|
||||
|
||||
55
include/media/stagefright/SoftwareRenderer.h
Normal file
55
include/media/stagefright/SoftwareRenderer.h
Normal 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_
|
||||
51
include/media/stagefright/SurfaceRenderer.h
Normal file
51
include/media/stagefright/SurfaceRenderer.h
Normal 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_
|
||||
49
include/media/stagefright/TimeSource.h
Normal file
49
include/media/stagefright/TimeSource.h
Normal 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_
|
||||
106
include/media/stagefright/TimedEventQueue.h
Normal file
106
include/media/stagefright/TimedEventQueue.h
Normal 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_
|
||||
37
include/media/stagefright/Utils.h
Normal file
37
include/media/stagefright/Utils.h
Normal 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_
|
||||
41
include/media/stagefright/VideoRenderer.h
Normal file
41
include/media/stagefright/VideoRenderer.h
Normal 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_
|
||||
54
include/media/stagefright/string.h
Normal file
54
include/media/stagefright/string.h
Normal 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_
|
||||
@@ -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)
|
||||
|
||||
@@ -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
561
media/libmedia/IOMX.cpp
Normal 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> ¶ms,
|
||||
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> ¶ms,
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
208
media/libmediaplayerservice/StagefrightPlayer.cpp
Normal file
208
media/libmediaplayerservice/StagefrightPlayer.cpp
Normal 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
|
||||
60
media/libmediaplayerservice/StagefrightPlayer.h
Normal file
60
media/libmediaplayerservice/StagefrightPlayer.h
Normal 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
|
||||
56
media/libstagefright/Android.mk
Normal file
56
media/libstagefright/Android.mk
Normal 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
|
||||
285
media/libstagefright/AudioPlayer.cpp
Normal file
285
media/libstagefright/AudioPlayer.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
162
media/libstagefright/CachingDataSource.cpp
Normal file
162
media/libstagefright/CachingDataSource.cpp
Normal 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
|
||||
226
media/libstagefright/CameraSource.cpp
Normal file
226
media/libstagefright/CameraSource.cpp
Normal 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
|
||||
76
media/libstagefright/DataSource.cpp
Normal file
76
media/libstagefright/DataSource.cpp
Normal 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
|
||||
196
media/libstagefright/ESDS.cpp
Normal file
196
media/libstagefright/ESDS.cpp
Normal 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
|
||||
|
||||
50
media/libstagefright/FileSource.cpp
Normal file
50
media/libstagefright/FileSource.cpp
Normal 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
|
||||
174
media/libstagefright/HTTPDataSource.cpp
Normal file
174
media/libstagefright/HTTPDataSource.cpp
Normal 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
|
||||
|
||||
285
media/libstagefright/HTTPStream.cpp
Normal file
285
media/libstagefright/HTTPStream.cpp
Normal 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
|
||||
|
||||
522
media/libstagefright/MP3Extractor.cpp
Normal file
522
media/libstagefright/MP3Extractor.cpp
Normal 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
|
||||
937
media/libstagefright/MPEG4Extractor.cpp
Normal file
937
media/libstagefright/MPEG4Extractor.cpp
Normal 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, ×cale, 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
|
||||
|
||||
641
media/libstagefright/MPEG4Writer.cpp
Normal file
641
media/libstagefright/MPEG4Writer.cpp
Normal 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
|
||||
174
media/libstagefright/MediaBuffer.cpp
Normal file
174
media/libstagefright/MediaBuffer.cpp
Normal 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
|
||||
|
||||
88
media/libstagefright/MediaBufferGroup.cpp
Normal file
88
media/libstagefright/MediaBufferGroup.cpp
Normal 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
|
||||
54
media/libstagefright/MediaExtractor.cpp
Normal file
54
media/libstagefright/MediaExtractor.cpp
Normal 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
|
||||
693
media/libstagefright/MediaPlayerImpl.cpp
Normal file
693
media/libstagefright/MediaPlayerImpl.cpp
Normal 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
|
||||
|
||||
60
media/libstagefright/MediaSource.cpp
Normal file
60
media/libstagefright/MediaSource.cpp
Normal 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
|
||||
232
media/libstagefright/MetaData.cpp
Normal file
232
media/libstagefright/MetaData.cpp
Normal 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
|
||||
|
||||
112
media/libstagefright/MmapSource.cpp
Normal file
112
media/libstagefright/MmapSource.cpp
Normal 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
|
||||
|
||||
369
media/libstagefright/OMXClient.cpp
Normal file
369
media/libstagefright/OMXClient.cpp
Normal 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
|
||||
1329
media/libstagefright/OMXDecoder.cpp
Normal file
1329
media/libstagefright/OMXDecoder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
139
media/libstagefright/QComHardwareRenderer.cpp
Normal file
139
media/libstagefright/QComHardwareRenderer.cpp
Normal 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
|
||||
598
media/libstagefright/SampleTable.cpp
Normal file
598
media/libstagefright/SampleTable.cpp
Normal 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
|
||||
|
||||
154
media/libstagefright/ShoutcastSource.cpp
Normal file
154
media/libstagefright/ShoutcastSource.cpp
Normal 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
|
||||
|
||||
173
media/libstagefright/SoftwareRenderer.cpp
Normal file
173
media/libstagefright/SoftwareRenderer.cpp
Normal 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
|
||||
69
media/libstagefright/SurfaceRenderer.cpp
Normal file
69
media/libstagefright/SurfaceRenderer.cpp
Normal 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
|
||||
40
media/libstagefright/TimeSource.cpp
Normal file
40
media/libstagefright/TimeSource.cpp
Normal 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
|
||||
|
||||
206
media/libstagefright/TimedEventQueue.cpp
Normal file
206
media/libstagefright/TimedEventQueue.cpp
Normal 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
|
||||
|
||||
45
media/libstagefright/Utils.cpp
Normal file
45
media/libstagefright/Utils.cpp
Normal 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
|
||||
|
||||
27
media/libstagefright/omx/Android.mk
Normal file
27
media/libstagefright/omx/Android.mk
Normal 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
|
||||
621
media/libstagefright/omx/OMX.cpp
Normal file
621
media/libstagefright/omx/OMX.cpp
Normal 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> ¶ms,
|
||||
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> ¶ms,
|
||||
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
|
||||
|
||||
132
media/libstagefright/omx/OMX.h
Normal file
132
media/libstagefright/omx/OMX.h
Normal 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> ¶ms,
|
||||
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> ¶ms,
|
||||
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_
|
||||
83
media/libstagefright/string.cpp
Normal file
83
media/libstagefright/string.cpp
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user