video-quality-tools
Advanced tools
Comparing version 1.0.0 to 1.1.0
{ | ||
"name": "video-quality-tools", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Set of tools to evaluate video stream quality.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -120,3 +120,3 @@ # Video Quality Tools module - helps to measure live stream characteristics by RTMP/HLS/DASH streams | ||
```json | ||
``` | ||
{ videos: | ||
@@ -218,3 +218,3 @@ [ { index: 1, | ||
run `framesMonitor.listen()` method. After that `framesMonitor` starts emitting `frame` event as soon as ffprobe | ||
decodes frame from the stream. It emits only video frames at now. | ||
decodes frame from the stream. It emits video and audio frames. | ||
@@ -255,3 +255,4 @@ ```javascript | ||
This event is generated on each video frame decoded by ffprobe. The structure of the frame object is the following: | ||
This event is generated on each video and audio frame decoded by ffprobe. | ||
The structure of the frame object is the following: | ||
@@ -263,6 +264,14 @@ ``` | ||
pkt_size: 3332, | ||
width: 640, | ||
height: 480, | ||
pict_type: 'P' } | ||
``` | ||
or | ||
``` | ||
{ media_type: 'audio', | ||
key_frame: 1, | ||
pkt_pts_time: 'N/A', | ||
pkt_size: 20 } | ||
``` | ||
## `exit` event | ||
@@ -364,3 +373,15 @@ | ||
max: 1525.95703125 }, | ||
fps: { mean: 30, min: 30, max: 30 } } | ||
fps: { | ||
mean: 30, | ||
min: 30, | ||
max: 30 }, | ||
gopDuration: { | ||
mean: 2, | ||
min: 1.9, | ||
max: 2.1 }, | ||
displayAspectRatio: '16:9', | ||
width: 1280, | ||
height: 720, | ||
hasAudioStream: true | ||
} | ||
``` | ||
@@ -380,8 +401,15 @@ | ||
For the full GOPs `processFrames` calculates min/max/mean values of bitrates (in kbit/s) and framerates and returns | ||
them in `payload` field. The result of the check for the similarity of GOP structures for the collected GOPs is returned in | ||
`areAllGopsIdentical` field. | ||
For the full GOPs `processFrames` calculates min/max/mean values of bitrates (in kbit/s), framerates and GOP duration | ||
(in seconds) and returns them in `payload` field. The result of the check for the similarity of GOP structures for | ||
the collected GOPs is returned in `areAllGopsIdentical` field. Fields `width`, `height` and `displayAspectRatio` | ||
are taken from data from first frame of the first collected GOP. Value of `hasAudioStream` reflects presence of | ||
audio frames. | ||
For display aspect ratio calculation method `processFrames::calculateDisplayAspectRatio` use list of | ||
[current video aspect ratio standards](https://en.wikipedia.org/wiki/Aspect_ratio_(image)) | ||
with approximation error of frames width and height ratio. If ratio hasn't a reflection in aspect ratio standards then | ||
[GCD algorithm](https://en.wikipedia.org/wiki/Greatest_common_divisor) is used. | ||
`processFrames` may throw `Errors.GopNotFoundError`. | ||
Also, you may extend the metrics. Check `src/processFrames.js` to find common functions. |
@@ -272,7 +272,5 @@ 'use strict'; | ||
errorLevel, | ||
'-select_streams', | ||
'v:0', | ||
'-show_frames', | ||
'-show_entries', | ||
'frame=pkt_size,pkt_pts_time,media_type,pict_type,key_frame', | ||
'frame=pkt_size,pkt_pts_time,media_type,pict_type,key_frame,width,height', | ||
'-i', | ||
@@ -279,0 +277,0 @@ `${this._url} timeout=${timeoutInSec}` |
@@ -7,2 +7,19 @@ 'use strict'; | ||
const AR_CALCULATION_PRECISION = 0.01; | ||
const SQUARE_AR_COEFFICIENT = 1; | ||
const SQUARE_AR = '1:1'; | ||
const TRADITIONAL_TV_AR_COEFFICIENT = 1.333; | ||
const TRADITIONAL_TV_AR = '4:3'; | ||
const HD_VIDEO_AR_COEFFICIENT = 1.777; | ||
const HD_VIDEO_AR = '16:9'; | ||
const UNIVISIUM_AR_COEFFICIENT = 2; | ||
const UNIVISIUM_AR = '18:9'; | ||
const WIDESCREEN_AR_COEFFICIENT = 2.33; | ||
const WIDESCREEN_AR = '21:9'; | ||
function processFrames(frames) { | ||
@@ -20,6 +37,45 @@ if (!Array.isArray(frames)) { | ||
const areAllGopsIdentical = processFrames.areAllGopsIdentical(gops); | ||
const bitrate = processFrames.calculateBitrate(gops); | ||
const fps = processFrames.calculateFps(gops); | ||
let areAllGopsIdentical = true; | ||
const hasAudioStream = processFrames.hasAudioFrames(frames); | ||
const baseGopSize = gops[0].frames.length; | ||
const bitrates = []; | ||
const fpsList = []; | ||
const gopDurations = []; | ||
gops.forEach(gop => { | ||
areAllGopsIdentical = areAllGopsIdentical && baseGopSize === gop.frames.length; | ||
const accumulatedPktSize = processFrames.accumulatePktSize(gop); | ||
const gopDuration = processFrames.gopDurationInSec(gop); | ||
const gopBitrate = processFrames.toKbs(accumulatedPktSize / gopDuration); | ||
bitrates.push(gopBitrate); | ||
const gopFps = gop.frames.length / gopDuration; | ||
fpsList.push(gopFps); | ||
gopDurations.push(gopDuration); | ||
}); | ||
const bitrate = { | ||
mean: _.mean(bitrates), | ||
min : Math.min(...bitrates), | ||
max : Math.max(...bitrates) | ||
}; | ||
const fps = { | ||
mean: _.mean(fpsList), | ||
min : Math.min(...fpsList), | ||
max : Math.max(...fpsList) | ||
}; | ||
const gopDuration = { | ||
mean: _.mean(gopDurations), | ||
min: Math.min(...gopDurations), | ||
max: Math.max(...gopDurations) | ||
}; | ||
const width = gops[0].frames[0].width; | ||
const height = gops[0].frames[0].height; | ||
const displayAspectRatio = calculateDisplayAspectRatio(width, height); | ||
return { | ||
@@ -29,3 +85,8 @@ payload : { | ||
bitrate, | ||
fps | ||
fps, | ||
gopDuration, | ||
displayAspectRatio, | ||
width, | ||
height, | ||
hasAudioStream | ||
}, | ||
@@ -36,10 +97,14 @@ remainedFrames: remainedFrames | ||
processFrames.identifyGops = identifyGops; | ||
processFrames.calculateBitrate = calculateBitrate; | ||
processFrames.calculateFps = calculateFps; | ||
processFrames.filterVideoFrames = filterVideoFrames; | ||
processFrames.gopDurationInSec = gopDurationInSec; | ||
processFrames.toKbs = toKbs; | ||
processFrames.accumulatePktSize = accumulatePktSize; | ||
processFrames.areAllGopsIdentical = areAllGopsIdentical; | ||
processFrames.identifyGops = identifyGops; | ||
processFrames.calculateBitrate = calculateBitrate; | ||
processFrames.calculateFps = calculateFps; | ||
processFrames.calculateGopDuration = calculateGopDuration; | ||
processFrames.filterVideoFrames = filterVideoFrames; | ||
processFrames.hasAudioFrames = hasAudioFrames; | ||
processFrames.gopDurationInSec = gopDurationInSec; | ||
processFrames.toKbs = toKbs; | ||
processFrames.accumulatePktSize = accumulatePktSize; | ||
processFrames.areAllGopsIdentical = areAllGopsIdentical; | ||
processFrames.findGcd = findGcd; | ||
processFrames.calculateDisplayAspectRatio = calculateDisplayAspectRatio; | ||
@@ -192,2 +257,54 @@ module.exports = processFrames; | ||
function calculateGopDuration(gops) { | ||
const gopsDurations = []; | ||
gops.forEach(gop => { | ||
const gopDurationInSec = processFrames.gopDurationInSec(gop); | ||
gopsDurations.push(gopDurationInSec); | ||
}); | ||
return { | ||
mean: _.mean(gopsDurations), | ||
min: Math.min(...gopsDurations), | ||
max: Math.max(...gopsDurations) | ||
}; | ||
} | ||
function calculateDisplayAspectRatio(width, height) { | ||
if (!_.isInteger(width) || width <= 0) { | ||
throw new TypeError('"width" must be a positive integer'); | ||
} | ||
if (!_.isInteger(height) || height <= 0) { | ||
throw new TypeError('"height" must be a positive integer'); | ||
} | ||
const arCoefficient = width / height; | ||
if (Math.abs(arCoefficient - SQUARE_AR_COEFFICIENT) <= AR_CALCULATION_PRECISION) { | ||
return SQUARE_AR; | ||
} | ||
if (Math.abs(arCoefficient - TRADITIONAL_TV_AR_COEFFICIENT) <= AR_CALCULATION_PRECISION) { | ||
return TRADITIONAL_TV_AR; | ||
} | ||
if (Math.abs(arCoefficient - HD_VIDEO_AR_COEFFICIENT) <= AR_CALCULATION_PRECISION) { | ||
return HD_VIDEO_AR; | ||
} | ||
if (Math.abs(arCoefficient - UNIVISIUM_AR_COEFFICIENT) <= AR_CALCULATION_PRECISION) { | ||
return UNIVISIUM_AR; | ||
} | ||
if (Math.abs(arCoefficient - WIDESCREEN_AR_COEFFICIENT) <= AR_CALCULATION_PRECISION) { | ||
return WIDESCREEN_AR; | ||
} | ||
const gcd = findGcd(width, height); | ||
return `${width / gcd}:${height / gcd}`; | ||
} | ||
function areAllGopsIdentical(gops) { | ||
@@ -201,4 +318,20 @@ return gops.every(gop => _.isEqual(gops[0].frames.length, gop.frames.length)); | ||
function hasAudioFrames(frames) { | ||
return frames.some(frame => frame.media_type === 'audio'); | ||
} | ||
function toKbs(val) { | ||
return val * 8 / 1024; | ||
} | ||
function findGcd(a, b) { | ||
if (a === 0 && b === 0) { | ||
return 0; | ||
} | ||
if (b === 0) { | ||
return a; | ||
} | ||
return findGcd(b, a % b); | ||
} |
@@ -9,2 +9,3 @@ 'use strict'; | ||
const Errors = require('./Errors/'); | ||
const processFrames = require('./processFrames'); | ||
@@ -133,3 +134,10 @@ const DAR_OR_SAR_NA = 'N/A'; | ||
video.sample_aspect_ratio = '1:1'; | ||
video.display_aspect_ratio = this._calculateDisplayAspectRatio(video.width, video.height); | ||
try { | ||
video.display_aspect_ratio = processFrames.calculateDisplayAspectRatio(video.width, video.height); | ||
} catch (err) { | ||
throw new Errors.StreamsInfoError( | ||
'Can not calculate aspect ratio due to invalid video resolution', | ||
{width: video.width, height: video.height, url: this._url} | ||
); | ||
} | ||
} | ||
@@ -140,28 +148,4 @@ | ||
} | ||
_calculateDisplayAspectRatio(width, height) { | ||
if (!_.isInteger(width) || !_.isInteger(height) || width <= 0 || height <= 0) { | ||
throw new Errors.StreamsInfoError( | ||
'Can not calculate aspect rate due to invalid video resolution', | ||
{width, height, url: this._url} | ||
); | ||
} | ||
const gcd = this._findGcd(width, height); | ||
return `${width / gcd}:${height / gcd}`; | ||
} | ||
_findGcd(a, b) { | ||
if (a === 0 && b === 0) { | ||
return 0; | ||
} | ||
if (b === 0) { | ||
return a; | ||
} | ||
return this._findGcd(b, a % b); | ||
} | ||
} | ||
module.exports = StreamsInfo; |
@@ -108,3 +108,3 @@ 'use strict'; | ||
const expectedPFramesCount = 240; | ||
const expectedAudioFramesCount = 0; | ||
const expectedAudioFramesCount = 431; | ||
@@ -124,11 +124,15 @@ const onFrame = {I: spyOnIFrame, P: spyOnPFrame}; | ||
framesMonitor.on('exit', reason => { | ||
assert.instanceOf(reason, ExitReasons.NormalExit); | ||
assert.strictEqual(reason.payload.code, expectedReturnCode); | ||
try { | ||
assert.instanceOf(reason, ExitReasons.NormalExit); | ||
assert.strictEqual(reason.payload.code, expectedReturnCode); | ||
assert.strictEqual(spyOnAudioFrame.callCount, expectedAudioFramesCount); | ||
assert.strictEqual(spyOnAudioFrame.callCount, expectedAudioFramesCount); | ||
assert.strictEqual(spyOnIFrame.callCount, expectedIFramesCount); | ||
assert.strictEqual(spyOnPFrame.callCount, expectedPFramesCount); | ||
assert.strictEqual(spyOnIFrame.callCount, expectedIFramesCount); | ||
assert.strictEqual(spyOnPFrame.callCount, expectedPFramesCount); | ||
done(); | ||
done(); | ||
} catch (err) { | ||
done(err); | ||
} | ||
}); | ||
@@ -135,0 +139,0 @@ }); |
@@ -45,2 +45,6 @@ 'use strict'; | ||
const expectedReturnCode = 0; | ||
const expectedWidth = 854; | ||
const expectedHeight = 480; | ||
const expectedAspectRatio = '16:9'; | ||
const expectAudio = true; | ||
@@ -74,2 +78,6 @@ const frames = []; | ||
const expectedMinGop = 0.16599999999999993; | ||
const expectedMaxGop = 0.16700000000000004; | ||
const expectedMeanGop = 0.16666101694915256; | ||
const expectedRemainedFrames = [ | ||
@@ -87,4 +95,9 @@ {key_frame: 1, pict_type: 'I'}, | ||
areAllGopsIdentical: true, | ||
fps : {mean: expectedMeanFps, min: expectedMinFps, max: expectedMaxFps}, | ||
bitrate : {mean: expectedMeanBitrate, min: expectedMinBitrate, max: expectedMaxBitrate} | ||
fps: {mean: expectedMeanFps, min: expectedMinFps, max: expectedMaxFps}, | ||
bitrate: {mean: expectedMeanBitrate, min: expectedMinBitrate, max: expectedMaxBitrate}, | ||
gopDuration: {mean: expectedMeanGop, min: expectedMinGop, max: expectedMaxGop}, | ||
aspectRatio: expectedAspectRatio, | ||
height: expectedHeight, | ||
width: expectedWidth, | ||
hasAudioStream: expectAudio | ||
}); | ||
@@ -104,2 +117,6 @@ | ||
const expectedReturnCode = 0; | ||
const expectedWidth = 854; | ||
const expectedHeight = 480; | ||
const expectedAspectRatio = '16:9'; | ||
const expectAudio = true; | ||
@@ -133,2 +150,6 @@ const frames = []; | ||
const expectedMinGop = 1.534; | ||
const expectedMaxGop = 5.000000000000001; | ||
const expectedMeanGop = 3.2113333333333336; | ||
const expectedRemainedFrames = [ | ||
@@ -152,4 +173,9 @@ {key_frame: 1, pict_type: 'I'}, | ||
areAllGopsIdentical: false, | ||
fps : {mean: expectedMeanFps, min: expectedMinFps, max: expectedMaxFps}, | ||
bitrate : {mean: expectedMeanBitrate, min: expectedMinBitrate, max: expectedMaxBitrate} | ||
fps: {mean: expectedMeanFps, min: expectedMinFps, max: expectedMaxFps}, | ||
bitrate: {mean: expectedMeanBitrate, min: expectedMinBitrate, max: expectedMaxBitrate}, | ||
gopDuration: {mean: expectedMeanGop, min: expectedMinGop, max: expectedMaxGop}, | ||
aspectRatio: expectedAspectRatio, | ||
width: expectedWidth, | ||
height: expectedHeight, | ||
hasAudioStream: expectAudio | ||
}); | ||
@@ -156,0 +182,0 @@ |
@@ -14,7 +14,5 @@ 'use strict'; | ||
errorLevel, | ||
'-select_streams', | ||
'v:0', | ||
'-show_frames', | ||
'-show_entries', | ||
'frame=pkt_size,pkt_pts_time,media_type,pict_type,key_frame', | ||
'frame=pkt_size,pkt_pts_time,media_type,pict_type,key_frame,width,height', | ||
'-i', | ||
@@ -21,0 +19,0 @@ `${url} timeout=${timeoutInSec}` |
@@ -62,25 +62,23 @@ 'use strict'; | ||
const frames1 = [ | ||
{pkt_size: 1, pkt_pts_time: 1, media_type: 'audio', key_frame: 1}, | ||
{pkt_size: 1, pkt_pts_time: 11, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 3, pkt_pts_time: 13, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 3, pkt_pts_time: 3, media_type: 'audio', key_frame: 1}, | ||
{pkt_size: 5, pkt_pts_time: 15, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 7, pkt_pts_time: 17, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 9, pkt_pts_time: 19, media_type: 'video', key_frame: 1} | ||
{width: 640, height: 480, pkt_size: 1, pkt_pts_time: 11, media_type: 'video', key_frame: 1}, | ||
{width: 640, height: 480, pkt_size: 3, pkt_pts_time: 13, media_type: 'video', key_frame: 0}, | ||
{width: 640, height: 480, pkt_size: 5, pkt_pts_time: 15, media_type: 'video', key_frame: 1}, | ||
{width: 640, height: 480, pkt_size: 7, pkt_pts_time: 17, media_type: 'video', key_frame: 0}, | ||
{width: 640, height: 480, pkt_size: 9, pkt_pts_time: 19, media_type: 'video', key_frame: 1} | ||
]; | ||
const frames2 = [ | ||
{pkt_size: 1, pkt_pts_time: 1, media_type: 'audio', key_frame: 1}, | ||
{pkt_size: 1, pkt_pts_time: 11, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 2, pkt_pts_time: 13, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 3, pkt_pts_time: 15, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 3, pkt_pts_time: 15, media_type: 'audio', key_frame: 1}, | ||
{pkt_size: 4, pkt_pts_time: 17, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 5, pkt_pts_time: 19, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 6, pkt_pts_time: 21, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 7, pkt_pts_time: 23, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 3, pkt_pts_time: 15, media_type: 'audio', key_frame: 1}, | ||
{pkt_size: 8, pkt_pts_time: 25, media_type: 'video', key_frame: 0}, | ||
{pkt_size: 9, pkt_pts_time: 27, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 10, pkt_pts_time: 29, media_type: 'video', key_frame: 0} | ||
{width: 854, height: 480, pkt_size: 1, pkt_pts_time: 1, media_type: 'audio', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 1, pkt_pts_time: 11, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 2, pkt_pts_time: 13, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 3, pkt_pts_time: 15, media_type: 'video', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 3, pkt_pts_time: 15, media_type: 'audio', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 4, pkt_pts_time: 17, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 5, pkt_pts_time: 19, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 6, pkt_pts_time: 21, media_type: 'video', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 7, pkt_pts_time: 23, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 3, pkt_pts_time: 15, media_type: 'audio', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 8, pkt_pts_time: 25, media_type: 'video', key_frame: 0}, | ||
{width: 854, height: 480, pkt_size: 9, pkt_pts_time: 27, media_type: 'video', key_frame: 1}, | ||
{width: 854, height: 480, pkt_size: 10, pkt_pts_time: 29, media_type: 'video', key_frame: 0} | ||
]; | ||
@@ -95,3 +93,3 @@ | ||
const expectedRemainedFrames1 = [ | ||
{pkt_size: 9, pkt_pts_time: 19, media_type: 'video', key_frame: 1} | ||
{pkt_size: 9, pkt_pts_time: 19, media_type: 'video', key_frame: 1, width: 640, height: 480} | ||
]; | ||
@@ -106,4 +104,4 @@ | ||
const expectedRemainedFrames2 = [ | ||
{pkt_size: 9, pkt_pts_time: 27, media_type: 'video', key_frame: 1}, | ||
{pkt_size: 10, pkt_pts_time: 29, media_type: 'video', key_frame: 0} | ||
{pkt_size: 9, pkt_pts_time: 27, media_type: 'video', key_frame: 1, width: 854, height: 480}, | ||
{pkt_size: 10, pkt_pts_time: 29, media_type: 'video', key_frame: 0, width: 854, height: 480} | ||
]; | ||
@@ -114,2 +112,23 @@ | ||
const expectedGopDuration1 = { | ||
min: 15 - 11, | ||
max: 19 - 15, | ||
mean: (15 - 11 + 19 - 15) / 2 | ||
}; | ||
const expectedGopDuration2 = { | ||
min: 21 - 15, | ||
max: 27 - 21, | ||
mean: (21 - 15 + 27 - 21) / 2 | ||
}; | ||
const expectedAspectRatio1 = '4:3'; | ||
const expectedAspectRatio2 = '16:9'; | ||
const expectedWidth1 = 640; | ||
const expectedHeight1 = 480; | ||
const expectedWidth2 = 854; | ||
const expectedHeight2 = 480; | ||
const expectAudio1 = false; | ||
const expectAudio2 = true; | ||
let res1 = processFrames(frames1); | ||
@@ -120,3 +139,8 @@ | ||
bitrate : expectedBitrate1, | ||
fps : expectedFps1 | ||
fps : expectedFps1, | ||
gopDuration : expectedGopDuration1, | ||
displayAspectRatio : expectedAspectRatio1, | ||
width : expectedWidth1, | ||
height : expectedHeight1, | ||
hasAudioStream : expectAudio1 | ||
}); | ||
@@ -131,3 +155,8 @@ | ||
bitrate : expectedBitrate2, | ||
fps : expectedFps2 | ||
fps : expectedFps2, | ||
gopDuration : expectedGopDuration2, | ||
displayAspectRatio : expectedAspectRatio2, | ||
width : expectedWidth2, | ||
height : expectedHeight2, | ||
hasAudioStream : expectAudio2 | ||
}); | ||
@@ -138,2 +167,52 @@ | ||
it('must detect that GOPs is not identical ', () => { | ||
const frames = [ | ||
{width: 640, height: 480, pkt_size: 1, pkt_pts_time: 11, media_type: 'video', key_frame: 1}, | ||
{width: 640, height: 480, pkt_size: 3, pkt_pts_time: 13, media_type: 'video', key_frame: 0}, | ||
{width: 640, height: 480, pkt_size: 5, pkt_pts_time: 15, media_type: 'video', key_frame: 1}, | ||
{width: 640, height: 480, pkt_size: 6, pkt_pts_time: 17, media_type: 'video', key_frame: 0}, | ||
{width: 640, height: 480, pkt_size: 9, pkt_pts_time: 19, media_type: 'video', key_frame: 0}, | ||
{width: 640, height: 480, pkt_size: 11, pkt_pts_time: 21, media_type: 'video', key_frame: 1} | ||
]; | ||
const expectedBitrate = { | ||
min: processFrames.toKbs((1 + 3) / (15 - 11)), | ||
max: processFrames.toKbs((5 + 6 + 9) / (21 - 15)), | ||
mean: processFrames.toKbs(((1 + 3) / (15 - 11) + (5 + 6 + 9) / (21 - 15)) / 2) | ||
}; | ||
const expectedRemainedFrames = [ | ||
{pkt_size: 11, pkt_pts_time: 21, media_type: 'video', key_frame: 1, width: 640, height: 480} | ||
]; | ||
const expectedFps = {min: 0.5, max: 0.5, mean: 0.5}; | ||
const expectedGopDuration = { | ||
min: 15 - 11, | ||
max: 21 - 15, | ||
mean: (15 - 11 + 21 - 15) / 2 | ||
}; | ||
const expectedAspectRatio = '4:3'; | ||
const expectedWidth = 640; | ||
const expectedHeight = 480; | ||
const expectAudio = false; | ||
const expectAreAllGopsIdentical = false; | ||
let res = processFrames(frames); | ||
assert.deepEqual(res.payload, { | ||
areAllGopsIdentical: expectAreAllGopsIdentical, | ||
bitrate: expectedBitrate, | ||
fps: expectedFps, | ||
gopDuration: expectedGopDuration, | ||
displayAspectRatio: expectedAspectRatio, | ||
width: expectedWidth, | ||
height: expectedHeight, | ||
hasAudioStream: expectAudio | ||
}); | ||
assert.deepEqual(res.remainedFrames, expectedRemainedFrames); | ||
}); | ||
}); |
@@ -38,3 +38,3 @@ const invalidParams = [ | ||
data : [{sample_aspect_ratio: '200:100', display_aspect_ratio: '0:1', width: 20, height: 10}], | ||
res : [{sample_aspect_ratio: '1:1', display_aspect_ratio: '2:1', width: 20, height: 10}] | ||
res : [{sample_aspect_ratio: '1:1', display_aspect_ratio: '18:9', width: 20, height: 10}] | ||
}, | ||
@@ -49,3 +49,3 @@ { | ||
data : [{sample_aspect_ratio: '200:100', display_aspect_ratio: 'N/A', width: 20, height: 10}], | ||
res : [{sample_aspect_ratio: '1:1', display_aspect_ratio: '2:1', width: 20, height: 10}] | ||
res : [{sample_aspect_ratio: '1:1', display_aspect_ratio: '18:9', width: 20, height: 10}] | ||
} | ||
@@ -52,0 +52,0 @@ ]; |
@@ -21,3 +21,3 @@ 'use strict'; | ||
dataDriven(invalidParams, function () { | ||
const expectedErrorMessage = 'Can not calculate aspect rate due to invalid video resolution'; | ||
const expectedErrorMessage = 'Can not calculate aspect ratio due to invalid video resolution'; | ||
const expectedErrorClass = StreamsInfoError; | ||
@@ -24,0 +24,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3236714
64
4026
410