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#include "SineSource.h"
18
19#include <inttypes.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23
24#include <binder/ProcessState.h>
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/foundation/ALooper.h>
27#include <media/stagefright/foundation/AMessage.h>
28#include <media/stagefright/AudioPlayer.h>
29#include <media/stagefright/MediaBufferGroup.h>
30#include <media/stagefright/MediaCodecSource.h>
31#include <media/stagefright/MediaDefs.h>
32#include <media/stagefright/MetaData.h>
33#include <media/stagefright/MPEG4Writer.h>
34#include <media/MediaPlayerInterface.h>
35
36#include <OMX_Video.h>
37
38using namespace android;
39
40// Print usage showing how to use this utility to record videos
41static void usage(const char *me) {
42    fprintf(stderr, "usage: %s\n", me);
43    fprintf(stderr, "       -h(elp)\n");
44    fprintf(stderr, "       -b bit rate in bits per second (default: 300000)\n");
45    fprintf(stderr, "       -c YUV420 color format: [0] semi planar or [1] planar or other omx YUV420 color format (default: 1)\n");
46    fprintf(stderr, "       -f frame rate in frames per second (default: 30)\n");
47    fprintf(stderr, "       -i I frame interval in seconds (default: 1)\n");
48    fprintf(stderr, "       -n number of frames to be recorded (default: 300)\n");
49    fprintf(stderr, "       -w width in pixels (default: 176)\n");
50    fprintf(stderr, "       -t height in pixels (default: 144)\n");
51    fprintf(stderr, "       -l encoder level. see omx il header (default: encoder specific)\n");
52    fprintf(stderr, "       -p encoder profile. see omx il header (default: encoder specific)\n");
53    fprintf(stderr, "       -v video codec: [0] AVC [1] M4V [2] H263 (default: 0)\n");
54    fprintf(stderr, "       -s(oftware) prefer software codec\n");
55    fprintf(stderr, "       -o filename: output file (default: /sdcard/output.mp4)\n");
56    exit(1);
57}
58
59class DummySource : public MediaSource {
60
61public:
62    DummySource(int width, int height, int nFrames, int fps, int colorFormat)
63        : mWidth(width),
64          mHeight(height),
65          mMaxNumFrames(nFrames),
66          mFrameRate(fps),
67          mColorFormat(colorFormat),
68          mSize((width * height * 3) / 2) {
69
70        mGroup.add_buffer(new MediaBuffer(mSize));
71    }
72
73    virtual sp<MetaData> getFormat() {
74        sp<MetaData> meta = new MetaData;
75        meta->setInt32(kKeyWidth, mWidth);
76        meta->setInt32(kKeyHeight, mHeight);
77        meta->setInt32(kKeyColorFormat, mColorFormat);
78        meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
79
80        return meta;
81    }
82
83    virtual status_t start(MetaData *params __unused) {
84        mNumFramesOutput = 0;
85        return OK;
86    }
87
88    virtual status_t stop() {
89        return OK;
90    }
91
92    virtual status_t read(
93            MediaBuffer **buffer, const MediaSource::ReadOptions *options __unused) {
94
95        if (mNumFramesOutput % 10 == 0) {
96            fprintf(stderr, ".");
97        }
98        if (mNumFramesOutput == mMaxNumFrames) {
99            return ERROR_END_OF_STREAM;
100        }
101
102        status_t err = mGroup.acquire_buffer(buffer);
103        if (err != OK) {
104            return err;
105        }
106
107        // We don't care about the contents. we just test video encoder
108        // Also, by skipping the content generation, we can return from
109        // read() much faster.
110#if 0
111        // iterate through solid planes of color.
112        static unsigned char x = 0x60;
113        memset((*buffer)->data(), x, mSize);
114        x = x >= 0xa0 ? 0x60 : x + 1;
115#endif
116        (*buffer)->set_range(0, mSize);
117        (*buffer)->meta_data()->clear();
118        (*buffer)->meta_data()->setInt64(
119                kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate);
120        ++mNumFramesOutput;
121
122        return OK;
123    }
124
125protected:
126    virtual ~DummySource() {}
127
128private:
129    MediaBufferGroup mGroup;
130    int mWidth, mHeight;
131    int mMaxNumFrames;
132    int mFrameRate;
133    int mColorFormat;
134    size_t mSize;
135    int64_t mNumFramesOutput;;
136
137    DummySource(const DummySource &);
138    DummySource &operator=(const DummySource &);
139};
140
141enum {
142    kYUV420SP = 0,
143    kYUV420P  = 1,
144};
145
146// returns -1 if mapping of the given color is unsuccessful
147// returns an omx color enum value otherwise
148static int translateColorToOmxEnumValue(int color) {
149    switch (color) {
150        case kYUV420SP:
151            return OMX_COLOR_FormatYUV420SemiPlanar;
152        case kYUV420P:
153            return OMX_COLOR_FormatYUV420Planar;
154        default:
155            fprintf(stderr, "Custom OMX color format: %d\n", color);
156            if (color == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar ||
157                color == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) {
158                return color;
159            }
160    }
161    return -1;
162}
163
164int main(int argc, char **argv) {
165
166    // Default values for the program if not overwritten
167    int frameRateFps = 30;
168    int width = 176;
169    int height = 144;
170    int bitRateBps = 300000;
171    int iFramesIntervalSeconds = 1;
172    int colorFormat = OMX_COLOR_FormatYUV420Planar;
173    int nFrames = 300;
174    int level = -1;        // Encoder specific default
175    int profile = -1;      // Encoder specific default
176    int codec = 0;
177    const char *fileName = "/sdcard/output.mp4";
178    bool preferSoftwareCodec = false;
179
180    android::ProcessState::self()->startThreadPool();
181    int res;
182    while ((res = getopt(argc, argv, "b:c:f:i:n:w:t:l:p:v:o:hs")) >= 0) {
183        switch (res) {
184            case 'b':
185            {
186                bitRateBps = atoi(optarg);
187                break;
188            }
189
190            case 'c':
191            {
192                colorFormat = translateColorToOmxEnumValue(atoi(optarg));
193                if (colorFormat == -1) {
194                    usage(argv[0]);
195                }
196                break;
197            }
198
199            case 'f':
200            {
201                frameRateFps = atoi(optarg);
202                break;
203            }
204
205            case 'i':
206            {
207                iFramesIntervalSeconds = atoi(optarg);
208                break;
209            }
210
211            case 'n':
212            {
213                nFrames = atoi(optarg);
214                break;
215            }
216
217            case 'w':
218            {
219                width = atoi(optarg);
220                break;
221            }
222
223            case 't':
224            {
225                height = atoi(optarg);
226                break;
227            }
228
229            case 'l':
230            {
231                level = atoi(optarg);
232                break;
233            }
234
235            case 'p':
236            {
237                profile = atoi(optarg);
238                break;
239            }
240
241            case 'v':
242            {
243                codec = atoi(optarg);
244                if (codec < 0 || codec > 2) {
245                    usage(argv[0]);
246                }
247                break;
248            }
249
250            case 'o':
251            {
252                fileName = optarg;
253                break;
254            }
255
256            case 's':
257            {
258                preferSoftwareCodec = true;
259                break;
260            }
261
262            case 'h':
263            default:
264            {
265                usage(argv[0]);
266                break;
267            }
268        }
269    }
270
271    status_t err = OK;
272    sp<MediaSource> source =
273        new DummySource(width, height, nFrames, frameRateFps, colorFormat);
274
275    sp<AMessage> enc_meta = new AMessage;
276    switch (codec) {
277        case 1:
278            enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
279            break;
280        case 2:
281            enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
282            break;
283        default:
284            enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
285            break;
286    }
287    enc_meta->setInt32("width", width);
288    enc_meta->setInt32("height", height);
289    enc_meta->setInt32("frame-rate", frameRateFps);
290    enc_meta->setInt32("bitrate", bitRateBps);
291    enc_meta->setInt32("stride", width);
292    enc_meta->setInt32("slice-height", height);
293    enc_meta->setInt32("i-frame-interval", iFramesIntervalSeconds);
294    enc_meta->setInt32("color-format", colorFormat);
295    if (level != -1) {
296        enc_meta->setInt32("level", level);
297    }
298    if (profile != -1) {
299        enc_meta->setInt32("profile", profile);
300    }
301
302    sp<ALooper> looper = new ALooper;
303    looper->setName("recordvideo");
304    looper->start();
305
306    sp<IMediaSource> encoder =
307        MediaCodecSource::Create(
308                looper, enc_meta, source, NULL /* consumer */,
309                preferSoftwareCodec ? MediaCodecSource::FLAG_PREFER_SOFTWARE_CODEC : 0);
310
311    int fd = open(fileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
312    if (fd < 0) {
313        fprintf(stderr, "couldn't open file");
314        return 1;
315    }
316    sp<MPEG4Writer> writer = new MPEG4Writer(fd);
317    close(fd);
318    writer->addSource(encoder);
319    int64_t start = systemTime();
320    CHECK_EQ((status_t)OK, writer->start());
321    while (!writer->reachedEOS()) {
322        usleep(100000);
323    }
324    err = writer->stop();
325    int64_t end = systemTime();
326
327    fprintf(stderr, "$\n");
328
329    if (err != OK && err != ERROR_END_OF_STREAM) {
330        fprintf(stderr, "record failed: %d\n", err);
331        return 1;
332    }
333    fprintf(stderr, "encoding %d frames in %" PRId64 " us\n", nFrames, (end-start)/1000);
334    fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
335    return 0;
336}
337