Merge "Visualizer: replace the FFT implementation with a faster one." into gingerbread
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 1415493..d0ed7df 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -30,6 +30,8 @@
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
@@ -269,24 +271,57 @@
mMap.removeItem(String8(key));
}
-static int parse_size(const char *str, int &width, int &height)
+// Parse string like "640x480" or "10000,20000"
+static int parse_pair(const char *str, int *first, int *second, char delim,
+ char **endptr = NULL)
{
- // Find the width.
+ // Find the first integer.
char *end;
int w = (int)strtol(str, &end, 10);
- // If an 'x' does not immediately follow, give up.
- if (*end != 'x')
+ // If a delimeter does not immediately follow, give up.
+ if (*end != delim) {
+ LOGE("Cannot find delimeter (%c) in str=%s", delim, str);
return -1;
+ }
- // Find the height, immediately after the 'x'.
- int h = (int)strtol(end+1, 0, 10);
+ // Find the second integer, immediately after the delimeter.
+ int h = (int)strtol(end+1, &end, 10);
- width = w;
- height = h;
+ *first = w;
+ *second = h;
+
+ if (endptr) {
+ *endptr = end;
+ }
return 0;
}
+static void parseSizesList(const char *sizesStr, Vector<Size> &sizes)
+{
+ if (sizesStr == 0) {
+ return;
+ }
+
+ char *sizeStartPtr = (char *)sizesStr;
+
+ while (true) {
+ int width, height;
+ int success = parse_pair(sizeStartPtr, &width, &height, 'x',
+ &sizeStartPtr);
+ if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) {
+ LOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr);
+ return;
+ }
+ sizes.push(Size(width, height));
+
+ if (*sizeStartPtr == '\0') {
+ return;
+ }
+ sizeStartPtr++;
+ }
+}
+
void CameraParameters::setPreviewSize(int width, int height)
{
char str[32];
@@ -296,19 +331,17 @@
void CameraParameters::getPreviewSize(int *width, int *height) const
{
- *width = -1;
- *height = -1;
-
+ *width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
const char *p = get(KEY_PREVIEW_SIZE);
- if (p == 0)
- return;
+ if (p == 0) return;
+ parse_pair(p, width, height, 'x');
+}
- int w, h;
- if (parse_size(p, w, h) == 0) {
- *width = w;
- *height = h;
- }
+void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
+{
+ const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
+ parseSizesList(previewSizesStr, sizes);
}
void CameraParameters::setPreviewFrameRate(int fps)
@@ -321,6 +354,14 @@
return getInt(KEY_PREVIEW_FRAME_RATE);
}
+void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const
+{
+ *min_fps = *max_fps = -1;
+ const char *p = get(KEY_PREVIEW_FPS_RANGE);
+ if (p == 0) return;
+ parse_pair(p, min_fps, max_fps, ',');
+}
+
void CameraParameters::setPreviewFormat(const char *format)
{
set(KEY_PREVIEW_FORMAT, format);
@@ -340,19 +381,17 @@
void CameraParameters::getPictureSize(int *width, int *height) const
{
- *width = -1;
- *height = -1;
-
+ *width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
const char *p = get(KEY_PICTURE_SIZE);
- if (p == 0)
- return;
+ if (p == 0) return;
+ parse_pair(p, width, height, 'x');
+}
- int w, h;
- if (parse_size(p, w, h) == 0) {
- *width = w;
- *height = h;
- }
+void CameraParameters::getSupportedPictureSizes(Vector<Size> &sizes) const
+{
+ const char *pictureSizesStr = get(KEY_SUPPORTED_PICTURE_SIZES);
+ parseSizesList(pictureSizesStr, sizes);
}
void CameraParameters::setPictureFormat(const char *format)
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 1529db7..6a66e3c 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -221,6 +221,7 @@
*/
extern "C" int HAL_getNumberOfCameras();
extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo);
+/* HAL should return NULL if it fails to open camera hardware. */
extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId);
}; // namespace android
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6a5d254..a5c7874 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -22,6 +22,21 @@
namespace android {
+struct Size {
+ int width;
+ int height;
+
+ Size() {
+ width = 0;
+ height = 0;
+ }
+
+ Size(int w, int h) {
+ width = w;
+ height = h;
+ }
+};
+
class CameraParameters
{
public:
@@ -43,12 +58,15 @@
void setPreviewSize(int width, int height);
void getPreviewSize(int *width, int *height) const;
+ void getSupportedPreviewSizes(Vector<Size> &sizes) const;
void setPreviewFrameRate(int fps);
int getPreviewFrameRate() const;
+ void getPreviewFpsRange(int *min_fps, int *max_fps) const;
void setPreviewFormat(const char *format);
const char *getPreviewFormat() const;
void setPictureSize(int width, int height);
void getPictureSize(int *width, int *height) const;
+ void getSupportedPictureSizes(Vector<Size> &sizes) const;
void setPictureFormat(const char *format);
const char *getPictureFormat() const;
@@ -65,6 +83,20 @@
// Supported preview frame sizes in pixels.
// Example value: "800x600,480x320". Read only.
static const char KEY_SUPPORTED_PREVIEW_SIZES[];
+ // The current minimum and maximum preview fps. This controls the rate of
+ // preview frames received (CAMERA_MSG_PREVIEW_FRAME). The minimum and
+ // maximum fps must be one of the elements from
+ // KEY_SUPPORTED_PREVIEW_FPS_RANGE parameter.
+ // Example value: "10500,26623"
+ static const char KEY_PREVIEW_FPS_RANGE[];
+ // The supported preview fps (frame-per-second) ranges. Each range contains
+ // a minimum fps and maximum fps. If minimum fps equals to maximum fps, the
+ // camera outputs frames in fixed frame rate. If not, the camera outputs
+ // frames in auto frame rate. The actual frame rate fluctuates between the
+ // minimum and the maximum. The list has at least one element. The list is
+ // sorted from small to large (first by maximum fps and then minimum fps).
+ // Example value: "(10500,26623),(15000,26623),(30000,30000)"
+ static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
// The image format for preview frames. See CAMERA_MSG_PREVIEW_FRAME in
// frameworks/base/include/camera/Camera.h.
// Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read/write.
@@ -337,11 +369,14 @@
static const char PIXEL_FORMAT_JPEG[];
// Values for focus mode settings.
- // Auto-focus mode.
+ // Auto-focus mode. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus in this mode.
static const char FOCUS_MODE_AUTO[];
// Focus is set at infinity. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_INFINITY[];
+ // Macro (close-up) focus mode. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus in this mode.
static const char FOCUS_MODE_MACRO[];
// Focus is fixed. The camera is always in this mode if the focus is not
// adjustable. If the camera has auto-focus, this mode can fix the
@@ -355,7 +390,8 @@
// Continuous auto focus mode. The camera continuously tries to focus. This
// is ideal for shooting video or shooting photo of moving object. Auto
// focus starts when the parameter is set. Applications should not call
- // CameraHardwareInterface.autoFocus in this mode.
+ // CameraHardwareInterface.autoFocus in this mode. To stop continuous
+ // focus, applications should change the focus mode to other modes.
static const char FOCUS_MODE_CONTINUOUS[];
// The camera determines the exposure by giving more weight to the
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 813dd43..aa965e1 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,8 +37,8 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
- virtual void stop();
- virtual void pause();
+ virtual status_t stop();
+ virtual status_t pause();
protected:
virtual ~AMRWriter();
@@ -57,7 +57,7 @@
int64_t mEstimatedDurationUs;
static void *ThreadWrapper(void *);
- void threadFunc();
+ status_t threadFunc();
bool exceedsFileSizeLimit();
bool exceedsFileDurationLimit();
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index be96935..de82b38 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,9 +37,9 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
virtual bool reachedEOS();
- virtual void stop();
- virtual void pause();
void beginBox(const char *fourcc);
void writeInt8(int8_t x);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8d3a9df..151bf16 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -35,8 +35,9 @@
virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0;
virtual status_t start(MetaData *params = NULL) = 0;
- virtual void stop() = 0;
- virtual void pause() = 0;
+ virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
virtual void setListener(const sp<IMediaRecorderClient>& listener) {
diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.h
rename to include/media/stagefright/foundation/ABitReader.h
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index a70bdff..bcd646a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -1626,9 +1626,15 @@
switch (param){
case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+ if (*pValueSize != sizeof(uint32_t)){
+ LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
+ return -EINVAL;
+ }
+ *pValueSize = sizeof(uint32_t);
+ break;
case BASSBOOST_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
- LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
+ LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
return -EINVAL;
}
*pValueSize = sizeof(int16_t);
@@ -1736,9 +1742,16 @@
switch (param){
case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+ if (*pValueSize != sizeof(uint32_t)){
+ LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
+ return -EINVAL;
+ }
+ *pValueSize = sizeof(uint32_t);
+ break;
+
case VIRTUALIZER_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
- LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
+ LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
return -EINVAL;
}
*pValueSize = sizeof(int16_t);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8481d49..796731b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -341,10 +341,14 @@
status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
- if (timeUs <= 100000LL) { // XXX: 100 milli-seconds
+ if (timeUs <= 0) {
+ LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs);
+ timeUs = 0; // Disable the duration limit for zero or negative values.
+ } else if (timeUs <= 100000LL) { // XXX: 100 milli-seconds
LOGE("Max file duration is too short: %lld us", timeUs);
return BAD_VALUE;
}
+
mMaxFileDurationUs = timeUs;
return OK;
}
@@ -675,7 +679,9 @@
encMeta->setInt32(kKeyChannelCount, mAudioChannels);
encMeta->setInt32(kKeySampleRate, mSampleRate);
encMeta->setInt32(kKeyBitRate, mAudioBitRate);
- encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ if (mAudioTimeScale > 0) {
+ encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -961,7 +967,9 @@
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
enc_meta->setInt32(kKeyColorFormat, colorFormat);
- enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ if (mVideoTimeScale > 0) {
+ enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ }
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -1041,7 +1049,9 @@
meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
- meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ if (mMovieTimeScale > 0) {
+ meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ }
if (mTrackEveryTimeDurationUs > 0) {
meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -1061,8 +1071,9 @@
status_t StagefrightRecorder::stop() {
LOGV("stop");
+ status_t err = OK;
if (mWriter != NULL) {
- mWriter->stop();
+ err = mWriter->stop();
mWriter.clear();
}
@@ -1084,7 +1095,7 @@
mOutputFd = -1;
}
- return OK;
+ return err;
}
status_t StagefrightRecorder::close() {
@@ -1117,9 +1128,9 @@
mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
- mMovieTimeScale = 1000;
- mAudioTimeScale = 1000;
- mVideoTimeScale = 1000;
+ mMovieTimeScale = -1;
+ mAudioTimeScale = -1;
+ mVideoTimeScale = -1;
mCameraId = 0;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index c71743e..71d48b3 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -136,16 +136,17 @@
return OK;
}
-void AMRWriter::pause() {
+status_t AMRWriter::pause() {
if (!mStarted) {
- return;
+ return OK;
}
mPaused = true;
+ return OK;
}
-void AMRWriter::stop() {
+status_t AMRWriter::stop() {
if (!mStarted) {
- return;
+ return OK;
}
mDone = true;
@@ -153,9 +154,17 @@
void *dummy;
pthread_join(mThread, &dummy);
- mSource->stop();
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
mStarted = false;
+ return err;
}
bool AMRWriter::exceedsFileSizeLimit() {
@@ -174,21 +183,20 @@
// static
void *AMRWriter::ThreadWrapper(void *me) {
- static_cast<AMRWriter *>(me)->threadFunc();
-
- return NULL;
+ return (void *) static_cast<AMRWriter *>(me)->threadFunc();
}
-void AMRWriter::threadFunc() {
+status_t AMRWriter::threadFunc() {
mEstimatedDurationUs = 0;
mEstimatedSizeBytes = 0;
bool stoppedPrematurely = true;
int64_t previousPausedDurationUs = 0;
int64_t maxTimestampUs = 0;
+ status_t err = OK;
while (!mDone) {
MediaBuffer *buffer;
- status_t err = mSource->read(&buffer);
+ err = mSource->read(&buffer);
if (err != OK) {
break;
@@ -260,6 +268,10 @@
fclose(mFile);
mFile = NULL;
mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
}
bool AMRWriter::reachedEOS() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index b8b2f3f..86fa668 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -39,6 +39,7 @@
TimedEventQueue.cpp \
Utils.cpp \
WAVExtractor.cpp \
+ avc_utils.cpp \
string.cpp
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6af3a7f..12a1e6e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1273,6 +1273,14 @@
hexdump(csd, csd_size);
#endif
+ if (csd_size == 0) {
+ // There's no further information, i.e. no codec specific data
+ // Let's assume that the information provided in the mpeg4 headers
+ // is accurate and hope for the best.
+
+ return OK;
+ }
+
if (csd_size < 2) {
return ERROR_MALFORMED;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9fe3864..568037e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -48,8 +48,8 @@
~Track();
status_t start(MetaData *params);
- void stop();
- void pause();
+ status_t stop();
+ status_t pause();
bool reachedEOS();
int64_t getDurationUs() const;
@@ -144,7 +144,7 @@
int64_t mTrackEveryTimeDurationUs;
static void *ThreadWrapper(void *me);
- void threadEntry();
+ status_t threadEntry();
const uint8_t *parseParamSet(
const uint8_t *data, size_t length, int type, size_t *paramSetLen);
@@ -168,6 +168,15 @@
void getCodecSpecificDataFromInputFormatIfPossible();
+ // Determine the track time scale
+ // If it is an audio track, try to use the sampling rate as
+ // the time scale; however, if user chooses the overwrite
+ // value, the user-supplied time scale will be used.
+ void setTimeScale();
+
+ // Simple validation on the codec specific data
+ status_t checkCodecSpecificData() const;
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -372,15 +381,20 @@
return OK;
}
-void MPEG4Writer::pause() {
+status_t MPEG4Writer::pause() {
if (mFile == NULL) {
- return;
+ return OK;
}
mPaused = true;
+ status_t err = OK;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- (*it)->pause();
+ status_t status = (*it)->pause();
+ if (status != OK) {
+ err = status;
+ }
}
+ return err;
}
void MPEG4Writer::stopWriterThread() {
@@ -397,15 +411,19 @@
pthread_join(mThread, &dummy);
}
-void MPEG4Writer::stop() {
+status_t MPEG4Writer::stop() {
if (mFile == NULL) {
- return;
+ return OK;
}
+ status_t err = OK;
int64_t maxDurationUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- (*it)->stop();
+ status_t status = (*it)->stop();
+ if (err == OK && status != OK) {
+ err = status;
+ }
int64_t durationUs = (*it)->getDurationUs();
if (durationUs > maxDurationUs) {
@@ -415,6 +433,15 @@
stopWriterThread();
+ // Do not write out movie header on error.
+ if (err != OK) {
+ fflush(mFile);
+ fclose(mFile);
+ mFile = NULL;
+ mStarted = false;
+ return err;
+ }
+
// Fix up the size of the 'mdat' chunk.
if (mUse32BitOffset) {
fseeko(mFile, mMdatOffset, SEEK_SET);
@@ -434,7 +461,7 @@
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
+ int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
beginBox("moov");
@@ -502,6 +529,7 @@
fclose(mFile);
mFile = NULL;
mStarted = false;
+ return err;
}
status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
@@ -749,10 +777,6 @@
mReachedEOS(false) {
getCodecSpecificDataFromInputFormatIfPossible();
- if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
- mTimeScale = 1000;
- }
-
const char *mime;
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -760,6 +784,28 @@
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ setTimeScale();
+}
+
+void MPEG4Writer::Track::setTimeScale() {
+ LOGV("setTimeScale");
+ // Default time scale
+ mTimeScale = 90000;
+
+ if (mIsAudio) {
+ // Use the sampling rate as the default time scale for audio track.
+ int32_t sampleRate;
+ bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
+ CHECK(success);
+ mTimeScale = sampleRate;
+ }
+
+ // If someone would like to overwrite the timescale, use user-supplied value.
+ int32_t timeScale;
+ if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
+ mTimeScale = timeScale;
+ }
+
CHECK(mTimeScale > 0);
}
@@ -1006,13 +1052,14 @@
return OK;
}
-void MPEG4Writer::Track::pause() {
+status_t MPEG4Writer::Track::pause() {
mPaused = true;
+ return OK;
}
-void MPEG4Writer::Track::stop() {
+status_t MPEG4Writer::Track::stop() {
if (mDone) {
- return;
+ return OK;
}
mDone = true;
@@ -1020,7 +1067,16 @@
void *dummy;
pthread_join(mThread, &dummy);
- mSource->stop();
+ status_t err = (status_t) dummy;
+
+ {
+ status_t status = mSource->stop();
+ if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
+ err = status;
+ }
+ }
+
+ return err;
}
bool MPEG4Writer::Track::reachedEOS() {
@@ -1031,9 +1087,8 @@
void *MPEG4Writer::Track::ThreadWrapper(void *me) {
Track *track = static_cast<Track *>(me);
- track->threadEntry();
-
- return NULL;
+ status_t err = track->threadEntry();
+ return (void *) err;
}
#include <ctype.h>
@@ -1328,7 +1383,7 @@
return false;
}
-void MPEG4Writer::Track::threadEntry() {
+status_t MPEG4Writer::Track::threadEntry() {
int32_t count = 0;
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
int64_t chunkTimestampUs = 0;
@@ -1336,6 +1391,8 @@
int32_t nZeroLengthFrames = 0;
int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
int64_t lastDurationUs = 0; // Between the previous two samples in ms
+ int64_t currDurationTicks = 0; // Timescale based ticks
+ int64_t lastDurationTicks = 0; // Timescale based ticks
int32_t sampleCount = 1; // Sample count in the current stts table entry
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
@@ -1467,10 +1524,24 @@
CHECK(timestampUs >= 0);
if (mNumSamples > 1) {
if (timestampUs <= lastTimestampUs) {
- LOGW("Drop a frame, since it arrives too late!");
+ LOGW("Frame arrives too late!");
+#if 0
+ // Drop the late frame.
copy->release();
copy = NULL;
continue;
+#else
+ // Don't drop the late frame, since dropping a frame may cause
+ // problems later during playback
+
+ // The idea here is to avoid having two or more samples with the
+ // same timestamp in the output file.
+ if (mTimeScale >= 1000000LL) {
+ timestampUs += 1;
+ } else {
+ timestampUs += (1000000LL + (mTimeScale >> 1)) / mTimeScale;
+ }
+#endif
}
}
@@ -1483,7 +1554,16 @@
mSampleSizes.push_back(sampleSize);
++mNumSamples;
if (mNumSamples > 2) {
- if (lastDurationUs != timestampUs - lastTimestampUs) {
+ // We need to use the time scale based ticks, rather than the
+ // timestamp itself to determine whether we have to use a new
+ // stts entry, since we may have rounding errors.
+ // The calculation is intended to reduce the accumulated
+ // rounding errors.
+ currDurationTicks =
+ ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+
+ if (currDurationTicks != lastDurationTicks) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
@@ -1498,6 +1578,7 @@
previousSampleSize = sampleSize;
}
lastDurationUs = timestampUs - lastTimestampUs;
+ lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
if (mIsRealTimeRecording && mIsAudio) {
wallClockTimeUs = systemTime() / 1000;
@@ -1559,7 +1640,9 @@
}
if (mSampleSizes.empty()) {
- err = UNKNOWN_ERROR;
+ err = ERROR_MALFORMED;
+ } else if (OK != checkCodecSpecificData()) {
+ err = ERROR_MALFORMED;
}
mOwner->trackProgressStatus(this, -1, err);
@@ -1590,6 +1673,10 @@
count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
logStatisticalData(mIsAudio);
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
}
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
@@ -1765,6 +1852,27 @@
return mEstimatedTrackSizeBytes;
}
+status_t MPEG4Writer::Track::checkCodecSpecificData() const {
+ const char *mime;
+ CHECK(mMeta->findCString(kKeyMIMEType, &mime));
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ if (!mCodecSpecificData ||
+ mCodecSpecificDataSize <= 0) {
+ // Missing codec specific data
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if (mCodecSpecificData ||
+ mCodecSpecificDataSize > 0) {
+ // Unexepected codec specific data found
+ return ERROR_MALFORMED;
+ }
+ }
+ return OK;
+}
+
void MPEG4Writer::Track::writeTrackHeader(
int32_t trackID, bool use32BitOffset) {
const char *mime;
@@ -1774,7 +1882,6 @@
LOGV("%s track time scale: %d",
mIsAudio? "Audio": "Video", mTimeScale);
-
time_t now = time(NULL);
int32_t mvhdTimeScale = mOwner->getTimeScale();
int64_t trakDurationUs = getDurationUs();
@@ -1938,7 +2045,6 @@
int32_t samplerate;
bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
CHECK(success);
-
mOwner->writeInt32(samplerate << 16);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
mOwner->beginBox("esds");
@@ -2089,10 +2195,18 @@
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSttsTableEntries.size());
+ int64_t prevTimestampUs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
- int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+
+ // Make sure that we are calculating the sample duration the exactly
+ // same way as we made decision on how to create stts entries.
+ int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+ int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
mOwner->writeInt32(dur);
}
mOwner->endBox(); // stts
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index b699d8f..9630092 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -158,7 +158,7 @@
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
- LOGI("seeking to offset %ld", pos);
+ LOGV("seeking to offset %ld", pos);
if (mExtractor->mImpl->seekToOffset(pos) != OK) {
return ERROR_END_OF_STREAM;
@@ -267,7 +267,7 @@
uint8_t header[27];
if (mSource->readAt(offset, header, sizeof(header))
< (ssize_t)sizeof(header)) {
- LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+ LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
return ERROR_IO;
}
@@ -384,7 +384,7 @@
packetSize);
if (n < (ssize_t)packetSize) {
- LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+ LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
return ERROR_IO;
}
@@ -418,7 +418,7 @@
buffer = NULL;
}
- LOGE("readPage returned %ld", n);
+ LOGV("readPage returned %ld", n);
return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
}
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
new file mode 100644
index 0000000..511ae12
--- /dev/null
+++ b/media/libstagefright/avc_utils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+static unsigned parseUE(ABitReader *br) {
+ unsigned numZeroes = 0;
+ while (br->getBits(1) == 0) {
+ ++numZeroes;
+ }
+
+ unsigned x = br->getBits(numZeroes);
+
+ return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+void FindAVCDimensions(
+ const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height) {
+ ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+ unsigned profile_idc = br.getBits(8);
+ br.skipBits(16);
+ parseUE(&br); // seq_parameter_set_id
+
+ if (profile_idc == 100 || profile_idc == 110
+ || profile_idc == 122 || profile_idc == 244
+ || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
+ unsigned chroma_format_idc = parseUE(&br);
+ if (chroma_format_idc == 3) {
+ br.skipBits(1); // residual_colour_transform_flag
+ }
+ parseUE(&br); // bit_depth_luma_minus8
+ parseUE(&br); // bit_depth_chroma_minus8
+ br.skipBits(1); // qpprime_y_zero_transform_bypass_flag
+ CHECK_EQ(br.getBits(1), 0u); // seq_scaling_matrix_present_flag
+ }
+
+ parseUE(&br); // log2_max_frame_num_minus4
+ unsigned pic_order_cnt_type = parseUE(&br);
+
+ if (pic_order_cnt_type == 0) {
+ parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
+ } else if (pic_order_cnt_type == 1) {
+ // offset_for_non_ref_pic, offset_for_top_to_bottom_field and
+ // offset_for_ref_frame are technically se(v), but since we are
+ // just skipping over them the midpoint does not matter.
+
+ br.getBits(1); // delta_pic_order_always_zero_flag
+ parseUE(&br); // offset_for_non_ref_pic
+ parseUE(&br); // offset_for_top_to_bottom_field
+
+ unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+ for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+ parseUE(&br); // offset_for_ref_frame
+ }
+ }
+
+ parseUE(&br); // num_ref_frames
+ br.getBits(1); // gaps_in_frame_num_value_allowed_flag
+
+ unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+ unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+ unsigned frame_mbs_only_flag = br.getBits(1);
+
+ *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+ *height = (2 - frame_mbs_only_flag)
+ * (pic_height_in_map_units_minus1 * 16 + 16);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index 389180c..52a391f 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -338,10 +338,15 @@
MediaBuffer *outputBuffer;
CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+ uint8_t *outPtr = (uint8_t *) outputBuffer->data();
+ uint32_t dataLength = outputBuffer->size();
- // Add 4 bytes for the start code 0x00000001
- uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
- uint32_t dataLength = outputBuffer->size() - 4;
+ if (!mSpsPpsHeaderReceived && mNumInputFrames < 0) {
+ // 4 bytes are reserved for holding the start code 0x00000001
+ // of the sequence parameter set at the beginning.
+ outPtr += 4;
+ dataLength -= 4;
+ }
int32_t type;
AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
@@ -358,7 +363,7 @@
switch (type) {
case AVC_NALTYPE_SPS:
++mNumInputFrames;
- memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+ memcpy((uint8_t *)outputBuffer->data(), "\x00\x00\x00\x01", 4);
outputBuffer->set_range(0, dataLength + 4);
outPtr += (dataLength + 4); // 4 bytes for next start code
dataLength = outputBuffer->size() -
diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.cpp
rename to media/libstagefright/foundation/ABitReader.cpp
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 35eea7e..f6a8a52 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -3,6 +3,7 @@
LOCAL_SRC_FILES:= \
AAtomizer.cpp \
+ ABitReader.cpp \
ABuffer.cpp \
ADebug.cpp \
AHandler.cpp \
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
new file mode 100644
index 0000000..cc405b5
--- /dev/null
+++ b/media/libstagefright/include/avc_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVC_UTILS_H_
+
+#define AVC_UTILS_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+void FindAVCDimensions(
+ const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+
+} // namespace android
+
+#endif // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index d05975d..26a0fb3 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -16,9 +16,10 @@
#include "ATSParser.h"
-#include "ABitReader.h"
#include "AnotherPacketSource.h"
+#include "include/avc_utils.h"
+#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -473,60 +474,6 @@
}
}
-static unsigned parseUE(ABitReader *br) {
- unsigned numZeroes = 0;
- while (br->getBits(1) == 0) {
- ++numZeroes;
- }
-
- unsigned x = br->getBits(numZeroes);
-
- return x + (1u << numZeroes) - 1;
-}
-
-// Determine video dimensions from the sequence parameterset.
-static void FindDimensions(
- const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
- ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
-
- unsigned profile_idc = br.getBits(8);
- br.skipBits(16);
- parseUE(&br); // seq_parameter_set_id
-
- if (profile_idc == 100 || profile_idc == 110
- || profile_idc == 122 || profile_idc == 144) {
- TRESPASS();
- }
-
- parseUE(&br); // log2_max_frame_num_minus4
- unsigned pic_order_cnt_type = parseUE(&br);
-
- if (pic_order_cnt_type == 0) {
- parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
- } else if (pic_order_cnt_type == 1) {
- br.getBits(1); // delta_pic_order_always_zero_flag
- parseUE(&br); // offset_for_non_ref_pic
- parseUE(&br); // offset_for_top_to_bottom_field
-
- unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
- for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
- parseUE(&br); // offset_for_ref_frame
- }
- }
-
- parseUE(&br); // num_ref_frames
- br.getBits(1); // gaps_in_frame_num_value_allowed_flag
-
- unsigned pic_width_in_mbs_minus1 = parseUE(&br);
- unsigned pic_height_in_map_units_minus1 = parseUE(&br);
- unsigned frame_mbs_only_flag = br.getBits(1);
-
- *width = pic_width_in_mbs_minus1 * 16 + 16;
-
- *height = (2 - frame_mbs_only_flag)
- * (pic_height_in_map_units_minus1 * 16 + 16);
-}
-
static sp<ABuffer> MakeAVCCodecSpecificData(
const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
const uint8_t *data = buffer->data();
@@ -537,7 +484,7 @@
return NULL;
}
- FindDimensions(seqParamSet, width, height);
+ FindAVCDimensions(seqParamSet, width, height);
size_t stopOffset;
sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index b6772eb..3544b4c 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -3,7 +3,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- ABitReader.cpp \
AnotherPacketSource.cpp \
ATSParser.cpp \
MPEG2TSExtractor.cpp \
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
new file mode 100644
index 0000000..7e633d7
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AMPEG4ElementaryAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <stdint.h>
+
+#define BE_VERBOSE 0
+
+namespace android {
+
+// static
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> ¬ify)
+ : mNotifyMsg(notify),
+ mAccessUnitRTPTime(0),
+ mNextExpectedSeqNoValid(false),
+ mNextExpectedSeqNo(0),
+ mAccessUnitDamaged(false) {
+}
+
+AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
+ const sp<ARTPSource> &source) {
+ List<sp<ABuffer> > *queue = source->queue();
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ if (mNextExpectedSeqNoValid) {
+ List<sp<ABuffer> >::iterator it = queue->begin();
+ while (it != queue->end()) {
+ if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+ break;
+ }
+
+ it = queue->erase(it);
+ }
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+ }
+
+ sp<ABuffer> buffer = *queue->begin();
+
+ if (!mNextExpectedSeqNoValid) {
+ mNextExpectedSeqNoValid = true;
+ mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+ } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if BE_VERBOSE
+ LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+ return WRONG_SEQUENCE_NUMBER;
+ }
+
+ uint32_t rtpTime;
+ CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+ if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+ submitAccessUnit();
+ }
+ mAccessUnitRTPTime = rtpTime;
+
+ mPackets.push_back(buffer);
+ // hexdump(buffer->data(), buffer->size());
+
+ queue->erase(queue->begin());
+ ++mNextExpectedSeqNo;
+
+ return OK;
+}
+
+void AMPEG4ElementaryAssembler::submitAccessUnit() {
+ CHECK(!mPackets.empty());
+
+#if BE_VERBOSE
+ LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
+#endif
+
+ uint64_t ntpTime;
+ CHECK((*mPackets.begin())->meta()->findInt64(
+ "ntp-time", (int64_t *)&ntpTime));
+
+ size_t totalSize = 0;
+ for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+ it != mPackets.end(); ++it) {
+ totalSize += (*it)->size();
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(totalSize);
+ size_t offset = 0;
+ for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+ it != mPackets.end(); ++it) {
+ sp<ABuffer> nal = *it;
+ memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+ offset += nal->size();
+ }
+
+ accessUnit->meta()->setInt64("ntp-time", ntpTime);
+
+#if 0
+ printf(mAccessUnitDamaged ? "X" : ".");
+ fflush(stdout);
+#endif
+
+ if (mAccessUnitDamaged) {
+ accessUnit->meta()->setInt32("damaged", true);
+ }
+
+ mPackets.clear();
+ mAccessUnitDamaged = false;
+
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setObject("access-unit", accessUnit);
+ msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
+ const sp<ARTPSource> &source) {
+ AssemblyStatus status = addPacket(source);
+ if (status == MALFORMED_PACKET) {
+ mAccessUnitDamaged = true;
+ }
+ return status;
+}
+
+void AMPEG4ElementaryAssembler::packetLost() {
+ CHECK(mNextExpectedSeqNoValid);
+ LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+
+ ++mNextExpectedSeqNo;
+
+ mAccessUnitDamaged = true;
+}
+
+void AMPEG4ElementaryAssembler::onByeReceived() {
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setInt32("eos", true);
+ msg->post();
+}
+
+} // namespace android
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
new file mode 100644
index 0000000..1566d00
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
+
+#define A_MPEG4_ELEM_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AMPEG4ElementaryAssembler : public ARTPAssembler {
+ AMPEG4ElementaryAssembler(const sp<AMessage> ¬ify);
+
+protected:
+ virtual ~AMPEG4ElementaryAssembler();
+
+ virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+ virtual void onByeReceived();
+ virtual void packetLost();
+
+private:
+ sp<AMessage> mNotifyMsg;
+
+ uint32_t mAccessUnitRTPTime;
+ bool mNextExpectedSeqNoValid;
+ uint32_t mNextExpectedSeqNo;
+ bool mAccessUnitDamaged;
+ List<sp<ABuffer> > mPackets;
+
+ AssemblyStatus addPacket(const sp<ARTPSource> &source);
+ void submitAccessUnit();
+
+ DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
+};
+
+} // namespace android
+
+#endif // A_MPEG4_ELEM_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 224b4bf..8c56cb7 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -18,6 +18,11 @@
#include "ASessionDescription.h"
+#include "avc_utils.h"
+
+#include <ctype.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -37,6 +42,10 @@
size_t keyLen = strlen(key);
for (;;) {
+ while (isspace(*s)) {
+ ++s;
+ }
+
const char *colonPos = strchr(s, ';');
size_t len =
@@ -90,7 +99,11 @@
return buffer;
}
-static sp<ABuffer> MakeAVCCodecSpecificData(const char *params) {
+static sp<ABuffer> MakeAVCCodecSpecificData(
+ const char *params, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
AString val;
if (!GetAttribute(params, "profile-level-id", &val)) {
return NULL;
@@ -172,6 +185,11 @@
memcpy(out, nal->data(), nal->size());
out += nal->size();
+
+ if (i == 0) {
+ FindAVCDimensions(nal, width, height);
+ LOG(INFO) << "dimensions " << *width << "x" << *height;
+ }
}
*out++ = numPicParameterSets;
@@ -187,7 +205,7 @@
out += nal->size();
}
- hexdump(csd->data(), csd->size());
+ // hexdump(csd->data(), csd->size());
return csd;
}
@@ -224,7 +242,162 @@
csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
- hexdump(csd->data(), csd->size());
+ // hexdump(csd->data(), csd->size());
+
+ return csd;
+}
+
+static size_t GetSizeWidth(size_t x) {
+ size_t n = 1;
+ while (x > 127) {
+ ++n;
+ x >>= 7;
+ }
+ return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+ while (x > 127) {
+ *dst++ = (x & 0x7f) | 0x80;
+ x >>= 7;
+ }
+ *dst++ = x;
+ return dst;
+}
+
+static bool ExtractDimensionsFromVOLHeader(
+ const sp<ABuffer> &config, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
+ const uint8_t *ptr = config->data();
+ size_t offset = 0;
+ bool foundVOL = false;
+ while (offset + 3 < config->size()) {
+ if (memcmp("\x00\x00\x01", &ptr[offset], 3)
+ || (ptr[offset + 3] & 0xf0) != 0x20) {
+ ++offset;
+ continue;
+ }
+
+ foundVOL = true;
+ break;
+ }
+
+ if (!foundVOL) {
+ return false;
+ }
+
+ ABitReader br(&ptr[offset + 4], config->size() - offset - 4);
+ br.skipBits(1); // random_accessible_vol
+ unsigned video_object_type_indication = br.getBits(8);
+
+ CHECK_NE(video_object_type_indication,
+ 0x21u /* Fine Granularity Scalable */);
+
+ unsigned video_object_layer_verid;
+ unsigned video_object_layer_priority;
+ if (br.getBits(1)) {
+ video_object_layer_verid = br.getBits(4);
+ video_object_layer_priority = br.getBits(3);
+ }
+ unsigned aspect_ratio_info = br.getBits(4);
+ if (aspect_ratio_info == 0x0f /* extended PAR */) {
+ br.skipBits(8); // par_width
+ br.skipBits(8); // par_height
+ }
+ if (br.getBits(1)) { // vol_control_parameters
+ br.skipBits(2); // chroma_format
+ br.skipBits(1); // low_delay
+ if (br.getBits(1)) { // vbv_parameters
+ TRESPASS();
+ }
+ }
+ unsigned video_object_layer_shape = br.getBits(2);
+ CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
+
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned vop_time_increment_resolution = br.getBits(16);
+ CHECK(br.getBits(1)); // marker_bit
+
+ if (br.getBits(1)) { // fixed_vop_rate
+ // range [0..vop_time_increment_resolution)
+
+ // vop_time_increment_resolution
+ // 2 => 0..1, 1 bit
+ // 3 => 0..2, 2 bits
+ // 4 => 0..3, 2 bits
+ // 5 => 0..4, 3 bits
+ // ...
+
+ CHECK_GT(vop_time_increment_resolution, 0u);
+ --vop_time_increment_resolution;
+
+ unsigned numBits = 0;
+ while (vop_time_increment_resolution > 0) {
+ ++numBits;
+ vop_time_increment_resolution >>= 1;
+ }
+
+ br.skipBits(numBits); // fixed_vop_time_increment
+ }
+
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned video_object_layer_width = br.getBits(13);
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned video_object_layer_height = br.getBits(13);
+ CHECK(br.getBits(1)); // marker_bit
+
+ unsigned interlaced = br.getBits(1);
+
+ *width = video_object_layer_width;
+ *height = video_object_layer_height;
+
+ LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
+
+ return true;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
+ const char *params, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
+ AString val;
+ CHECK(GetAttribute(params, "config", &val));
+
+ sp<ABuffer> config = decodeHex(val);
+ CHECK(config != NULL);
+
+ if (!ExtractDimensionsFromVOLHeader(config, width, height)) {
+ return NULL;
+ }
+
+ size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+ size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+ size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+ sp<ABuffer> csd = new ABuffer(len3);
+ uint8_t *dst = csd->data();
+ *dst++ = 0x03;
+ dst = EncodeSize(dst, len2 + 3);
+ *dst++ = 0x00; // ES_ID
+ *dst++ = 0x00;
+ *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ *dst++ = 0x04;
+ dst = EncodeSize(dst, len1 + 13);
+ *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile
+ for (size_t i = 0; i < 12; ++i) {
+ *dst++ = 0x00;
+ }
+
+ *dst++ = 0x05;
+ dst = EncodeSize(dst, config->size());
+ memcpy(dst, config->data(), config->size());
+ dst += config->size();
+
+ // hexdump(csd->data(), csd->size());
return csd;
}
@@ -253,25 +426,42 @@
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t width, height;
- sessionDesc->getDimensions(index, PT, &width, &height);
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ width = -1;
+ height = -1;
+ }
- mFormat->setInt32(kKeyWidth, width);
- mFormat->setInt32(kKeyHeight, height);
-
+ int32_t encWidth, encHeight;
sp<ABuffer> codecSpecificData =
- MakeAVCCodecSpecificData(params.c_str());
+ MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight);
if (codecSpecificData != NULL) {
+ if (width < 0) {
+ // If no explicit width/height given in the sdp, use the dimensions
+ // extracted from the first sequence parameter set.
+ width = encWidth;
+ height = encHeight;
+ }
+
mFormat->setData(
kKeyAVCC, 0,
codecSpecificData->data(), codecSpecificData->size());
+ } else if (width < 0) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
}
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
} else if (!strncmp(desc.c_str(), "H263-2000/", 10)
|| !strncmp(desc.c_str(), "H263-1998/", 10)) {
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
int32_t width, height;
- sessionDesc->getDimensions(index, PT, &width, &height);
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
+ }
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
@@ -317,6 +507,36 @@
if (sampleRate != 16000 || numChannels != 1) {
mInitCheck = ERROR_UNSUPPORTED;
}
+ } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+ int32_t width, height;
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ width = -1;
+ height = -1;
+ }
+
+ int32_t encWidth, encHeight;
+ sp<ABuffer> codecSpecificData =
+ MakeMPEG4VideoCodecSpecificData(
+ params.c_str(), &encWidth, &encHeight);
+
+ if (codecSpecificData != NULL) {
+ mFormat->setData(
+ kKeyESDS, 0,
+ codecSpecificData->data(), codecSpecificData->size());
+
+ if (width < 0) {
+ width = encWidth;
+ height = encHeight;
+ }
+ } else if (width < 0) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
+ }
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
} else {
mInitCheck = ERROR_UNSUPPORTED;
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 469af3e..6816c45 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -321,6 +321,8 @@
buffer->setRange(0, nbytes);
+ // LOG(INFO) << "received " << buffer->size() << " bytes.";
+
status_t err;
if (receiveRTP) {
err = parseRTP(s, buffer);
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 225f6e8..775c4ee 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -20,6 +20,7 @@
#include "AAVCAssembler.h"
#include "AH263Assembler.h"
#include "AMPEG4AudioAssembler.h"
+#include "AMPEG4ElementaryAssembler.h"
#include "ASessionDescription.h"
#include <media/stagefright/foundation/ABuffer.h>
@@ -63,6 +64,9 @@
mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
+ } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+ mAssembler = new AMPEG4ElementaryAssembler(notify);
+ mIssueFIRRequests = true;
} else {
TRESPASS();
}
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index d6dd597..d4eed7c 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -134,10 +134,10 @@
return OK;
}
-void ARTPWriter::stop() {
+status_t ARTPWriter::stop() {
Mutex::Autolock autoLock(mLock);
if (!(mFlags & kFlagStarted)) {
- return;
+ return OK;
}
(new AMessage(kWhatStop, mReflector->id()))->post();
@@ -145,9 +145,11 @@
while (mFlags & kFlagStarted) {
mCondition.wait(mLock);
}
+ return OK;
}
-void ARTPWriter::pause() {
+status_t ARTPWriter::pause() {
+ return OK;
}
static void StripStartcode(MediaBuffer *buffer) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index b1b8b45..fdc8d23 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -40,8 +40,8 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params);
- virtual void stop();
- virtual void pause();
+ virtual status_t stop();
+ virtual status_t pause();
virtual void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index e9162c0..5f8f5fd 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -136,6 +136,20 @@
return true;
}
+static void MakeSocketBlocking(int s, bool blocking) {
+ // Make socket non-blocking.
+ int flags = fcntl(s, F_GETFL, 0);
+ CHECK_NE(flags, -1);
+
+ if (blocking) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+
+ CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+}
+
void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
++mConnectionID;
@@ -150,10 +164,7 @@
mSocket = socket(AF_INET, SOCK_STREAM, 0);
- // Make socket non-blocking.
- int flags = fcntl(mSocket, F_GETFL, 0);
- CHECK_NE(flags, -1);
- CHECK_NE(fcntl(mSocket, F_SETFL, flags | O_NONBLOCK), -1);
+ MakeSocketBlocking(mSocket, false);
AString url;
CHECK(msg->findString("url", &url));
@@ -210,7 +221,7 @@
mSocket = -1;
flushPendingRequests();
- }
+ }
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
@@ -347,7 +358,13 @@
CHECK_GE(res, 0);
if (res == 1) {
- if (!receiveRTSPReponse()) {
+ MakeSocketBlocking(mSocket, true);
+
+ bool success = receiveRTSPReponse();
+
+ MakeSocketBlocking(mSocket, false);
+
+ if (!success) {
// Something horrible, irreparable has happened.
flushPendingRequests();
return;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index ad813cd..4ea7fda 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -203,13 +203,18 @@
}
}
-void ASessionDescription::getDimensions(
+bool ASessionDescription::getDimensions(
size_t index, unsigned long PT,
int32_t *width, int32_t *height) const {
+ *width = 0;
+ *height = 0;
+
char key[20];
sprintf(key, "a=framesize:%lu", PT);
AString value;
- CHECK(findAttribute(index, key, &value));
+ if (!findAttribute(index, key, &value)) {
+ return false;
+ }
const char *s = value.c_str();
char *end;
@@ -221,6 +226,8 @@
*height = strtoul(s, &end, 10);
CHECK_GT(end, s);
CHECK_EQ(*end, '\0');
+
+ return true;
}
bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h
index b26980f..a3fa79e 100644
--- a/media/libstagefright/rtsp/ASessionDescription.h
+++ b/media/libstagefright/rtsp/ASessionDescription.h
@@ -44,7 +44,7 @@
size_t index, unsigned long *PT,
AString *desc, AString *params) const;
- void getDimensions(
+ bool getDimensions(
size_t index, unsigned long PT,
int32_t *width, int32_t *height) const;
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 7f3659f..ed16059 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -7,6 +7,7 @@
AAVCAssembler.cpp \
AH263Assembler.cpp \
AMPEG4AudioAssembler.cpp \
+ AMPEG4ElementaryAssembler.cpp \
APacketSource.cpp \
ARTPAssembler.cpp \
ARTPConnection.cpp \
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f21c8dc..b19ad48 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -309,6 +309,16 @@
size_t trackIndex;
CHECK(msg->findSize("track-index", &trackIndex));
+ int32_t eos;
+ if (msg->findInt32("eos", &eos)) {
+ LOG(INFO) << "received BYE on track index " << trackIndex;
+#if 0
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+ track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+#endif
+ return;
+ }
+
sp<RefBase> obj;
CHECK(msg->findObject("access-unit", &obj));
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6e7633e..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1601,7 +1601,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
@@ -2021,6 +2021,11 @@
return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
}
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+ return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
: PlaybackThread(audioFlinger, output, id, device)
@@ -2365,7 +2370,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
@@ -2486,6 +2491,18 @@
return time;
}
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
@@ -2612,7 +2629,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5520551..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -664,6 +664,7 @@
virtual void deleteTrackName_l(int name) = 0;
virtual uint32_t activeSleepTimeUs() = 0;
virtual uint32_t idleSleepTimeUs() = 0;
+ virtual uint32_t suspendSleepTimeUs() = 0;
private:
@@ -724,6 +725,7 @@
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
AudioMixer* mAudioMixer;
};
@@ -744,6 +746,7 @@
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
private:
void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3b3904a..ea2c5d4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -145,7 +145,12 @@
return NULL;
}
- client = new Client(this, cameraClient, cameraId, callingPid);
+ sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
+ if (hardware == NULL) {
+ LOGE("Fail to open camera hardware (id=%d)", cameraId);
+ return NULL;
+ }
+ client = new Client(this, cameraClient, hardware, cameraId, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
@@ -285,16 +290,17 @@
// ----------------------------------------------------------------------------
CameraService::Client::Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient, int cameraId, int clientPid) {
+ const sp<ICameraClient>& cameraClient,
+ const sp<CameraHardwareInterface>& hardware,
+ int cameraId, int clientPid) {
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d)", callingPid);
mCameraService = cameraService;
mCameraClient = cameraClient;
+ mHardware = hardware;
mCameraId = cameraId;
mClientPid = clientPid;
-
- mHardware = HAL_openCameraHardware(cameraId);
mUseOverlay = mHardware->useOverlay();
mMsgEnabled = 0;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 77ccf41..0d69836 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -106,6 +106,7 @@
friend class CameraService;
Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
+ const sp<CameraHardwareInterface>& hardware,
int cameraId,
int clientPid);
~Client();