files: Ham Share's player — Auto quality, any by hand, or the file; 360 view; Transcode device setting
Every video plays YouTube's way: hls.js reads the connection and picks the quality, or one is picked by hand, or Original plays the file as it is. The qualities (a ladder by size, H.264) are made on the fly in 3 s pieces from wherever the viewer seeks, one ffmpeg per play, paused when far enough ahead. 360 videos open in a look-around view (drag, pinch or scroll to zoom from 8 to 300 degrees, phone motion). Settings -> Transcode device picks the machine and device that makes them, for every machine's files: a machine transcoding another's file reads it through the hub by a token for that one file. Replaces the codec/size picker and the single-quality re-encoder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f5f1cd79ba
commit
fc4126a0ed
@@ -25,6 +25,7 @@ type Track struct {
|
||||
ColorTransfer string `json:"color_transfer,omitempty"` // smpte2084 / arib-std-b67 mean HDR
|
||||
Channels int `json:"channels,omitempty"`
|
||||
AttachedPic bool `json:"attached_pic,omitempty"`
|
||||
Spherical bool `json:"spherical,omitempty"` // an equirectangular 360 picture
|
||||
}
|
||||
|
||||
// Info is what a file holds: the picture to take, the sound to go with it, and the whole.
|
||||
@@ -58,6 +59,10 @@ type probeJSON struct {
|
||||
Disposition struct {
|
||||
AttachedPic int `json:"attached_pic"`
|
||||
} `json:"disposition"`
|
||||
SideData []struct {
|
||||
Type string `json:"side_data_type"`
|
||||
Projection string `json:"projection"`
|
||||
} `json:"side_data_list"`
|
||||
} `json:"streams"`
|
||||
Format struct {
|
||||
FormatName string `json:"format_name"`
|
||||
@@ -74,7 +79,7 @@ func Inspect(ctx context.Context, c Caps, path string, root bool) (Info, error)
|
||||
return Info{}, errors.New("ffprobe is not installed")
|
||||
}
|
||||
argv := []string{c.FFprobe, "-v", "error", "-show_entries",
|
||||
"stream=index,codec_type,codec_name,profile,pix_fmt,width,height,r_frame_rate,avg_frame_rate,color_transfer,channels,disposition:format=format_name,duration,bit_rate,size",
|
||||
"stream=index,codec_type,codec_name,profile,pix_fmt,width,height,r_frame_rate,avg_frame_rate,color_transfer,channels,disposition:stream_side_data=side_data_type,projection:format=format_name,duration,bit_rate,size",
|
||||
"-of", "json", path}
|
||||
if root {
|
||||
argv = priv.Wrap(argv...)
|
||||
@@ -105,6 +110,15 @@ func Inspect(ctx context.Context, c Caps, path string, root bool) (Info, error)
|
||||
for _, s := range pj.Streams {
|
||||
st := &Track{Index: s.Index, Type: s.CodecType, Codec: s.CodecName, Profile: s.Profile, PixFmt: s.PixFmt, Width: s.Width, Height: s.Height,
|
||||
ColorTransfer: s.ColorTransfer, Channels: s.Channels, AttachedPic: s.Disposition.AttachedPic != 0}
|
||||
// A 360 video says so in its spherical tag (ffmpeg reads the old XML one and the sv3d
|
||||
// box); an untagged one is still taken for 360 when it is 2:1 and too big for anything
|
||||
// else.
|
||||
for _, sd := range s.SideData {
|
||||
if sd.Type == "Spherical Mapping" && strings.HasPrefix(sd.Projection, "equirectangular") {
|
||||
st.Spherical = true
|
||||
}
|
||||
}
|
||||
st.Spherical = st.Spherical || (s.CodecType == "video" && s.Width == 2*s.Height && s.Width >= 3840)
|
||||
st.FPS = fps(s.AvgFrameRate)
|
||||
if st.FPS == 0 {
|
||||
st.FPS = fps(s.RFrameRate)
|
||||
|
||||
Reference in New Issue
Block a user