Recursive AI

Get video rotation in C#

All onemedia recipes

A phone records portrait video as landscape frames plus a rotation that tells the player to turn them. media.Rotation reads that rotation from the MP4 or MOV track header as 0, 90, 180 or 270 degrees clockwise, and the frame-size fact gives the width and height as displayed. To have a rotated file you can share, the setup line below makes one from MDN’s CC0 flower clip with FFmpeg (6.0 or later), the way a phone held upright marks its video: the same frames, a 90° display matrix.

Terminal
dotnet new console -o Rotation && cd Rotation
dotnet add package RecursiveAI.OneMedia --version 0.1.0-preview.1
curl -L -o flower.mp4 https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4
ffmpeg -v error -display_rotation -90 -i flower.mp4 -c copy portrait.mp4

Put this in Program.cs and run dotnet run:

C#
using RecursiveAI.OneMedia;
using RecursiveAI.OneMedia.Metadata;
 
foreach (string path in new[] { "flower.mp4", "portrait.mp4" })
{
    using MediaFile media = Media.Open(path);
 
    // Clockwise degrees to turn the picture for display, and a mirror flag.
    VideoOrientation turn = media.Rotation.Value;
    FrameSize size = media.Metadata.Facts.FrameSize.Value;
 
    string shape = size.DisplayHeight > size.DisplayWidth ? "portrait" : "landscape";
    Console.WriteLine($"{path}: rotate {turn.RotationDegrees}° clockwise" +
                      $"{(turn.MirrorHorizontal ? ", mirrored" : "")}");
    Console.WriteLine($"  stored {size.CodedWidth}x{size.CodedHeight}," +
                      $" shown {size.DisplayWidth}x{size.DisplayHeight} ({shape})");
    Console.WriteLine($"  from {media.Rotation.Best?.Source}" +
                      $" {media.Rotation.Best?.RawValue}");
}
Text
flower.mp4: rotate 0° clockwise
  stored 960x540, shown 960x540 (landscape)
  from tkhd.matrix [00010000 00000000 00000000 00010000]
portrait.mp4: rotate 90° clockwise
  stored 960x540, shown 540x960 (portrait)
  from tkhd.matrix [00000000 00010000 FFFF0000 00000000]

The rotation comes from the matrix in the first video track’s header (tkhd): its four unit entries are a rotation by a multiple of 90°, possibly mirrored. RawValue shows them as stored. The same number is on the track as media.Video.Video.RotationDegrees, and the onemedia info tool prints it as rotate 90.

Clockwise or counterclockwise

OneMedia counts clockwise. FFmpeg counts counterclockwise: -display_rotation -90 above asks for 90° clockwise, and ffprobe reports the file as -90:

Terminal
ffprobe -v error -select_streams v -show_entries stream_side_data=rotation -of csv=p=0 portrait.mp4
Text
-90

So a file ffprobe calls 90 is 270 here. Both describe the same matrix.

Limits

  • No decoding, no rotating. OneMedia reads the rotation; it doesn’t turn the frames or rewrite the file.
  • MP4 family only. The rotation is read from the MP4, MOV and 3GP track matrix. 0.1 reads none from Matroska or WebM: there media.Rotation has no candidate, and Value is the default, 0.
  • Quarter turns. Only the eight matrices of 0, 90, 180 or 270°, mirrored or not, give a candidate; a scaled or skewed matrix gives none. A vertical flip reads as 180° and mirrored.

For the other basic facts of a video, see Get video duration, resolution and codec.