Home | History | Annotate | Download | only in stagefright
      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     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 
     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     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 
    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<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 
    196 MyConvertingStreamSource::~MyConvertingStreamSource() {
    197 }
    198 
    199 void MyConvertingStreamSource::setListener(
    200         const sp<IStreamListener> &listener) {
    201     mListener = listener;
    202 }
    203 
    204 void MyConvertingStreamSource::setBuffers(
    205         const Vector<sp<IMemory> > &buffers) {
    206     mBuffers = buffers;
    207 }
    208 
    209 ssize_t MyConvertingStreamSource::WriteDataWrapper(
    210         void *me, const void *data, size_t size) {
    211     return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size);
    212 }
    213 
    214 ssize_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 
    255 void 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 
    273 struct 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 
    294 protected:
    295     virtual ~MyClient() {
    296     }
    297 
    298 private:
    299     Mutex mLock;
    300     Condition mCondition;
    301 
    302     bool mEOS;
    303 
    304     DISALLOW_EVIL_CONSTRUCTORS(MyClient);
    305 };
    306 
    307 int 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