Output streamable MP4 file during MP4 file recording
When the reserved moov box space is not big enough, fall back to non-streamable MP4 file. Change-Id: I93382d037d657a3f3fe2af31e4ea26e1898b4d95
This commit is contained in:
@@ -61,6 +61,12 @@ private:
|
|||||||
FILE *mFile;
|
FILE *mFile;
|
||||||
off_t mOffset;
|
off_t mOffset;
|
||||||
off_t mMdatOffset;
|
off_t mMdatOffset;
|
||||||
|
uint8_t *mMoovBoxBuffer;
|
||||||
|
off_t mMoovBoxBufferOffset;
|
||||||
|
bool mWriteMoovBoxToMemory;
|
||||||
|
off_t mFreeBoxOffset;
|
||||||
|
bool mStreamableFile;
|
||||||
|
off_t mEstimatedMoovBoxSize;
|
||||||
uint32_t mInterleaveDurationUs;
|
uint32_t mInterleaveDurationUs;
|
||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
|
|
||||||
@@ -75,6 +81,8 @@ private:
|
|||||||
off_t addSample_l(MediaBuffer *buffer);
|
off_t addSample_l(MediaBuffer *buffer);
|
||||||
off_t addLengthPrefixedSample_l(MediaBuffer *buffer);
|
off_t addLengthPrefixedSample_l(MediaBuffer *buffer);
|
||||||
|
|
||||||
|
inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
|
||||||
|
|
||||||
MPEG4Writer(const MPEG4Writer &);
|
MPEG4Writer(const MPEG4Writer &);
|
||||||
MPEG4Writer &operator=(const MPEG4Writer &);
|
MPEG4Writer &operator=(const MPEG4Writer &);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ MPEG4Writer::MPEG4Writer(const char *filename)
|
|||||||
: mFile(fopen(filename, "wb")),
|
: mFile(fopen(filename, "wb")),
|
||||||
mOffset(0),
|
mOffset(0),
|
||||||
mMdatOffset(0),
|
mMdatOffset(0),
|
||||||
|
mEstimatedMoovBoxSize(0),
|
||||||
mInterleaveDurationUs(500000) {
|
mInterleaveDurationUs(500000) {
|
||||||
CHECK(mFile != NULL);
|
CHECK(mFile != NULL);
|
||||||
}
|
}
|
||||||
@@ -121,6 +122,7 @@ MPEG4Writer::MPEG4Writer(int fd)
|
|||||||
: mFile(fdopen(fd, "wb")),
|
: mFile(fdopen(fd, "wb")),
|
||||||
mOffset(0),
|
mOffset(0),
|
||||||
mMdatOffset(0),
|
mMdatOffset(0),
|
||||||
|
mEstimatedMoovBoxSize(0),
|
||||||
mInterleaveDurationUs(500000) {
|
mInterleaveDurationUs(500000) {
|
||||||
CHECK(mFile != NULL);
|
CHECK(mFile != NULL);
|
||||||
}
|
}
|
||||||
@@ -147,15 +149,33 @@ status_t MPEG4Writer::start() {
|
|||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStreamableFile = true;
|
||||||
|
mWriteMoovBoxToMemory = false;
|
||||||
|
mMoovBoxBuffer = NULL;
|
||||||
|
mMoovBoxBufferOffset = 0;
|
||||||
|
|
||||||
beginBox("ftyp");
|
beginBox("ftyp");
|
||||||
writeFourcc("isom");
|
writeFourcc("isom");
|
||||||
writeInt32(0);
|
writeInt32(0);
|
||||||
writeFourcc("isom");
|
writeFourcc("isom");
|
||||||
endBox();
|
endBox();
|
||||||
|
|
||||||
mMdatOffset = mOffset;
|
mFreeBoxOffset = mOffset;
|
||||||
write("\x00\x00\x00\x01mdat????????", 16);
|
|
||||||
|
|
||||||
|
if (mEstimatedMoovBoxSize == 0) {
|
||||||
|
// XXX: Estimate the moov box size
|
||||||
|
// based on max file size or duration limit
|
||||||
|
mEstimatedMoovBoxSize = 0x0F00;
|
||||||
|
}
|
||||||
|
CHECK(mEstimatedMoovBoxSize >= 8);
|
||||||
|
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
|
||||||
|
writeInt32(mEstimatedMoovBoxSize);
|
||||||
|
write("free", 4);
|
||||||
|
|
||||||
|
mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
|
||||||
|
mOffset = mMdatOffset;
|
||||||
|
fseeko(mFile, mMdatOffset, SEEK_SET);
|
||||||
|
write("\x00\x00\x00\x01mdat????????", 16);
|
||||||
for (List<Track *>::iterator it = mTracks.begin();
|
for (List<Track *>::iterator it = mTracks.begin();
|
||||||
it != mTracks.end(); ++it) {
|
it != mTracks.end(); ++it) {
|
||||||
status_t err = (*it)->start();
|
status_t err = (*it)->start();
|
||||||
@@ -189,14 +209,20 @@ void MPEG4Writer::stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Fix up the size of the 'mdat' chunk.
|
// Fix up the size of the 'mdat' chunk.
|
||||||
fseek(mFile, mMdatOffset + 8, SEEK_SET);
|
fseeko(mFile, mMdatOffset + 8, SEEK_SET);
|
||||||
int64_t size = mOffset - mMdatOffset;
|
int64_t size = mOffset - mMdatOffset;
|
||||||
size = hton64(size);
|
size = hton64(size);
|
||||||
fwrite(&size, 1, 8, mFile);
|
fwrite(&size, 1, 8, mFile);
|
||||||
fseek(mFile, mOffset, SEEK_SET);
|
fseeko(mFile, mOffset, SEEK_SET);
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
const off_t moovOffset = mOffset;
|
||||||
|
mWriteMoovBoxToMemory = true;
|
||||||
|
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
|
||||||
|
mMoovBoxBufferOffset = 0;
|
||||||
|
CHECK(mMoovBoxBuffer != NULL);
|
||||||
|
|
||||||
beginBox("moov");
|
beginBox("moov");
|
||||||
|
|
||||||
@@ -236,8 +262,30 @@ void MPEG4Writer::stop() {
|
|||||||
}
|
}
|
||||||
endBox(); // moov
|
endBox(); // moov
|
||||||
|
|
||||||
|
mWriteMoovBoxToMemory = false;
|
||||||
|
if (mStreamableFile) {
|
||||||
|
CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
|
||||||
|
|
||||||
|
// Moov box
|
||||||
|
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
|
||||||
|
mOffset = mFreeBoxOffset;
|
||||||
|
write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
|
||||||
|
|
||||||
|
// Free box
|
||||||
|
mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
|
||||||
|
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
|
||||||
|
writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
|
||||||
|
write("free", 4);
|
||||||
|
|
||||||
|
// Free temp memory
|
||||||
|
free(mMoovBoxBuffer);
|
||||||
|
mMoovBoxBuffer = NULL;
|
||||||
|
mMoovBoxBufferOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(mBoxes.empty());
|
CHECK(mBoxes.empty());
|
||||||
|
|
||||||
|
fflush(mFile);
|
||||||
fclose(mFile);
|
fclose(mFile);
|
||||||
mFile = NULL;
|
mFile = NULL;
|
||||||
}
|
}
|
||||||
@@ -317,10 +365,41 @@ off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
|
|||||||
return old_offset;
|
return old_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t MPEG4Writer::write(
|
||||||
|
const void *ptr, size_t size, size_t nmemb, FILE *stream) {
|
||||||
|
|
||||||
|
const size_t bytes = size * nmemb;
|
||||||
|
if (mWriteMoovBoxToMemory) {
|
||||||
|
if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) {
|
||||||
|
for (List<off_t>::iterator it = mBoxes.begin();
|
||||||
|
it != mBoxes.end(); ++it) {
|
||||||
|
(*it) += mOffset;
|
||||||
|
}
|
||||||
|
fseeko(mFile, mOffset, SEEK_SET);
|
||||||
|
fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream);
|
||||||
|
fwrite(ptr, size, nmemb, stream);
|
||||||
|
mOffset += (bytes + mMoovBoxBufferOffset);
|
||||||
|
free(mMoovBoxBuffer);
|
||||||
|
mMoovBoxBuffer = NULL;
|
||||||
|
mMoovBoxBufferOffset = 0;
|
||||||
|
mWriteMoovBoxToMemory = false;
|
||||||
|
mStreamableFile = false;
|
||||||
|
} else {
|
||||||
|
memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
|
||||||
|
mMoovBoxBufferOffset += bytes;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fwrite(ptr, size, nmemb, stream);
|
||||||
|
mOffset += bytes;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
void MPEG4Writer::beginBox(const char *fourcc) {
|
void MPEG4Writer::beginBox(const char *fourcc) {
|
||||||
CHECK_EQ(strlen(fourcc), 4);
|
CHECK_EQ(strlen(fourcc), 4);
|
||||||
|
|
||||||
mBoxes.push_back(mOffset);
|
mBoxes.push_back(mWriteMoovBoxToMemory?
|
||||||
|
mMoovBoxBufferOffset: mOffset);
|
||||||
|
|
||||||
writeInt32(0);
|
writeInt32(0);
|
||||||
writeFourcc(fourcc);
|
writeFourcc(fourcc);
|
||||||
@@ -332,51 +411,48 @@ void MPEG4Writer::endBox() {
|
|||||||
off_t offset = *--mBoxes.end();
|
off_t offset = *--mBoxes.end();
|
||||||
mBoxes.erase(--mBoxes.end());
|
mBoxes.erase(--mBoxes.end());
|
||||||
|
|
||||||
fseek(mFile, offset, SEEK_SET);
|
if (mWriteMoovBoxToMemory) {
|
||||||
writeInt32(mOffset - offset);
|
int32_t x = htonl(mMoovBoxBufferOffset - offset);
|
||||||
mOffset -= 4;
|
memcpy(mMoovBoxBuffer + offset, &x, 4);
|
||||||
fseek(mFile, mOffset, SEEK_SET);
|
} else {
|
||||||
|
fseeko(mFile, offset, SEEK_SET);
|
||||||
|
writeInt32(mOffset - offset);
|
||||||
|
mOffset -= 4;
|
||||||
|
fseeko(mFile, mOffset, SEEK_SET);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeInt8(int8_t x) {
|
void MPEG4Writer::writeInt8(int8_t x) {
|
||||||
fwrite(&x, 1, 1, mFile);
|
write(&x, 1, 1, mFile);
|
||||||
++mOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeInt16(int16_t x) {
|
void MPEG4Writer::writeInt16(int16_t x) {
|
||||||
x = htons(x);
|
x = htons(x);
|
||||||
fwrite(&x, 1, 2, mFile);
|
write(&x, 1, 2, mFile);
|
||||||
mOffset += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeInt32(int32_t x) {
|
void MPEG4Writer::writeInt32(int32_t x) {
|
||||||
x = htonl(x);
|
x = htonl(x);
|
||||||
fwrite(&x, 1, 4, mFile);
|
write(&x, 1, 4, mFile);
|
||||||
mOffset += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeInt64(int64_t x) {
|
void MPEG4Writer::writeInt64(int64_t x) {
|
||||||
x = hton64(x);
|
x = hton64(x);
|
||||||
fwrite(&x, 1, 8, mFile);
|
write(&x, 1, 8, mFile);
|
||||||
mOffset += 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeCString(const char *s) {
|
void MPEG4Writer::writeCString(const char *s) {
|
||||||
size_t n = strlen(s);
|
size_t n = strlen(s);
|
||||||
|
write(s, 1, n + 1, mFile);
|
||||||
fwrite(s, 1, n + 1, mFile);
|
|
||||||
mOffset += n + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::writeFourcc(const char *s) {
|
void MPEG4Writer::writeFourcc(const char *s) {
|
||||||
CHECK_EQ(strlen(s), 4);
|
CHECK_EQ(strlen(s), 4);
|
||||||
fwrite(s, 1, 4, mFile);
|
write(s, 1, 4, mFile);
|
||||||
mOffset += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPEG4Writer::write(const void *data, size_t size) {
|
void MPEG4Writer::write(const void *data, size_t size) {
|
||||||
fwrite(data, 1, size, mFile);
|
write(data, 1, size, mFile);
|
||||||
mOffset += size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MPEG4Writer::reachedEOS() {
|
bool MPEG4Writer::reachedEOS() {
|
||||||
|
|||||||
Reference in New Issue
Block a user