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