Add support rotate and scale to MP4 stream

This commit is contained in:
Alexey Khit
2023-07-06 10:55:58 +03:00
parent e8a7ba056c
commit ea82d7ec2b
3 changed files with 82 additions and 2 deletions

View File

@@ -1,15 +1,16 @@
package mp4 package mp4
import ( import (
"github.com/AlexxIT/go2rtc/internal/api/ws"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/internal/api/ws"
"github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/app"
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/mp4" "github.com/AlexxIT/go2rtc/pkg/mp4"
"github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/tcp"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@@ -153,6 +154,16 @@ func handlerMP4(w http.ResponseWriter, r *http.Request) {
header.Set("Content-Disposition", `attachment; filename="`+filename+`"`) header.Set("Content-Disposition", `attachment; filename="`+filename+`"`)
} }
if rotate := query.Get("rotate"); rotate != "" {
mp4.PatchVideoRotate(data, core.Atoi(rotate))
}
if scale := query.Get("scale"); scale != "" {
if sx, sy, ok := strings.Cut(scale, ":"); ok {
mp4.PatchVideoScale(data, core.Atoi(sx), core.Atoi(sy))
}
}
if _, err = w.Write(data); err != nil { if _, err = w.Write(data); err != nil {
log.Error().Err(err).Caller().Send() log.Error().Err(err).Caller().Send()
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@@ -41,6 +41,11 @@ func (m *Movie) WriteVideo(codec string, width, height uint16, conf []byte) {
m.Write(conf) m.Write(conf)
m.EndAtom() // AVCC m.EndAtom() // AVCC
m.StartAtom("pasp") // Pixel Aspect Ratio
m.WriteUint32(1) // hSpacing
m.WriteUint32(1) // vSpacing
m.EndAtom()
m.EndAtom() // AVC1 m.EndAtom() // AVC1
} }

View File

@@ -1,8 +1,11 @@
package mp4 package mp4
import ( import (
"github.com/AlexxIT/go2rtc/pkg/core" "bytes"
"encoding/binary"
"strings" "strings"
"github.com/AlexxIT/go2rtc/pkg/core"
) )
// ParseQuery - like usual parse, but with mp4 param handler // ParseQuery - like usual parse, but with mp4 param handler
@@ -99,6 +102,67 @@ func ParseCodecs(codecs string, parseAudio bool) (medias []*core.Media) {
return return
} }
// PatchVideoRotate - update video track transformation matrix.
// Rotation supported by many players and browsers (except Safari).
// Scale has low support and better not to use it.
// Supported only 0, 90, 180, 270 degrees.
func PatchVideoRotate(init []byte, degrees int) bool {
// search video atom
i := bytes.Index(init, []byte("vide"))
if i < 0 {
return false
}
// seek to video matrix position
i -= 4 + 3 + 1 + 8 + 32 + 8 + 4 + 4 + 4*9
// Rotation matrix:
// [ cos sin 0]
// [ -sin cos 0]
// [ 0 0 16384]
var cos, sin uint16
switch degrees {
case 0:
cos = 1
sin = 0
case 90:
cos = 0
sin = 1
case 180:
cos = 0xFFFF // -1
sin = 0
case 270:
cos = 0
sin = 0xFFFF // -1
default:
return false
}
binary.BigEndian.PutUint16(init[i:], cos)
binary.BigEndian.PutUint16(init[i+4:], sin)
binary.BigEndian.PutUint16(init[i+12:], -sin)
binary.BigEndian.PutUint16(init[i+16:], cos)
return true
}
// PatchVideoScale - update "Pixel Aspect Ratio" atom.
// Supported by many players and browsers (except Firefox).
// Supported only positive integers.
func PatchVideoScale(init []byte, scaleX, scaleY int) bool {
// search video atom
i := bytes.Index(init, []byte("pasp"))
if i < 0 {
return false
}
binary.BigEndian.PutUint32(init[i+4:], uint32(scaleX))
binary.BigEndian.PutUint32(init[i+8:], uint32(scaleY))
return true
}
const ( const (
stateNone byte = iota stateNone byte = iota
stateInit stateInit