-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #325 from Eyevinn/feat-add-sidx
New example add-side with advanced options.
- Loading branch information
Showing
14 changed files
with
489 additions
and
16 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
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,127 @@ | ||
// add-sidx adds a top-level sidx box describing the segments of a fragmented files. | ||
// | ||
// Segments are identified by styp boxes if they exist, otherwise by | ||
// the start of moof or emsg boxes. | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"strings" | ||
|
||
"github.com/Eyevinn/mp4ff/mp4" | ||
) | ||
|
||
var usg = `Usage of add-sidx: | ||
add-sidx adds a top-level sidx box to a fragmented file provided it does not exist. | ||
If styp boxes are present, they signal new segments. It is possible to interpret | ||
every moof box as the start of a new segment, by specifying the "-startSegOnMoof" option. | ||
One can further remove unused encryption boxes with the "-removeEnc" option. | ||
` | ||
|
||
var usage = func() { | ||
parts := strings.Split(os.Args[0], "/") | ||
name := parts[len(parts)-1] | ||
fmt.Fprintln(os.Stderr, usg) | ||
fmt.Fprintf(os.Stderr, "%s [options] <inFile> <outFile>\n", name) | ||
flag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
removeEncBoxes := flag.Bool("removeEnc", false, "Remove unused encryption boxes") | ||
usePTO := flag.Bool("nzEPT", false, "Use non-zero earliestPresentationTime") | ||
segOnMoof := flag.Bool("startSegOnMoof", false, "Start a new segment on every moof") | ||
version := flag.Bool("version", false, "Get mp4ff version") | ||
|
||
flag.Parse() | ||
|
||
if *version { | ||
fmt.Printf("add-sidx %s\n", mp4.GetVersion()) | ||
os.Exit(0) | ||
} | ||
flag.Parse() | ||
|
||
if *version { | ||
fmt.Printf("add-sidx %s\n", mp4.GetVersion()) | ||
os.Exit(0) | ||
} | ||
|
||
args := flag.Args() | ||
if len(args) != 2 { | ||
fmt.Fprintf(os.Stderr, "must specify infile and outfile\n") | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
inFilePath := flag.Arg(0) | ||
outFilePath := flag.Arg(1) | ||
|
||
ifd, err := os.Open(inFilePath) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
usage() | ||
os.Exit(1) | ||
} | ||
defer ifd.Close() | ||
ofd, err := os.Create(outFilePath) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
usage() | ||
os.Exit(1) | ||
} | ||
defer ofd.Close() | ||
err = run(ifd, ofd, *usePTO, *removeEncBoxes, *segOnMoof) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
func run(in io.Reader, out io.Writer, nonZeroEPT, removeEncBoxes, segOnMoof bool) error { | ||
var flags mp4.DecFileFlags | ||
if segOnMoof { | ||
flags |= mp4.DecStartOnMoof | ||
} | ||
mp4Root, err := mp4.DecodeFile(in, mp4.WithDecodeFlags(flags)) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Printf("creating sidx with %d segment(s)\n", len(mp4Root.Segments)) | ||
|
||
if removeEncBoxes { | ||
removeEncryptionBoxes(mp4Root) | ||
} | ||
|
||
addIfNotExists := true | ||
err = mp4Root.UpdateSidx(addIfNotExists, nonZeroEPT) | ||
if err != nil { | ||
return fmt.Errorf("addSidx failed: %w", err) | ||
} | ||
|
||
err = mp4Root.Encode(out) | ||
if err != nil { | ||
return fmt.Errorf("failed to encode output file: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func removeEncryptionBoxes(inFile *mp4.File) { | ||
for _, seg := range inFile.Segments { | ||
for _, frag := range seg.Fragments { | ||
bytesRemoved := uint64(0) | ||
for _, traf := range frag.Moof.Trafs { | ||
bytesRemoved += traf.RemoveEncryptionBoxes() | ||
} | ||
for _, traf := range frag.Moof.Trafs { | ||
for _, trun := range traf.Truns { | ||
trun.DataOffset -= int32(bytesRemoved) | ||
} | ||
} | ||
} | ||
} | ||
} |
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,93 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"testing" | ||
|
||
"github.com/Eyevinn/mp4ff/mp4" | ||
) | ||
|
||
func TestAddSidx(t *testing.T) { | ||
inPath := "testdata/clear_with_enc_boxes.mp4" | ||
testCases := []struct { | ||
desc string | ||
inPath string | ||
removeEnc bool | ||
segOnMoof bool | ||
wantedNrSegs uint32 | ||
wantedSize uint32 | ||
wantedFirstDur uint32 | ||
}{ | ||
{ | ||
desc: "sidx, enc boxes, 1 segment", | ||
inPath: inPath, | ||
removeEnc: false, | ||
segOnMoof: false, | ||
wantedNrSegs: 1, | ||
wantedFirstDur: 2 * 144144, | ||
}, | ||
{ | ||
desc: "sidx, enc boxes, many segments", | ||
inPath: inPath, | ||
removeEnc: false, | ||
segOnMoof: true, | ||
wantedNrSegs: 2, | ||
wantedFirstDur: 144144, | ||
}, | ||
{ | ||
desc: "sidx, no enc boxes, many segments", | ||
inPath: inPath, | ||
removeEnc: true, | ||
segOnMoof: true, | ||
wantedNrSegs: 2, | ||
wantedFirstDur: 144144, | ||
}, | ||
{ | ||
desc: "normal file with styp", | ||
inPath: "../resegmenter/testdata/testV300.mp4", | ||
removeEnc: false, | ||
segOnMoof: false, | ||
wantedNrSegs: 4, | ||
wantedFirstDur: 180000, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.desc, func(t *testing.T) { | ||
in, err := os.Open(tc.inPath) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
out := bytes.Buffer{} | ||
err = run(in, &out, false, tc.removeEnc, tc.segOnMoof) | ||
if err != nil { | ||
return | ||
} | ||
decOut, err := mp4.DecodeFile(&out) | ||
if err != nil { | ||
t.Error() | ||
} | ||
if decOut.Sidx == nil { | ||
t.Error("no sidx box") | ||
} | ||
sidxEntries := decOut.Sidx.SidxRefs | ||
gotNrEntries := len(sidxEntries) | ||
if gotNrEntries != int(tc.wantedNrSegs) { | ||
t.Errorf("got %d sidx entries instead of %d", gotNrEntries, tc.wantedNrSegs) | ||
} | ||
if sidxEntries[0].SubSegmentDuration != tc.wantedFirstDur { | ||
t.Errorf("got first duration %d instead of %d", sidxEntries[0].SubSegmentDuration, tc.wantedFirstDur) | ||
} | ||
if tc.removeEnc { | ||
for _, seg := range decOut.Segments { | ||
for _, frag := range seg.Fragments { | ||
if frag.Moof.Traf.Senc != nil { | ||
t.Error("senc is still present in fragment") | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
} |
Binary file not shown.
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
Oops, something went wrong.