/* * Copyright (C) 2010 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 "stream" #include "utils/Log.h" #include #include // for property_get #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace android; struct MyStreamSource : public BnStreamSource { // Object assumes ownership of fd. MyStreamSource(int fd); virtual void setListener(const sp &listener); virtual void setBuffers(const Vector > &buffers); virtual void onBufferAvailable(size_t index); protected: virtual ~MyStreamSource(); private: int mFd; off64_t mFileSize; uint64_t mNumPacketsSent; sp mListener; Vector > mBuffers; DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource); }; MyStreamSource::MyStreamSource(int fd) : mFd(fd), mFileSize(0), mNumPacketsSent(0) { CHECK_GE(fd, 0); mFileSize = lseek64(fd, 0, SEEK_END); lseek64(fd, 0, SEEK_SET); } MyStreamSource::~MyStreamSource() { close(mFd); mFd = -1; } void MyStreamSource::setListener(const sp &listener) { mListener = listener; } void MyStreamSource::setBuffers(const Vector > &buffers) { mBuffers = buffers; } void MyStreamSource::onBufferAvailable(size_t index) { CHECK_LT(index, mBuffers.size()); #if 0 if (mNumPacketsSent >= 20000) { ALOGI("signalling discontinuity now"); off64_t offset = 0; CHECK((offset % 188) == 0); lseek(mFd, offset, SEEK_SET); sp extra = new AMessage; extra->setInt32(IStreamListener::kKeyFormatChange, 0); mListener->issueCommand( IStreamListener::DISCONTINUITY, false /* synchronous */, extra); mNumPacketsSent = 0; } #endif sp mem = mBuffers.itemAt(index); ssize_t n = read(mFd, mem->pointer(), mem->size()); if (n <= 0) { mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); } else { mListener->queueBuffer(index, n); mNumPacketsSent += n / 188; } } //////////////////////////////////////////////////////////////////////////////// struct MyConvertingStreamSource : public BnStreamSource { MyConvertingStreamSource(const char *filename); virtual void setListener(const sp &listener); virtual void setBuffers(const Vector > &buffers); virtual void onBufferAvailable(size_t index); protected: virtual ~MyConvertingStreamSource(); private: Mutex mLock; Condition mCondition; sp mListener; Vector > mBuffers; sp mWriter; ssize_t mCurrentBufferIndex; size_t mCurrentBufferOffset; List mBufferQueue; static ssize_t WriteDataWrapper(void *me, const void *data, size_t size); ssize_t writeData(const void *data, size_t size); DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource); }; //////////////////////////////////////////////////////////////////////////////// MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) : mCurrentBufferIndex(-1), mCurrentBufferOffset(0) { sp dataSource = DataSource::CreateFromURI(NULL /* httpService */, filename); CHECK(dataSource != NULL); sp extractor = MediaExtractor::Create(dataSource); CHECK(extractor != NULL); mWriter = new MPEG2TSWriter( this, &MyConvertingStreamSource::WriteDataWrapper); for (size_t i = 0; i < extractor->countTracks(); ++i) { const sp &meta = extractor->getTrackMetaData(i); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { continue; } CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK); } CHECK_EQ(mWriter->start(), (status_t)OK); } MyConvertingStreamSource::~MyConvertingStreamSource() { } void MyConvertingStreamSource::setListener( const sp &listener) { mListener = listener; } void MyConvertingStreamSource::setBuffers( const Vector > &buffers) { mBuffers = buffers; } ssize_t MyConvertingStreamSource::WriteDataWrapper( void *me, const void *data, size_t size) { return static_cast(me)->writeData(data, size); } ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { size_t totalWritten = 0; while (size > 0) { Mutex::Autolock autoLock(mLock); if (mCurrentBufferIndex < 0) { while (mBufferQueue.empty()) { mCondition.wait(mLock); } mCurrentBufferIndex = *mBufferQueue.begin(); mCurrentBufferOffset = 0; mBufferQueue.erase(mBufferQueue.begin()); } sp mem = mBuffers.itemAt(mCurrentBufferIndex); size_t copy = size; if (copy + mCurrentBufferOffset > mem->size()) { copy = mem->size() - mCurrentBufferOffset; } memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); mCurrentBufferOffset += copy; if (mCurrentBufferOffset == mem->size()) { mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); mCurrentBufferIndex = -1; } data = (const uint8_t *)data + copy; size -= copy; totalWritten += copy; } return (ssize_t)totalWritten; } void MyConvertingStreamSource::onBufferAvailable(size_t index) { Mutex::Autolock autoLock(mLock); mBufferQueue.push_back(index); mCondition.signal(); if (mWriter->reachedEOS()) { if (mCurrentBufferIndex >= 0) { mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); mCurrentBufferIndex = -1; } mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); } } //////////////////////////////////////////////////////////////////////////////// struct MyClient : public BnMediaPlayerClient { MyClient() : mEOS(false) { } virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) { Mutex::Autolock autoLock(mLock); if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { mEOS = true; mCondition.signal(); } } void waitForEOS() { Mutex::Autolock autoLock(mLock); while (!mEOS) { mCondition.wait(mLock); } } protected: virtual ~MyClient() { } private: Mutex mLock; Condition mCondition; bool mEOS; DISALLOW_EVIL_CONSTRUCTORS(MyClient); }; int main(int argc, char **argv) { android::ProcessState::self()->startThreadPool(); DataSource::RegisterDefaultSniffers(); if (argc != 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); return 1; } sp composerClient = new SurfaceComposerClient; CHECK_EQ(composerClient->initCheck(), (status_t)OK); sp display(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); DisplayInfo info; SurfaceComposerClient::getDisplayInfo(display, &info); ssize_t displayWidth = info.w; ssize_t displayHeight = info.h; ALOGV("display is %d x %d\n", displayWidth, displayHeight); sp control = composerClient->createSurface( String8("A Surface"), displayWidth, displayHeight, PIXEL_FORMAT_RGB_565, 0); CHECK(control != NULL); CHECK(control->isValid()); SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); SurfaceComposerClient::closeGlobalTransaction(); sp surface = control->getSurface(); CHECK(surface != NULL); sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.player")); sp service = interface_cast(binder); CHECK(service.get() != NULL); sp client = new MyClient; sp source; char prop[PROPERTY_VALUE_MAX]; bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) && (!strcmp(prop, "1") || !strcasecmp(prop, "true")); size_t len = strlen(argv[1]); if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) || (usemp4 && len >= 4 && (!strcasecmp(".mp4", &argv[1][len - 4]) || !strcasecmp(".3gp", &argv[1][len- 4]) || !strcasecmp(".3g2", &argv[1][len- 4])))) { int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "Failed to open file '%s'.", argv[1]); return 1; } source = new MyStreamSource(fd); } else { printf("Converting file to transport stream for streaming...\n"); source = new MyConvertingStreamSource(argv[1]); } sp player = service->create(client, AUDIO_SESSION_ALLOCATE); if (player != NULL && player->setDataSource(source) == NO_ERROR) { player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer()); player->start(); client->waitForEOS(); player->stop(); } else { fprintf(stderr, "failed to instantiate player.\n"); } composerClient->dispose(); return 0; }