我录屏的方式是分别录制音频和视频,最后合并成mp4格式,比较麻烦,因为网上完整的教程比较少,所以我打算写一个完整版的,照着我的代码写完之后,至少是能够实现功能的,而不是简单的介绍下用法。

1既然是录制视频,我们应该有一个按钮控制开始和结束。

2在录制之前,需要先判断一下Android系统的版本是否大于5.0,并且动态申请一下权限(读写,录音,照相机),这一步可以在点开始按钮的时候执行

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions();
        }
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions();
        }
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions();
        }

        Intent intent = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            intent = mediaProjectionManager.createScreenCaptureIntent();
            startActivityForResult(intent, 101);//正常情况是要执行到这里的,作用是申请捕捉屏幕
        } else {
            ShowUtil.showToast(context, "Android版本太低,无法使用该功能");
        }

3定义MediaProjection和MediaProjectionManager等一些其他必要的变量

  boolean isrun = false;//用来标记录屏的状态private MediaProjectionManager mediaProjectionManager;
    private MediaProjection mediaProjection;//录制视频的工具private int width, height, dpi;//屏幕宽高和dpi,后面会用到
    private ScreenRecorder screenRecorder;//这个是自己写的录视频的工具类,下文会放完整的代码
    Thread thread;//录视频要放在线程里去执行
在onCreat里写好实例化
mediaProjectionManager = (MediaProjectionManager) context.getSystemService(MEDIA_PROJECTION_SERVICE);
WindowManager manager = this.getWindowManager();DisplayMetrics outMetrics = new DisplayMetrics();manager.getDefaultDisplay().getMetrics(outMetrics);width = outMetrics.widthPixels;height = outMetrics.heightPixels;dpi = outMetrics.densityDpi;

4我们在onActivityResult回调方法中,来处理返回的事件

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        ) {
            Toast.makeText(context, "缺少读写权限", Toast.LENGTH_SHORT).show();
            return;
        }
        ) {
            Toast.makeText(context, "缺少录音权限", Toast.LENGTH_SHORT).show();
            return;
        }
        ) {
            Toast.makeText(context, "缺少相机权限", Toast.LENGTH_SHORT).show();
            return;
        }
        if (requestCode != 101) {
            Log.e("HandDrawActivity", "error requestCode =" + requestCode);
        }
        if (resultCode != RESULT_OK) {
            Toast.makeText(context, "捕捉屏幕被禁止", Toast.LENGTH_SHORT).show();
            return;
        }

        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        if (mediaProjection != null) {
            screenRecorder = new ScreenRecorder(width, height, mediaProjection, dpi);
        }
        thread = new Thread() {
            @Override
            public void run() {
                screenRecorder.startRecorder();//跟ScreenRecorder有关的下文再说,总之这句话的意思就是开始录屏的意思
            }
        };
        thread.start();
        binding.startPlayer.setText("停止");//开始和停止我用的同一个按钮,所以开始录屏之后把按钮文字改一下
        isrun = true;//录屏状态改成真

    }

5先放上ScreenRecorder代码,只想要结果的朋友呢,直接把类粘贴走,把报错的地方改一改(在我自己的项目里可是不报错的),就实现了录制屏幕的功能了,还想看看的,可以往下看看

