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