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