Skip to content

Commit

Permalink
split senc decoding into two phases to be able to use data from other…
Browse files Browse the repository at this point in the history
… boxes
  • Loading branch information
tobbee committed Jan 1, 2022
1 parent b0ebce6 commit 93b89ab
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 124 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ The examples are:
This tool has been extended to support generation of segments with multiple tracks as well
as reading and writing `mdat` in lazy mode
4. `multitrack` parses a fragmented file with multiple tracks
5. `decrypt-cenc` decrypts a segmented mp4 file encrypted in `cenc` mode

## Stability
The APIs should be fairly stable, but minor non-backwards-compatible changes may happen until version 1.
Expand Down
27 changes: 24 additions & 3 deletions examples/decrypt-cenc/decrypt_cenc_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
package main

import "testing"
import (
"bytes"
"io/ioutil"
"os"
"testing"

"github.com/go-test/deep"
)

func TestDecodeCenc(t *testing.T) {
inFile := "testdata/prog_8s_enc_dashinit.mp4"
outFile := "testdata/dec.mp4"
expectedOutFile := "testdata/prog_8s_dec_dashinit.mp4"
hexString := "63cb5f7184dd4b689a5c5ff11ee6a328"
err := start(inFile, outFile, hexString)
ifh, err := os.Open(inFile)
if err != nil {
t.Error(err)
}
buf := bytes.Buffer{}
err = start(ifh, &buf, hexString)
if err != nil {
t.Error(err)
}
expectedOut, err := ioutil.ReadFile(expectedOutFile)
if err != nil {
t.Error(err)
}
gotOut := buf.Bytes()
diff := deep.Equal(expectedOut, gotOut)
if diff != nil {
t.Errorf("Mismatch: %s", diff)
}
}
55 changes: 33 additions & 22 deletions examples/decrypt-cenc/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// decrypt-cenc - decrypt a segmented mp4 file encrypted in cenc mode
//
// The output is in the same format as the input but with samples decrypted
// and encryption information boxes such as pssh and schm removed.
// An example file is given in testdata/prog_8s_enc_dashinit.mp4
package main

