mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add DASH streams for VP9 transcoding (#3275)
This commit is contained in:
206
vendor/github.com/zencoder/go-dash/v3/mpd/duration.go
generated
vendored
Normal file
206
vendor/github.com/zencoder/go-dash/v3/mpd/duration.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
// based on code from golang src/time/time.go
|
||||
|
||||
package mpd
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Duration time.Duration
|
||||
|
||||
var (
|
||||
rStart = "^P" // Must start with a 'P'
|
||||
rDays = "(\\d+D)?" // We only allow Days for durations, not Months or Years
|
||||
rTime = "(?:T" // If there's any 'time' units then they must be preceded by a 'T'
|
||||
rHours = "(\\d+H)?" // Hours
|
||||
rMinutes = "(\\d+M)?" // Minutes
|
||||
rSeconds = "([\\d.]+S)?" // Seconds (Potentially decimal)
|
||||
rEnd = ")?$" // end of regex must close "T" capture group
|
||||
)
|
||||
|
||||
var xmlDurationRegex = regexp.MustCompile(rStart + rDays + rTime + rHours + rMinutes + rSeconds + rEnd)
|
||||
|
||||
func (d Duration) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
|
||||
return xml.Attr{Name: name, Value: d.String()}, nil
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalXMLAttr(attr xml.Attr) error {
|
||||
dur, err := ParseDuration(attr.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(dur)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String renders a Duration in XML Duration Data Type format
|
||||
func (d *Duration) String() string {
|
||||
// Largest time is 2540400h10m10.000000000s
|
||||
var buf [32]byte
|
||||
w := len(buf)
|
||||
|
||||
u := uint64(*d)
|
||||
neg := *d < 0
|
||||
if neg {
|
||||
u = -u
|
||||
}
|
||||
|
||||
if u < uint64(time.Second) {
|
||||
// Special case: if duration is smaller than a second,
|
||||
// use smaller units, like 1.2ms
|
||||
var prec int
|
||||
w--
|
||||
buf[w] = 'S'
|
||||
w--
|
||||
if u == 0 {
|
||||
return "PT0S"
|
||||
}
|
||||
/*
|
||||
switch {
|
||||
case u < uint64(Millisecond):
|
||||
// print microseconds
|
||||
prec = 3
|
||||
// U+00B5 'µ' micro sign == 0xC2 0xB5
|
||||
w-- // Need room for two bytes.
|
||||
copy(buf[w:], "µ")
|
||||
default:
|
||||
// print milliseconds
|
||||
prec = 6
|
||||
buf[w] = 'm'
|
||||
}
|
||||
*/
|
||||
w, u = fmtFrac(buf[:w], u, prec)
|
||||
w = fmtInt(buf[:w], u)
|
||||
} else {
|
||||
w--
|
||||
buf[w] = 'S'
|
||||
|
||||
w, u = fmtFrac(buf[:w], u, 9)
|
||||
|
||||
// u is now integer seconds
|
||||
w = fmtInt(buf[:w], u%60)
|
||||
u /= 60
|
||||
|
||||
// u is now integer minutes
|
||||
if u > 0 {
|
||||
w--
|
||||
buf[w] = 'M'
|
||||
w = fmtInt(buf[:w], u%60)
|
||||
u /= 60
|
||||
|
||||
// u is now integer hours
|
||||
// Stop at hours because days can be different lengths.
|
||||
if u > 0 {
|
||||
w--
|
||||
buf[w] = 'H'
|
||||
w = fmtInt(buf[:w], u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
w--
|
||||
buf[w] = '-'
|
||||
}
|
||||
|
||||
return "PT" + string(buf[w:])
|
||||
}
|
||||
|
||||
// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
|
||||
// tail of buf, omitting trailing zeros. it omits the decimal
|
||||
// point too when the fraction is 0. It returns the index where the
|
||||
// output bytes begin and the value v/10**prec.
|
||||
func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
|
||||
// Omit trailing zeros up to and including decimal point.
|
||||
w := len(buf)
|
||||
print := false
|
||||
for i := 0; i < prec; i++ {
|
||||
digit := v % 10
|
||||
print = print || digit != 0
|
||||
if print {
|
||||
w--
|
||||
buf[w] = byte(digit) + '0'
|
||||
}
|
||||
v /= 10
|
||||
}
|
||||
if print {
|
||||
w--
|
||||
buf[w] = '.'
|
||||
}
|
||||
return w, v
|
||||
}
|
||||
|
||||
// fmtInt formats v into the tail of buf.
|
||||
// It returns the index where the output begins.
|
||||
func fmtInt(buf []byte, v uint64) int {
|
||||
w := len(buf)
|
||||
if v == 0 {
|
||||
w--
|
||||
buf[w] = '0'
|
||||
} else {
|
||||
for v > 0 {
|
||||
w--
|
||||
buf[w] = byte(v%10) + '0'
|
||||
v /= 10
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func ParseDuration(str string) (time.Duration, error) {
|
||||
if len(str) < 3 {
|
||||
return 0, errors.New("At least one number and designator are required")
|
||||
}
|
||||
|
||||
if strings.Contains(str, "-") {
|
||||
return 0, errors.New("Duration cannot be negative")
|
||||
}
|
||||
|
||||
// Check that only the parts we expect exist and that everything's in the correct order
|
||||
if !xmlDurationRegex.Match([]byte(str)) {
|
||||
return 0, errors.New("Duration must be in the format: P[nD][T[nH][nM][nS]]")
|
||||
}
|
||||
|
||||
var parts = xmlDurationRegex.FindStringSubmatch(str)
|
||||
var total time.Duration
|
||||
|
||||
if parts[1] != "" {
|
||||
days, err := strconv.Atoi(strings.TrimRight(parts[1], "D"))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Error parsing Days: %s", err)
|
||||
}
|
||||
total += time.Duration(days) * time.Hour * 24
|
||||
}
|
||||
|
||||
if parts[2] != "" {
|
||||
hours, err := strconv.Atoi(strings.TrimRight(parts[2], "H"))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Error parsing Hours: %s", err)
|
||||
}
|
||||
total += time.Duration(hours) * time.Hour
|
||||
}
|
||||
|
||||
if parts[3] != "" {
|
||||
mins, err := strconv.Atoi(strings.TrimRight(parts[3], "M"))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Error parsing Minutes: %s", err)
|
||||
}
|
||||
total += time.Duration(mins) * time.Minute
|
||||
}
|
||||
|
||||
if parts[4] != "" {
|
||||
secs, err := strconv.ParseFloat(strings.TrimRight(parts[4], "S"), 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Error parsing Seconds: %s", err)
|
||||
}
|
||||
total += time.Duration(secs * float64(time.Second))
|
||||
}
|
||||
|
||||
return total, nil
|
||||
}
|
||||
18
vendor/github.com/zencoder/go-dash/v3/mpd/events.go
generated
vendored
Normal file
18
vendor/github.com/zencoder/go-dash/v3/mpd/events.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package mpd
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
type EventStream struct {
|
||||
XMLName xml.Name `xml:"EventStream"`
|
||||
SchemeIDURI *string `xml:"schemeIdUri,attr"`
|
||||
Value *string `xml:"value,attr,omitempty"`
|
||||
Timescale *uint `xml:"timescale,attr"`
|
||||
Events []Event `xml:"Event,omitempty"`
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
XMLName xml.Name `xml:"Event"`
|
||||
ID *string `xml:"id,attr,omitempty"`
|
||||
PresentationTime *uint64 `xml:"presentationTime,attr,omitempty"`
|
||||
Duration *uint64 `xml:"duration,attr,omitempty"`
|
||||
}
|
||||
1168
vendor/github.com/zencoder/go-dash/v3/mpd/mpd.go
generated
vendored
Normal file
1168
vendor/github.com/zencoder/go-dash/v3/mpd/mpd.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
57
vendor/github.com/zencoder/go-dash/v3/mpd/mpd_attr.go
generated
vendored
Normal file
57
vendor/github.com/zencoder/go-dash/v3/mpd/mpd_attr.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package mpd
|
||||
|
||||
type AttrMPD interface {
|
||||
GetStrptr() *string
|
||||
}
|
||||
|
||||
type attrAvailabilityStartTime struct {
|
||||
strptr *string
|
||||
}
|
||||
|
||||
func (attr *attrAvailabilityStartTime) GetStrptr() *string {
|
||||
return attr.strptr
|
||||
}
|
||||
|
||||
// AttrAvailabilityStartTime returns AttrMPD object for NewMPD
|
||||
func AttrAvailabilityStartTime(value string) AttrMPD {
|
||||
return &attrAvailabilityStartTime{strptr: &value}
|
||||
}
|
||||
|
||||
type attrMinimumUpdatePeriod struct {
|
||||
strptr *string
|
||||
}
|
||||
|
||||
func (attr *attrMinimumUpdatePeriod) GetStrptr() *string {
|
||||
return attr.strptr
|
||||
}
|
||||
|
||||
// AttrMinimumUpdatePeriod returns AttrMPD object for NewMPD
|
||||
func AttrMinimumUpdatePeriod(value string) AttrMPD {
|
||||
return &attrMinimumUpdatePeriod{strptr: &value}
|
||||
}
|
||||
|
||||
type attrMediaPresentationDuration struct {
|
||||
strptr *string
|
||||
}
|
||||
|
||||
func (attr *attrMediaPresentationDuration) GetStrptr() *string {
|
||||
return attr.strptr
|
||||
}
|
||||
|
||||
// AttrMediaPresentationDuration returns AttrMPD object for NewMPD
|
||||
func AttrMediaPresentationDuration(value string) AttrMPD {
|
||||
return &attrMediaPresentationDuration{strptr: &value}
|
||||
}
|
||||
|
||||
type attrPublishTime struct {
|
||||
strptr *string
|
||||
}
|
||||
|
||||
func (attr *attrPublishTime) GetStrptr() *string {
|
||||
return attr.strptr
|
||||
}
|
||||
|
||||
// AttrPublishTime returns AttrMPD object for NewMPD
|
||||
func AttrPublishTime(value string) AttrMPD {
|
||||
return &attrPublishTime{strptr: &value}
|
||||
}
|
||||
87
vendor/github.com/zencoder/go-dash/v3/mpd/mpd_read_write.go
generated
vendored
Normal file
87
vendor/github.com/zencoder/go-dash/v3/mpd/mpd_read_write.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package mpd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Reads an MPD XML file from disk into a MPD object.
|
||||
// path - File path to an MPD on disk
|
||||
func ReadFromFile(path string) (*MPD, error) {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return Read(f)
|
||||
}
|
||||
|
||||
// Reads a string into a MPD object.
|
||||
// xmlStr - MPD manifest data as a string.
|
||||
func ReadFromString(xmlStr string) (*MPD, error) {
|
||||
b := bytes.NewBufferString(xmlStr)
|
||||
return Read(b)
|
||||
}
|
||||
|
||||
// Reads from an io.Reader interface into an MPD object.
|
||||
// r - Must implement the io.Reader interface.
|
||||
func Read(r io.Reader) (*MPD, error) {
|
||||
var mpd MPD
|
||||
d := xml.NewDecoder(r)
|
||||
err := d.Decode(&mpd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpd, nil
|
||||
}
|
||||
|
||||
// Writes an MPD object to a file on disk.
|
||||
// path - Output path to write the manifest to.
|
||||
func (m *MPD) WriteToFile(path string) error {
|
||||
// Open the file to write the XML to
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err = m.Write(f); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Writes an MPD object to a string.
|
||||
func (m *MPD) WriteToString() (string, error) {
|
||||
var b bytes.Buffer
|
||||
w := bufio.NewWriter(&b)
|
||||
err := m.Write(w)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = w.Flush()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
// Writes an MPD object to an io.Writer interface
|
||||
// w - Must implement the io.Writer interface.
|
||||
func (m *MPD) Write(w io.Writer) error {
|
||||
b, err := xml.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte(xml.Header))
|
||||
_, _ = w.Write(b)
|
||||
_, _ = w.Write([]byte("\n"))
|
||||
return nil
|
||||
}
|
||||
41
vendor/github.com/zencoder/go-dash/v3/mpd/pssh.go
generated
vendored
Normal file
41
vendor/github.com/zencoder/go-dash/v3/mpd/pssh.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package mpd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func MakePSSHBox(systemID, payload []byte) ([]byte, error) {
|
||||
if len(systemID) != 16 {
|
||||
return nil, fmt.Errorf("SystemID must be 16 bytes, was: %d", len(systemID))
|
||||
}
|
||||
|
||||
psshBuf := &bytes.Buffer{}
|
||||
size := uint32(12 + 16 + 4 + len(payload)) // 3 uint32s, systemID, "pssh" string and payload
|
||||
if err := binary.Write(psshBuf, binary.BigEndian, size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(psshBuf, binary.BigEndian, []byte("pssh")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(psshBuf, binary.BigEndian, uint32(0)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := psshBuf.Write(systemID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(psshBuf, binary.BigEndian, uint32(len(payload))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := psshBuf.Write(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return psshBuf.Bytes(), nil
|
||||
}
|
||||
47
vendor/github.com/zencoder/go-dash/v3/mpd/segment.go
generated
vendored
Normal file
47
vendor/github.com/zencoder/go-dash/v3/mpd/segment.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package mpd
|
||||
|
||||
type SegmentBase struct {
|
||||
Initialization *URL `xml:"Initialization,omitempty"`
|
||||
RepresentationIndex *URL `xml:"RepresentationIndex,omitempty"`
|
||||
Timescale *uint32 `xml:"timescale,attr,omitempty"`
|
||||
PresentationTimeOffset *uint64 `xml:"presentationTimeOffset,attr,omitempty"`
|
||||
IndexRange *string `xml:"indexRange,attr,omitempty"`
|
||||
IndexRangeExact *bool `xml:"indexRangeExact,attr,omitempty"`
|
||||
AvailabilityTimeOffset *float32 `xml:"availabilityTimeOffset,attr,omitempty"`
|
||||
AvailabilityTimeComplete *bool `xml:"availabilityTimeComplete,attr,omitempty"`
|
||||
}
|
||||
|
||||
type MultipleSegmentBase struct {
|
||||
SegmentBase
|
||||
SegmentTimeline *SegmentTimeline `xml:"SegmentTimeline,omitempty"`
|
||||
BitstreamSwitching *URL `xml:"BitstreamSwitching,omitempty"`
|
||||
Duration *uint32 `xml:"duration,attr,omitempty"`
|
||||
StartNumber *uint32 `xml:"startNumber,attr,omitempty"`
|
||||
}
|
||||
|
||||
type SegmentList struct {
|
||||
MultipleSegmentBase
|
||||
SegmentURLs []*SegmentURL `xml:"SegmentURL,omitempty"`
|
||||
}
|
||||
|
||||
type SegmentURL struct {
|
||||
Media *string `xml:"media,attr,omitempty"`
|
||||
MediaRange *string `xml:"mediaRange,attr,omitempty"`
|
||||
Index *string `xml:"index,attr,omitempty"`
|
||||
IndexRange *string `xml:"indexRange,attr,omitempty"`
|
||||
}
|
||||
|
||||
type SegmentTimeline struct {
|
||||
Segments []*SegmentTimelineSegment `xml:"S,omitempty"`
|
||||
}
|
||||
|
||||
type SegmentTimelineSegment struct {
|
||||
StartTime *uint64 `xml:"t,attr,omitempty"`
|
||||
Duration uint64 `xml:"d,attr"`
|
||||
RepeatCount *int `xml:"r,attr,omitempty"`
|
||||
}
|
||||
|
||||
type URL struct {
|
||||
SourceURL *string `xml:"sourceURL,attr,omitempty"`
|
||||
Range *string `xml:"range,attr,omitempty"`
|
||||
}
|
||||
9
vendor/github.com/zencoder/go-dash/v3/mpd/validate.go
generated
vendored
Normal file
9
vendor/github.com/zencoder/go-dash/v3/mpd/validate.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package mpd
|
||||
|
||||
// Validate checks for incomplete MPD object
|
||||
func (m *MPD) Validate() error {
|
||||
if m.Profiles == nil {
|
||||
return ErrNoDASHProfileSet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user