[bugfix] add support for media with rotation contained in stream side data (#3335)
* add support for media with embedded rotation data in stream side data list * *grumble grumble* linter
This commit is contained in:
parent
9c1dfd093d
commit
dc4059e9a2
|
@ -224,8 +224,11 @@ func ffprobe(ctx context.Context, filepath string) (*result, error) {
|
||||||
// Show specifically stream codec names, types, frame rate, duration, dimens, and pixel format.
|
// Show specifically stream codec names, types, frame rate, duration, dimens, and pixel format.
|
||||||
"stream=codec_name,codec_type,r_frame_rate,duration_ts,width,height,pix_fmt" + ":" +
|
"stream=codec_name,codec_type,r_frame_rate,duration_ts,width,height,pix_fmt" + ":" +
|
||||||
|
|
||||||
// Show orientation.
|
// Show orientation tag.
|
||||||
"tags=orientation",
|
"tags=orientation" + ":" +
|
||||||
|
|
||||||
|
// Show rotation data.
|
||||||
|
"side_data=rotation",
|
||||||
|
|
||||||
// Limit to reading the first
|
// Limit to reading the first
|
||||||
// 1s of data looking for "rotation"
|
// 1s of data looking for "rotation"
|
||||||
|
@ -490,7 +493,7 @@ func (res *ffprobeResult) Process() (*result, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check extra packet / frame information
|
// Check extra packet / frame information
|
||||||
// for provided orientation (not always set).
|
// for provided orientation (if provided).
|
||||||
for _, pf := range res.PacketsAndFrames {
|
for _, pf := range res.PacketsAndFrames {
|
||||||
|
|
||||||
// Ensure frame contains tags.
|
// Ensure frame contains tags.
|
||||||
|
@ -498,23 +501,24 @@ func (res *ffprobeResult) Process() (*result, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure orientation not
|
|
||||||
// already been specified.
|
|
||||||
if r.orientation != 0 {
|
|
||||||
return nil, errors.New("multiple sets of orientation data")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim any space from orientation value.
|
// Trim any space from orientation value.
|
||||||
str := strings.TrimSpace(pf.Tags.Orientation)
|
str := strings.TrimSpace(pf.Tags.Orientation)
|
||||||
|
|
||||||
// Parse as integer value.
|
// Parse as integer value.
|
||||||
i, _ := strconv.Atoi(str)
|
orient, _ := strconv.Atoi(str)
|
||||||
if i < 0 || i >= 9 {
|
if orient < 0 || orient >= 9 {
|
||||||
return nil, errors.New("invalid orientation data")
|
return nil, errors.New("invalid orientation data")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set orientation.
|
// Ensure different value has
|
||||||
r.orientation = i
|
// not already been specified.
|
||||||
|
if r.orientation != 0 &&
|
||||||
|
orient != r.orientation {
|
||||||
|
return nil, errors.New("multiple sets of orientation / rotation data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new orientation.
|
||||||
|
r.orientation = orient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preallocate streams to max possible lengths.
|
// Preallocate streams to max possible lengths.
|
||||||
|
@ -554,6 +558,57 @@ func (res *ffprobeResult) Process() (*result, error) {
|
||||||
framerate = float32(num / den)
|
framerate = float32(num / den)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for embedded sidedata
|
||||||
|
// which may contain rotation data.
|
||||||
|
for _, d := range s.SideDataList {
|
||||||
|
|
||||||
|
// Ensure frame side
|
||||||
|
// data IS rotation data.
|
||||||
|
if d.Rotation == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any decimal
|
||||||
|
// rotation value.
|
||||||
|
rot := int(d.Rotation)
|
||||||
|
|
||||||
|
// Round rotation to multiple of 90.
|
||||||
|
// More granularity is not needed.
|
||||||
|
if q := rot % 90; q > 45 {
|
||||||
|
rot += (90 - q)
|
||||||
|
} else {
|
||||||
|
rot -= q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any value above 360
|
||||||
|
// or below -360, these are
|
||||||
|
// just repeat full turns.
|
||||||
|
//
|
||||||
|
// Then convert to
|
||||||
|
// orientation value.
|
||||||
|
var orient int
|
||||||
|
switch rot % 360 {
|
||||||
|
case 0:
|
||||||
|
orient = orientationNormal
|
||||||
|
case 90, -270:
|
||||||
|
orient = orientationRotate90
|
||||||
|
case 180:
|
||||||
|
orient = orientationRotate180
|
||||||
|
case 270, -90:
|
||||||
|
orient = orientationRotate270
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure different value has
|
||||||
|
// not already been specified.
|
||||||
|
if r.orientation != 0 &&
|
||||||
|
orient != r.orientation {
|
||||||
|
return nil, errors.New("multiple sets of orientation / rotation data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new orientation.
|
||||||
|
r.orientation = orient
|
||||||
|
}
|
||||||
|
|
||||||
// Append video stream data to result.
|
// Append video stream data to result.
|
||||||
r.video = append(r.video, videoStream{
|
r.video = append(r.video, videoStream{
|
||||||
stream: stream{codec: s.CodecName},
|
stream: stream{codec: s.CodecName},
|
||||||
|
@ -580,6 +635,7 @@ type ffprobeResult struct {
|
||||||
type ffprobePacketOrFrame struct {
|
type ffprobePacketOrFrame struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Tags ffprobeTags `json:"tags"`
|
Tags ffprobeTags `json:"tags"`
|
||||||
|
// SideDataList []ffprobeSideData `json:"side_data_list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ffprobeTags struct {
|
type ffprobeTags struct {
|
||||||
|
@ -594,6 +650,11 @@ type ffprobeStream struct {
|
||||||
DurationTS uint `json:"duration_ts"`
|
DurationTS uint `json:"duration_ts"`
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
|
SideDataList []ffprobeSideData `json:"side_data_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ffprobeSideData struct {
|
||||||
|
Rotation float64 `json:"rotation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ffprobeFormat struct {
|
type ffprobeFormat struct {
|
||||||
|
|
Loading…
Reference in New Issue