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