mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-03 05:43:50 +02:00
Fix HEVC not being encodable in CI
This commit is contained in:
+2
-2
@@ -11,14 +11,14 @@
|
|||||||
const file = fileInput.files[0];
|
const file = fileInput.files[0];
|
||||||
const input = new Mediabunny.Input({
|
const input = new Mediabunny.Input({
|
||||||
formats: Mediabunny.ALL_FORMATS,
|
formats: Mediabunny.ALL_FORMATS,
|
||||||
source: new Mediabunny.BufferSource(await file.arrayBuffer()),
|
source: new Mediabunny.BlobSource(file),
|
||||||
});
|
});
|
||||||
|
|
||||||
const track = await input.getPrimaryVideoTrack();
|
const track = await input.getPrimaryVideoTrack();
|
||||||
const sink = new Mediabunny.EncodedPacketSink(track);
|
const sink = new Mediabunny.EncodedPacketSink(track);
|
||||||
|
|
||||||
for await (const packet of sink.packets()) {
|
for await (const packet of sink.packets()) {
|
||||||
//console.log(packet.timestamp, packet.duration, packet.type);
|
console.log(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -192,13 +192,17 @@ export class MpegTsMuxer extends Muxer {
|
|||||||
try {
|
try {
|
||||||
const trackData = this.getVideoTrackData(track, meta);
|
const trackData = this.getVideoTrackData(track, meta);
|
||||||
|
|
||||||
this.validateAndNormalizeTimestamp(trackData.track, packet.timestamp, packet.type === 'key');
|
const timestamp = this.validateAndNormalizeTimestamp(
|
||||||
|
trackData.track,
|
||||||
|
packet.timestamp,
|
||||||
|
packet.type === 'key',
|
||||||
|
);
|
||||||
|
|
||||||
const preparedData = this.prepareVideoPacket(trackData, packet, meta);
|
const preparedData = this.prepareVideoPacket(trackData, packet, meta);
|
||||||
|
|
||||||
trackData.packetQueue.push({
|
trackData.packetQueue.push({
|
||||||
data: preparedData,
|
data: preparedData,
|
||||||
timestamp: packet.timestamp,
|
timestamp,
|
||||||
isKeyframe: packet.type === 'key',
|
isKeyframe: packet.type === 'key',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -218,13 +222,17 @@ export class MpegTsMuxer extends Muxer {
|
|||||||
try {
|
try {
|
||||||
const trackData = this.getAudioTrackData(track, meta);
|
const trackData = this.getAudioTrackData(track, meta);
|
||||||
|
|
||||||
this.validateAndNormalizeTimestamp(trackData.track, packet.timestamp, packet.type === 'key');
|
const timestamp = this.validateAndNormalizeTimestamp(
|
||||||
|
trackData.track,
|
||||||
|
packet.timestamp,
|
||||||
|
packet.type === 'key',
|
||||||
|
);
|
||||||
|
|
||||||
const preparedData = this.prepareAudioPacket(trackData, packet, meta);
|
const preparedData = this.prepareAudioPacket(trackData, packet, meta);
|
||||||
|
|
||||||
trackData.packetQueue.push({
|
trackData.packetQueue.push({
|
||||||
data: preparedData,
|
data: preparedData,
|
||||||
timestamp: packet.timestamp,
|
timestamp,
|
||||||
isKeyframe: packet.type === 'key',
|
isKeyframe: packet.type === 'key',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ALL_FORMATS, MPEG_TS } from '../../src/input-format.js';
|
|||||||
import { Output } from '../../src/output.js';
|
import { Output } from '../../src/output.js';
|
||||||
import { MpegTsOutputFormat } from '../../src/output-format.js';
|
import { MpegTsOutputFormat } from '../../src/output-format.js';
|
||||||
import { BufferTarget, StreamTarget, StreamTargetChunk } from '../../src/target.js';
|
import { BufferTarget, StreamTarget, StreamTargetChunk } from '../../src/target.js';
|
||||||
import { CanvasSource, EncodedAudioPacketSource } from '../../src/media-source.js';
|
import { CanvasSource, EncodedAudioPacketSource, EncodedVideoPacketSource } from '../../src/media-source.js';
|
||||||
import { QUALITY_HIGH } from '../../src/encode.js';
|
import { QUALITY_HIGH } from '../../src/encode.js';
|
||||||
import { EncodedPacketSink } from '../../src/media-sink.js';
|
import { EncodedPacketSink } from '../../src/media-sink.js';
|
||||||
import { assert } from '../../src/misc.js';
|
import { assert } from '../../src/misc.js';
|
||||||
@@ -215,15 +215,7 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
target: new BufferTarget(),
|
target: new BufferTarget(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const canvas = new OffscreenCanvas(1280, 720);
|
const videoSource = new EncodedVideoPacketSource('hevc');
|
||||||
const context = canvas.getContext('2d')!;
|
|
||||||
context.fillStyle = '#000000';
|
|
||||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
const videoSource = new CanvasSource(canvas, {
|
|
||||||
codec: 'hevc',
|
|
||||||
bitrate: QUALITY_HIGH,
|
|
||||||
});
|
|
||||||
output.addVideoTrack(videoSource);
|
output.addVideoTrack(videoSource);
|
||||||
|
|
||||||
const audioSource = new EncodedAudioPacketSource('mp3');
|
const audioSource = new EncodedAudioPacketSource('mp3');
|
||||||
@@ -231,13 +223,32 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
|
|
||||||
await output.start();
|
await output.start();
|
||||||
|
|
||||||
const fps = 30;
|
|
||||||
const duration = 5;
|
const duration = 5;
|
||||||
const frameCount = fps * duration;
|
|
||||||
const frameDuration = 1 / fps;
|
|
||||||
|
|
||||||
for (let i = 0; i < frameCount; i++) {
|
// Extract HEVC packets from existing file
|
||||||
await videoSource.add(i * frameDuration, frameDuration);
|
using hevcInput = new Input({
|
||||||
|
source: new UrlSource('/video-h265.mp4'),
|
||||||
|
formats: ALL_FORMATS,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hevcTrack = await hevcInput.getPrimaryVideoTrack();
|
||||||
|
assert(hevcTrack);
|
||||||
|
|
||||||
|
const hevcSink = new EncodedPacketSink(hevcTrack);
|
||||||
|
|
||||||
|
let isFirstVideo = true;
|
||||||
|
let videoPacketCountWritten = 0;
|
||||||
|
for await (const packet of hevcSink.packets()) {
|
||||||
|
console.log(packet.timestamp);
|
||||||
|
if (packet.timestamp >= duration) break;
|
||||||
|
|
||||||
|
await videoSource.add(packet, {
|
||||||
|
decoderConfig: isFirstVideo
|
||||||
|
? (await hevcTrack.getDecoderConfig())!
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
isFirstVideo = false;
|
||||||
|
videoPacketCountWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract MP3 packets from existing file
|
// Extract MP3 packets from existing file
|
||||||
@@ -251,16 +262,16 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
|
|
||||||
const mp3Sink = new EncodedPacketSink(mp3Track);
|
const mp3Sink = new EncodedPacketSink(mp3Track);
|
||||||
|
|
||||||
let isFirst = true;
|
let isFirstAudio = true;
|
||||||
for await (const packet of mp3Sink.packets()) {
|
for await (const packet of mp3Sink.packets()) {
|
||||||
if (packet.timestamp >= duration) break;
|
if (packet.timestamp >= duration) break;
|
||||||
|
|
||||||
await audioSource.add(packet, {
|
await audioSource.add(packet, {
|
||||||
decoderConfig: isFirst
|
decoderConfig: isFirstAudio
|
||||||
? (await mp3Track.getDecoderConfig())!
|
? (await mp3Track.getDecoderConfig())!
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
isFirst = false;
|
isFirstAudio = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await output.finalize();
|
await output.finalize();
|
||||||
@@ -281,14 +292,10 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
|
|
||||||
expect(videoTrack.id).toBe(0x100);
|
expect(videoTrack.id).toBe(0x100);
|
||||||
expect(videoTrack.codec).toBe('hevc');
|
expect(videoTrack.codec).toBe('hevc');
|
||||||
expect(videoTrack.displayWidth).toBe(1280);
|
|
||||||
expect(videoTrack.displayHeight).toBe(720);
|
|
||||||
|
|
||||||
const videoDecoderConfig = await videoTrack.getDecoderConfig();
|
const videoDecoderConfig = await videoTrack.getDecoderConfig();
|
||||||
assert(videoDecoderConfig);
|
assert(videoDecoderConfig);
|
||||||
expect(videoDecoderConfig.codec).toMatch(/^hev1\./);
|
expect(videoDecoderConfig.codec).toMatch(/^hev1\./);
|
||||||
expect(videoDecoderConfig.codedWidth).toBe(1280);
|
|
||||||
expect(videoDecoderConfig.codedHeight).toBe(720);
|
|
||||||
expect(videoDecoderConfig.description).toBeUndefined(); // Annex B, no description
|
expect(videoDecoderConfig.description).toBeUndefined(); // Annex B, no description
|
||||||
|
|
||||||
// Verify audio track
|
// Verify audio track
|
||||||
@@ -316,7 +323,7 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
videoPacketCount++;
|
videoPacketCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(videoPacketCount).toBe(frameCount);
|
expect(videoPacketCount).toBe(videoPacketCountWritten);
|
||||||
|
|
||||||
// Verify audio packets are MP3 frames
|
// Verify audio packets are MP3 frames
|
||||||
const audioSink = new EncodedPacketSink(audioTrack);
|
const audioSink = new EncodedPacketSink(audioTrack);
|
||||||
@@ -333,7 +340,7 @@ test('MPEG-TS muxing with HEVC and MP3', async () => {
|
|||||||
const videoDuration = await videoTrack.computeDuration();
|
const videoDuration = await videoTrack.computeDuration();
|
||||||
const audioDuration = await audioTrack.computeDuration();
|
const audioDuration = await audioTrack.computeDuration();
|
||||||
|
|
||||||
expect(videoDuration).toBeCloseTo(5, 1);
|
expect(videoDuration).toBeCloseTo(4.933333333333334, 1);
|
||||||
expect(audioDuration).toBeCloseTo(5, 1);
|
expect(audioDuration).toBeCloseTo(5, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -482,32 +489,35 @@ test('MPEG-TS muxing with two video tracks', async () => {
|
|||||||
target: new BufferTarget(),
|
target: new BufferTarget(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const canvas1 = new OffscreenCanvas(640, 480);
|
const videoSource1 = new EncodedVideoPacketSource('hevc');
|
||||||
const ctx1 = canvas1.getContext('2d')!;
|
const videoSource2 = new EncodedVideoPacketSource('hevc');
|
||||||
ctx1.fillStyle = '#ff0000';
|
|
||||||
ctx1.fillRect(0, 0, canvas1.width, canvas1.height);
|
|
||||||
|
|
||||||
const canvas2 = new OffscreenCanvas(320, 240);
|
|
||||||
const ctx2 = canvas2.getContext('2d')!;
|
|
||||||
ctx2.fillStyle = '#00ff00';
|
|
||||||
ctx2.fillRect(0, 0, canvas2.width, canvas2.height);
|
|
||||||
|
|
||||||
const videoSource1 = new CanvasSource(canvas1, { codec: 'hevc', bitrate: QUALITY_HIGH });
|
|
||||||
const videoSource2 = new CanvasSource(canvas2, { codec: 'hevc', bitrate: QUALITY_HIGH });
|
|
||||||
|
|
||||||
output.addVideoTrack(videoSource1);
|
output.addVideoTrack(videoSource1);
|
||||||
output.addVideoTrack(videoSource2);
|
output.addVideoTrack(videoSource2);
|
||||||
|
|
||||||
await output.start();
|
await output.start();
|
||||||
|
|
||||||
const fps = 30;
|
|
||||||
const duration = 1;
|
const duration = 1;
|
||||||
const frameCount = fps * duration;
|
|
||||||
const frameDuration = 1 / fps;
|
|
||||||
|
|
||||||
for (let i = 0; i < frameCount; i++) {
|
using hevcInput = new Input({
|
||||||
await videoSource1.add(i * frameDuration, frameDuration);
|
source: new UrlSource('/video-h265.mp4'),
|
||||||
await videoSource2.add(i * frameDuration, frameDuration);
|
formats: ALL_FORMATS,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hevcTrack = await hevcInput.getPrimaryVideoTrack();
|
||||||
|
assert(hevcTrack);
|
||||||
|
|
||||||
|
const hevcSink = new EncodedPacketSink(hevcTrack);
|
||||||
|
const hevcDecoderConfig = await hevcTrack.getDecoderConfig();
|
||||||
|
|
||||||
|
let isFirst = true;
|
||||||
|
for await (const packet of hevcSink.packets()) {
|
||||||
|
if (packet.timestamp >= duration) break;
|
||||||
|
|
||||||
|
const meta = { decoderConfig: isFirst ? hevcDecoderConfig! : undefined };
|
||||||
|
await videoSource1.add(packet, meta);
|
||||||
|
await videoSource2.add(packet, meta);
|
||||||
|
isFirst = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await output.finalize();
|
await output.finalize();
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user