1
+ import { searchPattern } from "./array-utils" ;
2
+ import { AvcNalu , NalUnitType , NALU_PREFIX } from "./interfaces" ;
3
+
4
+ export class AvcNaluTransformer implements Transformer < Uint8Array , AvcNalu > {
5
+ private readonly buffered : Uint8Array [ ] = [ ] ;
6
+
7
+ private static parseNalu ( payload : Uint8Array ) : {
8
+ naluType : NalUnitType ;
9
+ refIdc : number ;
10
+ } {
11
+ return {
12
+ naluType : payload [ 0 ] & 0x1f ,
13
+ refIdc : ( payload [ 0 ] >> 5 ) & 0x03 ,
14
+ } ;
15
+ }
16
+
17
+ private async extractNalus (
18
+ chunk : Uint8Array ,
19
+ ) : Promise < Uint8Array [ ] > {
20
+ // Find start positions of NALUs in chunk
21
+ const positions = searchPattern < number > ( chunk , NALU_PREFIX ) ;
22
+
23
+ // No NALU start markers found in chunk. We append it to the (unfinished) buffered NALU
24
+ if ( positions . length === 0 ) {
25
+ this . buffered . push ( chunk ) ;
26
+ return [ ] ;
27
+ }
28
+
29
+ // We found at least one NALU start marker. This forms the end of the buffered NALU. The chunk may further contain more complete NALUs, which we
30
+ // extract.
31
+ const nalus = [
32
+ new Uint8Array ( await new Blob ( [ ...this . buffered , chunk . subarray ( 0 , positions [ 0 ] ) ] ) . arrayBuffer ( ) ) ,
33
+ ...positions . slice ( 0 , positions . length - 1 ) . map ( ( position , index ) => chunk . subarray ( position , positions [ index + 1 ] ) ) ,
34
+ ] ;
35
+
36
+ // The last NALU start marker demarcates the start of an assumed unfinished NALU.
37
+ this . buffered . splice ( 0 , this . buffered . length , chunk . subarray ( positions [ positions . length - 1 ] ) ) ;
38
+
39
+ return nalus ;
40
+ }
41
+
42
+ start ( ) : void { }
43
+
44
+ async transform ( chunk : Uint8Array , controller : TransformStreamDefaultController < AvcNalu > ) : Promise < void > {
45
+ const nalus = await this . extractNalus ( chunk ) ;
46
+
47
+ nalus . forEach ( naluPayload => {
48
+ const naluBody = naluPayload . subarray ( NALU_PREFIX . length ) ;
49
+ const { naluType, refIdc } = AvcNaluTransformer . parseNalu ( naluBody ) ;
50
+
51
+ controller . enqueue ( { naluType, refIdc, naluBody } ) ;
52
+ } ) ;
53
+ }
54
+
55
+ async flush ( controller : TransformStreamDefaultController < AvcNalu > ) : Promise < void > {
56
+ const naluPayload = new Uint8Array ( await new Blob ( this . buffered ) . arrayBuffer ( ) ) ;
57
+
58
+ const naluBody = naluPayload . subarray ( NALU_PREFIX . length ) ;
59
+ const { naluType, refIdc } = AvcNaluTransformer . parseNalu ( naluBody ) ;
60
+
61
+ this . buffered . splice ( 0 , this . buffered . length ) ;
62
+
63
+ controller . enqueue ( { naluType, refIdc, naluBody } ) ;
64
+ controller . terminate ( ) ;
65
+ }
66
+ }
0 commit comments