Skip to content

Commit

Permalink
Merge branch 'master' into xgplayer-2
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangxin92 authored Oct 22, 2020
2 parents 474b215 + 4a6d310 commit c8471de
Show file tree
Hide file tree
Showing 67 changed files with 4,115 additions and 12,829 deletions.
17 changes: 2 additions & 15 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
module.exports = {
"parser": "babel-eslint",
"extends": "standard",
"rules": {
"semi": 0
},
"overrides": [
{
"files": ["packages/**/**.js"],
"globals": {
"fetch": true,
"Headers": true
}
}
]
};
"extends": "standard"
};
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
<img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="commitizen">
</a>
</div>
<br>

English | [简体中文](README.zh-CN.md)

### Introduction

xgplayer is a web video player library. It has designed a separate, detachable UI component based on the principle that everything is componentized. More importantly, it is not only flexible in the UI layer, but also bold in its functionality: it gets rid of video loading, buffering, and format support for video dependence. Especially on mp4
it can be staged loading for that does not support streaming mp4. This means seamless switching with clarity, load control, and video savings. It also integrates on-demand and live support for FLV, HLS, and dash. [Document](http://h5player.bytedance.com/en/)
xgplayer is a web video and audio player library, designed with separate, detachable UI components. Since everything is componentized. the UI layer is very flexable.
xgplayer is bold in its functionality: it gets rid of video loading, buffering, and format support for video dependence.
For mp4 that does not support streaming, you can use staged loading. This means load control, seamless switching without artifacts, and video bandwidth savings. It also integrates on-demand and live support for FLV, HLS, and dash.

For more details, please read the [Documentation](http://h5player.bytedance.com/en/).

### Start

Expand Down Expand Up @@ -45,7 +50,7 @@ it can be staged loading for that does not support streaming mp4. This means sea
})
```
This is the easiest way to configure the player, then it runs with video. For more advanced content, see the plug-in section or documentation. [more config](http://h5player.bytedance.com/en/config/)
This is the easiest way to configure the video player. For more advanced content, see the plug-in section or documentation. [more config](http://h5player.bytedance.com/en/config/)
Expand All @@ -72,13 +77,13 @@ const player = new Player({

### Mobile Support

xgplayer supports mobile terminal, but android device brand and system are numerous, there are much compatibility problems, the player provides whitelist mechanism to ensure the perfect operation in mobile terminal. [whitelist](http://h5player.bytedance.com/en/config/#whitelist)
xgplayer supports mobile devices, but android brands and system customizations are numerous. Since there may be compatibility problems, the player provides a whitelist mechanism to ensure perfect operation in mobile devices. [whitelist configuration](http://h5player.bytedance.com/en/config/#whitelist)



### Dev

For debugging, we provide the example video resource which size is large in github. You can clone the whole git repository which includes codes and example videos with 'git clone --recurse-submodules -j8'. With 'git clone' you will pull only codes of xgplayer and its plugins.
For debugging, we provide example video files in github. You can clone the whole git repository, which includes both code and example videos with 'git clone --recurse-submodules -j8'. With 'git clone' you will pull only xgplayer code and its plugins.

```
$ git clone --recurse-submodules -j8 [email protected]:bytedance/xgplayer.git # OR git clone [email protected]:bytedance/xgplayer.git
Expand All @@ -87,7 +92,7 @@ $ npm install
$ npm run dev
```

please visit [http://localhost:9090/examples/index.html](http://localhost:9090/examples/index.html)
Then visit [http://localhost:9090/examples/index.html](http://localhost:9090/examples/index.html)


### Agreement
Expand All @@ -96,3 +101,7 @@ Welcome to use xgplayer! Please read the following terms carefully. Using xgplay
1. Xgplayer is licensed under the [MIT](http://opensource.org/licenses/MIT) License. You comply with its obligations by default.
2. By default, you authorize us to place your logo in xgplayer website, which using xgplayer.
If you have any problem, please let us know.


### Join Us
We welcome anyone with an interest in web media technology to join! Please contact us at [email protected]
12 changes: 12 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</a>
</div>

[English](README.md) | 简体中文

### 概述

Expand Down Expand Up @@ -98,3 +99,14 @@ $ npm run dev
1. 本开源项目中所有代码基于 [MIT](http://opensource.org/licenses/MIT) 许可协议,您默认遵守许可协议中约定的义务。
2. 您默认授权我们将您使用西瓜播放器所在业务的Logo放置在本官网展示。
若您有任何问题,请联系我们。

### 加入我们
欢迎各位对前端音视频感兴趣的小伙伴加入我们的技术团队!

工作地点:北京、上海、深圳等

岗位类型:社招、校招、实习

发送简历:[email protected]

邮件标题格式:【简历】+ 姓名 + 前端开发工程师 + 来源:xgplayer github
8 changes: 4 additions & 4 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Koa from 'koa'
import Serve from 'koa-static'
import Cors from '@koa/cors'
import Range from 'koa-range'
const Koa = require('koa');
const Serve = require('koa-static');
const Cors = require('@koa/cors');
const Range = require('koa-range');

const app = new Koa()
app.use(Range)
Expand Down
2 changes: 1 addition & 1 deletion examples
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
},
"cacheDir": ".changelog"
},
"version": "1.1.6-alpha.0"
"version": "1.1.5"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"main": "index.js",
"scripts": {
"test": "node_modules/.bin/lerna-changelog",
"dev": "npx babel-node app.js",
"dev": "node app.js",
"publish": "lerna publish",
"cz": "git cz",
"precz": "lint-staged",
Expand Down
2 changes: 1 addition & 1 deletion packages/xgplayer-dash/browser/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/xgplayer-dash/dist/index.js

Large diffs are not rendered by default.

250 changes: 250 additions & 0 deletions packages/xgplayer-demux/src/flv/AudioDemuxer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// refrence: https://github.com/video-dev/hls.js/blob/master/src/demux/adts.js
import Demuxer from './Demuxer'
import DataView4Read from '../../utils/DataView4Read'
// import { mp3Versions, mp3BitRate, audioSampleRate } from '../../constants/types';
import {
soundRateTypes,
samplingFrequencyTypes,
EventTypes,
browserTypes
} from '../../constants/types'
import sniffer from '../../utils/sniffer'
import Buffer from '../../write/Buffer'
export default class AudioDemuxer extends Demuxer {
constructor (store) {
super(store)
this.CLASS_NAME = this.constructor.name
this.currentTag = null
this.data = new Uint8Array(0)
this.readOffset = 0
this._store.audioMetaData = null
this.handleDataReady = () => {}
this.handleMetaDataReady = () => {}
this.handleMediaInfoReady = () => {}
}
resolve (tag) {
this.readOffset = 0
const { _store: store } = this
const { audioTrack: track } = store
this.currentTag = tag
this.data = tag.body
let {
audioMetaData: meta
} = store

if (!meta) {
meta = store.audioMetaData = {}
store.audioMetaData = this.initAudioMeta(meta)
}

const dv = new DataView4Read(tag.body.buffer, this)

const sound = dv.getUint8()

const soundFormatIdx = sound >>> 4 // UInt4
const soundRate = (sound & 12) >>> 2 // UInt2
// const soundSize = (sound & 2) >>> 1 // UInt1
const soundType = (sound % 1) // UInt1

meta.audioSampleRate = soundRateTypes[soundRate]
meta.channelCount = soundType === 0 ? 1 : 2

if (soundFormatIdx !== 10 && soundFormatIdx !== 2) {
this.error('only support AAC Audio format so far')
return
} else if (soundFormatIdx === 10) { // AAC
const aacInfo = this._parseAACAudio()
if (!aacInfo) {
return
}

const { data: aacData, data: { sampleFreq } } = aacInfo
if (aacInfo.packetType === 0) { // AAC sequence header
meta.sampleRate = sampleFreq
meta.channelCount = aacData.channelCount
meta.codec = aacData.codec
meta.manifestCodec = aacData.manifestCodec
meta.config = aacData.config
meta.refSampleDuration = 1024 / sampleFreq * meta.timeScale
if (store.hasInitialMetaDispatched) {
if (store.videoTrack.length || store.audioTrack.length) {
this.handleDataReady(store.videoTrack, store.audioTrack)
}
} else {
store.state._audioInitialMetadataDispatched = true
}

this.handleMetaDataReady('audio', meta)

const { mediaInfo: mi } = store
mi.audioCodec = meta.codec
mi.audioSampleRate = meta.sampleRate
mi.audioChannelCount = meta.channelCount
mi.audioConfig = meta.config
if (mi.hasVideo) {
if (mi.videoCodec) {
mi.mimeType = `video/x-flv; codecs="${mi.videoCodec},${mi.audioCodec}"`
mi.codec = mi.mimeType.replace('x-flv', 'mp4')
}
} else {
mi.mimeType = `video/x-flv; codecs="${mi.audioCodec}"`
mi.codec = mi.mimeType.replace('x-flv', 'mp4')
}

if (mi.isComplete) {
this.handleMediaInfoReady(mi)
}
} else if (aacInfo.packetType === 1) { // AAC raw frame data
let dts = store.state.timeStampBase + this.currentTag.getTime()
let aacSample = { unit: aacInfo.data, length: aacInfo.data.byteLength, dts: dts, pts: dts }
track.samples.push(aacSample)
track.length += aacInfo.data.length
}
}

this.resetStatus()
}

_parseAACAudio () {
if (this.unreadLength <= 1) {
return
}
const aacData = {}
let aacArray = new Uint8Array(this.data.buffer, this.readOffset, this.unreadLength)
const packetType = aacArray[0]
this.readOffset += 1
aacData.packetType = packetType
if (!packetType) {
const { position, tagSize } = this.currentTag
this._store.metaEndPosition = position + Buffer.readAsInt(tagSize) + 4
aacData.data = this._parseAACAudioSpecificConfig() // AAC Sequence header
} else {
aacData.data = aacArray.slice(1)
}

return aacData
}
_parseAACAudioSpecificConfig () {
const dv = new DataView4Read(this.data.buffer, this)
const { getAndNum } = DataView4Read

let resultObj = {
samplingFrequency: null,
extAudioObjectType: null,
extAudioSamplingIdx: null
}
let config = {}
const UInt0 = dv.getUint8()
const UInt1 = dv.getUint8()

let tempAudioObjectType
let audioObjectType = tempAudioObjectType = UInt0 >>> 3 // UInt5
let samplingIdx = ((UInt0 & getAndNum(5, 7)) << 1) | (UInt1 >>> 7) // UInt4
if (samplingIdx < 0 || samplingIdx > samplingFrequencyTypes.length) {
this.emitError('decoder', {
line: '141',
handle: '_parseAACAudioSpecificConfig',
msg: `invalid samplingFrequencyIndex ${samplingIdx}`
})
this.dispatch(EventTypes.ERROR, `error samplingFrequencyIndex: ${samplingIdx}`)
return
}

resultObj.samplingFrequency = samplingFrequencyTypes[samplingIdx]

let channelCount = resultObj.channelCount = (UInt1 & getAndNum(1, 4)) >>> 3
if (channelCount < 0 || channelCount > 7) {
this.emitError('decoder', {
line: '154',
handle: '_parseAACAudioSpecificConfig',
msg: `invalid Audio Channel Count: ${channelCount}`
})
this.dispatch(EventTypes.ERROR, `error Audio Channel Count: ${channelCount}`)
return
}

if (audioObjectType === 5) { // HE-AAC
const UInt2 = dv.getUint8()
resultObj.extAudioSamplingIdx = ((UInt1 & getAndNum(5, 7)) << 1) | UInt2 >>> 7
resultObj.extAudioObjectType = (UInt2 & getAndNum(1, 5)) >>> 2
}

if (sniffer.browser === browserTypes.FIRE_FOX) {
if (samplingIdx >= 6) {
// HE-AAC uses SBR, high frequencies are constructed from low frequencies
audioObjectType = 5
config = new Array(4)
resultObj.extAudioSamplingIdx = samplingIdx - 3
} else {
audioObjectType = 2
config = new Array(2)
resultObj.extAudioSamplingIdx = samplingIdx
}
} else if (sniffer.os.isAndroid) {
// Android : always use AAC
audioObjectType = 2
config = new Array(2)
resultObj.extAudioSamplingIdx = samplingIdx
} else {
/* for other browsers (Chrome/Vivaldi/Opera ...)
always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)
*/
audioObjectType = 5
resultObj.extensionSamplingIndex = samplingIdx
config = new Array(4)

if (samplingIdx >= 6) {
resultObj.extensionSamplingIdx = samplingIdx - 3
} else if (channelCount === 1) {
audioObjectType = 2
config = new Array(2)
resultObj.extensionSamplingIndex = samplingIdx
}
}

config[0] = audioObjectType << 3
config[0] |= (samplingIdx & 0x0E) >> 1
config[1] |= (samplingIdx & 0x01) << 7
config[1] |= channelCount << 3
if (audioObjectType === 5) {
config[1] |= (resultObj.extAudioSamplingIdx & 0x0E) >> 1
config[2] = (resultObj.extensionSamplingIdx & 0x01) << 7
// adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???
// https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc
config[2] |= 2 << 2
config[3] = 0
}

return {
config,
sampleFreq: resultObj.samplingFrequency,
channelCount,
codec: `mp4a.40.${audioObjectType}`,
manifestCodec: `mp4a.40.${tempAudioObjectType}`
}
}

initAudioMeta (meta) {
const { state, audioTrack: track } = this._store

meta.duration = state.duration
meta.timeScale = state.timeScale
meta.type = 'audio'
meta.id = track.id

return meta
}

resetStatus () {
this.currentTag = null
this.data = new Uint8Array(0)
this.readOffset = 0
}
get dataSize () {
return this.data.length
}

get unreadLength () {
return this.dataSize - this.readOffset
}
}
Loading

0 comments on commit c8471de

Please sign in to comment.