import android.hardware.display.DisplayManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ScreenRecorder {

    private int mWidth, mHeight, mDensty;
    private MediaProjection mediaProjection;
    private MediaCodec.BufferInfo mBufferInfo;
    private MediaCodec mEncorder;
    private Surface mInputSurface;
    private MediaMuxer mMuxer;
    private boolean isQuit = false;
    private boolean mMuxerStarted = false;
    private int mTrackIndex;
    private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cache";

    private MediaRecorder mediaRecorder;

    public ScreenRecorder(int mWidth, int mHeight, MediaProjection mediaProjection, int mDensty) {
        this.mWidth = mWidth;
        this.mHeight = mHeight;
        this.mediaProjection = mediaProjection;
        this.mDensty = mDensty;

        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    public void startRecorder() {
        prepareRecorder();
        startLuYin();
        startRecording();
    }

    public void stop() {
        isQuit = true;
        releaseEncorders(1);
        List<String> filePath = new ArrayList<>();
        filePath.add(path + "/APlanyinpin.amr");
        filePath.add(path + "/APlanshipin.mp4");
        joinVideo(filePath, path);
    }

    public void destory() {
        releaseEncorders(0);
    }

    private void startLuYin() {
        File file = new File(path, "APlanyinpin.amr");
        mediaRecorder = new MediaRecorder();
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
        mediaRecorder.setOutputFile(file.getAbsolutePath());

        try {
            mediaRecorder.prepare();
            mediaRecorder.start();
            Log.e("HandDrawActivity", "已经开始录音");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void prepareRecorder() {
        mBufferInfo = new MediaCodec.BufferInfo();  //元数据,描述bytebuffer的数据,尺寸,偏移
        //创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式
        MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
        int frameRate = 45;
        format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
        format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
        format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
        format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);

        try {
            mEncorder = MediaCodec.createEncoderByType("video/avc");
            mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mInputSurface = mEncorder.createInputSurface();
            mEncorder.start();
        } catch (IOException e) {
            e.printStackTrace();
            releaseEncorders(0);
        }
    }

    private void startRecording() {
        File saveFile = new File(path, "APlanshipin.mp4");
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                        mInputSurface, null, null);
                drainEncoder();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void drainEncoder() {
        while (!isQuit) {
            Log.e("TAG", "drain.....");
            int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
            if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
                if (!mMuxerStarted && mTrackIndex >= 0) {
                    mMuxer.start();
                    mMuxerStarted = true;

                    Log.e("HandDrawActivity", "已经开始录屏");
                }
            }
            if (bufferIndex >= 0) {
                Log.e("TAG", "drain...write..");
                ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    mBufferInfo.size = 0;
                }
                if (mBufferInfo.size != 0) {
                    if (mMuxerStarted) {
                        bufferData.position(mBufferInfo.offset);
                        bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
                        mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
                    }
                }
                mEncorder.releaseOutputBuffer(bufferIndex, false);
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    break;
                }
            }
        }
        Log.e("HandDrawActivity", "已经结束录屏");

    }

    private void releaseEncorders(int i) {
        if (mediaProjection != null) {
            mediaProjection.stop();
        }
        mBufferInfo = null;
        if (mEncorder != null) {
            mEncorder.stop();
        }
        mInputSurface = null;
        if (mMuxer != null && i == 1) {
            mMuxer.stop();
        }
        if (mediaRecorder != null) {
            mediaRecorder.stop();
            mediaRecorder.reset();
            mediaRecorder.release();
        }

    }

    private boolean joinVideo(List<String> filePaths, String resultPath) {
        Log.e("HandDrawActivity", "准备合成中");
        boolean result = false;

        if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
            throw new IllegalArgumentException();
        }

        if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并
            return true;
        }

        try {
            Movie[] inMovies = new Movie[filePaths.size()];
            for (int i = 0; i < filePaths.size(); i++) {
                Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
                File f = new File(filePaths.get(i));
                if (f.exists()) {
                    inMovies[i] = MovieCreator.build(filePaths.get(i));
                }
            }

            // 分别取出音轨和视频
            List<Track> videoTracks = new LinkedList<>();
            List<Track> audioTracks = new LinkedList<>();
            for (Movie m : inMovies) {
                for (Track t : m.getTracks()) {
                    if (t.getHandler().equals("soun")) {
                        audioTracks.add(t);
                    }
                    if (t.getHandler().equals("vide")) {
                        videoTracks.add(t);
                    }
                }
            }

            // 合并到最终的视频文件
            Movie outMovie = new Movie();

            if (audioTracks.size() > 0) {
                outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
            }
            if (videoTracks.size() > 0) {
                outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
            }

            Container mp4file = new DefaultMp4Builder().build(outMovie);

            // 将文件输出
            File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
            if (resultFile.exists() && resultFile.isFile()) {
                resultFile.delete();
            }
            FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
            mp4file.writeContainer(fc);
            fc.close();
            Log.e("HandDrawActivity", "合成完毕");
            // 合成完成后把原片段文件删除
            for (String filePath : filePaths) {
                File file = new File(filePath);
                file.delete();
            }
            result = true;

            HandDrawActivity.sendVideo();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;

    }
}

6从startRecorder方法说起

public void startRecorder() {
        prepareRecorder();//录视频前的准备
        startLuYin();//直接录音频(不用准备)
        startRecording();//录视频
    }
录音的方法private void startLuYin() {
        File file = new File(path, "APlanyinpin.amr");
        mediaRecorder = new MediaRecorder();
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//声音来源,麦克
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//音频格式,默认,其实就是上面定义好的amr了,除此之外还有mp4
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//编码格式,问题是我不知道编码格式对什么有影响,是音质高低还是文件大小还是解析快慢,等我有时间去专门研究一下
        mediaRecorder.setOutputFile(file.getAbsolutePath());

        try {
            mediaRecorder.prepare();
            mediaRecorder.start();
            Log.e("HandDrawActivity", "已经开始录音");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//录视频前的准备工作private void prepareRecorder() {
        mBufferInfo = new MediaCodec.BufferInfo();  //元数据,描述bytebuffer的数据,尺寸,偏移
        //创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式
        MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
        int frameRate = 45;
        format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
        format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
        format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
        format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);//编码器的设置,具体是设置的啥我也不太清楚,但是网上查一查都是这么写的!!!

        try {
            mEncorder = MediaCodec.createEncoderByType("video/avc");
            mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mInputSurface = mEncorder.createInputSurface();
            mEncorder.start();//让编码器先跑起来
        } catch (IOException e) {
            e.printStackTrace();
            releaseEncorders(0);
        }
    }
