Skip to content

Commit

Permalink
[bugfix] add support for media with rotation contained in stream side…
Browse files Browse the repository at this point in the history
… data (#3335)

* add support for media with embedded rotation data in stream side data list

* *grumble grumble* linter
  • Loading branch information
NyaaaWhatsUpDoc authored Sep 23, 2024
1 parent 9c1dfd0 commit dc4059e
Showing 1 changed file with 81 additions and 20 deletions.
101 changes: 81 additions & 20 deletions internal/media/ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"stream=codec_name,codec_type,r_frame_rate,duration_ts,width,height,pix_fmt" + ":" +

// Show orientation.
"tags=orientation",
// Show orientation tag.
"tags=orientation" + ":" +

// Show rotation data.
"side_data=rotation",

// Limit to reading the first
// 1s of data looking for "rotation"
Expand Down Expand Up @@ -490,31 +493,32 @@ func (res *ffprobeResult) Process() (*result, error) {
}

// Check extra packet / frame information
// for provided orientation (not always set).
// for provided orientation (if provided).
for _, pf := range res.PacketsAndFrames {

// Ensure frame contains tags.
if pf.Tags.Orientation == "" {
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.
str := strings.TrimSpace(pf.Tags.Orientation)

// Parse as integer value.
i, _ := strconv.Atoi(str)
if i < 0 || i >= 9 {
orient, _ := strconv.Atoi(str)
if orient < 0 || orient >= 9 {
return nil, errors.New("invalid orientation data")
}

// Set orientation.
r.orientation = i
// 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
}

// Preallocate streams to max possible lengths.
Expand Down Expand Up @@ -554,6 +558,57 @@ func (res *ffprobeResult) Process() (*result, error) {
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.
r.video = append(r.video, videoStream{
stream: stream{codec: s.CodecName},
Expand All @@ -580,20 +635,26 @@ type ffprobeResult struct {
type ffprobePacketOrFrame struct {
Type string `json:"type"`
Tags ffprobeTags `json:"tags"`
// SideDataList []ffprobeSideData `json:"side_data_list"`
}

type ffprobeTags struct {
Orientation string `json:"orientation"`
}

type ffprobeStream struct {
CodecName string `json:"codec_name"`
CodecType string `json:"codec_type"`
PixFmt string `json:"pix_fmt"`
RFrameRate string `json:"r_frame_rate"`
DurationTS uint `json:"duration_ts"`
Width int `json:"width"`
Height int `json:"height"`
CodecName string `json:"codec_name"`
CodecType string `json:"codec_type"`
PixFmt string `json:"pix_fmt"`
RFrameRate string `json:"r_frame_rate"`
DurationTS uint `json:"duration_ts"`
Width int `json:"width"`
Height int `json:"height"`
SideDataList []ffprobeSideData `json:"side_data_list"`
}

type ffprobeSideData struct {
Rotation float64 `json:"rotation"`
}

type ffprobeFormat struct {
Expand Down

0 comments on commit dc4059e

Please sign in to comment.