-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support AC3 in fMP4 and MPEG-TS (#81)
- Loading branch information
Showing
33 changed files
with
1,106 additions
and
64 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,7 @@ | ||
// Package ac3 contains utilities to work with the AC-3 codec. | ||
package ac3 | ||
|
||
const ( | ||
// SamplesPerFrame is the number of samples contained inside a frame. | ||
SamplesPerFrame = 1536 | ||
) |
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,74 @@ | ||
package ac3 | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/bluenviron/mediacommon/pkg/bits" | ||
) | ||
|
||
// BSI is a Bit Stream Information. | ||
// Specification: ATSC, AC-3, Table 5.2 | ||
type BSI struct { | ||
Bsid uint8 | ||
Bsmod uint8 | ||
Acmod uint8 | ||
LfeOn bool | ||
} | ||
|
||
// Unmarshal decodes a BSI. | ||
func (b *BSI) Unmarshal(buf []byte) error { | ||
if len(buf) < 2 { | ||
return fmt.Errorf("not enough bits") | ||
} | ||
|
||
b.Bsid = buf[0] >> 3 | ||
if b.Bsid != 0x08 { | ||
return fmt.Errorf("invalid bsid") | ||
} | ||
|
||
b.Bsmod = buf[0] & 0b111 | ||
|
||
buf = buf[1:] | ||
pos := 0 | ||
|
||
tmp := bits.ReadBitsUnsafe(buf, &pos, 3) | ||
b.Acmod = uint8(tmp) | ||
|
||
if ((b.Acmod & 0x1) != 0) && (b.Acmod != 0x1) { | ||
bits.ReadBitsUnsafe(buf, &pos, 2) // cmixlev | ||
} | ||
|
||
if (b.Acmod & 0x4) != 0 { | ||
bits.ReadBitsUnsafe(buf, &pos, 2) // surmixlev | ||
} | ||
|
||
if b.Acmod == 0x2 { | ||
bits.ReadBitsUnsafe(buf, &pos, 2) // dsurmod | ||
} | ||
|
||
b.LfeOn = bits.ReadFlagUnsafe(buf, &pos) | ||
|
||
return nil | ||
} | ||
|
||
// ChannelCount returns the channel count. | ||
func (b BSI) ChannelCount() int { | ||
var n int | ||
switch b.Acmod { | ||
case 0b001: | ||
n = 1 | ||
case 0b010, 0b000: | ||
n = 2 | ||
case 0b011, 0b100: | ||
n = 3 | ||
case 0b101, 0b110: | ||
n = 4 | ||
default: | ||
n = 5 | ||
} | ||
|
||
if b.LfeOn { | ||
return n + 1 | ||
} | ||
return n | ||
} |
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,25 @@ | ||
package ac3 | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestBSIUnmarshal(t *testing.T) { | ||
for _, ca := range ac3Cases { | ||
t.Run(ca.name, func(t *testing.T) { | ||
var bsi BSI | ||
err := bsi.Unmarshal(ca.enc[5:]) | ||
require.NoError(t, err) | ||
require.Equal(t, ca.bsi, bsi) | ||
}) | ||
} | ||
} | ||
|
||
func FuzzBSIUnmarshal(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, b []byte) { | ||
var bsi BSI | ||
bsi.Unmarshal(b) //nolint:errcheck | ||
}) | ||
} |
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,94 @@ | ||
package ac3 | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// ATSC, AC-3, Table 5.18 | ||
var frameSizes = [][]int{ | ||
{64, 69, 96}, | ||
{64, 70, 96}, | ||
{80, 87, 120}, | ||
{80, 88, 120}, | ||
{96, 104, 144}, | ||
{96, 105, 144}, | ||
{112, 121, 168}, | ||
{112, 122, 168}, | ||
{128, 139, 192}, | ||
{128, 140, 192}, | ||
{160, 174, 240}, | ||
{160, 175, 240}, | ||
{192, 208, 288}, | ||
{192, 209, 288}, | ||
{224, 243, 336}, | ||
{224, 244, 336}, | ||
{256, 278, 384}, | ||
{256, 279, 384}, | ||
{320, 348, 480}, | ||
{320, 349, 480}, | ||
{384, 417, 576}, | ||
{384, 418, 576}, | ||
{448, 487, 672}, | ||
{448, 488, 672}, | ||
{512, 557, 768}, | ||
{512, 558, 768}, | ||
{640, 696, 960}, | ||
{640, 697, 960}, | ||
{768, 835, 1152}, | ||
{768, 836, 1152}, | ||
{896, 975, 1344}, | ||
{896, 976, 1344}, | ||
{1024, 1114, 1536}, | ||
{1024, 1115, 1536}, | ||
{1152, 1253, 1728}, | ||
{1152, 1254, 1728}, | ||
{1280, 1393, 1920}, | ||
{1280, 1394, 1920}, | ||
} | ||
|
||
// SyncInfo is a synchronization information. | ||
// Specification: ATSC, AC-3, Table 5.1 | ||
type SyncInfo struct { | ||
Fscod uint8 | ||
Frmsizecod uint8 | ||
} | ||
|
||
// Unmarshal decodes a SyncInfo. | ||
func (s *SyncInfo) Unmarshal(frame []byte) error { | ||
if len(frame) < 5 { | ||
return fmt.Errorf("not enough bits") | ||
} | ||
|
||
if frame[0] != 0x0B || frame[1] != 0x77 { | ||
return fmt.Errorf("invalid sync word") | ||
} | ||
|
||
s.Fscod = frame[4] >> 6 | ||
if s.Fscod >= 3 { | ||
return fmt.Errorf("invalid fscod") | ||
} | ||
|
||
s.Frmsizecod = frame[4] & 0x3f | ||
if s.Frmsizecod >= 38 { | ||
return fmt.Errorf("invalid frmsizecod") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// FrameSize returns the frame size. | ||
func (s SyncInfo) FrameSize() int { | ||
return frameSizes[s.Frmsizecod][s.Fscod] * 2 | ||
} | ||
|
||
// SampleRate returns the frame sample rate. | ||
func (s SyncInfo) SampleRate() int { | ||
switch s.Fscod { | ||
case 0: | ||
return 48000 | ||
case 1: | ||
return 44100 | ||
default: | ||
return 32000 | ||
} | ||
} |
Oops, something went wrong.