Merge "Added downsample()"
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index b2808f5..4bc1799 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -188,10 +188,9 @@
// Supported flash modes.
// Example value: "auto,on,off". Read only.
static const char KEY_SUPPORTED_FLASH_MODES[];
- // Current focus mode. If the camera does not support auto-focus, the value
- // should be FOCUS_MODE_FIXED. If the focus mode is not FOCUS_MODE_FIXED or
- // or FOCUS_MODE_INFINITY, applications should call
- // CameraHardwareInterface.autoFocus to start the focus.
+ // Current focus mode. This will not be empty. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus if focus mode is
+ // FOCUS_MODE_AUTO or FOCUS_MODE_MACRO.
// Example value: "auto" or FOCUS_MODE_XXX constants. Read/write.
static const char KEY_FOCUS_MODE[];
// Supported focus modes.
@@ -248,11 +247,16 @@
// be in focus. The object is sharpest at the optimal focus distance. The
// depth of field is the far focus distance minus near focus distance.
//
- // Applications can read this parameter anytime to get the latest focus
- // distances. If the focus mode is FOCUS_MODE_EDOF, the values may be all
- // 0, which means focus distance is not applicable. If the focus mode is
- // FOCUS_MODE_CONTINUOUS and autofocus has started, focus distances may
- // change from time to time.
+ // Focus distances may change after starting auto focus, canceling auto
+ // focus, or starting the preview. Applications can read this anytime to get
+ // the latest focus distances. If the focus mode is FOCUS_MODE_CONTINUOUS,
+ // focus distances may change from time to time.
+ //
+ // This is intended to estimate the distance between the camera and the
+ // subject. After autofocus, the subject distance may be within near and far
+ // focus distance. However, the precision depends on the camera hardware,
+ // autofocus algorithm, the focus area, and the scene. The error can be
+ // large and it should be only used as a reference.
//
// Far focus distance > optimal focus distance > near focus distance. If
// the far focus distance is infinity, the value should be "Infinity" (case
@@ -350,11 +354,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
@@ -365,10 +372,11 @@
// continuously. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_EDOF[];
- // Continuous focus mode. The camera continuously tries to focus. This is
- // ideal for shooting video or shooting photo of moving object. Continuous
- // focus starts when CameraHardwareInterface.autoFocus is called. Focus
- // callback will be only called once as soon as the picture is in focus.
+ // 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. 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/AudioSource.h b/include/media/stagefright/AudioSource.h
index f1d45d2..2597e9e 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -60,7 +60,8 @@
int64_t mStartTimeUs;
int16_t mMaxAmplitude;
int64_t mPrevSampleTimeUs;
- int64_t mNumLostFrames;
+ int64_t mTotalLostFrames;
+ int64_t mPrevLostBytes;
MediaBufferGroup *mGroup;
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 2e1e8d8..be96935 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -128,6 +128,12 @@
// Write the first chunk from the given ChunkInfo.
void writeFirstChunk(ChunkInfo* info);
+ // Adjust other track media clock (presumably wall clock)
+ // based on audio track media clock with the drift time.
+ int64_t mDriftTimeUs;
+ void addDriftTimeUs(int64_t driftTimeUs);
+ int64_t getDriftTimeUs();
+
void lock();
void unlock();
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index ab1fa4f..43354c2 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -86,10 +86,10 @@
// Track authoring progress status
// kKeyTrackTimeStatus is used to track progress in elapsed time
- // kKeyTrackFrameStatus is used to track progress in authored frames
- kKeyTrackFrameStatus = 'tkfm', // int32_t
kKeyTrackTimeStatus = 'tktm', // int64_t
+ kKeyNotRealTime = 'ntrt', // bool (int32_t)
+
};
enum {
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 4eb63e8..947ff34 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -23,6 +23,7 @@
#include <camera/ICamera.h>
#include <media/IMediaRecorderClient.h>
#include <media/IMediaRecorder.h>
+#include <unistd.h>
namespace android {
@@ -373,6 +374,7 @@
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
reply->writeInt32(setOutputFile(fd, offset, length));
+ ::close(fd);
return NO_ERROR;
} break;
case SET_VIDEO_SIZE: {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 5756e53..a616aae 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -56,11 +56,6 @@
StagefrightRecorder::~StagefrightRecorder() {
LOGV("Destructor");
stop();
-
- if (mOutputFd >= 0) {
- ::close(mOutputFd);
- mOutputFd = -1;
- }
}
status_t StagefrightRecorder::init() {
@@ -736,7 +731,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);
@@ -1037,7 +1034,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);
}
@@ -1120,7 +1119,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);
}
@@ -1158,6 +1159,11 @@
mFlags = 0;
}
+ if (mOutputFd >= 0) {
+ ::close(mOutputFd);
+ mOutputFd = -1;
+ }
+
return OK;
}
@@ -1191,9 +1197,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/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 99978e8..c8dfede 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -35,7 +35,8 @@
: mStarted(false),
mCollectStats(false),
mPrevSampleTimeUs(0),
- mNumLostFrames(0),
+ mTotalLostFrames(0),
+ mPrevLostBytes(0),
mGroup(NULL) {
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
@@ -108,7 +109,8 @@
mStarted = false;
if (mCollectStats) {
- LOGI("Total lost audio frames: %lld", mNumLostFrames);
+ LOGI("Total lost audio frames: %lld",
+ mTotalLostFrames + (mPrevLostBytes >> 1));
}
return OK;
@@ -186,10 +188,11 @@
// Insert null frames when lost frames are detected.
int64_t timestampUs = mPrevSampleTimeUs;
uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
+ numLostBytes += mPrevLostBytes;
#if 0
// Simulate lost frames
- numLostBytes = ((rand() * 1.0 / RAND_MAX)) * kMaxBufferSize;
- numLostBytes &= 0xFFFFFFFE; // Alignment request
+ numLostBytes = ((rand() * 1.0 / RAND_MAX)) * 2 * kMaxBufferSize;
+ numLostBytes &= 0xFFFFFFFE; // Alignment requirement
// Reduce the chance to lose
if (rand() * 1.0 / RAND_MAX >= 0.05) {
@@ -197,13 +200,18 @@
}
#endif
if (numLostBytes > 0) {
- // Not expect too many lost frames!
- CHECK(numLostBytes <= kMaxBufferSize);
+ if (numLostBytes > kMaxBufferSize) {
+ mPrevLostBytes = numLostBytes - kMaxBufferSize;
+ numLostBytes = kMaxBufferSize;
+ }
- timestampUs += (1000000LL * numLostBytes >> 1) / sampleRate;
+ CHECK_EQ(numLostBytes & 1, 0);
+ timestampUs += ((1000000LL * (numLostBytes >> 1)) +
+ (sampleRate >> 1)) / sampleRate;
+
CHECK(timestampUs > mPrevSampleTimeUs);
if (mCollectStats) {
- mNumLostFrames += (numLostBytes >> 1);
+ mTotalLostFrames += (numLostBytes >> 1);
}
if ((err = skipFrame(timestampUs, options)) == -1) {
buffer->release();
@@ -240,7 +248,7 @@
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
CHECK(timestampUs > mPrevSampleTimeUs);
- if (mNumLostFrames == 0) {
+ if (mTotalLostFrames == 0) {
CHECK_EQ(mPrevSampleTimeUs,
mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index efdad43..f2653cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1216,7 +1216,7 @@
MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
return setDataSource_l(extractor);
- } else if (!strcmp("rtsp://gtalk", mUri.string())) {
+ } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
if (mLooper == NULL) {
mLooper = new ALooper;
mLooper->start(
@@ -1225,6 +1225,22 @@
PRIORITY_HIGHEST);
}
+ const char *startOfCodecString = &mUri.string()[13];
+ const char *startOfSlash1 = strchr(startOfCodecString, '/');
+ if (startOfSlash1 == NULL) {
+ return BAD_VALUE;
+ }
+ const char *startOfWidthString = &startOfSlash1[1];
+ const char *startOfSlash2 = strchr(startOfWidthString, '/');
+ if (startOfSlash2 == NULL) {
+ return BAD_VALUE;
+ }
+ const char *startOfHeightString = &startOfSlash2[1];
+
+ String8 codecString(startOfCodecString, startOfSlash1 - startOfCodecString);
+ String8 widthString(startOfWidthString, startOfSlash2 - startOfWidthString);
+ String8 heightString(startOfHeightString);
+
#if 0
mRTPPusher = new UDPPusher("/data/misc/rtpout.bin", 5434);
mLooper->registerHandler(mRTPPusher);
@@ -1251,8 +1267,8 @@
"a=rtpmap:97 AMR/8000/1\r\n"
"a=fmtp:97 octet-align\r\n";
#elif 1
- // My GTalk H.264 SDP
- static const char *raw =
+ String8 sdp;
+ sdp.appendFormat(
"v=0\r\n"
"o=- 64 233572944 IN IP4 127.0.0.0\r\n"
"s=QuickTime\r\n"
@@ -1262,24 +1278,16 @@
"m=video 5434 RTP/AVP 97\r\n"
"c=IN IP4 127.0.0.1\r\n"
"b=AS:30\r\n"
- "a=rtpmap:97 H264/90000\r\n"
- "a=cliprect:0,0,200,320\r\n"
- "a=framesize:97 320-200\r\n";
-#else
- // GTalk H263 SDP
- static const char *raw =
- "v=0\r\n"
- "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
- "s=QuickTime\r\n"
- "t=0 0\r\n"
- "a=range:npt=0-315\r\n"
- "a=isma-compliance:2,2.0,2\r\n"
- "m=video 5434 RTP/AVP 98\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "b=AS:30\r\n"
- "a=rtpmap:98 H263-1998/90000\r\n"
- "a=cliprect:0,0,200,320\r\n"
- "a=framesize:98 320-200\r\n";
+ "a=rtpmap:97 %s/90000\r\n"
+ "a=cliprect:0,0,%s,%s\r\n"
+ "a=framesize:97 %s-%s\r\n",
+
+ codecString.string(),
+ heightString.string(), widthString.string(),
+ widthString.string(), heightString.string()
+ );
+ const char *raw = sdp.string();
+
#endif
sp<ASessionDescription> desc = new ASessionDescription;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 5e7dd5c..9ccd140 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -260,6 +260,7 @@
void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
LOGV("signalBufferReturned: %p", buffer->data());
+ Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
if ((*it)->pointer() == buffer->data()) {
@@ -327,6 +328,7 @@
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+
return OK;
}
}
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 1fd256a..ba99501 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -187,6 +187,7 @@
if (mUseStillCameraForTimeLapse) {
void *dummy;
pthread_join(mThreadTimeLapse, &dummy);
+ CHECK_EQ(OK, mCamera->startPreview());
} else {
mCamera->stopRecording();
}
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 c860c5c..f52ec1a 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -72,6 +72,11 @@
bool mIsAudio;
bool mIsMPEG4;
int64_t mTrackDurationUs;
+
+ // For realtime applications, we need to adjust the media clock
+ // for video track based on the audio media clock
+ bool mIsRealTimeRecording;
+ int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMaxWriteTimeUs;
int32_t mTimeScale;
@@ -163,6 +168,12 @@
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();
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -429,7 +440,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");
@@ -744,10 +755,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);
@@ -755,6 +762,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);
}
@@ -940,6 +969,7 @@
mDone = false;
mIsFirstChunk = true;
+ mDriftTimeUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
ChunkInfo info;
@@ -967,6 +997,14 @@
startTimeUs = 0;
}
+ mIsRealTimeRecording = true;
+ {
+ int32_t isNotRealTime;
+ if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
+ mIsRealTimeRecording = (isNotRealTime == 0);
+ }
+ }
+
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
@@ -1322,10 +1360,16 @@
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;
int64_t timestampUs;
+
+ int64_t wallClockTimeUs = 0;
+ int64_t lastWallClockTimeUs = 0;
+
sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
@@ -1429,6 +1473,33 @@
}
timestampUs -= previousPausedDurationUs;
+ if (mIsRealTimeRecording && !mIsAudio) {
+ // The minor adjustment on the timestamp is heuristic/experimental
+ // We are adjusting the timestamp to reduce the fluctuation of the duration
+ // of neighboring samples. This in turn helps reduce the track header size,
+ // especially, the number of entries in the "stts" box.
+ if (mNumSamples > 1) {
+ int64_t durationUs = timestampUs + mOwner->getDriftTimeUs() - lastTimestampUs;
+ int64_t diffUs = (durationUs > lastDurationUs)
+ ? durationUs - lastDurationUs
+ : lastDurationUs - durationUs;
+ if (diffUs <= 5000) { // XXX: Magic number 5ms
+ timestampUs = lastTimestampUs + lastDurationUs;
+ } else {
+ timestampUs += mOwner->getDriftTimeUs();
+ }
+ }
+ }
+ CHECK(timestampUs >= 0);
+ if (mNumSamples > 1) {
+ if (timestampUs <= lastTimestampUs) {
+ LOGW("Drop a frame, since it arrives too late!");
+ copy->release();
+ copy = NULL;
+ continue;
+ }
+ }
+
LOGV("time stamp: %lld and previous paused duration %lld",
timestampUs, previousPausedDurationUs);
if (timestampUs > mTrackDurationUs) {
@@ -1438,7 +1509,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;
@@ -1453,7 +1533,16 @@
previousSampleSize = sampleSize;
}
lastDurationUs = timestampUs - lastTimestampUs;
+ lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
+ if (mIsRealTimeRecording && mIsAudio) {
+ wallClockTimeUs = systemTime() / 1000;
+ int64_t wallClockDurationUs = wallClockTimeUs - lastWallClockTimeUs;
+ if (mNumSamples > 2) {
+ mOwner->addDriftTimeUs(lastDurationUs - wallClockDurationUs);
+ }
+ lastWallClockTimeUs = wallClockTimeUs;
+ }
if (isSync != 0) {
mStssTableEntries.push_back(mNumSamples);
@@ -1679,6 +1768,18 @@
}
}
+void MPEG4Writer::addDriftTimeUs(int64_t driftTimeUs) {
+ LOGV("addDriftTimeUs: %lld us", driftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ mDriftTimeUs += driftTimeUs;
+}
+
+int64_t MPEG4Writer::getDriftTimeUs() {
+ LOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ return mDriftTimeUs;
+}
+
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
LOGV("bufferChunk");
@@ -1709,7 +1810,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();
@@ -1956,11 +2056,11 @@
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
- CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize > 0);
CHECK(23 + mCodecSpecificDataSize < 128);
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->beginBox("esds");
mOwner->writeInt32(0); // version=0, flags=0
@@ -2024,10 +2124,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/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index 6e74279..389180c 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -98,6 +98,7 @@
: mSource(source),
mMeta(meta),
mNumInputFrames(-1),
+ mPrevTimestampUs(-1),
mStarted(false),
mInputBuffer(NULL),
mInputFrameData(NULL),
@@ -391,8 +392,6 @@
if (err != OK) {
LOGE("Failed to read input video frame: %d", err);
outputBuffer->release();
- mInputBuffer->release();
- mInputBuffer = NULL;
return err;
}
@@ -407,6 +406,22 @@
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ // When the timestamp of the current sample is the same as
+ // that of the previous sample, the encoding of the sample
+ // is bypassed, and the output length is set to 0.
+ if (mNumInputFrames >= 1 && mPrevTimestampUs == timeUs) {
+ // Frame arrives too late
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ outputBuffer->set_range(0, 0);
+ *out = outputBuffer;
+ return OK;
+ }
+
+ // Don't accept out-of-order samples
+ CHECK(mPrevTimestampUs < timeUs);
+ mPrevTimestampUs = timeUs;
+
AVCFrameIO videoInput;
memset(&videoInput, 0, sizeof(videoInput));
videoInput.height = ((mVideoHeight + 15) >> 4) << 4;
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 1bef0e9..a011137 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -69,6 +69,7 @@
mMeta(meta),
mNumInputFrames(-1),
mNextModTimeUs(0),
+ mPrevTimestampUs(-1),
mStarted(false),
mInputBuffer(NULL),
mInputFrameData(NULL),
@@ -292,8 +293,6 @@
if (OK != mSource->read(&mInputBuffer, options)) {
LOGE("Failed to read from data source");
outputBuffer->release();
- mInputBuffer->release();
- mInputBuffer = NULL;
return UNKNOWN_ERROR;
}
@@ -306,8 +305,13 @@
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
- if (mNextModTimeUs > timeUs) {
- LOGV("mNextModTimeUs %lld > timeUs %lld", mNextModTimeUs, timeUs);
+
+ // When the timestamp of the current sample is the same as that
+ // of the previous sample, encoding of the current sample is
+ // bypassed, and the output length of the sample is set to 0
+ if (mNumInputFrames >= 1 &&
+ (mNextModTimeUs > timeUs || mPrevTimestampUs == timeUs)) {
+ // Frame arrives too late
outputBuffer->set_range(0, 0);
*out = outputBuffer;
mInputBuffer->release();
@@ -315,6 +319,10 @@
return OK;
}
+ // Don't accept out-of-order samples
+ CHECK(mPrevTimestampUs < timeUs);
+ mPrevTimestampUs = timeUs;
+
// Color convert to OMX_COLOR_FormatYUV420Planar if necessary
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
uint8_t *inPtr = (uint8_t *) mInputBuffer->data();
diff --git a/media/libstagefright/include/AVCEncoder.h b/media/libstagefright/include/AVCEncoder.h
index 4fe2e30..83e1f97 100644
--- a/media/libstagefright/include/AVCEncoder.h
+++ b/media/libstagefright/include/AVCEncoder.h
@@ -64,6 +64,7 @@
int32_t mVideoBitRate;
int32_t mVideoColorFormat;
int64_t mNumInputFrames;
+ int64_t mPrevTimestampUs;
status_t mInitCheck;
bool mStarted;
bool mSpsPpsHeaderReceived;
diff --git a/media/libstagefright/include/M4vH263Encoder.h b/media/libstagefright/include/M4vH263Encoder.h
index dd146f4..dbe9fd0 100644
--- a/media/libstagefright/include/M4vH263Encoder.h
+++ b/media/libstagefright/include/M4vH263Encoder.h
@@ -59,6 +59,7 @@
int32_t mVideoColorFormat;
int64_t mNumInputFrames;
int64_t mNextModTimeUs;
+ int64_t mPrevTimestampUs;
status_t mInitCheck;
bool mStarted;
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 8b59998..2818041 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -110,7 +110,7 @@
buffer->data()[0] = 0x00;
buffer->data()[1] = 0x00;
} else {
- buffer->setRange(2, buffer->size() - 2);
+ buffer->setRange(buffer->offset() + 2, buffer->size() - 2);
}
mPackets.push_back(buffer);
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 395cd28..224b4bf 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -376,7 +376,7 @@
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
- LOG(INFO) << "discarding damaged AU";
+ LOG(VERBOSE) << "discarding damaged AU";
return;
}
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index cc23856..d6dd597 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -43,7 +43,7 @@
#if 1
mRTPAddr.sin_addr.s_addr = INADDR_ANY;
#else
- mRTPAddr.sin_addr.s_addr = inet_addr("172.19.19.74");
+ mRTPAddr.sin_addr.s_addr = inet_addr("172.19.18.246");
#endif
mRTPAddr.sin_port = htons(5634);
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 9bfd00f..27dc796 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -325,9 +325,12 @@
else
break;
}
- putUInt8(count);
+ putUInt8(count > 0 ? count + 1 : 0);
for (int i = 0; i < count; i++)
putUInt16(string[i]);
+ // only terminate with zero if string is not empty
+ if (count > 0)
+ putUInt16(0);
}
#ifdef MTP_DEVICE
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index b458286..1467aab 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -83,7 +83,7 @@
void putString(const MtpStringBuffer& string);
void putString(const char* string);
void putString(const uint16_t* string);
- inline void putEmptyString() { putUInt16(0); }
+ inline void putEmptyString() { putUInt8(0); }
inline void putEmptyArray() { putUInt32(0); }
diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp
index 2d3cf69..8bf6731 100644
--- a/media/mtp/MtpStringBuffer.cpp
+++ b/media/mtp/MtpStringBuffer.cpp
@@ -112,7 +112,7 @@
void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
int count = mCharCount;
const uint8_t* src = mBuffer;
- packet->putUInt8(count);
+ packet->putUInt8(count > 0 ? count + 1 : 0);
// expand utf8 to 16 bit chars
for (int i = 0; i < count; i++) {
@@ -133,6 +133,9 @@
}
packet->putUInt16(ch);
}
+ // only terminate with zero if string is not empty
+ if (count > 0)
+ packet->putUInt16(0);
}
} // namespace android
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1c7faa4..6e7633e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1675,6 +1675,9 @@
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
+ if (masterMute) {
+ masterVolume = 0;
+ }
#ifdef LVMX
bool tracksConnectedChanged = false;
bool stateChanged = false;
@@ -1696,10 +1699,7 @@
// Delegate master volume control to effect in output mix effect chain if needed
sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
if (chain != 0) {
- uint32_t v = 0;
- if (!masterMute) {
- v = (uint32_t)(masterVolume * (1 << 24));
- }
+ uint32_t v = (uint32_t)(masterVolume * (1 << 24));
chain->setVolume_l(&v, &v);
masterVolume = (float)((v + (1 << 23)) >> 24);
chain.clear();