这里也是准备工作private void startRecording() {
        File saveFile = new File(path, "APlanshipin.mp4");
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);//百度一下MediaMuxer,讲的很详细的
                mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                        mInputSurface, null, null);
                drainEncoder();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
这个就是开始写视频文件了private void drainEncoder() {
        while (!isQuit) {
            Log.e("TAG", "drain.....");
            int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
            if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
                if (!mMuxerStarted && mTrackIndex >= 0) {
                    mMuxer.start();
                    mMuxerStarted = true;

                    Log.e("HandDrawActivity", "已经开始录屏");
                }
            }
            if (bufferIndex >= 0) {
                Log.e("TAG", "drain...write..");
                ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    mBufferInfo.size = 0;
                }
                if (mBufferInfo.size != 0) {
                    if (mMuxerStarted) {
                        bufferData.position(mBufferInfo.offset);
                        bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
                        mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
                    }
                }
                mEncorder.releaseOutputBuffer(bufferIndex, false);
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    break;
                }
            }
        }
        Log.e("HandDrawActivity", "已经结束录屏");

    }
这个就是把录好的音频和视频合并成mp4的方法了,也是点击停止录屏的时候用到的private boolean joinVideo(List<String> filePaths, String resultPath) {
        Log.e("HandDrawActivity", "准备合成中");
        boolean result = false;

        if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
            throw new IllegalArgumentException();
        }

        if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并
            return true;
        }

        try {
            Movie[] inMovies = new Movie[filePaths.size()];
            for (int i = 0; i < filePaths.size(); i++) {
                Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
                File f = new File(filePaths.get(i));
                if (f.exists()) {
                    inMovies[i] = MovieCreator.build(filePaths.get(i));
                }
            }

            // 分别取出音轨和视频
            List<Track> videoTracks = new LinkedList<>();
            List<Track> audioTracks = new LinkedList<>();
            for (Movie m : inMovies) {
                for (Track t : m.getTracks()) {
                    if (t.getHandler().equals("soun")) {
                        audioTracks.add(t);
                    }
                    if (t.getHandler().equals("vide")) {
                        videoTracks.add(t);
                    }
                }
            }

            // 合并到最终的视频文件
            Movie outMovie = new Movie();

            if (audioTracks.size() > 0) {
                outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
            }
            if (videoTracks.size() > 0) {
                outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
            }

            Container mp4file = new DefaultMp4Builder().build(outMovie);

            // 将文件输出
            File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
            if (resultFile.exists() && resultFile.isFile()) {
                resultFile.delete();
            }
            FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
            mp4file.writeContainer(fc);
            fc.close();
            Log.e("HandDrawActivity", "合成完毕");
            // 合成完成后把原片段文件删除
            for (String filePath : filePaths) {
                File file = new File(filePath);
                file.delete();
            }
            result = true;

            HandDrawActivity.sendVideo();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;

    }
这个就是结束的时候了,该清空的清空,该注销的注销, i是用来判断录没录的,有可能刚进入这个页面都没录过,直接就返回到别的页面了,那就有可能空指针异常,因为有些变量都没初始化,所以用i判断一下,也可以自己写别的方法判端private void releaseEncorders(int i) {
        if (mediaProjection != null) {
            mediaProjection.stop();
        }
        mBufferInfo = null;
        if (mEncorder != null) {
            mEncorder.stop();
        }
        mInputSurface = null;
        if (mMuxer != null && i == 1) {
            mMuxer.stop();
        }
        if (mediaRecorder != null) {
            mediaRecorder.stop();
            mediaRecorder.reset();
            mediaRecorder.release();
        }

    }

7部分代码也是我从网上扒的,但是网上的代码就没怎么见过比较完整的版本的,我上面写的都是经过我自己测试绝对没问题的而且代码也没什么遗漏的,要是发现有遗漏的代码我后续再补上。

