android 录像本地网络传输保存成mp4文件优化
Posted 红尘六欲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 录像本地网络传输保存成mp4文件优化相关的知识,希望对你有一定的参考价值。
为了兼容使用,在之前这个基础上进行一下修改android 录像本地网络传输保存成mp4文件
后面录像要加水印,使用mediarecorder不好实现,同时mediarecorder录像保存的文件相对来说会稍微大一点.改用medicodec+mediamuxer方式。
系统修改
系统版本android7.1,低版本的MediaMuxer.java还没有FileDescriptor接口增加这个接口
1.frameworks/base/media/java/android/media/MediaMuxer.java
public MediaMuxer(@NonNull String path, @Format int format) throws IOException
if (path == null)
throw new IllegalArgumentException("path must not be null");
if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
format != OutputFormat.MUXER_OUTPUT_WEBM)
throw new IllegalArgumentException("format is invalid");
// Use RandomAccessFile so we can open the file with RW access;
// RW access allows the native writer to memory map the output file.
RandomAccessFile file = null;
try
file = new RandomAccessFile(path, "rws");
FileDescriptor fd = file.getFD();
mNativeObject = nativeSetup(fd, format);
mState = MUXER_STATE_INITIALIZED;
mCloseGuard.open("release");
finally
if (file != null)
file.close();
SystemProperties.set("ease.mp4seek.mode","false");//add by hclydao
//add by hclydao
public MediaMuxer(@NonNull FileDescriptor fd, @Format int format) throws IOException
if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
format != OutputFormat.MUXER_OUTPUT_WEBM)
throw new IllegalArgumentException("format is invalid");
// Use RandomAccessFile so we can open the file with RW access;
// RW access allows the native writer to memory map the output file.
mNativeObject = nativeSetup(fd, format);
mState = MUXER_STATE_INITIALIZED;
mCloseGuard.open("release");
SystemProperties.set("ease.mp4seek.mode","true");
增加public MediaMuxer(@NonNull FileDescriptor fd, @Format int format)
接口,然后设置了一个"ease.mp4seek.mode"
,这个后面用来判断是写文件,还是通过网络传输
2.frameworks/av/include/media/stagefright/MPEG4Writer.h
void myseek(int fd, off64_t seek, int whence);
bool myseekMode;
增加seek操作,网络传输无法像文件那种进行回写,所以处理一下文件seek的操作,增加myseek接口
3. frameworks/av/media/libstagefright/MPEG4Writer.cpp
MPEG4Writer::MPEG4Writer(int fd)
: mFd(dup(fd)),
mInitCheck(mFd < 0? NO_INIT: OK),
mIsRealTimeRecording(true),
mUse4ByteNalLength(true),
mUse32BitOffset(true),
mIsFileSizeLimitExplicitlyRequested(false),
mPaused(false),
mStarted(false),
mWriterThreadStarted(false),
mOffset(0),
mMdatOffset(0),
mMoovBoxBuffer(NULL),
mMoovBoxBufferOffset(0),
mWriteMoovBoxToMemory(false),
mFreeBoxOffset(0),
mStreamableFile(false),
mEstimatedMoovBoxSize(0),
mMoovExtraSize(0),
mInterleaveDurationUs(1000000),
mTimeScale(-1),
mStartTimestampUs(-1ll),
mLatitudex10000(0),
mLongitudex10000(0),
mAreGeoTagsAvailable(false),
mStartTimeOffsetMs(-1),
mMetaKeys(new AMessage())
addDeviceMeta();
myseekMode = false;
char value[PROPERTY_VALUE_MAX];
if (property_get("ease.mp4seek.mode", value, NULL) &&
(!strcasecmp(value, "true") || !strcasecmp(value, "1")))
myseekMode = true;
ALOGD("myseekMode:%d", myseekMode);
// Verify mFd is seekable
off64_t off = lseek64(mFd, 0, SEEK_SET);
if (off < 0)
ALOGE("cannot seek mFd: %s (%d)", strerror(errno), errno);
//release();
这里要将release注释,网络传输时,这里会报错退出,然后将其它调用lseek64的地方,换成myseek函数,例如
if (mStreamableFile)
// Reserve a 'free' box only for streamable file
//lseek64(mFd, mFreeBoxOffset, SEEK_SET);
myseek(mFd, mFreeBoxOffset, SEEK_SET);
writeInt32(mEstimatedMoovBoxSize);
write("free", 4);
mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
else
mMdatOffset = mOffset;
myseek函数如下
//add by hclydao
void MPEG4Writer::myseek(int fd, off64_t offset, int whence)
//bool notUse = (mMaxFileSizeLimitBytes == (300 * 1024 * 1024));
if(!myseekMode)
lseek64(fd, offset, whence);
else
uint32_t size = htonl(offset);
::write(fd, "seek", 4);
::write(fd, &size, 4);
::write(fd, "seekend", 7);
也就是使用public MediaMuxer(@NonNull FileDescriptor fd, @Format int format) 接口时,默认为网络传输,网络传输seek操作会将内容进行转换.
应用操作
- MediaMuxer使用上一篇中提到的localserver获取FileDescriptor进行初始化
mMuxer = new MediaMuxer(DataBase.localServer.getLocalSocket().getFileDescriptor(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
2.文件处理
通过网络将数据都保存后,然后对文件进行处理
private static String fileName = "/sdcard/test.mp4";//源文件
private static String destFileName = "/sdcard/test1.mp4";//能正常播放的mp4文件
public static void DealFileSeek()
RandomAccessFile srcfile = null;
RandomAccessFile destfile = null;
int startindex = 0;
long starttime,endtime;
starttime = System.currentTimeMillis();
new LogTools(TAG,"DealFileSeek start time :" + starttime,debug);
ArrayList<Integer> seekIndex = searchSeek(fileName);
/*
for (int i = 0;i<seekIndex.size();i++)
new LogTools(TAG,"seek in:0x" + Integer.toHexString(seekIndex.get(i)));
*/
try
File file1 = new File(destFileName);
if(file1.exists())
file1.delete();
srcfile = new RandomAccessFile(fileName, "r");
destfile = new RandomAccessFile(destFileName, "rw");
for(int i =0; i < seekIndex.size();i++)
int index = seekIndex.get(i);
new LogTools(TAG,"seek : " + index + " 0x:" + Integer.toHexString(index));
srcfile.seek(startindex);
int datlen = index - startindex - 4 - 4;//"seek" 4 bytes seek to 4 bytes
new LogTools(TAG,"datlen : " + datlen);
if(datlen > 0)
byte[] mdat;
if (datlen < (1024 * 1024))
mdat = new byte[datlen];
srcfile.read(mdat);
destfile.write(mdat);
else
int mediaDataSize = datlen;
while (true)
mdat = new byte[100 * 1024];
srcfile.read(mdat);
if (mediaDataSize > mdat.length)
mediaDataSize -= mdat.length;
destfile.write(mdat);
else
destfile.write(mdat, 0, mediaDataSize);
mediaDataSize = 0;
//new LogTools(TAG,"media size1:" + mediaDataSize,debug);
if (mediaDataSize <= 0)
break;
mdat = null;
new LogTools(TAG,"src file seek to 0x:" + Integer.toHexString(index - 4),debug);
srcfile.seek(index - 4);
int destseek = srcfile.readInt();
new LogTools(TAG,"destseek:" + destseek + " 0x:" + Integer.toHexString(destseek),debug);
destfile.seek(destseek);
startindex = index+7;//"seekend" 7 bytes
//new LogTools(TAG,"startindex:" + startindex,debug);
srcfile.close();
destfile.close();
catch (FileNotFoundException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
endtime = System.currentTimeMillis();
new LogTools(TAG,"DealFileSeek end time :" + endtime + " cal:" + (endtime - starttime),debug);
System.gc();
private static ArrayList<Integer> searchSeek(String fileName)
String searchStr = "seekend";
ArrayList<Integer> indexSeek = new ArrayList<>();
File file = new File(fileName);
int seekstart = 0;
long starttime,endtime;
starttime = System.currentTimeMillis();
new LogTools(TAG,"searchSeek start time :" + starttime,debug);
try
RandomAccessFile srcfile = new RandomAccessFile(fileName, "r");
byte[] dat = new byte[1024];
srcfile.read(dat);
String str = new String(dat,StandardCharsets.ISO_8859_1);
int start = 0;
for (;;)
int index = str.indexOf(searchStr,start);
if(index < 0)
break;
//new LogTools(TAG,"index:0x" + Integer.toHexString(index) + " start:0x" + Integer.toHexString(start));
indexSeek.add(index+seekstart);
start = index + searchStr.length();
seekstart = (int)(file.length() * 0.95);
srcfile.seek(seekstart);
dat = new byte[(int) file.length() - seekstart];
new LogTools(TAG, " seekstart:0x" + Integer.toHexString(seekstart) + " search len:" + dat.length);
srcfile.read(dat);
str = new String(dat,StandardCharsets.ISO_8859_1);
start = 0;
for (;;)
int index = str.indexOf(searchStr,start);
if(index < 0)
break;
//new LogTools(TAG,"index:0x" + Integer.toHexString(index) + " start:0x" + Integer.toHexString(start));
indexSeek.add(index + seekstart);
start = index + searchStr.length();
dat = null;
str = "";
System.gc();
catch (FileNotFoundException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
endtime = System.currentTimeMillis();
new LogTools(TAG,"searchSeek end time :" + endtime + " cal:" + (endtime - starttime),debug);
return indexSeek;
以上是关于android 录像本地网络传输保存成mp4文件优化的主要内容,如果未能解决你的问题,请参考以下文章