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