mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-17 05:41:21 +08:00
Add support rotate and scale to MP4 stream
This commit is contained in:
@@ -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)
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user