import (
Expand All @@ -13,25 +18,26 @@ import (
)

func main() {

inFilePath := flag.String("i", "", "Required: Path to input file")
outFilePath := flag.String("o", "", "Required: Output file")
hexKey := flag.String("k", "", "Required: key (hex)")

err := start(*inFilePath, *outFilePath, *hexKey)
ifh, err := os.Open(*inFilePath)
if err != nil {
log.Fatal(err)
}
defer ifh.Close()
ofh, err := os.Create(*outFilePath)
if err != nil {
log.Fatal(err)
}
err = start(ifh, ofh, *hexKey)
if err != nil {
log.Fatalln(err)
}

}

func start(inPath, outPath, hexKey string) error {

ifh, err := os.Open(inPath)
if err != nil {
return err
}
defer ifh.Close()
func start(r io.Reader, w io.Writer, hexKey string) error {

if len(hexKey) != 32 {
return fmt.Errorf("Hex key must have length 32 chars")
Expand All @@ -41,7 +47,7 @@ func start(inPath, outPath, hexKey string) error {
return err
}

err = decryptMP4withCenc(ifh, key, outPath)
err = decryptMP4withCenc(r, key, w)
if err != nil {
return err
}
Expand All @@ -64,7 +70,7 @@ func findTrackInfo(tracks []trackInfo, trackID uint32) trackInfo {
}

// decryptMP4withCenc - decrypt segmented mp4 file with CENC encryption
func decryptMP4withCenc(r io.Reader, key []byte, outPath string) error {
func decryptMP4withCenc(r io.Reader, key []byte, w io.Writer) error {
inMp4, err := mp4.DecodeFile(r)
if err != nil {
return err
Expand All @@ -73,11 +79,6 @@ func decryptMP4withCenc(r io.Reader, key []byte, outPath string) error {
return fmt.Errorf("file not fragmented. Not supported")
}

ofh, err := os.Create(outPath)
if err != nil {
return err
}

tracks := make([]trackInfo, 0, len(inMp4.Init.Moov.Traks))

moov := inMp4.Init.Moov
Expand Down Expand Up @@ -135,16 +136,15 @@ func decryptMP4withCenc(r io.Reader, key []byte, outPath string) error {
}

// Write the modified init segment
err = inMp4.Init.Encode(ofh)
err = inMp4.Init.Encode(w)
if err != nil {
return err
}

err = decryptAndWriteSegments(inMp4.Segments, tracks, key, ofh)
err = decryptAndWriteSegments(inMp4.Segments, tracks, key, w)
if err != nil {
return err
}
ofh.Close()
return nil
}

Expand Down Expand Up @@ -176,13 +176,24 @@ func decryptFragment(frag *mp4.Fragment, tracks []trackInfo, key []byte) error {
moof := frag.Moof
var nrBytesRemoved uint64 = 0
for _, traf := range moof.Trafs {
senc := traf.Senc
hasSenc, isParsed := traf.ContainsSencBox()
if !hasSenc {
return fmt.Errorf("no senc box in traf")
}
ti := findTrackInfo(tracks, traf.Tfhd.TrackID)
if !isParsed {
defaultIVSize := ti.sinf.Schi.Tenc.DefaultPerSampleIVSize
err := traf.ParseReadSenc(defaultIVSize, moof.StartPos)
if err != nil {
return fmt.Errorf("parseReadSenc: %w", err)
}
}
samples, err := frag.GetFullSamples(ti.trex)
if err != nil {
return err
}
err = decryptSamplesInPlace(samples, key, senc)

err = decryptSamplesInPlace(samples, key, traf.Senc)
if err != nil {
return err
}
Expand Down
Binary file not shown.
21 changes: 20 additions & 1 deletion mp4/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,31 @@ LoopBoxes:
if err != nil {
return nil, err
}
if boxType == "mdat" {
switch boxType {
case "mdat":
if f.isFragmented {
if lastBoxType != "moof" {
return nil, fmt.Errorf("Does not support %v between moof and mdat", lastBoxType)
}
}
case "moof":
moof := box.(*MoofBox)
for _, traf := range moof.Trafs {
if ok, parsed := traf.ContainsSencBox(); ok && !parsed {
defaultIVSize := byte(0) // Should get this from tenc in sinf
if f.Moov != nil {
trackID := traf.Tfhd.TrackID
sinf := f.Moov.GetSinf(trackID)
if sinf != nil && sinf.Schi != nil && sinf.Schi.Tenc != nil {
defaultIVSize = sinf.Schi.Tenc.DefaultPerSampleIVSize
}
}
err = traf.ParseReadSenc(defaultIVSize, moof.StartPos)
if err != nil {
return nil, err
}
}
}
}
f.AddChild(box, boxStartPos)
lastBoxType = boxType
Expand Down
19 changes: 18 additions & 1 deletion mp4/moov.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type MoovBox struct {
Pssh *PsshBox
Psshs []*PsshBox
Children []Box
StartPos uint64
}

// NewMoovBox - Generate a new empty moov box
Expand All @@ -24,7 +25,6 @@ func NewMoovBox() *MoovBox {

// AddChild - Add a child box
func (m *MoovBox) AddChild(box Box) {

switch box.Type() {
case "mvhd":
m.Mvhd = box.(*MvhdBox)
Expand Down Expand Up @@ -62,6 +62,7 @@ func DecodeMoov(hdr *boxHeader, startPos uint64, r io.Reader) (Box, error) {
return nil, err
}
m := NewMoovBox()
m.StartPos = startPos
for _, b := range l {
m.AddChild(b)
}
Expand Down Expand Up @@ -111,3 +112,19 @@ func (m *MoovBox) RemovePsshs() []*PsshBox {

return psshs
}

func (m *MoovBox) GetSinf(trackID uint32) *SinfBox {
for _, trak := range m.Traks {
if trak.Tkhd.TrackID == trackID {
stsd := trak.Mdia.Minf.Stbl.Stsd
sd := stsd.Children[0] // Get first (and only)
if visual, ok := sd.(*VisualSampleEntryBox); ok {
return visual.Sinf
}
if audio, ok := sd.(*AudioSampleEntryBox); ok {
return audio.Sinf
}
}
}
return nil
}
5 changes: 3 additions & 2 deletions mp4/saio.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"io/ioutil"
)

// SaioBox - Sample Auxiliary Information Offsets Box (saiz)
// SaioBox - Sample Auxiliary Information Offsets Box (saiz) (in stbl or traf box)
type SaioBox struct {
Version byte
Flags uint32
Expand Down Expand Up @@ -101,8 +101,9 @@ func (b *SaioBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string
}
bd.write(" - sampleCount: %d", len(b.Offset))
level := getInfoLevel(b, specificBoxLevels)
bd.write(" - offset[%d]=%d", 1, b.Offset[0])
if level > 0 {
for i := 0; i < len(b.Offset); i++ {
for i := 1; i < len(b.Offset); i++ {
bd.write(" - offset[%d]=%d", i+1, b.Offset[i])
}
}
Expand Down
2 changes: 1 addition & 1 deletion mp4/saiz.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"io/ioutil"
)

// SaizBox - Sample Auxiliary Information Sizes Box (saiz)
// SaizBox - Sample Auxiliary Information Sizes Box (saiz) (in stbl or traf box)
type SaizBox struct {
Version byte
Flags uint32
Expand Down
2 changes: 1 addition & 1 deletion mp4/schm.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (b *SchmBox) Encode(w io.Writer) error {
func (b *SchmBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string) (err error) {
bd := newInfoDumper(w, indent, b, int(b.Version), b.Flags)
bd.write(" - schemeType: %s", b.SchemeType)
bd.write(" - schemeVersion: %d", b.SchemeVersion)
bd.write(" - schemeVersion: %d (%d.%d)", b.SchemeVersion, b.SchemeVersion>>16, b.SchemeVersion&0xffff)
if b.Flags&0x01 != 0 {
bd.write(" - schemeURI: %q", b.SchemeURI)
}
Expand Down
Loading

0 comments on commit 93b89ab

Please sign in to comment.