Skip to content

Commit

Permalink
Drop inherits dependency, convert to ES class
Browse files Browse the repository at this point in the history
Convert to ES class in order to drop the `inherits` dependency. This is not a breaking change since the exposed interface is still the same, i.e.

```js
var stream = mp4.decode()
```

and

```js
var stream = mp4.encode()
```
  • Loading branch information
feross committed Aug 6, 2019
1 parent ae68c60 commit 5b160bb
Show file tree
Hide file tree
Showing 5 changed files with 2,113 additions and 255 deletions.
289 changes: 141 additions & 148 deletions decode.js
Original file line number Diff line number Diff line change
@@ -1,190 +1,183 @@
var stream = require('readable-stream')
var inherits = require('inherits')
var nextEvent = require('next-event')
var Box = require('mp4-box-encoding')

var EMPTY = Buffer.alloc(0)

module.exports = Decoder
class Decoder extends stream.Writable {
constructor (opts) {
super(opts)

function Decoder () {
if (!(this instanceof Decoder)) return new Decoder()
stream.Writable.call(this)
this.destroyed = false

this.destroyed = false
this._pending = 0
this._missing = 0
this._ignoreEmpty = false
this._buf = null
this._str = null
this._cb = null
this._ondrain = null
this._writeBuffer = null
this._writeCb = null

this._pending = 0
this._missing = 0
this._ignoreEmpty = false
this._buf = null
this._str = null
this._cb = null
this._ondrain = null
this._writeBuffer = null
this._writeCb = null
this._ondrain = null
this._kick()
}

this._ondrain = null
this._kick()
}
destroy (err) {
if (this.destroyed) return
this.destroyed = true
if (err) this.emit('error', err)
this.emit('close')
}

inherits(Decoder, stream.Writable)
_write (data, enc, next) {
if (this.destroyed) return
var drained = !this._str || !this._str._writableState.needDrain

Decoder.prototype.destroy = function (err) {
if (this.destroyed) return
this.destroyed = true
if (err) this.emit('error', err)
this.emit('close')
}
while (data.length && !this.destroyed) {
if (!this._missing && !this._ignoreEmpty) {
this._writeBuffer = data
this._writeCb = next
return
}

Decoder.prototype._write = function (data, enc, next) {
if (this.destroyed) return
var drained = !this._str || !this._str._writableState.needDrain
var consumed = data.length < this._missing ? data.length : this._missing
if (this._buf) data.copy(this._buf, this._buf.length - this._missing)
else if (this._str) drained = this._str.write(consumed === data.length ? data : data.slice(0, consumed))

while (data.length && !this.destroyed) {
if (!this._missing && !this._ignoreEmpty) {
this._writeBuffer = data
this._writeCb = next
return
}
this._missing -= consumed

var consumed = data.length < this._missing ? data.length : this._missing
if (this._buf) data.copy(this._buf, this._buf.length - this._missing)
else if (this._str) drained = this._str.write(consumed === data.length ? data : data.slice(0, consumed))
if (!this._missing) {
var buf = this._buf
var cb = this._cb
var stream = this._str

this._missing -= consumed
this._buf = this._cb = this._str = this._ondrain = null
drained = true

if (!this._missing) {
var buf = this._buf
var cb = this._cb
var stream = this._str
this._ignoreEmpty = false
if (stream) stream.end()
if (cb) cb(buf)
}

this._buf = this._cb = this._str = this._ondrain = null
drained = true
data = consumed === data.length ? EMPTY : data.slice(consumed)
}

this._ignoreEmpty = false
if (stream) stream.end()
if (cb) cb(buf)
if (this._pending && !this._missing) {
this._writeBuffer = data
this._writeCb = next
return
}

data = consumed === data.length ? EMPTY : data.slice(consumed)
if (drained) next()
else this._ondrain(next)
}

if (this._pending && !this._missing) {
this._writeBuffer = data
this._writeCb = next
return
_buffer (size, cb) {
this._missing = size
this._buf = Buffer.alloc(size)
this._cb = cb
}

if (drained) next()
else this._ondrain(next)
}
_stream (size, cb) {
this._missing = size
this._str = new MediaData(this)
this._ondrain = nextEvent(this._str, 'drain')
this._pending++
this._str.on('end', () => {
this._pending--
this._kick()
})
this._cb = cb
return this._str
}

Decoder.prototype._buffer = function (size, cb) {
this._missing = size
this._buf = Buffer.alloc(size)
this._cb = cb
}
_readBox () {
const bufferHeaders = (len, buf) => {
this._buffer(len, additionalBuf => {
if (buf) {
buf = Buffer.concat([buf, additionalBuf])
} else {
buf = additionalBuf
}
var headers = Box.readHeaders(buf)
if (typeof headers === 'number') {
bufferHeaders(headers - buf.length, buf)
} else {
this._pending++
this._headers = headers
this.emit('box', headers)
}
})
}

Decoder.prototype._stream = function (size, cb) {
var self = this
this._missing = size
this._str = new MediaData(this)
this._ondrain = nextEvent(this._str, 'drain')
this._pending++
this._str.on('end', function () {
self._pending--
self._kick()
})
this._cb = cb
return this._str
}
bufferHeaders(8)
}

Decoder.prototype._readBox = function () {
var self = this
bufferHeaders(8)
stream () {
if (!this._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = this._headers
this._headers = null

function bufferHeaders (len, buf) {
self._buffer(len, function (additionalBuf) {
if (buf) {
buf = Buffer.concat([buf, additionalBuf])
} else {
buf = additionalBuf
}
var headers = Box.readHeaders(buf)
if (typeof headers === 'number') {
bufferHeaders(headers - buf.length, buf)
} else {
self._pending++
self._headers = headers
self.emit('box', headers)
}
})
return this._stream(headers.contentLen, null)
}
}

Decoder.prototype.stream = function () {
var self = this
if (!self._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = self._headers
self._headers = null
decode (cb) {
if (!this._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = this._headers
this._headers = null

return self._stream(headers.contentLen, null)
}

Decoder.prototype.decode = function (cb) {
var self = this
if (!self._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = self._headers
self._headers = null

self._buffer(headers.contentLen, function (buf) {
var box = Box.decodeWithoutHeaders(headers, buf)
cb(box)
self._pending--
self._kick()
})
}
this._buffer(headers.contentLen, buf => {
var box = Box.decodeWithoutHeaders(headers, buf)
cb(box)
this._pending--
this._kick()
})
}

Decoder.prototype.ignore = function () {
var self = this
if (!self._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = self._headers
self._headers = null
ignore () {
if (!this._headers) throw new Error('this function can only be called once after \'box\' is emitted')
var headers = this._headers
this._headers = null

this._missing = headers.contentLen
if (this._missing === 0) {
this._ignoreEmpty = true
this._missing = headers.contentLen
if (this._missing === 0) {
this._ignoreEmpty = true
}
this._cb = () => {
this._pending--
this._kick()
}
}
this._cb = function () {
self._pending--
self._kick()

_kick () {
if (this._pending) return
if (!this._buf && !this._str) this._readBox()
if (this._writeBuffer) {
var next = this._writeCb
var buffer = this._writeBuffer
this._writeBuffer = null
this._writeCb = null
this._write(buffer, null, next)
}
}
}

Decoder.prototype._kick = function () {
if (this._pending) return
if (!this._buf && !this._str) this._readBox()
if (this._writeBuffer) {
var next = this._writeCb
var buffer = this._writeBuffer
this._writeBuffer = null
this._writeCb = null
this._write(buffer, null, next)
class MediaData extends stream.PassThrough {
constructor (parent) {
super()
this._parent = parent
this.destroyed = false
}
}

function MediaData (parent) {
this._parent = parent
this.destroyed = false
stream.PassThrough.call(this)
destroy (err) {
if (this.destroyed) return
this.destroyed = true
this._parent.destroy(err)
if (err) this.emit('error', err)
this.emit('close')
}
}

inherits(MediaData, stream.PassThrough)

MediaData.prototype.destroy = function (err) {
if (this.destroyed) return
this.destroyed = true
this._parent.destroy(err)
if (err) this.emit('error', err)
this.emit('close')
}
module.exports = Decoder
Loading

0 comments on commit 5b160bb

Please sign in to comment.