录像默认会有4G的限制,之所以有这样的限制,是因为目前很多存储设备都是fat格式的,fat的size是32位表示的,天然就有4G的限制,而且实际上在限制4G的情况下只能录出来3.8G。是因为
1)要留一部分buffer写moovbox
2)有的还要留部分buffer作为写数据的cache
在文件\frameworks\av\media\libstagefright\MPEG4Writer.cpp中,有对视频文件size的限制函数fileSizeLimit,源码如下:
bool MPEG4Writer::exceedsFileSizeLimit() {
// No limit
if (mMaxFileSizeLimitBytes == 0) {
return false;
}
int64_t nTotalBytesEstimate = static_cast<int64_t>(mInMemoryCacheSize);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
}
if (!mStreamableFile) {
// Add 1024 bytes as error tolerance
return nTotalBytesEstimate + 1024 >= mMaxFileSizeLimitBytes;
}
// Be conservative in the estimate: do not exceed 95% of
// the target file limit. For small target file size limit, though,
// this will not help.
return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
}
可以看到return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);的计算方式。mMaxFileSizeLimitBytes是4G,4G * 95% = 3.8G。
这种情况下的处理方式:
方式一:
通过adb shell blkid dev/block/mmcblk1p1判断sdcard的限制,此时如果输出的结果:
dev/block/mmcblk1p1: UUID="70CD-07EB" TYPE="vfat"
则表示sdcard是fat型的,fat用32位表示文件的大小,天然就有2^32 = 4G的限制。
这种情况下,软件是无法无法处理的,接受现状。
方式二:
在sdcard没有限制的情况下,可以通过软件在/frameworks/base/media/java/android/media/MediaRecorder.java的setVideoEncodingBitRate()中加入语句 setParameter(“param-use-64bit-offset=1”);来突破4G的限制,逻辑如下:
添加之后的函数:
public void setVideoEncodingBitRate(int bitRate) {
if (bitRate <= 0) {
throw new IllegalArgumentException("Video encoding bit rate is not positive");
}
setParameter("video-param-encoding-bitrate=" + bitRate);
setParameter("param-use-64bit-offset=1"); //add this
}
从后往前查找 /frameworks/av/media/libstagefright/MPEG4Writer.cpp的函数MPEG4Writer::start中有通过
param->findInt32(kKey64BitFileOffset, &use64BitOffset)来判断fat是否用32位表示文件大小,如有如下设置则表示文件是64位来表示的,也就是没有4G的限制了。
......
int32_t use64BitOffset;
if (param &&
param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
use64BitOffset) {
mUse32BitOffset = false;
}
......
而use64BitOffset的值取决于kKey64BitFileOffsetkKey64BitFileOffset的值是在文件/frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp中通过语句(*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset)设置的,可以看到具体函数:
void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) {
int64_t startTimeUs = systemTime() / 1000;
(*meta)->setInt64(kKeyTime, startTimeUs);
(*meta)->setInt32(kKeyFileType, mOutputFormat);
(*meta)->setInt32(kKeyBitRate, mTotalBitRate);
if (mMovieTimeScale > 0) {
(*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
}
if (mOutputFormat != OUTPUT_FORMAT_WEBM) {
(*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
if (mTrackEveryTimeDurationUs > 0) {
(*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
if (mRotationDegrees != 0) {
(*meta)->setInt32(kKeyRotation, mRotationDegrees);
}
}
}
所以需要查看mUse64BitFileOffset的值。看他的赋值函数,主要取决于use64Bit。
status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) {
ALOGV("setParam64BitFileOffset: %s",
use64Bit? "use 64 bit file offset": "use 32 bit file offset");
mUse64BitFileOffset = use64Bit;
return OK;
}
setParam64BitFileOffset被调用的地方:
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
ALOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
if (key == "max-duration") {
int64_t max_duration_ms;
if (safe_strtoi64(value.string(), &max_duration_ms)) {
return setParamMaxFileDurationUs(1000LL * max_duration_ms);
}
} else if (key == "max-filesize") {
int64_t max_filesize_bytes;
if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
return setParamMaxFileSizeBytes(max_filesize_bytes);
}
......
} else if (key == "param-use-64bit-offset") {
int32_t use64BitOffset;
if (safe_strtoi32(value.string(), &use64BitOffset)) {
return setParam64BitFileOffset(use64BitOffset != 0);
}
......
可以看到主要是通过key == "param-use-64bit-offset"来决定的,所以添加语句 setParameter(“param-use-64bit-offset=1”);来设置这个key的参数,就可以表示所录制的视频文件大小可以超过4G了。
方式三:
在https://android.googlesource.com/platform/frameworks/av/+/android-8.1.0_r14/media/libmediaplayerservice/StagefrightRecorder.cpp中也有看到如下处理方式(但此方法未经过实测验证,不能保证没有风险,仅供参考):
Enable recording files larger than 4GB by forcing 64-bit file-offsets
in the writer, if application indicates max-file-size greater than 4GB.
CRs-Fixed: 807377
Change-Id: Id1af5bdf3543af156e6d3d80be2e00c7df3b4134
— a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -87,6 +87,7 @@ static const char *kRecorderVideoLevel = "android.media.mediarecorder.video-enco
static const char *kRecorderCaptureFpsEnable = “android.media.mediarecorder.capture-fpsenable”;
static const char *kRecorderCaptureFps = “android.media.mediarecorder.capture-fps”;
static const char *kRecorderRotation = “android.media.mediarecorder.rotation”;
+static const int64_t kMax32BitFileSize = 0x00ffffffffLL; // 4GB
// To collect the encoder usage for the battery app
static void addBatteryData(uint32_t params) {
@@ -581,6 +582,10 @@ status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) {
}
mMaxFileSizeBytes = bytes;
+
- // If requested size is >4GB, force 64-bit offsets
- mUse64BitFileOffset |= (bytes >= kMax32BitFileSize);