1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "stream" 19#include "utils/Log.h" 20 21#include <binder/ProcessState.h> 22#include <cutils/properties.h> // for property_get 23 24#include <media/IStreamSource.h> 25#include <media/mediaplayer.h> 26#include <media/stagefright/foundation/ADebug.h> 27#include <media/stagefright/foundation/AMessage.h> 28#include <media/stagefright/DataSource.h> 29#include <media/stagefright/MPEG2TSWriter.h> 30#include <media/stagefright/MediaExtractor.h> 31#include <media/stagefright/MediaSource.h> 32#include <media/stagefright/MetaData.h> 33 34#include <binder/IServiceManager.h> 35#include <media/IMediaPlayerService.h> 36#include <gui/ISurfaceComposer.h> 37#include <gui/SurfaceComposerClient.h> 38#include <gui/Surface.h> 39 40#include <fcntl.h> 41#include <ui/DisplayInfo.h> 42 43using namespace android; 44 45struct MyStreamSource : public BnStreamSource { 46 // Object assumes ownership of fd. 47 MyStreamSource(int fd); 48 49 virtual void setListener(const sp<IStreamListener> &listener); 50 virtual void setBuffers(const Vector<sp<IMemory> > &buffers); 51 52 virtual void onBufferAvailable(size_t index); 53 54protected: 55 virtual ~MyStreamSource(); 56 57private: 58 int mFd; 59 off64_t mFileSize; 60 uint64_t mNumPacketsSent; 61 62 sp<IStreamListener> mListener; 63 Vector<sp<IMemory> > mBuffers; 64 65 DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource); 66}; 67 68MyStreamSource::MyStreamSource(int fd) 69 : mFd(fd), 70 mFileSize(0), 71 mNumPacketsSent(0) { 72 CHECK_GE(fd, 0); 73 74 mFileSize = lseek64(fd, 0, SEEK_END); 75 lseek64(fd, 0, SEEK_SET); 76} 77 78MyStreamSource::~MyStreamSource() { 79 close(mFd); 80 mFd = -1; 81} 82 83void MyStreamSource::setListener(const sp<IStreamListener> &listener) { 84 mListener = listener; 85} 86 87void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) { 88 mBuffers = buffers; 89} 90 91void MyStreamSource::onBufferAvailable(size_t index) { 92 CHECK_LT(index, mBuffers.size()); 93 94#if 0 95 if (mNumPacketsSent >= 20000) { 96 ALOGI("signalling discontinuity now"); 97 98 off64_t offset = 0; 99 CHECK((offset % 188) == 0); 100 101 lseek(mFd, offset, SEEK_SET); 102 103 sp<AMessage> extra = new AMessage; 104 extra->setInt32(IStreamListener::kKeyFormatChange, 0); 105 106 mListener->issueCommand( 107 IStreamListener::DISCONTINUITY, false /* synchronous */, extra); 108 109 mNumPacketsSent = 0; 110 } 111#endif 112 113 sp<IMemory> mem = mBuffers.itemAt(index); 114 115 ssize_t n = read(mFd, mem->pointer(), mem->size()); 116 if (n <= 0) { 117 mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); 118 } else { 119 mListener->queueBuffer(index, n); 120 121 mNumPacketsSent += n / 188; 122 } 123} 124//////////////////////////////////////////////////////////////////////////////// 125 126struct MyConvertingStreamSource : public BnStreamSource { 127 MyConvertingStreamSource(const char *filename); 128 129 virtual void setListener(const sp<IStreamListener> &listener); 130 virtual void setBuffers(const Vector<sp<IMemory> > &buffers); 131 132 virtual void onBufferAvailable(size_t index); 133 134protected: 135 virtual ~MyConvertingStreamSource(); 136 137private: 138 Mutex mLock; 139 Condition mCondition; 140 141 sp<IStreamListener> mListener; 142 Vector<sp<IMemory> > mBuffers; 143 144 sp<MPEG2TSWriter> mWriter; 145 146 ssize_t mCurrentBufferIndex; 147 size_t mCurrentBufferOffset; 148 149 List<size_t> mBufferQueue; 150 151 static ssize_t WriteDataWrapper(void *me, const void *data, size_t size); 152 ssize_t writeData(const void *data, size_t size); 153 154 DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource); 155}; 156 157//////////////////////////////////////////////////////////////////////////////// 158 159MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) 160 : mCurrentBufferIndex(-1), 161 mCurrentBufferOffset(0) { 162 sp<DataSource> dataSource = DataSource::CreateFromURI(filename); 163 CHECK(dataSource != NULL); 164 165 sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); 166 CHECK(extractor != NULL); 167 168 mWriter = new MPEG2TSWriter( 169 this, &MyConvertingStreamSource::WriteDataWrapper); 170 171 for (size_t i = 0; i < extractor->countTracks(); ++i) { 172 const sp<MetaData> &meta = extractor->getTrackMetaData(i); 173 174 const char *mime; 175 CHECK(meta->findCString(kKeyMIMEType, &mime)); 176 177 if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { 178 continue; 179 } 180 181 CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK); 182 } 183 184 CHECK_EQ(mWriter->start(), (status_t)OK); 185} 186 187MyConvertingStreamSource::~MyConvertingStreamSource() { 188} 189 190void MyConvertingStreamSource::setListener( 191 const sp<IStreamListener> &listener) { 192 mListener = listener; 193} 194 195void MyConvertingStreamSource::setBuffers( 196 const Vector<sp<IMemory> > &buffers) { 197 mBuffers = buffers; 198} 199 200ssize_t MyConvertingStreamSource::WriteDataWrapper( 201 void *me, const void *data, size_t size) { 202 return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size); 203} 204 205ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { 206 size_t totalWritten = 0; 207 208 while (size > 0) { 209 Mutex::Autolock autoLock(mLock); 210 211 if (mCurrentBufferIndex < 0) { 212 while (mBufferQueue.empty()) { 213 mCondition.wait(mLock); 214 } 215 216 mCurrentBufferIndex = *mBufferQueue.begin(); 217 mCurrentBufferOffset = 0; 218 219 mBufferQueue.erase(mBufferQueue.begin()); 220 } 221 222 sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex); 223 224 size_t copy = size; 225 if (copy + mCurrentBufferOffset > mem->size()) { 226 copy = mem->size() - mCurrentBufferOffset; 227 } 228 229 memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); 230 mCurrentBufferOffset += copy; 231 232 if (mCurrentBufferOffset == mem->size()) { 233 mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); 234 mCurrentBufferIndex = -1; 235 } 236 237 data = (const uint8_t *)data + copy; 238 size -= copy; 239 240 totalWritten += copy; 241 } 242 243 return (ssize_t)totalWritten; 244} 245 246void MyConvertingStreamSource::onBufferAvailable(size_t index) { 247 Mutex::Autolock autoLock(mLock); 248 249 mBufferQueue.push_back(index); 250 mCondition.signal(); 251 252 if (mWriter->reachedEOS()) { 253 if (mCurrentBufferIndex >= 0) { 254 mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); 255 mCurrentBufferIndex = -1; 256 } 257 258 mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); 259 } 260} 261 262//////////////////////////////////////////////////////////////////////////////// 263 264struct MyClient : public BnMediaPlayerClient { 265 MyClient() 266 : mEOS(false) { 267 } 268 269 virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) { 270 Mutex::Autolock autoLock(mLock); 271 272 if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { 273 mEOS = true; 274 mCondition.signal(); 275 } 276 } 277 278 void waitForEOS() { 279 Mutex::Autolock autoLock(mLock); 280 while (!mEOS) { 281 mCondition.wait(mLock); 282 } 283 } 284 285protected: 286 virtual ~MyClient() { 287 } 288 289private: 290 Mutex mLock; 291 Condition mCondition; 292 293 bool mEOS; 294 295 DISALLOW_EVIL_CONSTRUCTORS(MyClient); 296}; 297 298int main(int argc, char **argv) { 299 android::ProcessState::self()->startThreadPool(); 300 301 DataSource::RegisterDefaultSniffers(); 302 303 if (argc != 2) { 304 fprintf(stderr, "Usage: %s filename\n", argv[0]); 305 return 1; 306 } 307 308 sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; 309 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 310 311 sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay( 312 ISurfaceComposer::eDisplayIdMain)); 313 DisplayInfo info; 314 SurfaceComposerClient::getDisplayInfo(display, &info); 315 ssize_t displayWidth = info.w; 316 ssize_t displayHeight = info.h; 317 318 ALOGV("display is %d x %d\n", displayWidth, displayHeight); 319 320 sp<SurfaceControl> control = 321 composerClient->createSurface( 322 String8("A Surface"), 323 displayWidth, 324 displayHeight, 325 PIXEL_FORMAT_RGB_565, 326 0); 327 328 CHECK(control != NULL); 329 CHECK(control->isValid()); 330 331 SurfaceComposerClient::openGlobalTransaction(); 332 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 333 CHECK_EQ(control->show(), (status_t)OK); 334 SurfaceComposerClient::closeGlobalTransaction(); 335 336 sp<Surface> surface = control->getSurface(); 337 CHECK(surface != NULL); 338 339 sp<IServiceManager> sm = defaultServiceManager(); 340 sp<IBinder> binder = sm->getService(String16("media.player")); 341 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 342 343 CHECK(service.get() != NULL); 344 345 sp<MyClient> client = new MyClient; 346 347 sp<IStreamSource> source; 348 349 char prop[PROPERTY_VALUE_MAX]; 350 bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) && 351 (!strcmp(prop, "1") || !strcasecmp(prop, "true")); 352 353 size_t len = strlen(argv[1]); 354 if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) || 355 (usemp4 && len >= 4 && 356 (!strcasecmp(".mp4", &argv[1][len - 4]) 357 || !strcasecmp(".3gp", &argv[1][len- 4]) 358 || !strcasecmp(".3g2", &argv[1][len- 4])))) { 359 int fd = open(argv[1], O_RDONLY); 360 361 if (fd < 0) { 362 fprintf(stderr, "Failed to open file '%s'.", argv[1]); 363 return 1; 364 } 365 366 source = new MyStreamSource(fd); 367 } else { 368 printf("Converting file to transport stream for streaming...\n"); 369 370 source = new MyConvertingStreamSource(argv[1]); 371 } 372 373 sp<IMediaPlayer> player = 374 service->create(client, 0); 375 376 if (player != NULL && player->setDataSource(source) == NO_ERROR) { 377 player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer()); 378 player->start(); 379 380 client->waitForEOS(); 381 382 player->stop(); 383 } else { 384 fprintf(stderr, "failed to instantiate player.\n"); 385 } 386 387 composerClient->dispose(); 388 389 return 0; 390} 391