Merge "File writer has a designated writer thread now" into gingerbread
This commit is contained in:
@ -75,6 +75,7 @@ private:
|
|||||||
uint32_t mInterleaveDurationUs;
|
uint32_t mInterleaveDurationUs;
|
||||||
int32_t mTimeScale;
|
int32_t mTimeScale;
|
||||||
int64_t mStartTimestampUs;
|
int64_t mStartTimestampUs;
|
||||||
|
|
||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
|
|
||||||
List<Track *> mTracks;
|
List<Track *> mTracks;
|
||||||
@ -87,6 +88,46 @@ private:
|
|||||||
size_t numTracks();
|
size_t numTracks();
|
||||||
int64_t estimateMoovBoxSize(int32_t bitRate);
|
int64_t estimateMoovBoxSize(int32_t bitRate);
|
||||||
|
|
||||||
|
struct Chunk {
|
||||||
|
Track *mTrack; // Owner
|
||||||
|
int64_t mTimeStampUs; // Timestamp of the 1st sample
|
||||||
|
List<MediaBuffer *> mSamples; // Sample data
|
||||||
|
|
||||||
|
// Convenient constructor
|
||||||
|
Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples)
|
||||||
|
: mTrack(track), mTimeStampUs(timeUs), mSamples(samples) {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
struct ChunkInfo {
|
||||||
|
Track *mTrack; // Owner
|
||||||
|
List<Chunk> mChunks; // Remaining chunks to be written
|
||||||
|
};
|
||||||
|
|
||||||
|
bool mIsFirstChunk;
|
||||||
|
volatile bool mDone; // Writer thread is done?
|
||||||
|
pthread_t mThread; // Thread id for the writer
|
||||||
|
List<ChunkInfo> mChunkInfos; // Chunk infos
|
||||||
|
Condition mChunkReadyCondition; // Signal that chunks are available
|
||||||
|
|
||||||
|
// Writer thread handling
|
||||||
|
status_t startWriterThread();
|
||||||
|
void stopWriterThread();
|
||||||
|
static void *ThreadWrapper(void *me);
|
||||||
|
void threadFunc();
|
||||||
|
|
||||||
|
// Buffer a single chunk to be written out later.
|
||||||
|
void bufferChunk(const Chunk& chunk);
|
||||||
|
|
||||||
|
// Write all buffered chunks from all tracks
|
||||||
|
void writeChunks();
|
||||||
|
|
||||||
|
// Write a chunk if there is one
|
||||||
|
status_t writeOneChunk();
|
||||||
|
|
||||||
|
// Write the first chunk from the given ChunkInfo.
|
||||||
|
void writeFirstChunk(ChunkInfo* info);
|
||||||
|
|
||||||
void lock();
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
|
@ -52,6 +52,11 @@ public:
|
|||||||
int64_t getDurationUs() const;
|
int64_t getDurationUs() const;
|
||||||
int64_t getEstimatedTrackSizeBytes() const;
|
int64_t getEstimatedTrackSizeBytes() const;
|
||||||
void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
|
void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
|
||||||
|
void bufferChunk(int64_t timestampUs);
|
||||||
|
bool isAvc() const { return mIsAvc; }
|
||||||
|
bool isAudio() const { return mIsAudio; }
|
||||||
|
bool isMPEG4() const { return mIsMPEG4; }
|
||||||
|
void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MPEG4Writer *mOwner;
|
MPEG4Writer *mOwner;
|
||||||
@ -60,8 +65,12 @@ private:
|
|||||||
volatile bool mDone;
|
volatile bool mDone;
|
||||||
volatile bool mPaused;
|
volatile bool mPaused;
|
||||||
volatile bool mResumed;
|
volatile bool mResumed;
|
||||||
|
bool mIsAvc;
|
||||||
|
bool mIsAudio;
|
||||||
|
bool mIsMPEG4;
|
||||||
int64_t mMaxTimeStampUs;
|
int64_t mMaxTimeStampUs;
|
||||||
int64_t mEstimatedTrackSizeBytes;
|
int64_t mEstimatedTrackSizeBytes;
|
||||||
|
int64_t mMaxWriteTimeUs;
|
||||||
int32_t mTimeScale;
|
int32_t mTimeScale;
|
||||||
|
|
||||||
pthread_t mThread;
|
pthread_t mThread;
|
||||||
@ -117,7 +126,6 @@ private:
|
|||||||
|
|
||||||
status_t makeAVCCodecSpecificData(
|
status_t makeAVCCodecSpecificData(
|
||||||
const uint8_t *data, size_t size);
|
const uint8_t *data, size_t size);
|
||||||
void writeOneChunk(bool isAvc);
|
|
||||||
|
|
||||||
// Track authoring progress status
|
// Track authoring progress status
|
||||||
void trackProgressStatus(int64_t timeUs, status_t err = OK);
|
void trackProgressStatus(int64_t timeUs, status_t err = OK);
|
||||||
@ -320,10 +328,17 @@ status_t MPEG4Writer::start(MetaData *param) {
|
|||||||
} else {
|
} else {
|
||||||
write("\x00\x00\x00\x01mdat????????", 16);
|
write("\x00\x00\x00\x01mdat????????", 16);
|
||||||
}
|
}
|
||||||
status_t err = startTracks(param);
|
|
||||||
|
status_t err = startWriterThread();
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = startTracks(param);
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
mStarted = true;
|
mStarted = true;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@ -339,6 +354,20 @@ void MPEG4Writer::pause() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MPEG4Writer::stopWriterThread() {
|
||||||
|
LOGV("stopWriterThread");
|
||||||
|
|
||||||
|
{
|
||||||
|
Mutex::Autolock autolock(mLock);
|
||||||
|
|
||||||
|
mDone = true;
|
||||||
|
mChunkReadyCondition.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dummy;
|
||||||
|
pthread_join(mThread, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
void MPEG4Writer::stop() {
|
void MPEG4Writer::stop() {
|
||||||
if (mFile == NULL) {
|
if (mFile == NULL) {
|
||||||
return;
|
return;
|
||||||
@ -355,6 +384,7 @@ void MPEG4Writer::stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopWriterThread();
|
||||||
|
|
||||||
// Fix up the size of the 'mdat' chunk.
|
// Fix up the size of the 'mdat' chunk.
|
||||||
if (mUse32BitOffset) {
|
if (mUse32BitOffset) {
|
||||||
@ -693,6 +723,14 @@ MPEG4Writer::Track::Track(
|
|||||||
if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
|
if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
|
||||||
mTimeScale = 1000;
|
mTimeScale = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *mime;
|
||||||
|
mMeta->findCString(kKeyMIMEType, &mime);
|
||||||
|
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
|
||||||
|
mIsAudio = !strncasecmp(mime, "audio/", 6);
|
||||||
|
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
|
||||||
|
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
|
||||||
|
|
||||||
CHECK(mTimeScale > 0);
|
CHECK(mTimeScale > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,6 +789,148 @@ void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void *MPEG4Writer::ThreadWrapper(void *me) {
|
||||||
|
LOGV("ThreadWrapper: %p", me);
|
||||||
|
MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
|
||||||
|
writer->threadFunc();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPEG4Writer::bufferChunk(const Chunk& chunk) {
|
||||||
|
LOGV("bufferChunk: %p", chunk.mTrack);
|
||||||
|
Mutex::Autolock autolock(mLock);
|
||||||
|
CHECK_EQ(mDone, false);
|
||||||
|
|
||||||
|
for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
|
||||||
|
it != mChunkInfos.end(); ++it) {
|
||||||
|
|
||||||
|
if (chunk.mTrack == it->mTrack) { // Found owner
|
||||||
|
it->mChunks.push_back(chunk);
|
||||||
|
mChunkReadyCondition.signal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK("Received a chunk for a unknown track" == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPEG4Writer::writeFirstChunk(ChunkInfo* info) {
|
||||||
|
LOGV("writeFirstChunk: %p", info->mTrack);
|
||||||
|
|
||||||
|
List<Chunk>::iterator chunkIt = info->mChunks.begin();
|
||||||
|
for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
|
||||||
|
it != chunkIt->mSamples.end(); ++it) {
|
||||||
|
|
||||||
|
off_t offset = info->mTrack->isAvc()
|
||||||
|
? addLengthPrefixedSample_l(*it)
|
||||||
|
: addSample_l(*it);
|
||||||
|
if (it == chunkIt->mSamples.begin()) {
|
||||||
|
info->mTrack->addChunkOffset(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done with the current chunk.
|
||||||
|
// Release all the samples in this chunk.
|
||||||
|
while (!chunkIt->mSamples.empty()) {
|
||||||
|
List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
|
||||||
|
(*it)->release();
|
||||||
|
(*it) = NULL;
|
||||||
|
chunkIt->mSamples.erase(it);
|
||||||
|
}
|
||||||
|
chunkIt->mSamples.clear();
|
||||||
|
info->mChunks.erase(chunkIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPEG4Writer::writeChunks() {
|
||||||
|
LOGV("writeChunks");
|
||||||
|
size_t outstandingChunks = 0;
|
||||||
|
while (!mChunkInfos.empty()) {
|
||||||
|
List<ChunkInfo>::iterator it = mChunkInfos.begin();
|
||||||
|
while (!it->mChunks.empty()) {
|
||||||
|
CHECK_EQ(OK, writeOneChunk());
|
||||||
|
++outstandingChunks;
|
||||||
|
}
|
||||||
|
it->mTrack = NULL;
|
||||||
|
mChunkInfos.erase(it);
|
||||||
|
}
|
||||||
|
mChunkInfos.clear();
|
||||||
|
LOGD("%d chunks are written in the last batch", outstandingChunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t MPEG4Writer::writeOneChunk() {
|
||||||
|
LOGV("writeOneChunk");
|
||||||
|
|
||||||
|
// Find the smallest timestamp, and write that chunk out
|
||||||
|
// XXX: What if some track is just too slow?
|
||||||
|
int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
|
||||||
|
Track *track = NULL;
|
||||||
|
for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
|
||||||
|
it != mChunkInfos.end(); ++it) {
|
||||||
|
if (!it->mChunks.empty()) {
|
||||||
|
List<Chunk>::iterator chunkIt = it->mChunks.begin();
|
||||||
|
if (chunkIt->mTimeStampUs < minTimestampUs) {
|
||||||
|
minTimestampUs = chunkIt->mTimeStampUs;
|
||||||
|
track = it->mTrack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track == NULL) {
|
||||||
|
LOGV("Nothing to be written after all");
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsFirstChunk) {
|
||||||
|
mIsFirstChunk = false;
|
||||||
|
}
|
||||||
|
for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
|
||||||
|
it != mChunkInfos.end(); ++it) {
|
||||||
|
if (it->mTrack == track) {
|
||||||
|
writeFirstChunk(&(*it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPEG4Writer::threadFunc() {
|
||||||
|
LOGV("threadFunc");
|
||||||
|
|
||||||
|
while (!mDone) {
|
||||||
|
{
|
||||||
|
Mutex::Autolock autolock(mLock);
|
||||||
|
mChunkReadyCondition.wait(mLock);
|
||||||
|
CHECK_EQ(writeOneChunk(), OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Write ALL samples
|
||||||
|
Mutex::Autolock autolock(mLock);
|
||||||
|
writeChunks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t MPEG4Writer::startWriterThread() {
|
||||||
|
LOGV("startWriterThread");
|
||||||
|
|
||||||
|
mDone = false;
|
||||||
|
mIsFirstChunk = true;
|
||||||
|
for (List<Track *>::iterator it = mTracks.begin();
|
||||||
|
it != mTracks.end(); ++it) {
|
||||||
|
ChunkInfo info;
|
||||||
|
info.mTrack = *it;
|
||||||
|
mChunkInfos.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||||
|
pthread_create(&mThread, &attr, ThreadWrapper, this);
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
status_t MPEG4Writer::Track::start(MetaData *params) {
|
status_t MPEG4Writer::Track::start(MetaData *params) {
|
||||||
if (!mDone && mPaused) {
|
if (!mDone && mPaused) {
|
||||||
mPaused = false;
|
mPaused = false;
|
||||||
@ -926,13 +1106,6 @@ static bool collectStatisticalData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::Track::threadEntry() {
|
void MPEG4Writer::Track::threadEntry() {
|
||||||
sp<MetaData> meta = mSource->getFormat();
|
|
||||||
const char *mime;
|
|
||||||
meta->findCString(kKeyMIMEType, &mime);
|
|
||||||
bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
|
|
||||||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
|
|
||||||
bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
|
|
||||||
bool is_audio = !strncasecmp(mime, "audio/", 6);
|
|
||||||
int32_t count = 0;
|
int32_t count = 0;
|
||||||
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
|
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
|
||||||
int64_t chunkTimestampUs = 0;
|
int64_t chunkTimestampUs = 0;
|
||||||
@ -943,10 +1116,12 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
int32_t sampleCount = 1; // Sample count in the current stts table entry
|
int32_t sampleCount = 1; // Sample count in the current stts table entry
|
||||||
uint32_t previousSampleSize = 0; // Size of the previous sample
|
uint32_t previousSampleSize = 0; // Size of the previous sample
|
||||||
int64_t previousPausedDurationUs = 0;
|
int64_t previousPausedDurationUs = 0;
|
||||||
|
int64_t timestampUs;
|
||||||
sp<MetaData> meta_data;
|
sp<MetaData> meta_data;
|
||||||
bool collectStats = collectStatisticalData();
|
bool collectStats = collectStatisticalData();
|
||||||
|
|
||||||
mNumSamples = 0;
|
mNumSamples = 0;
|
||||||
|
mMaxWriteTimeUs = 0;
|
||||||
status_t err = OK;
|
status_t err = OK;
|
||||||
MediaBuffer *buffer;
|
MediaBuffer *buffer;
|
||||||
while (!mDone && (err = mSource->read(&buffer)) == OK) {
|
while (!mDone && (err = mSource->read(&buffer)) == OK) {
|
||||||
@ -973,13 +1148,13 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
&& isCodecConfig) {
|
&& isCodecConfig) {
|
||||||
CHECK(!mGotAllCodecSpecificData);
|
CHECK(!mGotAllCodecSpecificData);
|
||||||
|
|
||||||
if (is_avc) {
|
if (mIsAvc) {
|
||||||
status_t err = makeAVCCodecSpecificData(
|
status_t err = makeAVCCodecSpecificData(
|
||||||
(const uint8_t *)buffer->data()
|
(const uint8_t *)buffer->data()
|
||||||
+ buffer->range_offset(),
|
+ buffer->range_offset(),
|
||||||
buffer->range_length());
|
buffer->range_length());
|
||||||
CHECK_EQ(OK, err);
|
CHECK_EQ(OK, err);
|
||||||
} else if (is_mpeg4) {
|
} else if (mIsMPEG4) {
|
||||||
mCodecSpecificDataSize = buffer->range_length();
|
mCodecSpecificDataSize = buffer->range_length();
|
||||||
mCodecSpecificData = malloc(mCodecSpecificDataSize);
|
mCodecSpecificData = malloc(mCodecSpecificDataSize);
|
||||||
memcpy(mCodecSpecificData,
|
memcpy(mCodecSpecificData,
|
||||||
@ -994,7 +1169,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
mGotAllCodecSpecificData = true;
|
mGotAllCodecSpecificData = true;
|
||||||
continue;
|
continue;
|
||||||
} else if (!mGotAllCodecSpecificData &&
|
} else if (!mGotAllCodecSpecificData &&
|
||||||
count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
|
count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
|
||||||
// The TI mpeg4 encoder does not properly set the
|
// The TI mpeg4 encoder does not properly set the
|
||||||
// codec-specific-data flag.
|
// codec-specific-data flag.
|
||||||
|
|
||||||
@ -1034,7 +1209,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mGotAllCodecSpecificData = true;
|
mGotAllCodecSpecificData = true;
|
||||||
} else if (!mGotAllCodecSpecificData && is_avc && count < 3) {
|
} else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
|
||||||
// The TI video encoder does not flag codec specific data
|
// The TI video encoder does not flag codec specific data
|
||||||
// as such and also splits up SPS and PPS across two buffers.
|
// as such and also splits up SPS and PPS across two buffers.
|
||||||
|
|
||||||
@ -1090,10 +1265,10 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
buffer->release();
|
buffer->release();
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
|
|
||||||
if (is_avc) StripStartcode(copy);
|
if (mIsAvc) StripStartcode(copy);
|
||||||
|
|
||||||
size_t sampleSize;
|
size_t sampleSize;
|
||||||
sampleSize = is_avc
|
sampleSize = mIsAvc
|
||||||
#if USE_NALLEN_FOUR
|
#if USE_NALLEN_FOUR
|
||||||
? copy->range_length() + 4
|
? copy->range_length() + 4
|
||||||
#else
|
#else
|
||||||
@ -1116,7 +1291,6 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
int32_t isSync = false;
|
int32_t isSync = false;
|
||||||
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
|
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
|
||||||
|
|
||||||
int64_t timestampUs;
|
|
||||||
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
|
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1168,7 +1342,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
trackProgressStatus(timestampUs);
|
trackProgressStatus(timestampUs);
|
||||||
}
|
}
|
||||||
if (mOwner->numTracks() == 1) {
|
if (mOwner->numTracks() == 1) {
|
||||||
off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
|
off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
|
||||||
: mOwner->addSample_l(copy);
|
: mOwner->addSample_l(copy);
|
||||||
if (mChunkOffsets.empty()) {
|
if (mChunkOffsets.empty()) {
|
||||||
mChunkOffsets.push_back(offset);
|
mChunkOffsets.push_back(offset);
|
||||||
@ -1182,7 +1356,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
if (interleaveDurationUs == 0) {
|
if (interleaveDurationUs == 0) {
|
||||||
StscTableEntry stscEntry(++nChunks, 1, 1);
|
StscTableEntry stscEntry(++nChunks, 1, 1);
|
||||||
mStscTableEntries.push_back(stscEntry);
|
mStscTableEntries.push_back(stscEntry);
|
||||||
writeOneChunk(is_avc);
|
bufferChunk(timestampUs);
|
||||||
} else {
|
} else {
|
||||||
if (chunkTimestampUs == 0) {
|
if (chunkTimestampUs == 0) {
|
||||||
chunkTimestampUs = timestampUs;
|
chunkTimestampUs = timestampUs;
|
||||||
@ -1199,7 +1373,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
mChunkSamples.size(), 1);
|
mChunkSamples.size(), 1);
|
||||||
mStscTableEntries.push_back(stscEntry);
|
mStscTableEntries.push_back(stscEntry);
|
||||||
}
|
}
|
||||||
writeOneChunk(is_avc);
|
bufferChunk(timestampUs);
|
||||||
chunkTimestampUs = timestampUs;
|
chunkTimestampUs = timestampUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1220,7 +1394,7 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
++nChunks;
|
++nChunks;
|
||||||
StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
|
StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
|
||||||
mStscTableEntries.push_back(stscEntry);
|
mStscTableEntries.push_back(stscEntry);
|
||||||
writeOneChunk(is_avc);
|
bufferChunk(timestampUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't really know how long the last frame lasts, since
|
// We don't really know how long the last frame lasts, since
|
||||||
@ -1234,10 +1408,10 @@ void MPEG4Writer::Track::threadEntry() {
|
|||||||
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
|
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
|
||||||
mSttsTableEntries.push_back(sttsEntry);
|
mSttsTableEntries.push_back(sttsEntry);
|
||||||
mReachedEOS = true;
|
mReachedEOS = true;
|
||||||
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
|
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
|
||||||
count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video");
|
count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
|
||||||
|
|
||||||
logStatisticalData(is_audio);
|
logStatisticalData(mIsAudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
|
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
|
||||||
@ -1380,24 +1554,17 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::Track::writeOneChunk(bool isAvc) {
|
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
|
||||||
mOwner->lock();
|
LOGV("bufferChunk");
|
||||||
for (List<MediaBuffer *>::iterator it = mChunkSamples.begin();
|
|
||||||
it != mChunkSamples.end(); ++it) {
|
int64_t startTimeUs = systemTime() / 1000;
|
||||||
off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it)
|
Chunk chunk(this, timestampUs, mChunkSamples);
|
||||||
: mOwner->addSample_l(*it);
|
mOwner->bufferChunk(chunk);
|
||||||
if (it == mChunkSamples.begin()) {
|
|
||||||
mChunkOffsets.push_back(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mOwner->unlock();
|
|
||||||
while (!mChunkSamples.empty()) {
|
|
||||||
List<MediaBuffer *>::iterator it = mChunkSamples.begin();
|
|
||||||
(*it)->release();
|
|
||||||
(*it) = NULL;
|
|
||||||
mChunkSamples.erase(it);
|
|
||||||
}
|
|
||||||
mChunkSamples.clear();
|
mChunkSamples.clear();
|
||||||
|
int64_t endTimeUs = systemTime() / 1000;
|
||||||
|
if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
|
||||||
|
mMaxWriteTimeUs = endTimeUs - startTimeUs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t MPEG4Writer::Track::getDurationUs() const {
|
int64_t MPEG4Writer::Track::getDurationUs() const {
|
||||||
@ -1414,9 +1581,8 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
bool success = mMeta->findCString(kKeyMIMEType, &mime);
|
bool success = mMeta->findCString(kKeyMIMEType, &mime);
|
||||||
CHECK(success);
|
CHECK(success);
|
||||||
|
|
||||||
bool is_audio = !strncasecmp(mime, "audio/", 6);
|
|
||||||
LOGV("%s track time scale: %d",
|
LOGV("%s track time scale: %d",
|
||||||
is_audio? "Audio": "Video", mTimeScale);
|
mIsAudio? "Audio": "Video", mTimeScale);
|
||||||
|
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
@ -1440,7 +1606,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
mOwner->writeInt32(0); // reserved
|
mOwner->writeInt32(0); // reserved
|
||||||
mOwner->writeInt16(0); // layer
|
mOwner->writeInt16(0); // layer
|
||||||
mOwner->writeInt16(0); // alternate group
|
mOwner->writeInt16(0); // alternate group
|
||||||
mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
|
mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
|
||||||
mOwner->writeInt16(0); // reserved
|
mOwner->writeInt16(0); // reserved
|
||||||
|
|
||||||
mOwner->writeInt32(0x10000); // matrix
|
mOwner->writeInt32(0x10000); // matrix
|
||||||
@ -1453,7 +1619,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
mOwner->writeInt32(0);
|
mOwner->writeInt32(0);
|
||||||
mOwner->writeInt32(0x40000000);
|
mOwner->writeInt32(0x40000000);
|
||||||
|
|
||||||
if (is_audio) {
|
if (mIsAudio) {
|
||||||
mOwner->writeInt32(0);
|
mOwner->writeInt32(0);
|
||||||
mOwner->writeInt32(0);
|
mOwner->writeInt32(0);
|
||||||
} else {
|
} else {
|
||||||
@ -1511,16 +1677,16 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
mOwner->beginBox("hdlr");
|
mOwner->beginBox("hdlr");
|
||||||
mOwner->writeInt32(0); // version=0, flags=0
|
mOwner->writeInt32(0); // version=0, flags=0
|
||||||
mOwner->writeInt32(0); // component type: should be mhlr
|
mOwner->writeInt32(0); // component type: should be mhlr
|
||||||
mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype
|
mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
|
||||||
mOwner->writeInt32(0); // reserved
|
mOwner->writeInt32(0); // reserved
|
||||||
mOwner->writeInt32(0); // reserved
|
mOwner->writeInt32(0); // reserved
|
||||||
mOwner->writeInt32(0); // reserved
|
mOwner->writeInt32(0); // reserved
|
||||||
// Removing "r" for the name string just makes the string 4 byte aligned
|
// Removing "r" for the name string just makes the string 4 byte aligned
|
||||||
mOwner->writeCString(is_audio ? "SoundHandle": "VideoHandle"); // name
|
mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
|
||||||
mOwner->endBox();
|
mOwner->endBox();
|
||||||
|
|
||||||
mOwner->beginBox("minf");
|
mOwner->beginBox("minf");
|
||||||
if (is_audio) {
|
if (mIsAudio) {
|
||||||
mOwner->beginBox("smhd");
|
mOwner->beginBox("smhd");
|
||||||
mOwner->writeInt32(0); // version=0, flags=0
|
mOwner->writeInt32(0); // version=0, flags=0
|
||||||
mOwner->writeInt16(0); // balance
|
mOwner->writeInt16(0); // balance
|
||||||
@ -1553,7 +1719,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
mOwner->beginBox("stsd");
|
mOwner->beginBox("stsd");
|
||||||
mOwner->writeInt32(0); // version=0, flags=0
|
mOwner->writeInt32(0); // version=0, flags=0
|
||||||
mOwner->writeInt32(1); // entry count
|
mOwner->writeInt32(1); // entry count
|
||||||
if (is_audio) {
|
if (mIsAudio) {
|
||||||
const char *fourcc = NULL;
|
const char *fourcc = NULL;
|
||||||
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
|
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
|
||||||
fourcc = "samr";
|
fourcc = "samr";
|
||||||
@ -1735,7 +1901,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
|||||||
}
|
}
|
||||||
mOwner->endBox(); // stts
|
mOwner->endBox(); // stts
|
||||||
|
|
||||||
if (!is_audio) {
|
if (!mIsAudio) {
|
||||||
mOwner->beginBox("stss");
|
mOwner->beginBox("stss");
|
||||||
mOwner->writeInt32(0); // version=0, flags=0
|
mOwner->writeInt32(0); // version=0, flags=0
|
||||||
mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
|
mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
|
||||||
|
Reference in New Issue
Block a user