StagefrightRecorder.cpp revision 54ef1bae010f12dfe6a40ff4452695b1b11ff449
1/* 2 * Copyright (C) 2009 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 "StagefrightRecorder" 19#include <inttypes.h> 20#include <utils/Log.h> 21 22#include "WebmWriter.h" 23#include "StagefrightRecorder.h" 24 25#include <binder/IPCThreadState.h> 26#include <binder/IServiceManager.h> 27 28#include <media/IMediaPlayerService.h> 29#include <media/stagefright/foundation/ABuffer.h> 30#include <media/stagefright/foundation/ADebug.h> 31#include <media/stagefright/foundation/AMessage.h> 32#include <media/stagefright/foundation/ALooper.h> 33#include <media/stagefright/AudioSource.h> 34#include <media/stagefright/AMRWriter.h> 35#include <media/stagefright/AACWriter.h> 36#include <media/stagefright/CameraSource.h> 37#include <media/stagefright/CameraSourceTimeLapse.h> 38#include <media/stagefright/MPEG2TSWriter.h> 39#include <media/stagefright/MPEG4Writer.h> 40#include <media/stagefright/MediaDefs.h> 41#include <media/stagefright/MetaData.h> 42#include <media/stagefright/MediaCodecSource.h> 43#include <media/stagefright/OMXClient.h> 44#include <media/stagefright/OMXCodec.h> 45#include <media/MediaProfiles.h> 46#include <camera/ICamera.h> 47#include <camera/CameraParameters.h> 48 49#include <utils/Errors.h> 50#include <sys/types.h> 51#include <ctype.h> 52#include <unistd.h> 53 54#include <system/audio.h> 55 56#include "ARTPWriter.h" 57 58namespace android { 59 60// To collect the encoder usage for the battery app 61static void addBatteryData(uint32_t params) { 62 sp<IBinder> binder = 63 defaultServiceManager()->getService(String16("media.player")); 64 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 65 CHECK(service.get() != NULL); 66 67 service->addBatteryData(params); 68} 69 70 71StagefrightRecorder::StagefrightRecorder() 72 : mWriter(NULL), 73 mOutputFd(-1), 74 mAudioSource(AUDIO_SOURCE_CNT), 75 mVideoSource(VIDEO_SOURCE_LIST_END), 76 mCaptureTimeLapse(false), 77 mStarted(false) { 78 79 ALOGV("Constructor"); 80 reset(); 81} 82 83StagefrightRecorder::~StagefrightRecorder() { 84 ALOGV("Destructor"); 85 stop(); 86 87 if (mLooper != NULL) { 88 mLooper->stop(); 89 } 90} 91 92status_t StagefrightRecorder::init() { 93 ALOGV("init"); 94 95 mLooper = new ALooper; 96 mLooper->setName("recorder_looper"); 97 mLooper->start(); 98 99 return OK; 100} 101 102// The client side of mediaserver asks it to creat a SurfaceMediaSource 103// and return a interface reference. The client side will use that 104// while encoding GL Frames 105sp<IGraphicBufferProducer> StagefrightRecorder::querySurfaceMediaSource() const { 106 ALOGV("Get SurfaceMediaSource"); 107 return mGraphicBufferProducer; 108} 109 110status_t StagefrightRecorder::setAudioSource(audio_source_t as) { 111 ALOGV("setAudioSource: %d", as); 112 if (as < AUDIO_SOURCE_DEFAULT || 113 as >= AUDIO_SOURCE_CNT) { 114 ALOGE("Invalid audio source: %d", as); 115 return BAD_VALUE; 116 } 117 118 if (as == AUDIO_SOURCE_DEFAULT) { 119 mAudioSource = AUDIO_SOURCE_MIC; 120 } else { 121 mAudioSource = as; 122 } 123 124 return OK; 125} 126 127status_t StagefrightRecorder::setVideoSource(video_source vs) { 128 ALOGV("setVideoSource: %d", vs); 129 if (vs < VIDEO_SOURCE_DEFAULT || 130 vs >= VIDEO_SOURCE_LIST_END) { 131 ALOGE("Invalid video source: %d", vs); 132 return BAD_VALUE; 133 } 134 135 if (vs == VIDEO_SOURCE_DEFAULT) { 136 mVideoSource = VIDEO_SOURCE_CAMERA; 137 } else { 138 mVideoSource = vs; 139 } 140 141 return OK; 142} 143 144status_t StagefrightRecorder::setOutputFormat(output_format of) { 145 ALOGV("setOutputFormat: %d", of); 146 if (of < OUTPUT_FORMAT_DEFAULT || 147 of >= OUTPUT_FORMAT_LIST_END) { 148 ALOGE("Invalid output format: %d", of); 149 return BAD_VALUE; 150 } 151 152 if (of == OUTPUT_FORMAT_DEFAULT) { 153 mOutputFormat = OUTPUT_FORMAT_THREE_GPP; 154 } else { 155 mOutputFormat = of; 156 } 157 158 return OK; 159} 160 161status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) { 162 ALOGV("setAudioEncoder: %d", ae); 163 if (ae < AUDIO_ENCODER_DEFAULT || 164 ae >= AUDIO_ENCODER_LIST_END) { 165 ALOGE("Invalid audio encoder: %d", ae); 166 return BAD_VALUE; 167 } 168 169 if (ae == AUDIO_ENCODER_DEFAULT) { 170 mAudioEncoder = AUDIO_ENCODER_AMR_NB; 171 } else { 172 mAudioEncoder = ae; 173 } 174 175 return OK; 176} 177 178status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { 179 ALOGV("setVideoEncoder: %d", ve); 180 if (ve < VIDEO_ENCODER_DEFAULT || 181 ve >= VIDEO_ENCODER_LIST_END) { 182 ALOGE("Invalid video encoder: %d", ve); 183 return BAD_VALUE; 184 } 185 186 mVideoEncoder = ve; 187 188 return OK; 189} 190 191status_t StagefrightRecorder::setVideoSize(int width, int height) { 192 ALOGV("setVideoSize: %dx%d", width, height); 193 if (width <= 0 || height <= 0) { 194 ALOGE("Invalid video size: %dx%d", width, height); 195 return BAD_VALUE; 196 } 197 198 // Additional check on the dimension will be performed later 199 mVideoWidth = width; 200 mVideoHeight = height; 201 202 return OK; 203} 204 205status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { 206 ALOGV("setVideoFrameRate: %d", frames_per_second); 207 if ((frames_per_second <= 0 && frames_per_second != -1) || 208 frames_per_second > 120) { 209 ALOGE("Invalid video frame rate: %d", frames_per_second); 210 return BAD_VALUE; 211 } 212 213 // Additional check on the frame rate will be performed later 214 mFrameRate = frames_per_second; 215 216 return OK; 217} 218 219status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera, 220 const sp<ICameraRecordingProxy> &proxy) { 221 ALOGV("setCamera"); 222 if (camera == 0) { 223 ALOGE("camera is NULL"); 224 return BAD_VALUE; 225 } 226 if (proxy == 0) { 227 ALOGE("camera proxy is NULL"); 228 return BAD_VALUE; 229 } 230 231 mCamera = camera; 232 mCameraProxy = proxy; 233 return OK; 234} 235 236status_t StagefrightRecorder::setPreviewSurface(const sp<IGraphicBufferProducer> &surface) { 237 ALOGV("setPreviewSurface: %p", surface.get()); 238 mPreviewSurface = surface; 239 240 return OK; 241} 242 243status_t StagefrightRecorder::setOutputFile(const char * /* path */) { 244 ALOGE("setOutputFile(const char*) must not be called"); 245 // We don't actually support this at all, as the media_server process 246 // no longer has permissions to create files. 247 248 return -EPERM; 249} 250 251status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { 252 ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); 253 // These don't make any sense, do they? 254 CHECK_EQ(offset, 0ll); 255 CHECK_EQ(length, 0ll); 256 257 if (fd < 0) { 258 ALOGE("Invalid file descriptor: %d", fd); 259 return -EBADF; 260 } 261 262 if (mOutputFd >= 0) { 263 ::close(mOutputFd); 264 } 265 mOutputFd = dup(fd); 266 267 return OK; 268} 269 270// Attempt to parse an int64 literal optionally surrounded by whitespace, 271// returns true on success, false otherwise. 272static bool safe_strtoi64(const char *s, int64_t *val) { 273 char *end; 274 275 // It is lame, but according to man page, we have to set errno to 0 276 // before calling strtoll(). 277 errno = 0; 278 *val = strtoll(s, &end, 10); 279 280 if (end == s || errno == ERANGE) { 281 return false; 282 } 283 284 // Skip trailing whitespace 285 while (isspace(*end)) { 286 ++end; 287 } 288 289 // For a successful return, the string must contain nothing but a valid 290 // int64 literal optionally surrounded by whitespace. 291 292 return *end == '\0'; 293} 294 295// Return true if the value is in [0, 0x007FFFFFFF] 296static bool safe_strtoi32(const char *s, int32_t *val) { 297 int64_t temp; 298 if (safe_strtoi64(s, &temp)) { 299 if (temp >= 0 && temp <= 0x007FFFFFFF) { 300 *val = static_cast<int32_t>(temp); 301 return true; 302 } 303 } 304 return false; 305} 306 307// Trim both leading and trailing whitespace from the given string. 308static void TrimString(String8 *s) { 309 size_t num_bytes = s->bytes(); 310 const char *data = s->string(); 311 312 size_t leading_space = 0; 313 while (leading_space < num_bytes && isspace(data[leading_space])) { 314 ++leading_space; 315 } 316 317 size_t i = num_bytes; 318 while (i > leading_space && isspace(data[i - 1])) { 319 --i; 320 } 321 322 s->setTo(String8(&data[leading_space], i - leading_space)); 323} 324 325status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) { 326 ALOGV("setParamAudioSamplingRate: %d", sampleRate); 327 if (sampleRate <= 0) { 328 ALOGE("Invalid audio sampling rate: %d", sampleRate); 329 return BAD_VALUE; 330 } 331 332 // Additional check on the sample rate will be performed later. 333 mSampleRate = sampleRate; 334 return OK; 335} 336 337status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { 338 ALOGV("setParamAudioNumberOfChannels: %d", channels); 339 if (channels <= 0 || channels >= 3) { 340 ALOGE("Invalid number of audio channels: %d", channels); 341 return BAD_VALUE; 342 } 343 344 // Additional check on the number of channels will be performed later. 345 mAudioChannels = channels; 346 return OK; 347} 348 349status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) { 350 ALOGV("setParamAudioEncodingBitRate: %d", bitRate); 351 if (bitRate <= 0) { 352 ALOGE("Invalid audio encoding bit rate: %d", bitRate); 353 return BAD_VALUE; 354 } 355 356 // The target bit rate may not be exactly the same as the requested. 357 // It depends on many factors, such as rate control, and the bit rate 358 // range that a specific encoder supports. The mismatch between the 359 // the target and requested bit rate will NOT be treated as an error. 360 mAudioBitRate = bitRate; 361 return OK; 362} 363 364status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { 365 ALOGV("setParamVideoEncodingBitRate: %d", bitRate); 366 if (bitRate <= 0) { 367 ALOGE("Invalid video encoding bit rate: %d", bitRate); 368 return BAD_VALUE; 369 } 370 371 // The target bit rate may not be exactly the same as the requested. 372 // It depends on many factors, such as rate control, and the bit rate 373 // range that a specific encoder supports. The mismatch between the 374 // the target and requested bit rate will NOT be treated as an error. 375 mVideoBitRate = bitRate; 376 return OK; 377} 378 379// Always rotate clockwise, and only support 0, 90, 180 and 270 for now. 380status_t StagefrightRecorder::setParamVideoRotation(int32_t degrees) { 381 ALOGV("setParamVideoRotation: %d", degrees); 382 if (degrees < 0 || degrees % 90 != 0) { 383 ALOGE("Unsupported video rotation angle: %d", degrees); 384 return BAD_VALUE; 385 } 386 mRotationDegrees = degrees % 360; 387 return OK; 388} 389 390status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { 391 ALOGV("setParamMaxFileDurationUs: %lld us", timeUs); 392 393 // This is meant for backward compatibility for MediaRecorder.java 394 if (timeUs <= 0) { 395 ALOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs); 396 timeUs = 0; // Disable the duration limit for zero or negative values. 397 } else if (timeUs <= 100000LL) { // XXX: 100 milli-seconds 398 ALOGE("Max file duration is too short: %lld us", timeUs); 399 return BAD_VALUE; 400 } 401 402 if (timeUs <= 15 * 1000000LL) { 403 ALOGW("Target duration (%lld us) too short to be respected", timeUs); 404 } 405 mMaxFileDurationUs = timeUs; 406 return OK; 407} 408 409status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { 410 ALOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); 411 412 // This is meant for backward compatibility for MediaRecorder.java 413 if (bytes <= 0) { 414 ALOGW("Max file size is not positive: %lld bytes. " 415 "Disabling file size limit.", bytes); 416 bytes = 0; // Disable the file size limit for zero or negative values. 417 } else if (bytes <= 1024) { // XXX: 1 kB 418 ALOGE("Max file size is too small: %lld bytes", bytes); 419 return BAD_VALUE; 420 } 421 422 if (bytes <= 100 * 1024) { 423 ALOGW("Target file size (%lld bytes) is too small to be respected", bytes); 424 } 425 426 mMaxFileSizeBytes = bytes; 427 return OK; 428} 429 430status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { 431 ALOGV("setParamInterleaveDuration: %d", durationUs); 432 if (durationUs <= 500000) { // 500 ms 433 // If interleave duration is too small, it is very inefficient to do 434 // interleaving since the metadata overhead will count for a significant 435 // portion of the saved contents 436 ALOGE("Audio/video interleave duration is too small: %d us", durationUs); 437 return BAD_VALUE; 438 } else if (durationUs >= 10000000) { // 10 seconds 439 // If interleaving duration is too large, it can cause the recording 440 // session to use too much memory since we have to save the output 441 // data before we write them out 442 ALOGE("Audio/video interleave duration is too large: %d us", durationUs); 443 return BAD_VALUE; 444 } 445 mInterleaveDurationUs = durationUs; 446 return OK; 447} 448 449// If seconds < 0, only the first frame is I frame, and rest are all P frames 450// If seconds == 0, all frames are encoded as I frames. No P frames 451// If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames 452status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t seconds) { 453 ALOGV("setParamVideoIFramesInterval: %d seconds", seconds); 454 mIFramesIntervalSec = seconds; 455 return OK; 456} 457 458status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) { 459 ALOGV("setParam64BitFileOffset: %s", 460 use64Bit? "use 64 bit file offset": "use 32 bit file offset"); 461 mUse64BitFileOffset = use64Bit; 462 return OK; 463} 464 465status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) { 466 ALOGV("setParamVideoCameraId: %d", cameraId); 467 if (cameraId < 0) { 468 return BAD_VALUE; 469 } 470 mCameraId = cameraId; 471 return OK; 472} 473 474status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { 475 ALOGV("setParamTrackTimeStatus: %lld", timeDurationUs); 476 if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? 477 ALOGE("Tracking time duration too short: %lld us", timeDurationUs); 478 return BAD_VALUE; 479 } 480 mTrackEveryTimeDurationUs = timeDurationUs; 481 return OK; 482} 483 484status_t StagefrightRecorder::setParamVideoEncoderProfile(int32_t profile) { 485 ALOGV("setParamVideoEncoderProfile: %d", profile); 486 487 // Additional check will be done later when we load the encoder. 488 // For now, we are accepting values defined in OpenMAX IL. 489 mVideoEncoderProfile = profile; 490 return OK; 491} 492 493status_t StagefrightRecorder::setParamVideoEncoderLevel(int32_t level) { 494 ALOGV("setParamVideoEncoderLevel: %d", level); 495 496 // Additional check will be done later when we load the encoder. 497 // For now, we are accepting values defined in OpenMAX IL. 498 mVideoEncoderLevel = level; 499 return OK; 500} 501 502status_t StagefrightRecorder::setParamMovieTimeScale(int32_t timeScale) { 503 ALOGV("setParamMovieTimeScale: %d", timeScale); 504 505 // The range is set to be the same as the audio's time scale range 506 // since audio's time scale has a wider range. 507 if (timeScale < 600 || timeScale > 96000) { 508 ALOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale); 509 return BAD_VALUE; 510 } 511 mMovieTimeScale = timeScale; 512 return OK; 513} 514 515status_t StagefrightRecorder::setParamVideoTimeScale(int32_t timeScale) { 516 ALOGV("setParamVideoTimeScale: %d", timeScale); 517 518 // 60000 is chosen to make sure that each video frame from a 60-fps 519 // video has 1000 ticks. 520 if (timeScale < 600 || timeScale > 60000) { 521 ALOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale); 522 return BAD_VALUE; 523 } 524 mVideoTimeScale = timeScale; 525 return OK; 526} 527 528status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) { 529 ALOGV("setParamAudioTimeScale: %d", timeScale); 530 531 // 96000 Hz is the highest sampling rate support in AAC. 532 if (timeScale < 600 || timeScale > 96000) { 533 ALOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale); 534 return BAD_VALUE; 535 } 536 mAudioTimeScale = timeScale; 537 return OK; 538} 539 540status_t StagefrightRecorder::setParamTimeLapseEnable(int32_t timeLapseEnable) { 541 ALOGV("setParamTimeLapseEnable: %d", timeLapseEnable); 542 543 if(timeLapseEnable == 0) { 544 mCaptureTimeLapse = false; 545 } else if (timeLapseEnable == 1) { 546 mCaptureTimeLapse = true; 547 } else { 548 return BAD_VALUE; 549 } 550 return OK; 551} 552 553status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) { 554 ALOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs); 555 556 // Not allowing time more than a day 557 if (timeUs <= 0 || timeUs > 86400*1E6) { 558 ALOGE("Time between time lapse frame capture (%lld) is out of range [0, 1 Day]", timeUs); 559 return BAD_VALUE; 560 } 561 562 mTimeBetweenTimeLapseFrameCaptureUs = timeUs; 563 return OK; 564} 565 566status_t StagefrightRecorder::setParamGeoDataLongitude( 567 int64_t longitudex10000) { 568 569 if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { 570 return BAD_VALUE; 571 } 572 mLongitudex10000 = longitudex10000; 573 return OK; 574} 575 576status_t StagefrightRecorder::setParamGeoDataLatitude( 577 int64_t latitudex10000) { 578 579 if (latitudex10000 > 900000 || latitudex10000 < -900000) { 580 return BAD_VALUE; 581 } 582 mLatitudex10000 = latitudex10000; 583 return OK; 584} 585 586status_t StagefrightRecorder::setParameter( 587 const String8 &key, const String8 &value) { 588 ALOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); 589 if (key == "max-duration") { 590 int64_t max_duration_ms; 591 if (safe_strtoi64(value.string(), &max_duration_ms)) { 592 return setParamMaxFileDurationUs(1000LL * max_duration_ms); 593 } 594 } else if (key == "max-filesize") { 595 int64_t max_filesize_bytes; 596 if (safe_strtoi64(value.string(), &max_filesize_bytes)) { 597 return setParamMaxFileSizeBytes(max_filesize_bytes); 598 } 599 } else if (key == "interleave-duration-us") { 600 int32_t durationUs; 601 if (safe_strtoi32(value.string(), &durationUs)) { 602 return setParamInterleaveDuration(durationUs); 603 } 604 } else if (key == "param-movie-time-scale") { 605 int32_t timeScale; 606 if (safe_strtoi32(value.string(), &timeScale)) { 607 return setParamMovieTimeScale(timeScale); 608 } 609 } else if (key == "param-use-64bit-offset") { 610 int32_t use64BitOffset; 611 if (safe_strtoi32(value.string(), &use64BitOffset)) { 612 return setParam64BitFileOffset(use64BitOffset != 0); 613 } 614 } else if (key == "param-geotag-longitude") { 615 int64_t longitudex10000; 616 if (safe_strtoi64(value.string(), &longitudex10000)) { 617 return setParamGeoDataLongitude(longitudex10000); 618 } 619 } else if (key == "param-geotag-latitude") { 620 int64_t latitudex10000; 621 if (safe_strtoi64(value.string(), &latitudex10000)) { 622 return setParamGeoDataLatitude(latitudex10000); 623 } 624 } else if (key == "param-track-time-status") { 625 int64_t timeDurationUs; 626 if (safe_strtoi64(value.string(), &timeDurationUs)) { 627 return setParamTrackTimeStatus(timeDurationUs); 628 } 629 } else if (key == "audio-param-sampling-rate") { 630 int32_t sampling_rate; 631 if (safe_strtoi32(value.string(), &sampling_rate)) { 632 return setParamAudioSamplingRate(sampling_rate); 633 } 634 } else if (key == "audio-param-number-of-channels") { 635 int32_t number_of_channels; 636 if (safe_strtoi32(value.string(), &number_of_channels)) { 637 return setParamAudioNumberOfChannels(number_of_channels); 638 } 639 } else if (key == "audio-param-encoding-bitrate") { 640 int32_t audio_bitrate; 641 if (safe_strtoi32(value.string(), &audio_bitrate)) { 642 return setParamAudioEncodingBitRate(audio_bitrate); 643 } 644 } else if (key == "audio-param-time-scale") { 645 int32_t timeScale; 646 if (safe_strtoi32(value.string(), &timeScale)) { 647 return setParamAudioTimeScale(timeScale); 648 } 649 } else if (key == "video-param-encoding-bitrate") { 650 int32_t video_bitrate; 651 if (safe_strtoi32(value.string(), &video_bitrate)) { 652 return setParamVideoEncodingBitRate(video_bitrate); 653 } 654 } else if (key == "video-param-rotation-angle-degrees") { 655 int32_t degrees; 656 if (safe_strtoi32(value.string(), °rees)) { 657 return setParamVideoRotation(degrees); 658 } 659 } else if (key == "video-param-i-frames-interval") { 660 int32_t seconds; 661 if (safe_strtoi32(value.string(), &seconds)) { 662 return setParamVideoIFramesInterval(seconds); 663 } 664 } else if (key == "video-param-encoder-profile") { 665 int32_t profile; 666 if (safe_strtoi32(value.string(), &profile)) { 667 return setParamVideoEncoderProfile(profile); 668 } 669 } else if (key == "video-param-encoder-level") { 670 int32_t level; 671 if (safe_strtoi32(value.string(), &level)) { 672 return setParamVideoEncoderLevel(level); 673 } 674 } else if (key == "video-param-camera-id") { 675 int32_t cameraId; 676 if (safe_strtoi32(value.string(), &cameraId)) { 677 return setParamVideoCameraId(cameraId); 678 } 679 } else if (key == "video-param-time-scale") { 680 int32_t timeScale; 681 if (safe_strtoi32(value.string(), &timeScale)) { 682 return setParamVideoTimeScale(timeScale); 683 } 684 } else if (key == "time-lapse-enable") { 685 int32_t timeLapseEnable; 686 if (safe_strtoi32(value.string(), &timeLapseEnable)) { 687 return setParamTimeLapseEnable(timeLapseEnable); 688 } 689 } else if (key == "time-between-time-lapse-frame-capture") { 690 int64_t timeBetweenTimeLapseFrameCaptureUs; 691 if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureUs)) { 692 return setParamTimeBetweenTimeLapseFrameCapture( 693 timeBetweenTimeLapseFrameCaptureUs); 694 } 695 } else { 696 ALOGE("setParameter: failed to find key %s", key.string()); 697 } 698 return BAD_VALUE; 699} 700 701status_t StagefrightRecorder::setParameters(const String8 ¶ms) { 702 ALOGV("setParameters: %s", params.string()); 703 const char *cparams = params.string(); 704 const char *key_start = cparams; 705 for (;;) { 706 const char *equal_pos = strchr(key_start, '='); 707 if (equal_pos == NULL) { 708 ALOGE("Parameters %s miss a value", cparams); 709 return BAD_VALUE; 710 } 711 String8 key(key_start, equal_pos - key_start); 712 TrimString(&key); 713 if (key.length() == 0) { 714 ALOGE("Parameters %s contains an empty key", cparams); 715 return BAD_VALUE; 716 } 717 const char *value_start = equal_pos + 1; 718 const char *semicolon_pos = strchr(value_start, ';'); 719 String8 value; 720 if (semicolon_pos == NULL) { 721 value.setTo(value_start); 722 } else { 723 value.setTo(value_start, semicolon_pos - value_start); 724 } 725 if (setParameter(key, value) != OK) { 726 return BAD_VALUE; 727 } 728 if (semicolon_pos == NULL) { 729 break; // Reaches the end 730 } 731 key_start = semicolon_pos + 1; 732 } 733 return OK; 734} 735 736status_t StagefrightRecorder::setListener(const sp<IMediaRecorderClient> &listener) { 737 mListener = listener; 738 739 return OK; 740} 741 742status_t StagefrightRecorder::setClientName(const String16& clientName) { 743 mClientName = clientName; 744 745 return OK; 746} 747 748status_t StagefrightRecorder::prepareInternal() { 749 ALOGV("prepare"); 750 if (mOutputFd < 0) { 751 ALOGE("Output file descriptor is invalid"); 752 return INVALID_OPERATION; 753 } 754 755 // Get UID here for permission checking 756 mClientUid = IPCThreadState::self()->getCallingUid(); 757 758 status_t status = OK; 759 760 switch (mOutputFormat) { 761 case OUTPUT_FORMAT_DEFAULT: 762 case OUTPUT_FORMAT_THREE_GPP: 763 case OUTPUT_FORMAT_MPEG_4: 764 case OUTPUT_FORMAT_WEBM: 765 status = setupMPEG4orWEBMRecording(); 766 break; 767 768 case OUTPUT_FORMAT_AMR_NB: 769 case OUTPUT_FORMAT_AMR_WB: 770 status = setupAMRRecording(); 771 break; 772 773 case OUTPUT_FORMAT_AAC_ADIF: 774 case OUTPUT_FORMAT_AAC_ADTS: 775 status = setupAACRecording(); 776 break; 777 778 case OUTPUT_FORMAT_RTP_AVP: 779 status = setupRTPRecording(); 780 break; 781 782 case OUTPUT_FORMAT_MPEG2TS: 783 status = setupMPEG2TSRecording(); 784 break; 785 786 default: 787 ALOGE("Unsupported output file format: %d", mOutputFormat); 788 status = UNKNOWN_ERROR; 789 break; 790 } 791 792 return status; 793} 794 795status_t StagefrightRecorder::prepare() { 796 if (mVideoSource == VIDEO_SOURCE_SURFACE) { 797 return prepareInternal(); 798 } 799 return OK; 800} 801 802status_t StagefrightRecorder::start() { 803 ALOGV("start"); 804 if (mOutputFd < 0) { 805 ALOGE("Output file descriptor is invalid"); 806 return INVALID_OPERATION; 807 } 808 809 status_t status = OK; 810 811 if (mVideoSource != VIDEO_SOURCE_SURFACE) { 812 status = prepareInternal(); 813 if (status != OK) { 814 return status; 815 } 816 } 817 818 if (mWriter == NULL) { 819 ALOGE("File writer is not avaialble"); 820 return UNKNOWN_ERROR; 821 } 822 823 switch (mOutputFormat) { 824 case OUTPUT_FORMAT_DEFAULT: 825 case OUTPUT_FORMAT_THREE_GPP: 826 case OUTPUT_FORMAT_MPEG_4: 827 case OUTPUT_FORMAT_WEBM: 828 { 829 bool isMPEG4 = true; 830 if (mOutputFormat == OUTPUT_FORMAT_WEBM) { 831 isMPEG4 = false; 832 } 833 sp<MetaData> meta = new MetaData; 834 setupMPEG4orWEBMMetaData(&meta); 835 status = mWriter->start(meta.get()); 836 break; 837 } 838 839 case OUTPUT_FORMAT_AMR_NB: 840 case OUTPUT_FORMAT_AMR_WB: 841 case OUTPUT_FORMAT_AAC_ADIF: 842 case OUTPUT_FORMAT_AAC_ADTS: 843 case OUTPUT_FORMAT_RTP_AVP: 844 case OUTPUT_FORMAT_MPEG2TS: 845 { 846 status = mWriter->start(); 847 break; 848 } 849 850 default: 851 { 852 ALOGE("Unsupported output file format: %d", mOutputFormat); 853 status = UNKNOWN_ERROR; 854 break; 855 } 856 } 857 858 if (status != OK) { 859 mWriter.clear(); 860 mWriter = NULL; 861 } 862 863 if ((status == OK) && (!mStarted)) { 864 mStarted = true; 865 866 uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted; 867 if (mAudioSource != AUDIO_SOURCE_CNT) { 868 params |= IMediaPlayerService::kBatteryDataTrackAudio; 869 } 870 if (mVideoSource != VIDEO_SOURCE_LIST_END) { 871 params |= IMediaPlayerService::kBatteryDataTrackVideo; 872 } 873 874 addBatteryData(params); 875 } 876 877 return status; 878} 879 880sp<MediaSource> StagefrightRecorder::createAudioSource() { 881 sp<AudioSource> audioSource = 882 new AudioSource( 883 mAudioSource, 884 mSampleRate, 885 mAudioChannels); 886 887 status_t err = audioSource->initCheck(); 888 889 if (err != OK) { 890 ALOGE("audio source is not initialized"); 891 return NULL; 892 } 893 894 sp<AMessage> format = new AMessage; 895 const char *mime; 896 switch (mAudioEncoder) { 897 case AUDIO_ENCODER_AMR_NB: 898 case AUDIO_ENCODER_DEFAULT: 899 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB); 900 break; 901 case AUDIO_ENCODER_AMR_WB: 902 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB); 903 break; 904 case AUDIO_ENCODER_AAC: 905 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); 906 format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC); 907 break; 908 case AUDIO_ENCODER_HE_AAC: 909 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); 910 format->setInt32("aac-profile", OMX_AUDIO_AACObjectHE); 911 break; 912 case AUDIO_ENCODER_AAC_ELD: 913 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); 914 format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD); 915 break; 916 917 default: 918 ALOGE("Unknown audio encoder: %d", mAudioEncoder); 919 return NULL; 920 } 921 922 int32_t maxInputSize; 923 CHECK(audioSource->getFormat()->findInt32( 924 kKeyMaxInputSize, &maxInputSize)); 925 926 format->setInt32("max-input-size", maxInputSize); 927 format->setInt32("channel-count", mAudioChannels); 928 format->setInt32("sample-rate", mSampleRate); 929 format->setInt32("bitrate", mAudioBitRate); 930 if (mAudioTimeScale > 0) { 931 format->setInt32("time-scale", mAudioTimeScale); 932 } 933 934 sp<MediaSource> audioEncoder = 935 MediaCodecSource::Create(mLooper, format, audioSource); 936 mAudioSourceNode = audioSource; 937 938 if (audioEncoder == NULL) { 939 ALOGE("Failed to create audio encoder"); 940 } 941 942 return audioEncoder; 943} 944 945status_t StagefrightRecorder::setupAACRecording() { 946 // FIXME: 947 // Add support for OUTPUT_FORMAT_AAC_ADIF 948 CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS); 949 950 CHECK(mAudioEncoder == AUDIO_ENCODER_AAC || 951 mAudioEncoder == AUDIO_ENCODER_HE_AAC || 952 mAudioEncoder == AUDIO_ENCODER_AAC_ELD); 953 CHECK(mAudioSource != AUDIO_SOURCE_CNT); 954 955 mWriter = new AACWriter(mOutputFd); 956 return setupRawAudioRecording(); 957} 958 959status_t StagefrightRecorder::setupAMRRecording() { 960 CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB || 961 mOutputFormat == OUTPUT_FORMAT_AMR_WB); 962 963 if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) { 964 if (mAudioEncoder != AUDIO_ENCODER_DEFAULT && 965 mAudioEncoder != AUDIO_ENCODER_AMR_NB) { 966 ALOGE("Invalid encoder %d used for AMRNB recording", 967 mAudioEncoder); 968 return BAD_VALUE; 969 } 970 } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB 971 if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { 972 ALOGE("Invlaid encoder %d used for AMRWB recording", 973 mAudioEncoder); 974 return BAD_VALUE; 975 } 976 } 977 978 mWriter = new AMRWriter(mOutputFd); 979 return setupRawAudioRecording(); 980} 981 982status_t StagefrightRecorder::setupRawAudioRecording() { 983 if (mAudioSource >= AUDIO_SOURCE_CNT) { 984 ALOGE("Invalid audio source: %d", mAudioSource); 985 return BAD_VALUE; 986 } 987 988 status_t status = BAD_VALUE; 989 if (OK != (status = checkAudioEncoderCapabilities())) { 990 return status; 991 } 992 993 sp<MediaSource> audioEncoder = createAudioSource(); 994 if (audioEncoder == NULL) { 995 return UNKNOWN_ERROR; 996 } 997 998 CHECK(mWriter != 0); 999 mWriter->addSource(audioEncoder); 1000 1001 if (mMaxFileDurationUs != 0) { 1002 mWriter->setMaxFileDuration(mMaxFileDurationUs); 1003 } 1004 if (mMaxFileSizeBytes != 0) { 1005 mWriter->setMaxFileSize(mMaxFileSizeBytes); 1006 } 1007 mWriter->setListener(mListener); 1008 1009 return OK; 1010} 1011 1012status_t StagefrightRecorder::setupRTPRecording() { 1013 CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP); 1014 1015 if ((mAudioSource != AUDIO_SOURCE_CNT 1016 && mVideoSource != VIDEO_SOURCE_LIST_END) 1017 || (mAudioSource == AUDIO_SOURCE_CNT 1018 && mVideoSource == VIDEO_SOURCE_LIST_END)) { 1019 // Must have exactly one source. 1020 return BAD_VALUE; 1021 } 1022 1023 if (mOutputFd < 0) { 1024 return BAD_VALUE; 1025 } 1026 1027 sp<MediaSource> source; 1028 1029 if (mAudioSource != AUDIO_SOURCE_CNT) { 1030 source = createAudioSource(); 1031 } else { 1032 setDefaultVideoEncoderIfNecessary(); 1033 1034 sp<MediaSource> mediaSource; 1035 status_t err = setupMediaSource(&mediaSource); 1036 if (err != OK) { 1037 return err; 1038 } 1039 1040 err = setupVideoEncoder(mediaSource, &source); 1041 if (err != OK) { 1042 return err; 1043 } 1044 } 1045 1046 mWriter = new ARTPWriter(mOutputFd); 1047 mWriter->addSource(source); 1048 mWriter->setListener(mListener); 1049 1050 return OK; 1051} 1052 1053status_t StagefrightRecorder::setupMPEG2TSRecording() { 1054 CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS); 1055 1056 sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd); 1057 1058 if (mAudioSource != AUDIO_SOURCE_CNT) { 1059 if (mAudioEncoder != AUDIO_ENCODER_AAC && 1060 mAudioEncoder != AUDIO_ENCODER_HE_AAC && 1061 mAudioEncoder != AUDIO_ENCODER_AAC_ELD) { 1062 return ERROR_UNSUPPORTED; 1063 } 1064 1065 status_t err = setupAudioEncoder(writer); 1066 1067 if (err != OK) { 1068 return err; 1069 } 1070 } 1071 1072 if (mVideoSource < VIDEO_SOURCE_LIST_END) { 1073 if (mVideoEncoder != VIDEO_ENCODER_H264) { 1074 ALOGE("MPEG2TS recording only supports H.264 encoding!"); 1075 return ERROR_UNSUPPORTED; 1076 } 1077 1078 sp<MediaSource> mediaSource; 1079 status_t err = setupMediaSource(&mediaSource); 1080 if (err != OK) { 1081 return err; 1082 } 1083 1084 sp<MediaSource> encoder; 1085 err = setupVideoEncoder(mediaSource, &encoder); 1086 1087 if (err != OK) { 1088 return err; 1089 } 1090 1091 writer->addSource(encoder); 1092 } 1093 1094 if (mMaxFileDurationUs != 0) { 1095 writer->setMaxFileDuration(mMaxFileDurationUs); 1096 } 1097 1098 if (mMaxFileSizeBytes != 0) { 1099 writer->setMaxFileSize(mMaxFileSizeBytes); 1100 } 1101 1102 mWriter = writer; 1103 1104 return OK; 1105} 1106 1107void StagefrightRecorder::clipVideoFrameRate() { 1108 ALOGV("clipVideoFrameRate: encoder %d", mVideoEncoder); 1109 if (mFrameRate == -1) { 1110 mFrameRate = mEncoderProfiles->getCamcorderProfileParamByName( 1111 "vid.fps", mCameraId, CAMCORDER_QUALITY_LOW); 1112 ALOGW("Using default video fps %d", mFrameRate); 1113 } 1114 1115 int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName( 1116 "enc.vid.fps.min", mVideoEncoder); 1117 int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName( 1118 "enc.vid.fps.max", mVideoEncoder); 1119 if (mFrameRate < minFrameRate && minFrameRate != -1) { 1120 ALOGW("Intended video encoding frame rate (%d fps) is too small" 1121 " and will be set to (%d fps)", mFrameRate, minFrameRate); 1122 mFrameRate = minFrameRate; 1123 } else if (mFrameRate > maxFrameRate && maxFrameRate != -1) { 1124 ALOGW("Intended video encoding frame rate (%d fps) is too large" 1125 " and will be set to (%d fps)", mFrameRate, maxFrameRate); 1126 mFrameRate = maxFrameRate; 1127 } 1128} 1129 1130void StagefrightRecorder::clipVideoBitRate() { 1131 ALOGV("clipVideoBitRate: encoder %d", mVideoEncoder); 1132 int minBitRate = mEncoderProfiles->getVideoEncoderParamByName( 1133 "enc.vid.bps.min", mVideoEncoder); 1134 int maxBitRate = mEncoderProfiles->getVideoEncoderParamByName( 1135 "enc.vid.bps.max", mVideoEncoder); 1136 if (mVideoBitRate < minBitRate && minBitRate != -1) { 1137 ALOGW("Intended video encoding bit rate (%d bps) is too small" 1138 " and will be set to (%d bps)", mVideoBitRate, minBitRate); 1139 mVideoBitRate = minBitRate; 1140 } else if (mVideoBitRate > maxBitRate && maxBitRate != -1) { 1141 ALOGW("Intended video encoding bit rate (%d bps) is too large" 1142 " and will be set to (%d bps)", mVideoBitRate, maxBitRate); 1143 mVideoBitRate = maxBitRate; 1144 } 1145} 1146 1147void StagefrightRecorder::clipVideoFrameWidth() { 1148 ALOGV("clipVideoFrameWidth: encoder %d", mVideoEncoder); 1149 int minFrameWidth = mEncoderProfiles->getVideoEncoderParamByName( 1150 "enc.vid.width.min", mVideoEncoder); 1151 int maxFrameWidth = mEncoderProfiles->getVideoEncoderParamByName( 1152 "enc.vid.width.max", mVideoEncoder); 1153 if (mVideoWidth < minFrameWidth && minFrameWidth != -1) { 1154 ALOGW("Intended video encoding frame width (%d) is too small" 1155 " and will be set to (%d)", mVideoWidth, minFrameWidth); 1156 mVideoWidth = minFrameWidth; 1157 } else if (mVideoWidth > maxFrameWidth && maxFrameWidth != -1) { 1158 ALOGW("Intended video encoding frame width (%d) is too large" 1159 " and will be set to (%d)", mVideoWidth, maxFrameWidth); 1160 mVideoWidth = maxFrameWidth; 1161 } 1162} 1163 1164status_t StagefrightRecorder::checkVideoEncoderCapabilities( 1165 bool *supportsCameraSourceMetaDataMode) { 1166 /* hardware codecs must support camera source meta data mode */ 1167 Vector<CodecCapabilities> codecs; 1168 OMXClient client; 1169 CHECK_EQ(client.connect(), (status_t)OK); 1170 QueryCodecs( 1171 client.interface(), 1172 (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 : 1173 mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 : 1174 mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""), 1175 false /* decoder */, true /* hwCodec */, &codecs); 1176 *supportsCameraSourceMetaDataMode = codecs.size() > 0; 1177 ALOGV("encoder %s camera source meta-data mode", 1178 *supportsCameraSourceMetaDataMode ? "supports" : "DOES NOT SUPPORT"); 1179 1180 if (!mCaptureTimeLapse) { 1181 // Dont clip for time lapse capture as encoder will have enough 1182 // time to encode because of slow capture rate of time lapse. 1183 clipVideoBitRate(); 1184 clipVideoFrameRate(); 1185 clipVideoFrameWidth(); 1186 clipVideoFrameHeight(); 1187 setDefaultProfileIfNecessary(); 1188 } 1189 return OK; 1190} 1191 1192// Set to use AVC baseline profile if the encoding parameters matches 1193// CAMCORDER_QUALITY_LOW profile; this is for the sake of MMS service. 1194void StagefrightRecorder::setDefaultProfileIfNecessary() { 1195 ALOGV("setDefaultProfileIfNecessary"); 1196 1197 camcorder_quality quality = CAMCORDER_QUALITY_LOW; 1198 1199 int64_t durationUs = mEncoderProfiles->getCamcorderProfileParamByName( 1200 "duration", mCameraId, quality) * 1000000LL; 1201 1202 int fileFormat = mEncoderProfiles->getCamcorderProfileParamByName( 1203 "file.format", mCameraId, quality); 1204 1205 int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName( 1206 "vid.codec", mCameraId, quality); 1207 1208 int videoBitRate = mEncoderProfiles->getCamcorderProfileParamByName( 1209 "vid.bps", mCameraId, quality); 1210 1211 int videoFrameRate = mEncoderProfiles->getCamcorderProfileParamByName( 1212 "vid.fps", mCameraId, quality); 1213 1214 int videoFrameWidth = mEncoderProfiles->getCamcorderProfileParamByName( 1215 "vid.width", mCameraId, quality); 1216 1217 int videoFrameHeight = mEncoderProfiles->getCamcorderProfileParamByName( 1218 "vid.height", mCameraId, quality); 1219 1220 int audioCodec = mEncoderProfiles->getCamcorderProfileParamByName( 1221 "aud.codec", mCameraId, quality); 1222 1223 int audioBitRate = mEncoderProfiles->getCamcorderProfileParamByName( 1224 "aud.bps", mCameraId, quality); 1225 1226 int audioSampleRate = mEncoderProfiles->getCamcorderProfileParamByName( 1227 "aud.hz", mCameraId, quality); 1228 1229 int audioChannels = mEncoderProfiles->getCamcorderProfileParamByName( 1230 "aud.ch", mCameraId, quality); 1231 1232 if (durationUs == mMaxFileDurationUs && 1233 fileFormat == mOutputFormat && 1234 videoCodec == mVideoEncoder && 1235 videoBitRate == mVideoBitRate && 1236 videoFrameRate == mFrameRate && 1237 videoFrameWidth == mVideoWidth && 1238 videoFrameHeight == mVideoHeight && 1239 audioCodec == mAudioEncoder && 1240 audioBitRate == mAudioBitRate && 1241 audioSampleRate == mSampleRate && 1242 audioChannels == mAudioChannels) { 1243 if (videoCodec == VIDEO_ENCODER_H264) { 1244 ALOGI("Force to use AVC baseline profile"); 1245 setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline); 1246 } 1247 } 1248} 1249 1250void StagefrightRecorder::setDefaultVideoEncoderIfNecessary() { 1251 if (mVideoEncoder == VIDEO_ENCODER_DEFAULT) { 1252 if (mOutputFormat == OUTPUT_FORMAT_WEBM) { 1253 // default to VP8 for WEBM recording 1254 mVideoEncoder = VIDEO_ENCODER_VP8; 1255 } else { 1256 // pick the default encoder for CAMCORDER_QUALITY_LOW 1257 int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName( 1258 "vid.codec", mCameraId, CAMCORDER_QUALITY_LOW); 1259 1260 if (videoCodec > VIDEO_ENCODER_DEFAULT && 1261 videoCodec < VIDEO_ENCODER_LIST_END) { 1262 mVideoEncoder = (video_encoder)videoCodec; 1263 } else { 1264 // default to H.264 if camcorder profile not available 1265 mVideoEncoder = VIDEO_ENCODER_H264; 1266 } 1267 } 1268 } 1269} 1270 1271status_t StagefrightRecorder::checkAudioEncoderCapabilities() { 1272 clipAudioBitRate(); 1273 clipAudioSampleRate(); 1274 clipNumberOfAudioChannels(); 1275 return OK; 1276} 1277 1278void StagefrightRecorder::clipAudioBitRate() { 1279 ALOGV("clipAudioBitRate: encoder %d", mAudioEncoder); 1280 1281 int minAudioBitRate = 1282 mEncoderProfiles->getAudioEncoderParamByName( 1283 "enc.aud.bps.min", mAudioEncoder); 1284 if (minAudioBitRate != -1 && mAudioBitRate < minAudioBitRate) { 1285 ALOGW("Intended audio encoding bit rate (%d) is too small" 1286 " and will be set to (%d)", mAudioBitRate, minAudioBitRate); 1287 mAudioBitRate = minAudioBitRate; 1288 } 1289 1290 int maxAudioBitRate = 1291 mEncoderProfiles->getAudioEncoderParamByName( 1292 "enc.aud.bps.max", mAudioEncoder); 1293 if (maxAudioBitRate != -1 && mAudioBitRate > maxAudioBitRate) { 1294 ALOGW("Intended audio encoding bit rate (%d) is too large" 1295 " and will be set to (%d)", mAudioBitRate, maxAudioBitRate); 1296 mAudioBitRate = maxAudioBitRate; 1297 } 1298} 1299 1300void StagefrightRecorder::clipAudioSampleRate() { 1301 ALOGV("clipAudioSampleRate: encoder %d", mAudioEncoder); 1302 1303 int minSampleRate = 1304 mEncoderProfiles->getAudioEncoderParamByName( 1305 "enc.aud.hz.min", mAudioEncoder); 1306 if (minSampleRate != -1 && mSampleRate < minSampleRate) { 1307 ALOGW("Intended audio sample rate (%d) is too small" 1308 " and will be set to (%d)", mSampleRate, minSampleRate); 1309 mSampleRate = minSampleRate; 1310 } 1311 1312 int maxSampleRate = 1313 mEncoderProfiles->getAudioEncoderParamByName( 1314 "enc.aud.hz.max", mAudioEncoder); 1315 if (maxSampleRate != -1 && mSampleRate > maxSampleRate) { 1316 ALOGW("Intended audio sample rate (%d) is too large" 1317 " and will be set to (%d)", mSampleRate, maxSampleRate); 1318 mSampleRate = maxSampleRate; 1319 } 1320} 1321 1322void StagefrightRecorder::clipNumberOfAudioChannels() { 1323 ALOGV("clipNumberOfAudioChannels: encoder %d", mAudioEncoder); 1324 1325 int minChannels = 1326 mEncoderProfiles->getAudioEncoderParamByName( 1327 "enc.aud.ch.min", mAudioEncoder); 1328 if (minChannels != -1 && mAudioChannels < minChannels) { 1329 ALOGW("Intended number of audio channels (%d) is too small" 1330 " and will be set to (%d)", mAudioChannels, minChannels); 1331 mAudioChannels = minChannels; 1332 } 1333 1334 int maxChannels = 1335 mEncoderProfiles->getAudioEncoderParamByName( 1336 "enc.aud.ch.max", mAudioEncoder); 1337 if (maxChannels != -1 && mAudioChannels > maxChannels) { 1338 ALOGW("Intended number of audio channels (%d) is too large" 1339 " and will be set to (%d)", mAudioChannels, maxChannels); 1340 mAudioChannels = maxChannels; 1341 } 1342} 1343 1344void StagefrightRecorder::clipVideoFrameHeight() { 1345 ALOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder); 1346 int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( 1347 "enc.vid.height.min", mVideoEncoder); 1348 int maxFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( 1349 "enc.vid.height.max", mVideoEncoder); 1350 if (minFrameHeight != -1 && mVideoHeight < minFrameHeight) { 1351 ALOGW("Intended video encoding frame height (%d) is too small" 1352 " and will be set to (%d)", mVideoHeight, minFrameHeight); 1353 mVideoHeight = minFrameHeight; 1354 } else if (maxFrameHeight != -1 && mVideoHeight > maxFrameHeight) { 1355 ALOGW("Intended video encoding frame height (%d) is too large" 1356 " and will be set to (%d)", mVideoHeight, maxFrameHeight); 1357 mVideoHeight = maxFrameHeight; 1358 } 1359} 1360 1361// Set up the appropriate MediaSource depending on the chosen option 1362status_t StagefrightRecorder::setupMediaSource( 1363 sp<MediaSource> *mediaSource) { 1364 if (mVideoSource == VIDEO_SOURCE_DEFAULT 1365 || mVideoSource == VIDEO_SOURCE_CAMERA) { 1366 sp<CameraSource> cameraSource; 1367 status_t err = setupCameraSource(&cameraSource); 1368 if (err != OK) { 1369 return err; 1370 } 1371 *mediaSource = cameraSource; 1372 } else if (mVideoSource == VIDEO_SOURCE_SURFACE) { 1373 *mediaSource = NULL; 1374 } else { 1375 return INVALID_OPERATION; 1376 } 1377 return OK; 1378} 1379 1380status_t StagefrightRecorder::setupCameraSource( 1381 sp<CameraSource> *cameraSource) { 1382 status_t err = OK; 1383 bool encoderSupportsCameraSourceMetaDataMode; 1384 if ((err = checkVideoEncoderCapabilities( 1385 &encoderSupportsCameraSourceMetaDataMode)) != OK) { 1386 return err; 1387 } 1388 Size videoSize; 1389 videoSize.width = mVideoWidth; 1390 videoSize.height = mVideoHeight; 1391 if (mCaptureTimeLapse) { 1392 if (mTimeBetweenTimeLapseFrameCaptureUs < 0) { 1393 ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld", 1394 mTimeBetweenTimeLapseFrameCaptureUs); 1395 return BAD_VALUE; 1396 } 1397 1398 mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( 1399 mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, 1400 videoSize, mFrameRate, mPreviewSurface, 1401 mTimeBetweenTimeLapseFrameCaptureUs, 1402 encoderSupportsCameraSourceMetaDataMode); 1403 *cameraSource = mCameraSourceTimeLapse; 1404 } else { 1405 *cameraSource = CameraSource::CreateFromCamera( 1406 mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, 1407 videoSize, mFrameRate, 1408 mPreviewSurface, encoderSupportsCameraSourceMetaDataMode); 1409 } 1410 mCamera.clear(); 1411 mCameraProxy.clear(); 1412 if (*cameraSource == NULL) { 1413 return UNKNOWN_ERROR; 1414 } 1415 1416 if ((*cameraSource)->initCheck() != OK) { 1417 (*cameraSource).clear(); 1418 *cameraSource = NULL; 1419 return NO_INIT; 1420 } 1421 1422 // When frame rate is not set, the actual frame rate will be set to 1423 // the current frame rate being used. 1424 if (mFrameRate == -1) { 1425 int32_t frameRate = 0; 1426 CHECK ((*cameraSource)->getFormat()->findInt32( 1427 kKeyFrameRate, &frameRate)); 1428 ALOGI("Frame rate is not explicitly set. Use the current frame " 1429 "rate (%d fps)", frameRate); 1430 mFrameRate = frameRate; 1431 } 1432 1433 CHECK(mFrameRate != -1); 1434 1435 mIsMetaDataStoredInVideoBuffers = 1436 (*cameraSource)->isMetaDataStoredInVideoBuffers(); 1437 1438 return OK; 1439} 1440 1441status_t StagefrightRecorder::setupVideoEncoder( 1442 sp<MediaSource> cameraSource, 1443 sp<MediaSource> *source) { 1444 source->clear(); 1445 1446 sp<AMessage> format = new AMessage(); 1447 1448 switch (mVideoEncoder) { 1449 case VIDEO_ENCODER_H263: 1450 format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263); 1451 break; 1452 1453 case VIDEO_ENCODER_MPEG_4_SP: 1454 format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4); 1455 break; 1456 1457 case VIDEO_ENCODER_H264: 1458 format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); 1459 break; 1460 1461 case VIDEO_ENCODER_VP8: 1462 format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8); 1463 break; 1464 1465 default: 1466 CHECK(!"Should not be here, unsupported video encoding."); 1467 break; 1468 } 1469 1470 if (cameraSource != NULL) { 1471 sp<MetaData> meta = cameraSource->getFormat(); 1472 1473 int32_t width, height, stride, sliceHeight, colorFormat; 1474 CHECK(meta->findInt32(kKeyWidth, &width)); 1475 CHECK(meta->findInt32(kKeyHeight, &height)); 1476 CHECK(meta->findInt32(kKeyStride, &stride)); 1477 CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); 1478 CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); 1479 1480 format->setInt32("width", width); 1481 format->setInt32("height", height); 1482 format->setInt32("stride", stride); 1483 format->setInt32("slice-height", sliceHeight); 1484 format->setInt32("color-format", colorFormat); 1485 } else { 1486 format->setInt32("width", mVideoWidth); 1487 format->setInt32("height", mVideoHeight); 1488 format->setInt32("stride", mVideoWidth); 1489 format->setInt32("slice-height", mVideoWidth); 1490 format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); 1491 1492 // set up time lapse/slow motion for surface source 1493 if (mCaptureTimeLapse) { 1494 if (mTimeBetweenTimeLapseFrameCaptureUs <= 0) { 1495 ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld", 1496 mTimeBetweenTimeLapseFrameCaptureUs); 1497 return BAD_VALUE; 1498 } 1499 format->setInt64("time-lapse", 1500 mTimeBetweenTimeLapseFrameCaptureUs); 1501 } 1502 } 1503 1504 format->setInt32("bitrate", mVideoBitRate); 1505 format->setInt32("frame-rate", mFrameRate); 1506 format->setInt32("i-frame-interval", mIFramesIntervalSec); 1507 1508 if (mVideoTimeScale > 0) { 1509 format->setInt32("time-scale", mVideoTimeScale); 1510 } 1511 if (mVideoEncoderProfile != -1) { 1512 format->setInt32("profile", mVideoEncoderProfile); 1513 } 1514 if (mVideoEncoderLevel != -1) { 1515 format->setInt32("level", mVideoEncoderLevel); 1516 } 1517 1518 uint32_t flags = 0; 1519 if (mIsMetaDataStoredInVideoBuffers) { 1520 flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT; 1521 } 1522 1523 if (cameraSource == NULL) { 1524 flags |= MediaCodecSource::FLAG_USE_SURFACE_INPUT; 1525 } 1526 1527 sp<MediaCodecSource> encoder = 1528 MediaCodecSource::Create(mLooper, format, cameraSource, flags); 1529 if (encoder == NULL) { 1530 ALOGE("Failed to create video encoder"); 1531 // When the encoder fails to be created, we need 1532 // release the camera source due to the camera's lock 1533 // and unlock mechanism. 1534 if (cameraSource != NULL) { 1535 cameraSource->stop(); 1536 } 1537 return UNKNOWN_ERROR; 1538 } 1539 1540 if (cameraSource == NULL) { 1541 mGraphicBufferProducer = encoder->getGraphicBufferProducer(); 1542 } 1543 1544 *source = encoder; 1545 1546 return OK; 1547} 1548 1549status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { 1550 status_t status = BAD_VALUE; 1551 if (OK != (status = checkAudioEncoderCapabilities())) { 1552 return status; 1553 } 1554 1555 switch(mAudioEncoder) { 1556 case AUDIO_ENCODER_AMR_NB: 1557 case AUDIO_ENCODER_AMR_WB: 1558 case AUDIO_ENCODER_AAC: 1559 case AUDIO_ENCODER_HE_AAC: 1560 case AUDIO_ENCODER_AAC_ELD: 1561 break; 1562 1563 default: 1564 ALOGE("Unsupported audio encoder: %d", mAudioEncoder); 1565 return UNKNOWN_ERROR; 1566 } 1567 1568 sp<MediaSource> audioEncoder = createAudioSource(); 1569 if (audioEncoder == NULL) { 1570 return UNKNOWN_ERROR; 1571 } 1572 1573 writer->addSource(audioEncoder); 1574 return OK; 1575} 1576 1577status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { 1578 mWriter.clear(); 1579 mTotalBitRate = 0; 1580 1581 status_t err = OK; 1582 sp<MediaWriter> writer; 1583 if (mOutputFormat == OUTPUT_FORMAT_WEBM) { 1584 writer = new WebmWriter(mOutputFd); 1585 } else { 1586 writer = new MPEG4Writer(mOutputFd); 1587 } 1588 1589 if (mVideoSource < VIDEO_SOURCE_LIST_END) { 1590 setDefaultVideoEncoderIfNecessary(); 1591 1592 sp<MediaSource> mediaSource; 1593 err = setupMediaSource(&mediaSource); 1594 if (err != OK) { 1595 return err; 1596 } 1597 1598 sp<MediaSource> encoder; 1599 err = setupVideoEncoder(mediaSource, &encoder); 1600 if (err != OK) { 1601 return err; 1602 } 1603 1604 writer->addSource(encoder); 1605 mTotalBitRate += mVideoBitRate; 1606 } 1607 1608 if (mOutputFormat != OUTPUT_FORMAT_WEBM) { 1609 // Audio source is added at the end if it exists. 1610 // This help make sure that the "recoding" sound is suppressed for 1611 // camcorder applications in the recorded files. 1612 // TODO Audio source is currently unsupported for webm output; vorbis encoder needed. 1613 if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) { 1614 err = setupAudioEncoder(writer); 1615 if (err != OK) return err; 1616 mTotalBitRate += mAudioBitRate; 1617 } 1618 1619 if (mInterleaveDurationUs > 0) { 1620 reinterpret_cast<MPEG4Writer *>(writer.get())-> 1621 setInterleaveDuration(mInterleaveDurationUs); 1622 } 1623 if (mLongitudex10000 > -3600000 && mLatitudex10000 > -3600000) { 1624 reinterpret_cast<MPEG4Writer *>(writer.get())-> 1625 setGeoData(mLatitudex10000, mLongitudex10000); 1626 } 1627 } 1628 if (mMaxFileDurationUs != 0) { 1629 writer->setMaxFileDuration(mMaxFileDurationUs); 1630 } 1631 if (mMaxFileSizeBytes != 0) { 1632 writer->setMaxFileSize(mMaxFileSizeBytes); 1633 } 1634 if (mVideoSource == VIDEO_SOURCE_DEFAULT 1635 || mVideoSource == VIDEO_SOURCE_CAMERA) { 1636 mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId); 1637 } else if (mVideoSource == VIDEO_SOURCE_SURFACE) { 1638 // surface source doesn't need large initial delay 1639 mStartTimeOffsetMs = 200; 1640 } 1641 if (mStartTimeOffsetMs > 0) { 1642 writer->setStartTimeOffsetMs(mStartTimeOffsetMs); 1643 } 1644 1645 writer->setListener(mListener); 1646 mWriter = writer; 1647 return OK; 1648} 1649 1650void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { 1651 int64_t startTimeUs = systemTime() / 1000; 1652 (*meta)->setInt64(kKeyTime, startTimeUs); 1653 (*meta)->setInt32(kKeyFileType, mOutputFormat); 1654 (*meta)->setInt32(kKeyBitRate, mTotalBitRate); 1655 if (mMovieTimeScale > 0) { 1656 (*meta)->setInt32(kKeyTimeScale, mMovieTimeScale); 1657 } 1658 if (mOutputFormat != OUTPUT_FORMAT_WEBM) { 1659 (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset); 1660 if (mTrackEveryTimeDurationUs > 0) { 1661 (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs); 1662 } 1663 if (mRotationDegrees != 0) { 1664 (*meta)->setInt32(kKeyRotation, mRotationDegrees); 1665 } 1666 } 1667} 1668 1669status_t StagefrightRecorder::pause() { 1670 ALOGV("pause"); 1671 if (mWriter == NULL) { 1672 return UNKNOWN_ERROR; 1673 } 1674 mWriter->pause(); 1675 1676 if (mStarted) { 1677 mStarted = false; 1678 1679 uint32_t params = 0; 1680 if (mAudioSource != AUDIO_SOURCE_CNT) { 1681 params |= IMediaPlayerService::kBatteryDataTrackAudio; 1682 } 1683 if (mVideoSource != VIDEO_SOURCE_LIST_END) { 1684 params |= IMediaPlayerService::kBatteryDataTrackVideo; 1685 } 1686 1687 addBatteryData(params); 1688 } 1689 1690 1691 return OK; 1692} 1693 1694status_t StagefrightRecorder::stop() { 1695 ALOGV("stop"); 1696 status_t err = OK; 1697 1698 if (mCaptureTimeLapse && mCameraSourceTimeLapse != NULL) { 1699 mCameraSourceTimeLapse->startQuickReadReturns(); 1700 mCameraSourceTimeLapse = NULL; 1701 } 1702 1703 if (mWriter != NULL) { 1704 err = mWriter->stop(); 1705 mWriter.clear(); 1706 } 1707 1708 mGraphicBufferProducer.clear(); 1709 1710 if (mOutputFd >= 0) { 1711 ::close(mOutputFd); 1712 mOutputFd = -1; 1713 } 1714 1715 if (mStarted) { 1716 mStarted = false; 1717 1718 uint32_t params = 0; 1719 if (mAudioSource != AUDIO_SOURCE_CNT) { 1720 params |= IMediaPlayerService::kBatteryDataTrackAudio; 1721 } 1722 if (mVideoSource != VIDEO_SOURCE_LIST_END) { 1723 params |= IMediaPlayerService::kBatteryDataTrackVideo; 1724 } 1725 1726 addBatteryData(params); 1727 } 1728 1729 return err; 1730} 1731 1732status_t StagefrightRecorder::close() { 1733 ALOGV("close"); 1734 stop(); 1735 1736 return OK; 1737} 1738 1739status_t StagefrightRecorder::reset() { 1740 ALOGV("reset"); 1741 stop(); 1742 1743 // No audio or video source by default 1744 mAudioSource = AUDIO_SOURCE_CNT; 1745 mVideoSource = VIDEO_SOURCE_LIST_END; 1746 1747 // Default parameters 1748 mOutputFormat = OUTPUT_FORMAT_THREE_GPP; 1749 mAudioEncoder = AUDIO_ENCODER_AMR_NB; 1750 mVideoEncoder = VIDEO_ENCODER_DEFAULT; 1751 mVideoWidth = 176; 1752 mVideoHeight = 144; 1753 mFrameRate = -1; 1754 mVideoBitRate = 192000; 1755 mSampleRate = 8000; 1756 mAudioChannels = 1; 1757 mAudioBitRate = 12200; 1758 mInterleaveDurationUs = 0; 1759 mIFramesIntervalSec = 1; 1760 mAudioSourceNode = 0; 1761 mUse64BitFileOffset = false; 1762 mMovieTimeScale = -1; 1763 mAudioTimeScale = -1; 1764 mVideoTimeScale = -1; 1765 mCameraId = 0; 1766 mStartTimeOffsetMs = -1; 1767 mVideoEncoderProfile = -1; 1768 mVideoEncoderLevel = -1; 1769 mMaxFileDurationUs = 0; 1770 mMaxFileSizeBytes = 0; 1771 mTrackEveryTimeDurationUs = 0; 1772 mCaptureTimeLapse = false; 1773 mTimeBetweenTimeLapseFrameCaptureUs = -1; 1774 mCameraSourceTimeLapse = NULL; 1775 mIsMetaDataStoredInVideoBuffers = false; 1776 mEncoderProfiles = MediaProfiles::getInstance(); 1777 mRotationDegrees = 0; 1778 mLatitudex10000 = -3600000; 1779 mLongitudex10000 = -3600000; 1780 mTotalBitRate = 0; 1781 1782 mOutputFd = -1; 1783 1784 return OK; 1785} 1786 1787status_t StagefrightRecorder::getMaxAmplitude(int *max) { 1788 ALOGV("getMaxAmplitude"); 1789 1790 if (max == NULL) { 1791 ALOGE("Null pointer argument"); 1792 return BAD_VALUE; 1793 } 1794 1795 if (mAudioSourceNode != 0) { 1796 *max = mAudioSourceNode->getMaxAmplitude(); 1797 } else { 1798 *max = 0; 1799 } 1800 1801 return OK; 1802} 1803 1804status_t StagefrightRecorder::dump( 1805 int fd, const Vector<String16>& args) const { 1806 ALOGV("dump"); 1807 const size_t SIZE = 256; 1808 char buffer[SIZE]; 1809 String8 result; 1810 if (mWriter != 0) { 1811 mWriter->dump(fd, args); 1812 } else { 1813 snprintf(buffer, SIZE, " No file writer\n"); 1814 result.append(buffer); 1815 } 1816 snprintf(buffer, SIZE, " Recorder: %p\n", this); 1817 snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd); 1818 result.append(buffer); 1819 snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat); 1820 result.append(buffer); 1821 snprintf(buffer, SIZE, " Max file size (bytes): %" PRId64 "\n", mMaxFileSizeBytes); 1822 result.append(buffer); 1823 snprintf(buffer, SIZE, " Max file duration (us): %" PRId64 "\n", mMaxFileDurationUs); 1824 result.append(buffer); 1825 snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32); 1826 result.append(buffer); 1827 snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs); 1828 result.append(buffer); 1829 snprintf(buffer, SIZE, " Progress notification: %" PRId64 " us\n", mTrackEveryTimeDurationUs); 1830 result.append(buffer); 1831 snprintf(buffer, SIZE, " Audio\n"); 1832 result.append(buffer); 1833 snprintf(buffer, SIZE, " Source: %d\n", mAudioSource); 1834 result.append(buffer); 1835 snprintf(buffer, SIZE, " Encoder: %d\n", mAudioEncoder); 1836 result.append(buffer); 1837 snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mAudioBitRate); 1838 result.append(buffer); 1839 snprintf(buffer, SIZE, " Sampling rate (hz): %d\n", mSampleRate); 1840 result.append(buffer); 1841 snprintf(buffer, SIZE, " Number of channels: %d\n", mAudioChannels); 1842 result.append(buffer); 1843 snprintf(buffer, SIZE, " Max amplitude: %d\n", mAudioSourceNode == 0? 0: mAudioSourceNode->getMaxAmplitude()); 1844 result.append(buffer); 1845 snprintf(buffer, SIZE, " Video\n"); 1846 result.append(buffer); 1847 snprintf(buffer, SIZE, " Source: %d\n", mVideoSource); 1848 result.append(buffer); 1849 snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); 1850 result.append(buffer); 1851 snprintf(buffer, SIZE, " Start time offset (ms): %d\n", mStartTimeOffsetMs); 1852 result.append(buffer); 1853 snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder); 1854 result.append(buffer); 1855 snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile); 1856 result.append(buffer); 1857 snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); 1858 result.append(buffer); 1859 snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesIntervalSec); 1860 result.append(buffer); 1861 snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); 1862 result.append(buffer); 1863 snprintf(buffer, SIZE, " Frame rate (fps): %d\n", mFrameRate); 1864 result.append(buffer); 1865 snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mVideoBitRate); 1866 result.append(buffer); 1867 ::write(fd, result.string(), result.size()); 1868 return OK; 1869} 1870} // namespace android 1871