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