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