Android5.0以上版本录屏实现
我录屏的方式是分别录制音频和视频,最后合并成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以上版本录屏实现的更多相关文章
- Android5.0免Root截屏,录屏
http://blog.csdn.net/wds1181977/article/details/52174840 MediaProjection介绍 MediaProjection可以用来捕捉屏幕,具 ...
- Android5.0以后版本把应用移动到SD或者TF卡的方法
由于手机内存较小,才8G,用的时间一久,内部存储就满了,天天删垃圾,WIFI还老断线,终于忍无可忍了,决定把应用移动到SD卡,实践下来,只有少部分App默认支持移动到SD卡,大部分程序不支持只能装在内 ...
- 友情提醒:欲开发android5.0以上应用,请全部更新开发工具至最新
周末帮人完成一个项目,android5.0以上版本,谁知道被开发工具折腾的死去活来.我的开发环境是adt-bundle-windows-x86-20140702.zip版本,也是目前能找到的adt-b ...
- Fundebug录屏插件更新至0.6.0
摘要: 录屏插件的性能进一步优化,传输的数据体积大幅度减少. 录屏功能介绍 Fundebug提供专业的异常监控服务,当线上应用出现 BUG 的时候,我们可以第一时间报警,帮助开发者及时发现 BUG,提 ...
- Fundebug录屏插件更新至0.5.0,新增domain参数
摘要: 通过配置domain来保证"视频"的正确录制 录屏功能介绍 Fundebug提供专业的异常监控服务,当线上应用出现 BUG 的时候,我们可以第一时间报警,帮助开发者及时发现 ...
- Fundebug录屏插件更新至0.4.0,修复BUG,优化性能
摘要: 录屏功能更加强大,欢迎免费试用! 关于Fundebug录屏功能 Fundebug是专业的程序BUG监控服务,当线上应用出现BUG的时候,我们可以第一时间报警,帮助开发者及时发现BUG,提高De ...
- Android Material Design Ripple Effect在Android5.0(SDK=21)以下Android版本崩溃问题解决
Android Material Design Ripple Effect在Android5.0(SDK=21)以下Android版本崩溃问题解决 附录1的Android Ripple Effect水 ...
- 华为联运游戏审核驳回:在未安装或需更新HMS Core的手机上,提示安装,点击取消后,游戏卡屏(集成的6.1.0.301版本游戏SDK)
问题描述 更新游戏SDK到6.1.0.301版本之后,游戏包被审核驳回:在未安装或需更新华为移动服务版本(HMS Core)的手机上,提示安装华为移动服务(HMS Core),点击取消,游戏卡屏.修改 ...
- 手游录屏直播技术详解 | 直播 SDK 性能优化实践
在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...
随机推荐
- JSON标准格式
标准JSON的合法符号:{(左大括号) }(右大括号) "(双引号) :(冒号) ,(逗号) [(左中括号) ](右中括号) JSON字符串:特殊字符可在字符前面加 \ 或使用 ...
- POJ2217(最长公共子串)
Secretary Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 992 Accepted: 408 Descripti ...
- 注册页面Page的内置属性以及函数 路由 模块化
Page.prototype.route route字段可以获取到当前页面的路径 Page.prototype.setData() setData函数用于将数据从逻辑层发送到视图层,同时改变对应的t ...
- docker容器firewalld端口转发规则
docker容器firewalld端口转发规则 1.配置firewalld端口转发,要先打开端口转发,则需要先 #firewall-cmd --zone=public --add-maspuerade ...
- [poj2778]DNA Sequence(AC自动机+矩阵快速幂)
题意:有m种DNA序列是有疾病的,问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列.(仅含A,T,C,G四个字符) 解题关键:AC自动机,实际上就是一个状态转移图,注意能少取模就少取模, ...
- Git(一)
Git概念 Git其实是一种分布式版本控制系统,与CVS,Subversion等集中化的版本控制系统相对.它主要有几个特点: • 速度快 • 简单的设计 • 对非线性开发模式的强力支持(允许上千个并行 ...
- MFC CMap整理
映射表类(CMap)是MFC集合类中的一个模板类,也称作为“字典”.CMap是把唯一关键码映射到值的字典收集类,使用CMap可以构造一个关键字和元素值映射的集合类.一旦在映射中插入了一个关键码值对(元 ...
- 51nod1770(xjb)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1770 题意:中文题诶- 思路:随便写几个例子不难发现乘机中间部 ...
- scrapy.Request使用meta传递数据,以及deepcopy的使用
scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False]) ...
- 远程kafka通信实例,各种bug解决----虚拟机+本地电脑
为了实现远程kafka通信,我可谓是呕心沥血.期间各种bug各种调,太煎熬了 (T.T) 介绍: 我用一台虚拟机作为远程消息的发送方,用本地电脑主机作为消息的接收方 虚拟机:安装java,kafka, ...