Android5.0以上版本录屏实现的更多相关文章

  1. Android5.0免Root截屏,录屏

    http://blog.csdn.net/wds1181977/article/details/52174840 MediaProjection介绍 MediaProjection可以用来捕捉屏幕,具 ...

  2. Android5.0以后版本把应用移动到SD或者TF卡的方法

    由于手机内存较小,才8G,用的时间一久,内部存储就满了,天天删垃圾,WIFI还老断线,终于忍无可忍了,决定把应用移动到SD卡,实践下来,只有少部分App默认支持移动到SD卡,大部分程序不支持只能装在内 ...

  3. 友情提醒:欲开发android5.0以上应用,请全部更新开发工具至最新

    周末帮人完成一个项目,android5.0以上版本,谁知道被开发工具折腾的死去活来.我的开发环境是adt-bundle-windows-x86-20140702.zip版本,也是目前能找到的adt-b ...

  4. Fundebug录屏插件更新至0.6.0

    摘要: 录屏插件的性能进一步优化,传输的数据体积大幅度减少. 录屏功能介绍 Fundebug提供专业的异常监控服务,当线上应用出现 BUG 的时候,我们可以第一时间报警,帮助开发者及时发现 BUG,提 ...

  5. Fundebug录屏插件更新至0.5.0,新增domain参数

    摘要: 通过配置domain来保证"视频"的正确录制 录屏功能介绍 Fundebug提供专业的异常监控服务,当线上应用出现 BUG 的时候,我们可以第一时间报警,帮助开发者及时发现 ...

  6. Fundebug录屏插件更新至0.4.0,修复BUG,优化性能

    摘要: 录屏功能更加强大,欢迎免费试用! 关于Fundebug录屏功能 Fundebug是专业的程序BUG监控服务,当线上应用出现BUG的时候,我们可以第一时间报警,帮助开发者及时发现BUG,提高De ...

  7. Android Material Design Ripple Effect在Android5.0(SDK=21)以下Android版本崩溃问题解决

    Android Material Design Ripple Effect在Android5.0(SDK=21)以下Android版本崩溃问题解决 附录1的Android Ripple Effect水 ...

  8. 华为联运游戏审核驳回:在未安装或需更新HMS Core的手机上,提示安装,点击取消后,游戏卡屏(集成的6.1.0.301版本游戏SDK)

    问题描述 更新游戏SDK到6.1.0.301版本之后,游戏包被审核驳回:在未安装或需更新华为移动服务版本(HMS Core)的手机上,提示安装华为移动服务(HMS Core),点击取消,游戏卡屏.修改 ...

  9. 手游录屏直播技术详解 | 直播 SDK 性能优化实践

    在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...

随机推荐

  1. ubuntu 16.04 安装 Matlab R2016b后启动出现的问题

    (1)报以下错误: License checkout failed.License Manager Error -95MATLAB is unable to connect to the licens ...

  2. This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x0501 or higher is recommended

    VS2005转换成VS2010时出现的问题: This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x05 ...

  3. Jasper:API / API 策略和最佳做法

    ylbtech-Jasper:API / API 策略和最佳做法 1.返回顶部 1. API 策略和最佳做法 Cisco Jasper 已经建立了一项 API 公平使用策略,确保所有 Control  ...

  4. 使用memcpy 复制unsigned int 型的数据

    转载请注明出处:http://blog.csdn.net/qq_26093511/article/details/53214692 函数原型: void *memcpy(void *dest, con ...

  5. redis 操作使用

    /*1.Connection*/ $redis = new Redis(); $redis->connect('127.0.0.1',6379,1);//短链接,本地host,端口为6379,超 ...

  6. 通过libVirt抓取kvm虚拟机监控指标数据

    通常在我们的云环境中,为了保证云平台中虚拟机的正常运行,基本都需要这样一个功能,就是收集虚拟机的监控数据,比如cpu的使用率.内存的使用率.磁盘io.网络io等基本信息.可以利用这些信息及时调整云平台 ...

  7. OpenFileDialog无法弹出的解决方法

    今天在写一个socket通信的winform小程序,由于socket的receive方法会阻塞线程,所以就使用了多线程解决.但在新建的线程中创建OpenFileDialog并调用其ShowDialog ...

  8. error:未定义的引用

    用qtcreator编程的话,先在.pro文件中这样写: INCLUDEPATH += \ /usr/local/include/ LIBS += \ -L"/usr/local/lib&q ...

  9. 对比<input type="text" id="">和<asp:TextBox runat="server" ID="">

    首先这两个都是对应文本输入框形式: <input type="text"class="form-control"id="txt_add_pro_ ...

  10. Subresource Integrity(子资源一致性)和JS DDos 攻击

    以下文章转载自 http://www.cnblogs.com/zoucaitou/p/4505483.html 和 http://www.puronglong.com/blog//2015/04/12 ...