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