-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic unit tests for h264 decoder
- Loading branch information
Showing
2 changed files
with
109 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
const expect = chai.expect; | ||
|
||
import { H264Parser, H264Context } from '../core/decoders/h264.js'; | ||
import Base64 from '../core/base64.js'; | ||
|
||
/* This video was generated using the following commands: | ||
* magick -size 16x16 xc:#ff0000ff 1.png | ||
* magick -size 16x16 xc:#00ff00ff 2.png | ||
* magick -size 16x16 xc:#0000ffff 3.png | ||
* ffmpeg -pattern_type glob -i '*.png' -c:v libx264 -pix_fmt yuv420p -profile:v baseline video.h264 | ||
* | ||
* It is a 3 frame 16x16 video where the first frame is solid red, the second | ||
* is solid green and the third is solid blue. | ||
*/ | ||
const redGreenBlue16x16Video = new Uint8Array(Base64.decode( | ||
'AAAAAWdCwArZHpqAgQEgAAADACAAAAZB4kTJAAAAAWjLg8sgAAABBgX//23cRem95tlIt5Ys2CDZ' + | ||
'I+7veDI2NCAtIGNvcmUgMTY0IHIzMTA4IDMxZTE5ZjkgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVj' + | ||
'IC0gQ29weWxlZnQgMjAwMy0yMDIzIC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1s' + | ||
'IC0gb3B0aW9uczogY2FiYWM9MCByZWY9MyBkZWJsb2NrPTE6MDowIGFuYWx5c2U9MHgxOjB4MTEx' + | ||
'IG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFu' + | ||
'Z2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0wIGNxbT0wIGRlYWR6b25lPTIxLDEx' + | ||
'IGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MSBsb29rYWhlYWRfdGhy' + | ||
'ZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVy' + | ||
'YXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTAgd2VpZ2h0cD0wIGtleWlu' + | ||
'dD0yNTAga2V5aW50X21pbj0yNSBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmNfbG9va2Fo' + | ||
'ZWFkPTQwIHJjPWNyZiBtYnRyZWU9MSBjcmY9MjMuMCBxY29tcD0wLjYwIHFwbWluPTAgcXBtYXg9' + | ||
'NjkgcXBzdGVwPTQgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAWWIhArxGKAAJAMcAAQ644AA' + | ||
'l8YAAAABQYiIK8RigACFJHAAEeuOAAJPOAAAAAFBiJArxGKAAJ6ccAAS+I4AAgu4')); | ||
|
||
function createSolidColorFrameBuffer(color, width, height) { | ||
const r = (color >> 24) & 0xff; | ||
const g = (color >> 16) & 0xff; | ||
const b = (color >> 8) & 0xff; | ||
const a = (color >> 0) & 0xff; | ||
|
||
const size = width * height * 4; | ||
let array = new Uint8Array(size); | ||
|
||
for (let i = 0; i < size / 4; ++i) { | ||
array[i * 4 + 0] = r; | ||
array[i * 4 + 1] = g; | ||
array[i * 4 + 2] = b; | ||
array[i * 4 + 3] = a; | ||
} | ||
|
||
return array; | ||
} | ||
|
||
function frameBufferFromCanvasContext(ctx) { | ||
let imageData = ctx.getImageData(0, 0, 16, 16); | ||
let buffer = imageData.data.buffer; | ||
return new Uint8Array(buffer); | ||
} | ||
|
||
describe('H.264 Parser', function () { | ||
it('should parse constrained baseline video', function () { | ||
let parser = new H264Parser(redGreenBlue16x16Video); | ||
|
||
let frame = parser.parse(); | ||
expect(frame).to.have.property('key', true); | ||
|
||
expect(parser).to.have.property('profileIdc', 66); | ||
expect(parser).to.have.property('constraintSet', 192); | ||
expect(parser).to.have.property('levelIdc', 10); | ||
|
||
frame = parser.parse(); | ||
expect(frame).to.have.property('key', false); | ||
|
||
frame = parser.parse(); | ||
expect(frame).to.have.property('key', false); | ||
|
||
frame = parser.parse(); | ||
expect(frame).to.be.null; | ||
}); | ||
}); | ||
|
||
describe('H.264 Context', function () { | ||
it('should decode constrained baseline video chunk', async function () { | ||
let context = new H264Context(16, 16); | ||
let pendingFrame = context.decode(redGreenBlue16x16Video); | ||
|
||
expect(pendingFrame).to.have.property('keep', true); | ||
|
||
if (!pendingFrame.ready) { | ||
await pendingFrame.promise; | ||
} | ||
expect(pendingFrame.ready).to.be.true; | ||
|
||
let frame = pendingFrame.frame; | ||
|
||
expect(frame.visibleRect.width).to.equal(16); | ||
expect(frame.visibleRect.height).to.equal(16); | ||
|
||
// Note: VideoFrame.copyTo() doesn't work at all on Firefox and it won't | ||
// do the RGBA conversion on Chrome. | ||
|
||
// TODO: Use something more portable | ||
let canvas = new OffscreenCanvas(16, 16); | ||
let ctx = canvas.getContext('2d'); | ||
|
||
ctx.drawImage(frame, 0, 0); | ||
let framebuffer = frameBufferFromCanvasContext(ctx); | ||
|
||
const solidBlue = createSolidColorFrameBuffer(0x0000ffff, 16, 16); | ||
expect(framebuffer).to.eql(solidBlue); | ||
}); | ||
}); |