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