美文网首页
2020-04-12

2020-04-12

作者: 玫瑰的lover | 来源:发表于2020-04-12 19:09 被阅读0次

    ##小程序,大世界

    ###简介

    · 小程序解决了什么问题

    · 小程序技术栈

    HTML/CSS/JavaScript • NodeJS

    移动适配

    HTTP协议/HTTPS

    OAuth2

    GIT

    · 主流的小程序平台

    ·类似⼩小程序的技术

    Cordova:通过webview渲染,通过插件调⽤用系统服务

    PWA:Service Worker和Push API

    React Native/Weex:JavaScript通过JavaScriptCore等执⾏行行,并

    通过Bridges和Native组件交互

    Flutter:Dart直接与独⽴立系统的UI库进⾏行行交互

    ###小程序的技术架构

    • ⽂文件结构及其含义

    .json 后缀的 JSON 配置⽂文件

    .wxml 后缀的 WXML 模板⽂文件

    .wxss 后缀的 WXSS 样式⽂文件

    .js 后缀的 JS 脚本逻辑⽂文件

    ⽂文件结构及其含义:JSON

    ⽂文件结构及其含义:WXML

    本质是HTML模版

    有特定的标签

    接管⼀一些简单的逻辑判断

    JS不不直接操作DOM,只负责set数据

    ⽂文件结构及其含义: WXSS

    提供rpx单位

    精简的CSS

    提供全局和局部的CSS

    ⽂文件结构及其含义: JS

    负责逻辑交互

    APP 、Page、Component三个构造函数

    可调⽤用系统API

    • 双线程模型

    • 生命周期

    • 组件

    • 其他

    插件机制

    云端函数

    ⼩小游戏

    ###开发发布流程

    开发者在⼩小程序平台注册⼩小程序,以获得APPID

    初始化代码并完成代码仓库配置

    开发代码并调试

    上传并发布

    ###小程序的发展

    多端同构框架

    ⾃自动化

    硬件框架

    云IDE

    W3C⼩小程序⼯工作组

    同构框架

    意义:⼀一次编写适配多端,⼀一次迭代各端同步

    利利⽤用Web的优点,以及对各个平台进⾏行行动态适配

    ⼩小程序⾃自动化

    控制⼩小程序跳转到指定⻚页⾯面 • 获取⼩小程序⻚页⾯面数据

    获取⼩小程序⻚页⾯面元素状态

    触发⼩小程序元素绑定事件

    往 AppService 注⼊入代码⽚片段 • 调⽤用 wx 对象上任意接⼝口

    硬件框架

    云IDE

    W3C小程序工作组

    ##Web前端点播直播入门

    ###什么是视频?

    1、格式与内容

    · 文件扩展名≈媒体封装格式(媒体容器类型)

    · 媒体封装格式≠音视频编码格式(使用了谁家的编码器)

    · 文件内容:

    1) 头信息(格式、时长、帧率、码率、分辨率...)

    2) 索引信息

    3) 视频数据

    4) 音频数据

    5) 附加增强数据...

    2、视频数据

    · 显示器颜色呈现基于RGB(红绿蓝)颜色空间模型

    · 视频领域大多基于YUV颜色空间做抽样存储

    · 帧内预测&帧间预测复用进一步有效的压缩数据

    · P帧(前向预测帧)、B帧(双向预测帧)、I帧(参考帧)

    · 基于通用标准集N多技术于一身 --- 视频编码器

    H.264(AVC)、H.265(HEVC)、VP8、VP9...

    3、音频数据

    · 声音:不同振幅&频率而产生的机械波;数字形式是一维波形

    · 对自然中连续的声波采样,做数字化PCM存储

    · 扬声器还原PCM(脉冲编码调制)数字信号为模拟音频信号

    · 音频压缩基本算法:预测、变换

    · 基于通用标准集N多技术于一身 --- 音频编码器​​​​​​​

    AAC、MP3...

    4、传输协议

    传统场景

    流媒体(直播)

    · HLS:苹果为利用现有CDN设施而发明的"流媒体"协议

    · HTTP(S)-FLV:基于HTTP的流媒体协议

    · RTMP、RTP/RTSP、TS、MMS...

    点播传输

    · HTTP(S):通过Range方式或参数方式完成Seek

    Web端

    ·HTTP(S)、WS(S)、P2P...

    5、播放器原理

    · 解协议(加载数据)

    · 解封装(解复用)

    · 解码

    · 渲染

    ###好玩的WebAPI

    1、媒体兼容判断

    let videoEl = document.createElement("video");

    let types = {

      'mp4': 'audio/mp4',

      'MP4': 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',

      'webm': 'video/webm; codecs="vp8, vorbis"',

      'ogg': 'video/ogg; codecs="theora, vorbis"',

      'm3u8': 'application/vnd.apple.mpegURL',

      'ts': 'video/mp2t; codecs="avc1.42E01E,mp4a.40.2"'

    };

    Object.keys(types).forEach(key => {

      const type = types[key];

      const ret = videoEl.canPlayType(type) || '不支持';

      console.log(key + ': ' + ret);

    });

    2、交互式视频

    let video = $('video');

    video.ontimeupdate = ()=>{

      let {currentTime} = video;

      show(currentTime > 64 ? '.s2' : '.s1');

      hide(currentTime > 64 ? '.s1' : '.s2');

      if(

        (currentTime > 64 && currentTime < 65) ||

        (currentTime > 113 && currentTime < 114)

      ){

        video.pause();

      }

    };

    let ppBtn = $('paly_pause');

    video.onplay = ()=>{

      ppBtn.innerText = '暂停';

    };

    video.onpause = ()=>{

      ppBtn.innerText = '播放';

    };

    ppBtn.onclick = ()=>{

      video[video.paused ? 'play' : 'pause' ]();

    };

    $('start').onclick = ()=>{

      video.currentTime = 1;

      video.play();

    };

    $('step').onclick = ()=>{

      video.currentTime = 60;

      video.play();

    };

    $('dream').onclick = ()=>{

      video.currentTime = 83;

      video.play();

    };

    $('drink').onclick = ()=>{

      video.currentTime = 116;

      video.play();

    };

    hide('.s2');

    function show(sel){

      document.querySelectorAll(sel).forEach(el=>{

        el.style.display='inline'

      });

    }

    function hide(sel){

      document.querySelectorAll(sel).forEach(el=>{

        el.style.display='none'

      });

    }

    function $(id){

      return document.getElementById(id);

    }

    3、播放本地视频文件

    let iptFileEl = document.querySelector('input[type="file"]');

    let videoEl = document.querySelector('video');

    iptFileEl.onchange = e =>{

      let file = iptFileEl.files && iptFileEl.files[0];

      playFile(file);

    };

    function playFile(file){

      if(file){

        let fileReader = new FileReader();

        fileReader.onload = evt => {

          if(FileReader.DONE == fileReader.readyState){

            videoEl.src = fileReader.result;

          }else{

            console.log('FileReader Error:', evt);

          }

        }

        fileReader.readAsDataURL(file);

      }else{

        videoEl.src = '';

      }

    }

    4、播放硬件资源(调用摄像头或麦克风)

    const getUserMediaPromise = options => new Promise((resolve, reject) => {

      const nvgt = window.navigator;

      if(nvgt) {

        if(nvgt.mediaDevices && nvgt.mediaDevices.getUserMedia) {

          return nvgt.mediaDevices.getUserMedia(options).then(resolve, reject);

        }

        const getUserMedia = nvgt.getUserMedia || nvgt.webkitGetUserMedia || nvgt.mozGetUserMedia;

        if(getUserMedia) {

          return getUserMedia(options, resolve, reject)

        }

      }

      reject('当前环境不支持获取媒体设备。');

    });

    let streamTrack;

    const video = document.querySelector('video');

    document.querySelector('#play').onclick = () => {

      getUserMediaPromise({

        audio: false,

        video: true

      }).then(stream => {

        video.srcObject = stream;

        streamTrack = stream.getTracks()[0];

      },

      err => {

        console.log('getUserMedia error: [' + err.name + '] ' + err.message)

      });

    };

    document.querySelector('#stop').onclick = () => {

      streamTrack && streamTrack.stop();

    };

    const box = document.querySelector('div');

    document.querySelector('#sketch').onclick = () => {

      box.className = box.className ==='' ? 'sketch' : '';

    };

    5、实现视频录制

    const getUserMediaPromise = options => new Promise((resolve, reject) => {

      const nvgt = window.navigator;

      if(nvgt) {

        if(nvgt.mediaDevices && nvgt.mediaDevices.getUserMedia) {

          return nvgt.mediaDevices.getUserMedia(options).then(resolve, reject);

        }

        const getUserMedia = nvgt.getUserMedia || nvgt.webkitGetUserMedia || nvgt.mozGetUserMedia;

        if(getUserMedia) {

          return getUserMedia(options, resolve, reject)

        }

      }

      reject('当前环境不支持获取媒体设备。');

    });

    const video = document.querySelector('#preview');

    let cameraStream;

    const opencameraBtn = document.querySelector('#opencamera');

    const closecameraBtn = document.querySelector('#closecamera');

    const recordBtn = document.querySelector('#record');

    const stopRecordBtn = document.querySelector('#stoprecord');

    const playBtn = document.querySelector('#play');

    const downloadBtn = document.querySelector('#download');

    opencameraBtn.onclick = () => getUserMediaPromise({

      audio: false,

      video: true

    }).then(

      stream => {

        cameraStream = video.srcObject = stream;

        opencameraBtn.disabled = true;

        closecameraBtn.disabled = false;

        recordBtn.disabled = false;

      },

      err => {

        console.log('getUserMedia error: [' + err.name + '] ' + err.message)

      }

    );

    closecameraBtn.onclick = () => {

      cameraStream && cameraStream.getTracks()[0].stop();

      cameraStream = null;

      opencameraBtn.disabled = false;

      closecameraBtn.disabled = true;

      stopRecordBtn.onclick();

    };

    let mediaRecorder;

    let recordedBlobs;

    const mimeType = ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm', ''].find(type => {

      return MediaRecorder.isTypeSupported(type);

    });

    // console.log('mimeType', mimeType);

    recordBtn.onclick = () => {

      recordedBlobs = [];

      try {

        mediaRecorder = new MediaRecorder(cameraStream, { mimeType });

      } catch(e) {

        alert('Exception while creating MediaRecorder: ' + e + '. mimeType: ' + mimeType);

        return;

      }

      recordBtn.disabled = true;

      stopRecordBtn.disabled = false;

      playBtn.disabled = true;

      downloadBtn.disabled = true;

      mediaRecorder.onstop = evt => {

        console.log('Recorder stopped');

      };

      mediaRecorder.ondataavailable = function(event) {

        if (event.data && event.data.size > 0) {

          recordedBlobs.push(event.data);

        }

      };

      mediaRecorder.start(20); // 单次收集数据毫秒时长,ondataavailable 触发频率时长间隔

    };

    const recordedVideo = document.querySelector('#recorded');

    stopRecordBtn.onclick = () => {

      mediaRecorder && mediaRecorder.stop();

      mediaRecorder = null;

      // console.log('Recorded Blobs: ', recordedBlobs);

      recordedVideo.controls = true;

      playBtn.disabled = false;

      downloadBtn.disabled = false;

      stopRecordBtn.disabled = true;

      if(!cameraStream) {

        recordBtn.disabled = true;

      }

    };

    const getRecordedBlobUrl = () => {

      const superBuffer = new Blob(recordedBlobs, {type: mimeType.split(';')[0]});

      return window.URL.createObjectURL(superBuffer);

    };

    playBtn.onclick = () => {

      recordedVideo.src = getRecordedBlobUrl();

    }

    downloadBtn.onclick = () => {

      var a = document.createElement('a');

      a.style.display = 'none';

      a.href = getRecordedBlobUrl();

      a.download = 'test.webm';

      document.body.appendChild(a);

      a.click();

      setTimeout(function() {

        document.body.removeChild(a);

        window.URL.revokeObjectURL(url);

      }, 100);

    }

    6、播放JS拉取的媒体数据

    const video = document.querySelector('video');

    const fetchMp4 = (url, cb) => {

      const xhr = new XMLHttpRequest();

      xhr.open('get', url);

      xhr.responseType = 'arraybuffer';

      xhr.onload = function () {

        cb(xhr.response);

      };

      xhr.send();

    };

    const assetURL = 'https://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4';

    const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

    // 创建动态媒体源,并关联到video元素上

    const mediaSource = new MediaSource();

    video.src = URL.createObjectURL(mediaSource);

    mediaSource.addEventListener('sourceopen', () => {

      const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

      // 拉取数据

      fetchMp4(assetURL, buf => {

        sourceBuffer.addEventListener('updateend', () => {

          // 媒体流传输完毕

          mediaSource.endOfStream();

          // video.play();

        });

        // 将数据喂给 Video -- 注意这里只是一次性输入整个MP4数据

        sourceBuffer.appendBuffer(buf);

      });

    });

    ###Web端点播直播&播放器解决方案

    1、点播直播的区别

    应用流程

    · 点播:创作者 => 上传 => 转码 => 存储 <=> CDN分发 <=> 观众

    · 直播:创作者 => 推流 <=> 存储 <=> 转码 <=> CDN分发 <=> 观众

    媒体类型的选择

    · HTTP(S)-MP4.. 点播服务

    · HTTP(S)-FLV 点播、直播

    · HTTP(S)-HLS 点播、直播(高延迟)

    2、播放器解决方案

    原生浏览器支持的

    · 直接走原生Video播放

    原生浏览器不支持的

    · 协议或容器类型不支持

    JS解协议下载数据、解容器、重新封装,然后通过MSE喂给Video解码、渲染播放

    例如Web端播放FLV、HLS:http://chimee.org

    · 解码器不支持

    JS下载数据,WASM 解容器、解码,通过 WebGL&WebAudio 渲染播放

    例如Web端播放HEVC编码视频:https://zyun.360.cn/developer/doc?did=QHWWPlayer

    · 有解密需求的

    参考前两条,在解容器之后对每帧数据启用解密逻辑。

    ##前端代码的自我修养

    如何衡量代码质量的好坏?

    衡量代码质量的唯一有效标准:WTF/min —— Robert C. Martin

    代码的自我修养

    · 代码规范

    · 格式

    · 流程化

    代码规范:

    安装Eslint:yarn global add eslint

    使用Eslint规范代码

    流程化:

    如何优雅地提交代码

    · git commit message规范

    · 合并提交

    ##技术翻译:进阶的直梯

    1、翻译类型

    文学翻译 非文学翻译

    艺术成分多一些 科学成分多一些

    需要更多的灵感 需要更多的勤奋

    责任小一些 责任大一些

    2、技术翻译的意义

    翻译技术文章,学习新技术思想

    翻译技术文档,掌握标准和工具

    翻译技术图书,获得名声和报酬

    3、技术翻译的标准

    准确、地道、简洁

    4、技术翻译的方法

    消化吸收原文

    母语地道表达

    就是翻译意思

    5、技术翻译要坚持技术驱动

    示例原文

    An object literal can only use a symbol as a property inside the computed property syntax.

    let s1 = Symbol('foo'),

    let o = {

      [s1]: 'foo val'

    };

    // Also valid:

    // o[s1] = 'foo val';

    console.log(o);

    // {Symbol{foo}: foo val}

    参考译文

    对象字面量只能在计算属性语法中使用符号作为属性。

    语法中使用符号作为属性。

    相关文章

      网友评论

          本文标题:2020-04-12

          本文链接:https://www.haomeiwen.com/subject/gwmbmhtx.html