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