Added initial validation on video/audio encoding parameters before/at start()

Change-Id: I1a40dd247202f70e3e5429d74533d349b88f1d54
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 57db7e4..12e8fe6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/OMXCodec.h>
 #include <camera/ICamera.h>
 #include <camera/Camera.h>
+#include <camera/CameraParameters.h>
 #include <surfaceflinger/ISurface.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
@@ -88,6 +89,12 @@
 }
 
 status_t StagefrightRecorder::setVideoSize(int width, int height) {
+    if (width <= 0 || height <= 0) {
+        LOGE("Invalid video size: %dx%d", width, height);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the dimension will be performed later
     mVideoWidth = width;
     mVideoHeight = height;
 
@@ -95,6 +102,12 @@
 }
 
 status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
+    if (frames_per_second <= 0 || frames_per_second > 30) {
+        LOGE("Invalid video frame rate: %d", frames_per_second);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the frame rate will be performed later
     mFrameRate = frames_per_second;
 
     return OK;
@@ -152,9 +165,9 @@
 
 // Attempt to parse an int64 literal optionally surrounded by whitespace,
 // returns true on success, false otherwise.
-static bool safe_strtoi64(const char *s, int32_t *val) {
+static bool safe_strtoi64(const char *s, int64_t *val) {
     char *end;
-    *val = static_cast<int32_t>(strtoll(s, &end, 10));
+    *val = strtoll(s, &end, 10);
 
     if (end == s || errno == ERANGE) {
         return false;
@@ -171,6 +184,18 @@
     return *end == '\0';
 }
 
+// Return true if the value is in [0, 0x007FFFFFFF]
+static bool safe_strtoi32(const char *s, int32_t *val) {
+    int64_t temp;
+    if (safe_strtoi64(s, &temp)) {
+        if (temp >= 0 && temp <= 0x007FFFFFFF) {
+            *val = static_cast<int32_t>(temp);
+            return true;
+        }
+    }
+    return false;
+}
+
 // Trim both leading and trailing whitespace from the given string.
 static void TrimString(String8 *s) {
     size_t num_bytes = s->bytes();
@@ -191,85 +216,129 @@
 
 status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) {
     LOGV("setParamAudioSamplingRate: %d", sampleRate);
+    if (sampleRate <= 0) {
+        LOGE("Invalid audio sampling rate: %d", sampleRate);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the sample rate will be performed later.
     mSampleRate = sampleRate;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) {
     LOGV("setParamAudioNumberOfChannels: %d", channels);
+    if (channels <= 0 || channels >= 3) {
+        LOGE("Invalid number of audio channels: %d", channels);
+    }
+
+    // Additional check on the number of channels will be performed later.
     mAudioChannels = channels;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) {
     LOGV("setParamAudioEncodingBitRate: %d", bitRate);
+    if (bitRate <= 0) {
+        LOGE("Invalid audio encoding bit rate: %d", bitRate);
+        return BAD_VALUE;
+    }
+
+    // The target bit rate may not be exactly the same as the requested.
+    // It depends on many factors, such as rate control, and the bit rate
+    // range that a specific encoder supports. The mismatch between the
+    // the target and requested bit rate will NOT be treated as an error.
     mAudioBitRate = bitRate;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) {
     LOGV("setParamVideoEncodingBitRate: %d", bitRate);
+    if (bitRate <= 0) {
+        LOGE("Invalid video encoding bit rate: %d", bitRate);
+        return BAD_VALUE;
+    }
+
+    // The target bit rate may not be exactly the same as the requested.
+    // It depends on many factors, such as rate control, and the bit rate
+    // range that a specific encoder supports. The mismatch between the
+    // the target and requested bit rate will NOT be treated as an error.
     mVideoBitRate = bitRate;
     return OK;
 }
 
-status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int32_t limit,
+status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit,
         bool limit_is_duration) {
     LOGV("setParamMaxDurationOrFileSize: limit (%d) for %s",
             limit, limit_is_duration?"duration":"size");
+    if (limit_is_duration) {  // limit is in ms
+        if (limit <= 1000) {  // XXX: 1 second
+            LOGE("Max file duration is too short: %lld us", limit);
+        }
+        mMaxFileDurationUs = limit * 1000LL;
+    } else {
+        if (limit <= 1024) {  // XXX: 1 kB
+            LOGE("Max file size is too small: %lld bytes", limit);
+        }
+        mMaxFileSizeBytes = limit;
+    }
     return OK;
 }
 
 status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) {
     LOGV("setParamInterleaveDuration: %d", durationUs);
+    if (durationUs <= 20000) {  // XXX: 20 ms
+        LOGE("Audio/video interleave duration is too small: %d us", durationUs);
+        return BAD_VALUE;
+    }
     mInterleaveDurationUs = durationUs;
     return OK;
 }
+
 status_t StagefrightRecorder::setParameter(
         const String8 &key, const String8 &value) {
     LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
     if (key == "max-duration") {
-        int32_t max_duration_ms;
+        int64_t max_duration_ms;
         if (safe_strtoi64(value.string(), &max_duration_ms)) {
             return setParamMaxDurationOrFileSize(
                     max_duration_ms, true /* limit_is_duration */);
         }
     } else if (key == "max-filesize") {
-        int32_t max_filesize_bytes;
+        int64_t max_filesize_bytes;
         if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
             return setParamMaxDurationOrFileSize(
                     max_filesize_bytes, false /* limit is filesize */);
         }
     } else if (key == "audio-param-sampling-rate") {
         int32_t sampling_rate;
-        if (safe_strtoi64(value.string(), &sampling_rate)) {
+        if (safe_strtoi32(value.string(), &sampling_rate)) {
             return setParamAudioSamplingRate(sampling_rate);
         }
     } else if (key == "audio-param-number-of-channels") {
         int32_t number_of_channels;
-        if (safe_strtoi64(value.string(), &number_of_channels)) {
+        if (safe_strtoi32(value.string(), &number_of_channels)) {
             return setParamAudioNumberOfChannels(number_of_channels);
         }
     } else if (key == "audio-param-encoding-bitrate") {
         int32_t audio_bitrate;
-        if (safe_strtoi64(value.string(), &audio_bitrate)) {
+        if (safe_strtoi32(value.string(), &audio_bitrate)) {
             return setParamAudioEncodingBitRate(audio_bitrate);
         }
     } else if (key == "video-param-encoding-bitrate") {
         int32_t video_bitrate;
-        if (safe_strtoi64(value.string(), &video_bitrate)) {
+        if (safe_strtoi32(value.string(), &video_bitrate)) {
             return setParamVideoEncodingBitRate(video_bitrate);
         }
     } else if (key == "param-interleave-duration-us") {
         int32_t durationUs;
-        if (safe_strtoi64(value.string(), &durationUs)) {
+        if (safe_strtoi32(value.string(), &durationUs)) {
             return setParamInterleaveDuration(durationUs);
         }
     } else {
         LOGE("setParameter: failed to find key %s", key.string());
-        return BAD_VALUE;
     }
-    return OK;
+    return BAD_VALUE;
 }
 
 status_t StagefrightRecorder::setParameters(const String8 &params) {
@@ -332,6 +401,10 @@
         case OUTPUT_FORMAT_AMR_WB:
             return startAMRRecording();
 
+        case OUTPUT_FORMAT_AAC_ADIF:
+        case OUTPUT_FORMAT_AAC_ADTS:
+            return startAACRecording();
+
         default:
             return UNKNOWN_ERROR;
     }
@@ -377,6 +450,7 @@
     encMeta->setInt32(kKeyMaxInputSize, maxInputSize);
     encMeta->setInt32(kKeyChannelCount, mAudioChannels);
     encMeta->setInt32(kKeySampleRate, mSampleRate);
+    encMeta->setInt32(kKeyBitRate, mAudioBitRate);
 
     OMXClient client;
     CHECK_EQ(client.connect(), OK);
@@ -388,18 +462,55 @@
     return audioEncoder;
 }
 
+status_t StagefrightRecorder::startAACRecording() {
+    CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF ||
+          mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+
+    CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
+    CHECK(mAudioSource != AUDIO_SOURCE_LIST_END);
+    CHECK(mOutputFd >= 0);
+
+    CHECK(0 == "AACWriter is not implemented yet");
+
+    return OK;
+}
+
 status_t StagefrightRecorder::startAMRRecording() {
-    if (mAudioSource == AUDIO_SOURCE_LIST_END
-        || mVideoSource != VIDEO_SOURCE_LIST_END) {
+    CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
+          mOutputFormat == OUTPUT_FORMAT_AMR_WB);
+
+    if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) {
+        if (mAudioEncoder != AUDIO_ENCODER_DEFAULT &&
+            mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
+            LOGE("Invalid encoder %d used for AMRNB recording",
+                    mAudioEncoder);
+            return UNKNOWN_ERROR;
+        }
+        if (mSampleRate != 8000) {
+            LOGE("Invalid sampling rate %d used for AMRNB recording",
+                    mSampleRate);
+            return UNKNOWN_ERROR;
+        }
+    } else {  // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
+        if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+            LOGE("Invlaid encoder %d used for AMRWB recording",
+                    mAudioEncoder);
+            return UNKNOWN_ERROR;
+        }
+        if (mSampleRate != 16000) {
+            LOGE("Invalid sample rate %d used for AMRWB recording",
+                    mSampleRate);
+            return UNKNOWN_ERROR;
+        }
+    }
+    if (mAudioChannels != 1) {
+        LOGE("Invalid number of audio channels %d used for amr recording",
+                mAudioChannels);
         return UNKNOWN_ERROR;
     }
 
-    if (mOutputFormat == OUTPUT_FORMAT_AMR_NB
-            && mAudioEncoder != AUDIO_ENCODER_DEFAULT
-            && mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
-        return UNKNOWN_ERROR;
-    } else if (mOutputFormat == OUTPUT_FORMAT_AMR_WB
-            && mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+    if (mAudioSource >= AUDIO_SOURCE_LIST_END) {
+        LOGE("Invalid audio source: %d", mAudioSource);
         return UNKNOWN_ERROR;
     }
 
@@ -444,6 +555,36 @@
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
         CHECK(mCamera != NULL);
 
+        if (mCamera == 0) {
+            mCamera = Camera::connect(0);
+        }
+        CHECK(mCamera != NULL);
+
+        // Set the actual video recording frame size
+        CameraParameters params(mCamera->getParameters());
+        params.setPreviewSize(mVideoWidth, mVideoHeight);
+        params.setPreviewFrameRate(mFrameRate);
+        String8 s = params.flatten();
+        CHECK_EQ(OK, mCamera->setParameters(s));
+        CameraParameters newCameraParams(mCamera->getParameters());
+
+        // Check on video frame size
+        int frameWidth = 0, frameHeight = 0;
+        newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
+        if (frameWidth  < 0 || frameWidth  != mVideoWidth ||
+            frameHeight < 0 || frameHeight != mVideoHeight) {
+            LOGE("Failed to set the video frame size to %dx%d",
+                    mVideoWidth, mVideoHeight);
+            return UNKNOWN_ERROR;
+        }
+
+        // Check on video frame rate
+        int frameRate = newCameraParams.getPreviewFrameRate();
+        if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
+            LOGE("Failed to set frame rate to %d", mFrameRate);
+            return UNKNOWN_ERROR;
+        }
+
         sp<CameraSource> cameraSource =
             CameraSource::CreateFromCamera(mCamera);
 
@@ -452,6 +593,9 @@
         cameraSource->setPreviewSurface(mPreviewSurface);
 
         sp<MetaData> enc_meta = new MetaData;
+        enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+        enc_meta->setInt32(kKeySampleRate, mFrameRate);  // XXX: kKeySampleRate?
+
         switch (mVideoEncoder) {
             case VIDEO_ENCODER_H263:
                 enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
@@ -491,7 +635,12 @@
         mWriter->addSource(encoder);
     }
 
-    ((MPEG4Writer *)mWriter.get())->setInterleaveDuration(mInterleaveDurationUs);
+    {
+        // MPEGWriter specific handling
+        MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());  // mWriter is an MPEGWriter
+        writer->setInterleaveDuration(mInterleaveDurationUs);
+    }
+
     mWriter->start();
     return OK;
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 3b99e91..b7d554b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -75,6 +75,8 @@
     int32_t mAudioChannels;
     int32_t mSampleRate;
     int32_t mInterleaveDurationUs;
+    int64_t mMaxFileSizeBytes;
+    int64_t mMaxFileDurationUs;
 
     String8 mParams;
     int mOutputFd;
@@ -82,6 +84,7 @@
 
     status_t startMPEG4Recording();
     status_t startAMRRecording();
+    status_t startAACRecording();
     sp<MediaSource> createAudioSource();
     status_t setParameter(const String8 &key, const String8 &value);
     status_t setParamVideoEncodingBitRate(int32_t bitRate);
@@ -89,7 +92,7 @@
     status_t setParamAudioNumberOfChannels(int32_t channles);
     status_t setParamAudioSamplingRate(int32_t sampleRate);
     status_t setParamInterleaveDuration(int32_t durationUs);
-    status_t setParamMaxDurationOrFileSize(int32_t limit, bool limit_is_duration);
+    status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
 
     StagefrightRecorder(const StagefrightRecorder &);
     StagefrightRecorder &operator=(const StagefrightRecorder &);