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 44using namespace android; 45 46struct MyStreamSource : public BnStreamSource { 47 // Object assumes ownership of fd. 48 explicit 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 55protected: 56 virtual ~MyStreamSource(); 57 58private: 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 69MyStreamSource::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 79MyStreamSource::~MyStreamSource() { 80 close(mFd); 81 mFd = -1; 82} 83 84void MyStreamSource::setListener(const sp<IStreamListener> &listener) { 85 mListener = listener; 86} 87 88void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) { 89 mBuffers = buffers; 90} 91 92void 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 127struct MyConvertingStreamSource : public BnStreamSource { 128 explicit 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 135protected: 136 virtual ~MyConvertingStreamSource(); 137 138private: 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 160MyConvertingStreamSource::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<IMediaExtractor> extractor = MediaExtractor::Create(dataSource); 169 CHECK(extractor != NULL); 170 171 mWriter = new MPEG2TSWriter( 172 this, &MyConvertingStreamSource::WriteDataWrapper); 173 174 size_t numTracks = extractor->countTracks(); 175 for (size_t i = 0; i < numTracks; ++i) { 176 const sp<MetaData> &meta = extractor->getTrackMetaData(i); 177 178 const char *mime; 179 CHECK(meta->findCString(kKeyMIMEType, &mime)); 180 181 if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { 182 continue; 183 } 184 185 sp<IMediaSource> track = extractor->getTrack(i); 186 if (track == nullptr) { 187 fprintf(stderr, "skip NULL track %zu, total tracks %zu\n", i, numTracks); 188 continue; 189 } 190 CHECK_EQ(mWriter->addSource(track), (status_t)OK); 191 } 192 193 CHECK_EQ(mWriter->start(), (status_t)OK); 194} 195 196MyConvertingStreamSource::~MyConvertingStreamSource() { 197} 198 199void MyConvertingStreamSource::setListener( 200 const sp<IStreamListener> &listener) { 201 mListener = listener; 202} 203 204void MyConvertingStreamSource::setBuffers( 205 const Vector<sp<IMemory> > &buffers) { 206 mBuffers = buffers; 207} 208 209ssize_t MyConvertingStreamSource::WriteDataWrapper( 210 void *me, const void *data, size_t size) { 211 return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size); 212} 213 214ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { 215 size_t totalWritten = 0; 216 217 while (size > 0) { 218 Mutex::Autolock autoLock(mLock); 219 220 if (mCurrentBufferIndex < 0) { 221 while (mBufferQueue.empty()) { 222 mCondition.wait(mLock); 223 } 224 225 mCurrentBufferIndex = *mBufferQueue.begin(); 226 mCurrentBufferOffset = 0; 227 228 mBufferQueue.erase(mBufferQueue.begin()); 229 } 230 231 sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex); 232 233 size_t copy = size; 234 if (copy + mCurrentBufferOffset > mem->size()) { 235 copy = mem->size() - mCurrentBufferOffset; 236 } 237 238 memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); 239 mCurrentBufferOffset += copy; 240 241 if (mCurrentBufferOffset == mem->size()) { 242 mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); 243 mCurrentBufferIndex = -1; 244 } 245 246 data = (const uint8_t *)data + copy; 247 size -= copy; 248 249 totalWritten += copy; 250 } 251 252 return (ssize_t)totalWritten; 253} 254 255void MyConvertingStreamSource::onBufferAvailable(size_t index) { 256 Mutex::Autolock autoLock(mLock); 257 258 mBufferQueue.push_back(index); 259 mCondition.signal(); 260 261 if (mWriter->reachedEOS()) { 262 if (mCurrentBufferIndex >= 0) { 263 mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); 264 mCurrentBufferIndex = -1; 265 } 266 267 mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); 268 } 269} 270 271//////////////////////////////////////////////////////////////////////////////// 272 273struct MyClient : public BnMediaPlayerClient { 274 MyClient() 275 : mEOS(false) { 276 } 277 278 virtual void notify(int msg, int ext1 __unused, int ext2 __unused, const Parcel *obj __unused) { 279 Mutex::Autolock autoLock(mLock); 280 281 if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { 282 mEOS = true; 283 mCondition.signal(); 284 } 285 } 286 287 void waitForEOS() { 288 Mutex::Autolock autoLock(mLock); 289 while (!mEOS) { 290 mCondition.wait(mLock); 291 } 292 } 293 294protected: 295 virtual ~MyClient() { 296 } 297 298private: 299 Mutex mLock; 300 Condition mCondition; 301 302 bool mEOS; 303 304 DISALLOW_EVIL_CONSTRUCTORS(MyClient); 305}; 306 307int main(int argc, char **argv) { 308 android::ProcessState::self()->startThreadPool(); 309 310 if (argc != 2) { 311 fprintf(stderr, "Usage: %s filename\n", argv[0]); 312 return 1; 313 } 314 315 sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; 316 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 317 318 sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay( 319 ISurfaceComposer::eDisplayIdMain)); 320 DisplayInfo info; 321 SurfaceComposerClient::getDisplayInfo(display, &info); 322 ssize_t displayWidth = info.w; 323 ssize_t displayHeight = info.h; 324 325 ALOGV("display is %zd x %zd\n", displayWidth, displayHeight); 326 327 sp<SurfaceControl> control = 328 composerClient->createSurface( 329 String8("A Surface"), 330 displayWidth, 331 displayHeight, 332 PIXEL_FORMAT_RGB_565, 333 0); 334 335 CHECK(control != NULL); 336 CHECK(control->isValid()); 337 338 SurfaceComposerClient::openGlobalTransaction(); 339 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 340 CHECK_EQ(control->show(), (status_t)OK); 341 SurfaceComposerClient::closeGlobalTransaction(); 342 343 sp<Surface> surface = control->getSurface(); 344 CHECK(surface != NULL); 345 346 sp<IServiceManager> sm = defaultServiceManager(); 347 sp<IBinder> binder = sm->getService(String16("media.player")); 348 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 349 350 CHECK(service.get() != NULL); 351 352 sp<MyClient> client = new MyClient; 353 354 sp<IStreamSource> source; 355 356 bool usemp4 = property_get_bool("media.stagefright.use-mp4source", false); 357 358 size_t len = strlen(argv[1]); 359 if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) || 360 (usemp4 && len >= 4 && 361 (!strcasecmp(".mp4", &argv[1][len - 4]) 362 || !strcasecmp(".3gp", &argv[1][len- 4]) 363 || !strcasecmp(".3g2", &argv[1][len- 4])))) { 364 int fd = open(argv[1], O_RDONLY); 365 366 if (fd < 0) { 367 fprintf(stderr, "Failed to open file '%s'.", argv[1]); 368 return 1; 369 } 370 371 source = new MyStreamSource(fd); 372 } else { 373 printf("Converting file to transport stream for streaming...\n"); 374 375 source = new MyConvertingStreamSource(argv[1]); 376 } 377 378 sp<IMediaPlayer> player = 379 service->create(client, AUDIO_SESSION_ALLOCATE); 380 381 if (player != NULL && player->setDataSource(source) == NO_ERROR) { 382 player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer()); 383 player->start(); 384 385 client->waitForEOS(); 386 387 player->stop(); 388 } else { 389 fprintf(stderr, "failed to instantiate player.\n"); 390 } 391 392 composerClient->dispose(); 393 394 return 0; 395} 396