Merge "File writer has a designated writer thread now" into gingerbread
This commit is contained in:
@ -75,6 +75,7 @@ private:
|
||||
uint32_t mInterleaveDurationUs;
|
||||
int32_t mTimeScale;
|
||||
int64_t mStartTimestampUs;
|
||||
|
||||
Mutex mLock;
|
||||
|
||||
List<Track *> mTracks;
|
||||
@ -87,6 +88,46 @@ private:
|
||||
size_t numTracks();
|
||||
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 unlock();
|
||||
|
||||
|
@ -52,6 +52,11 @@ public:
|
||||
int64_t getDurationUs() const;
|
||||
int64_t getEstimatedTrackSizeBytes() const;
|
||||
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:
|
||||
MPEG4Writer *mOwner;
|
||||
@ -60,8 +65,12 @@ private:
|
||||
volatile bool mDone;
|
||||
volatile bool mPaused;
|
||||
volatile bool mResumed;
|
||||
bool mIsAvc;
|
||||
bool mIsAudio;
|
||||
bool mIsMPEG4;
|
||||
int64_t mMaxTimeStampUs;
|
||||
int64_t mEstimatedTrackSizeBytes;
|
||||
int64_t mMaxWriteTimeUs;
|
||||
int32_t mTimeScale;
|
||||
|
||||
pthread_t mThread;
|
||||
@ -117,7 +126,6 @@ private:
|
||||
|
||||
status_t makeAVCCodecSpecificData(
|
||||
const uint8_t *data, size_t size);
|
||||
void writeOneChunk(bool isAvc);
|
||||
|
||||
// Track authoring progress status
|
||||
void trackProgressStatus(int64_t timeUs, status_t err = OK);
|
||||
@ -320,10 +328,17 @@ status_t MPEG4Writer::start(MetaData *param) {
|
||||
} else {
|
||||
write("\x00\x00\x00\x01mdat????????", 16);
|
||||
}
|
||||
status_t err = startTracks(param);
|
||||
|
||||
status_t err = startWriterThread();
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = startTracks(param);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
mStarted = true;
|
||||
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() {
|
||||
if (mFile == NULL) {
|
||||
return;
|
||||
@ -355,6 +384,7 @@ void MPEG4Writer::stop() {
|
||||
}
|
||||
}
|
||||
|
||||
stopWriterThread();
|
||||
|
||||
// Fix up the size of the 'mdat' chunk.
|
||||
if (mUse32BitOffset) {
|
||||
@ -693,6 +723,14 @@ MPEG4Writer::Track::Track(
|
||||
if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
if (!mDone && mPaused) {
|
||||
mPaused = false;
|
||||
@ -926,13 +1106,6 @@ static bool collectStatisticalData() {
|
||||
}
|
||||
|
||||
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;
|
||||
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
|
||||
int64_t chunkTimestampUs = 0;
|
||||
@ -943,10 +1116,12 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
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;
|
||||
sp<MetaData> meta_data;
|
||||
bool collectStats = collectStatisticalData();
|
||||
|
||||
mNumSamples = 0;
|
||||
mMaxWriteTimeUs = 0;
|
||||
status_t err = OK;
|
||||
MediaBuffer *buffer;
|
||||
while (!mDone && (err = mSource->read(&buffer)) == OK) {
|
||||
@ -973,13 +1148,13 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
&& isCodecConfig) {
|
||||
CHECK(!mGotAllCodecSpecificData);
|
||||
|
||||
if (is_avc) {
|
||||
if (mIsAvc) {
|
||||
status_t err = makeAVCCodecSpecificData(
|
||||
(const uint8_t *)buffer->data()
|
||||
+ buffer->range_offset(),
|
||||
buffer->range_length());
|
||||
CHECK_EQ(OK, err);
|
||||
} else if (is_mpeg4) {
|
||||
} else if (mIsMPEG4) {
|
||||
mCodecSpecificDataSize = buffer->range_length();
|
||||
mCodecSpecificData = malloc(mCodecSpecificDataSize);
|
||||
memcpy(mCodecSpecificData,
|
||||
@ -994,7 +1169,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
mGotAllCodecSpecificData = true;
|
||||
continue;
|
||||
} else if (!mGotAllCodecSpecificData &&
|
||||
count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
|
||||
count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
|
||||
// The TI mpeg4 encoder does not properly set the
|
||||
// codec-specific-data flag.
|
||||
|
||||
@ -1034,7 +1209,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
}
|
||||
|
||||
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
|
||||
// as such and also splits up SPS and PPS across two buffers.
|
||||
|
||||
@ -1090,10 +1265,10 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
buffer->release();
|
||||
buffer = NULL;
|
||||
|
||||
if (is_avc) StripStartcode(copy);
|
||||
if (mIsAvc) StripStartcode(copy);
|
||||
|
||||
size_t sampleSize;
|
||||
sampleSize = is_avc
|
||||
sampleSize = mIsAvc
|
||||
#if USE_NALLEN_FOUR
|
||||
? copy->range_length() + 4
|
||||
#else
|
||||
@ -1116,7 +1291,6 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
int32_t isSync = false;
|
||||
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
|
||||
|
||||
int64_t timestampUs;
|
||||
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1168,7 +1342,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
trackProgressStatus(timestampUs);
|
||||
}
|
||||
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);
|
||||
if (mChunkOffsets.empty()) {
|
||||
mChunkOffsets.push_back(offset);
|
||||
@ -1182,7 +1356,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
if (interleaveDurationUs == 0) {
|
||||
StscTableEntry stscEntry(++nChunks, 1, 1);
|
||||
mStscTableEntries.push_back(stscEntry);
|
||||
writeOneChunk(is_avc);
|
||||
bufferChunk(timestampUs);
|
||||
} else {
|
||||
if (chunkTimestampUs == 0) {
|
||||
chunkTimestampUs = timestampUs;
|
||||
@ -1199,7 +1373,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
mChunkSamples.size(), 1);
|
||||
mStscTableEntries.push_back(stscEntry);
|
||||
}
|
||||
writeOneChunk(is_avc);
|
||||
bufferChunk(timestampUs);
|
||||
chunkTimestampUs = timestampUs;
|
||||
}
|
||||
}
|
||||
@ -1220,7 +1394,7 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
++nChunks;
|
||||
StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
|
||||
mStscTableEntries.push_back(stscEntry);
|
||||
writeOneChunk(is_avc);
|
||||
bufferChunk(timestampUs);
|
||||
}
|
||||
|
||||
// We don't really know how long the last frame lasts, since
|
||||
@ -1234,10 +1408,10 @@ void MPEG4Writer::Track::threadEntry() {
|
||||
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
|
||||
mSttsTableEntries.push_back(sttsEntry);
|
||||
mReachedEOS = true;
|
||||
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
|
||||
count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video");
|
||||
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
|
||||
count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
|
||||
|
||||
logStatisticalData(is_audio);
|
||||
logStatisticalData(mIsAudio);
|
||||
}
|
||||
|
||||
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) {
|
||||
mOwner->lock();
|
||||
for (List<MediaBuffer *>::iterator it = mChunkSamples.begin();
|
||||
it != mChunkSamples.end(); ++it) {
|
||||
off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it)
|
||||
: mOwner->addSample_l(*it);
|
||||
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);
|
||||
}
|
||||
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
|
||||
LOGV("bufferChunk");
|
||||
|
||||
int64_t startTimeUs = systemTime() / 1000;
|
||||
Chunk chunk(this, timestampUs, mChunkSamples);
|
||||
mOwner->bufferChunk(chunk);
|
||||
mChunkSamples.clear();
|
||||
int64_t endTimeUs = systemTime() / 1000;
|
||||
if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
|
||||
mMaxWriteTimeUs = endTimeUs - startTimeUs;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t MPEG4Writer::Track::getDurationUs() const {
|
||||
@ -1414,9 +1581,8 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
bool success = mMeta->findCString(kKeyMIMEType, &mime);
|
||||
CHECK(success);
|
||||
|
||||
bool is_audio = !strncasecmp(mime, "audio/", 6);
|
||||
LOGV("%s track time scale: %d",
|
||||
is_audio? "Audio": "Video", mTimeScale);
|
||||
mIsAudio? "Audio": "Video", mTimeScale);
|
||||
|
||||
|
||||
time_t now = time(NULL);
|
||||
@ -1440,7 +1606,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
mOwner->writeInt32(0); // reserved
|
||||
mOwner->writeInt16(0); // layer
|
||||
mOwner->writeInt16(0); // alternate group
|
||||
mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
|
||||
mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
|
||||
mOwner->writeInt16(0); // reserved
|
||||
|
||||
mOwner->writeInt32(0x10000); // matrix
|
||||
@ -1453,7 +1619,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
mOwner->writeInt32(0);
|
||||
mOwner->writeInt32(0x40000000);
|
||||
|
||||
if (is_audio) {
|
||||
if (mIsAudio) {
|
||||
mOwner->writeInt32(0);
|
||||
mOwner->writeInt32(0);
|
||||
} else {
|
||||
@ -1511,16 +1677,16 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
mOwner->beginBox("hdlr");
|
||||
mOwner->writeInt32(0); // version=0, flags=0
|
||||
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
|
||||
// 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->beginBox("minf");
|
||||
if (is_audio) {
|
||||
if (mIsAudio) {
|
||||
mOwner->beginBox("smhd");
|
||||
mOwner->writeInt32(0); // version=0, flags=0
|
||||
mOwner->writeInt16(0); // balance
|
||||
@ -1553,7 +1719,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
mOwner->beginBox("stsd");
|
||||
mOwner->writeInt32(0); // version=0, flags=0
|
||||
mOwner->writeInt32(1); // entry count
|
||||
if (is_audio) {
|
||||
if (mIsAudio) {
|
||||
const char *fourcc = NULL;
|
||||
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
|
||||
fourcc = "samr";
|
||||
@ -1735,7 +1901,7 @@ void MPEG4Writer::Track::writeTrackHeader(
|
||||
}
|
||||
mOwner->endBox(); // stts
|
||||
|
||||
if (!is_audio) {
|
||||
if (!mIsAudio) {
|
||||
mOwner->beginBox("stss");
|
||||
mOwner->writeInt32(0); // version=0, flags=0
|
||||
mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
|
||||
|
Reference in New Issue
Block a user