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操作会将内容进行转换.

应用操作

  1. 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文件优化的主要内容,如果未能解决你的问题,请参考以下文章

android 录像本地网络传输保存成mp4文件

android 录像本地网络传输保存成mp4文件优化

android 录像本地网络传输保存成mp4文件优化

android 录像本地网络传输保存成mp4文件优化

5.3Android Studio录像

VLC拉网络流保存成本地视频