unwarpvr is a command-line ffmpeg-based video filter that reverses the distortion and chromatic abberation introduced by the Oculus Rift software to compensate for lens distortion. This allows you to record videos during normal play (see recording instructions), then unwarp them for the purpose of viewing on your monitor or sharing online:
On Windows, you do not need to install anything else beforehand, just click on the GREEN BUTTON at the bottom of this page. This release supports only videos either generated for DK1 with SDK version 0.2.5c, or for DK1/DK2 with the latest Oculus SDK version 0.4.2 (although you can try it on other videos if you wish). On Linux, you will need the libx264 and libjansson libraries (Ubuntu: libjansson4 libx264-142).
New in this version: forward warping, DK1 support (with SDKs 0.2.5c and 0.4.2), DK1<->DK2 video conversion, Linux support, allow use of multiple unwarpvr filters
Sample command lines
Basic DK2 example
Cropped side-by-side view, no scaling:
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
- -i dk2.mp4 : specifies the input file
- -vf unwarpvr : applies the unwarpvr video filter
- 1920:1080 : the desired output resolution
- -c:a copy : use the "copy" audio codec which preserves the original audio stream
- -c:v libx264 : use the H.264 video encoder (same codec used by YouTube)
- -crf 18 : video encoding quality (smaller numbers = higher quality, bigger file)
- -pix_fmt yuv420p : saves file size and ensures compatibilty with some video editors like Adobe Premiere Pro
- out.mp4 : output filename
If you receive an error that your Oculus user profile can't be found/parsed, or you changed your eye relief dial setting since recording, you must manually specify your original eye relief dial setting at time of recording (0-10, 10 is farthest out):
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080:eye_relief_dial=3 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
Testing on samples
If you want to test your settings by rendering just the first frame of your video, do this:
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080 -ss 0 -vframes 1 -f image2 out.png
You can also test on the first few seconds of your video using "-t":
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080 -t 10 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
More DK2 examples
Left/right eyes swapped for cross-eyed viewing:
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080:swap_eyes=1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
Height stretched by a factor of 2 for full compatibility with YouTube 3D mode (see how to upload with YouTube 3D enabled) and some 3D video players:
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1920:1080:scale_height=2 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
To get the full uncropped original render targets including fuzzy borders, use the render target resolution (2364x1461 for default Unity apps, adjusted slightly to make the height even):
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=2364:1462 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
To render a 720p mono view (there's no point going to 1080p with a standard DK2 video), use this:
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1280:720:left_eye_only=1:scale_width=1.2:scale_height=1.2 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
Forward warping
This tool can also take undistorted images/video and warp it for viewing on an HMD. To use this, specify forward_warp=1 and the desired ppd (pixels per degree) at the center of the image/video (this affects how close you will appear to be sitting to the screen):
ffmpeg-unwarpvr -i unwarped.mp4 -vf unwarpvr=1920:1080:forward_warp=1:ppd=15 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
The above command is for 3D side-by-side (SBS) inputs. If the input is vertically stretched, as most 3BS 3D films are, use "scale_in_height=2":
ffmpeg-unwarpvr -i unwarped.mp4 -vf unwarpvr=1920:1080:forward_warp=1:ppd=15:scale_in_height=2 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
If your input has the left/right eyes swapped, use "swap_eyes=1":
ffmpeg-unwarpvr -i unwarped.mp4 -vf unwarpvr=1920:1080:forward_warp=1:ppd=15:swap_eyes=1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
If your input is not 3D SBS, use "mono_input=1":
ffmpeg-unwarpvr -i unwarped.mp4 -vf unwarpvr=1920:1080:forward_warp=1:ppd=15:mono_input=1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
To output for DK1, use "device=RiftDK1":
ffmpeg-unwarpvr -i unwarped.mp4 -vf unwarpvr=1920:1080:forward_warp=1:ppd=15:device=RiftDK1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk1.mp4
Note the image/video will not have headtracking - it is recommended when possible to use a VR media viewing application instead.
Notes: If your input video is anamorphic, you may need to specify "-aspect 16:9" (or "-aspect 16:10" for DK1) before the output video. Top/bottom 3D is not currently supported. Forward warping automatically scales to achieve the requested output resolution, even if it does not match the device resolution. One special use of forward warping is converting video between HMD devices. See "Converting videos between DK1 and DK2" below.
DK1 examples
Cropped side-by-side view, no scaling:
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1280:720:device=RiftDK1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
DK1 apps can run at a variety of resolutions and aspect ratios. Any of these will work without modifying the command line. If you recorded at a higher resolution, you may wish to also output at a higher resolution using the scale parameters:
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1920:1080:device=RiftDK1:scale_width=1.5:scale_height=1.5 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=2560:1440:device=RiftDK1:scale_width=2:scale_height=2 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
If your recording is cropped from the original, you will need to use the scale_in parameters. For example, if you cropped from 1920x1200 to 1920x1080, you should specify a scale_in_height of 1200/1080 = 1.111:
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1920:1080:device=RiftDK1:scale_width=1.5:scale_height=1.5:scale_in_height=1.111 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
For compatibility with YouTube 3D, multiply scale_height by 2, for example:
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1920:1080:device=RiftDK1:scale_width=1.5:scale_height=3.0 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
By increasing the output resolution you can get the full uncropped render target. The horizontal resolution will need to be somewhat higher than the original render target to make room for the black stripe in the middle. For Unity applications the following sizes work for 1280x800 and 1920x1200 recordings:
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1900:1026:device=RiftDK1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=2850:1538:device=RiftDK1:scale_width=1.5:scale_height=1.5 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
To render a mono view, use one of the following. There's no point going over 640x360 for a 1280x800 recording or 1280x720 for a 1920x1200 recording.
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=640:360:device=RiftDK1:left_eye_only=1 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=854:480:device=RiftDK1:left_eye_only=1:scale_width=1.25:scale_height=1.25 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1280:720:device=RiftDK1:left_eye_only=1:scale_width=1.85:scale_height=1.85 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk1.mp4 -vf unwarpvr=1920:1080:device=RiftDK1:left_eye_only=1:scale_width=2.8:scale_height=2.8 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
Converting videos between DK1 and DK2
Converting between two devices requires two steps: first an unwarp to get the original undistorted render target, then a forward warp to the target device. To get an appropriate field of view, the forward warp must be supplied with the pixels-per-degree (PPD) of the source device. DK1's PPD is 6.84 and DK2's is 10.34.
Here is a typical example converting from a DK1 recording to DK2 in a single ffmpeg command:
ffmpeg-unwarpvr -i dk1.mp4 -vf "unwarpvr=1900:1026:device=RiftDK1 , unwarpvr=1920:1080:forward_warp=1:device=RiftDK2:ppd=6.84" -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
Converting from DK2 to DK1 is similar:
ffmpeg-unwarpvr -i dk2.mp4 -vf "unwarpvr=2364:1462:device=RiftDK2 , unwarpvr=1280:800:forward_warp=1:device=RiftDK1:ppd=10.34" -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk1.mp4
For a higher quality result (particularly with high-resolution recordings), you can scale up the intermediate render target by multiplying the resolution and scale of the first transform by a value, and also multiplying the ppd of the second transform by the same value. Here we use the value 2 with the DK1 to DK2 conversion above:
ffmpeg-unwarpvr -i dk1.mp4 -vf "unwarpvr=3800:2052:device=RiftDK1:scale_width=2:scale_height=2 , unwarpvr=1920:1080:forward_warp=1:device=RiftDK2:ppd=13.68" -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
We can further improve the quality by using supersampling in the second transform:
ffmpeg-unwarpvr -i dk1.mp4 -vf "unwarpvr=3800:2052:device=RiftDK1:scale_width=2:scale_height=2 , unwarpvr=3840:2160:forward_warp=1:device=RiftDK2:ppd=13.68 , scale=1920:1080" -sws_flags lanczos+accurate_rnd+full_chroma_int -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk2.mp4
And here is a high-quality version with supersampling of DK2 to DK1 conversion:
ffmpeg-unwarpvr -i dk2.mp4 -vf "unwarpvr=4728:2924:device=RiftDK2:scale_width=2:scale_height=2 , unwarpvr=2560:1600:forward_warp=1:device=RiftDK1:ppd=20.68 , scale=1280:800" -sws_flags lanczos+accurate_rnd+full_chroma_int -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out_dk1.mp4
Downscaling and antialiasing
If you want a reduced-resolution version, which will render faster and be smaller and quicker to upload, just lower the resolution and proportionally lower the scale parameters (e.g. 1280/1920 = 0.6667):
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=1280:720:scale_width=0.6667:scale_height=0.6667 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
ffmpeg-unwarpvr -i dk2.mp4 -vf unwarpvr=854:480:scale_width=0.4448:scale_height=0.4444 -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
To add antialiasing by supersampling, conversely, you can increase the output resolution and proportionally increase the scaling factors (e.g. both by a factor of 2), then add a scale filter to lower the resolution afterwards:
ffmpeg-unwarpvr -i dk2.mp4 -sws_flags lanczos+accurate_rnd+full_chroma_int -vf "unwarpvr=3840:2160:scale_width=2.0:scale_height=2.0 , scale=1920:1080" -c:a copy -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
The sws_flags options improve the quality of downscaling.
Options reference
- Output resolution: You must always specify output resolution. If the output resolution is smaller than the render target (2364x1461 for Unity apps), the center of each eye view will be cropped out. If the output resolution is larger than the render target, each eye view will be centered against a black background.
- left_eye_only=1: Renders only left eye. (preserves aspect ratio, does not stretch single eye view)
- swap_eyes=1: Swaps left/right eye views, or if left_eye_only is given, does right eye only instead.
- scale_width, scale_height: Scales width/height of output so it no longer matches original render target resolution (1.0 for no scaling, < 1.0 to shrink, > 1.0 to stretch).
- scale_in_width, scale_in_height: Scales input down by the given factor before processing it (1.0 for no scaling, < 1.0 to stretch, > 1.0 to shrink).
- forward_warp=1: Warp an undistorted video to a distorted one for a specific device, rather than unwarping.
- ppd: When forward warping, specifies pixels-per-degree at center of screen. If omitted, ppd will be assumed to match that of the target device.
- device: Specifies the device that the input recording was intended to be viewed on (or for forward rendering, the device the output video is intended to be viewed on). Current options: RiftDK1, RiftDK2.
- sdkversion: Specifies version of SDK used by application that generated the input recording (or for forward rendering, which version to emulate). Available versions depend on device. Current options: 0.2.5c, 0.4.2.
- mono_input=1: Specifies that the input includes only one eye view, which should be used to create both eye views in the output.
- eye_relief_dial: Setting of eye relief dial at time of recording (0-10, 10 is farthest out). By default this will be set from the current setting of your locally installed Oculus Runtime default user.
Technical/performance notes
Build: This release is a custom build of the ffmpeg command-line video editing tool based on the latest master version of ffmpeg, but with the new filter added. It is a static Windows binary.
Eye relief effect: Eye relief only affects chromatic abberation, not distortion, with the Oculus SDK 0.4.2. Even then the effect is minor and hard to see. Eye relief should affect distortion of DK1 with SDK version 0.4.2, but this is not yet implemented.
Why yuv420p? By default the filter outputs in yuv444p format. The yuv420p pixel format option is needed for Premiere Pro compatibility but not for YouTube - however it does improve filesize and YouTube uses this pixel format anyway, so there's no reason not to always use it.
Performance: The current implementation is CPU-based (multithreaded over frames), and its performance is directly based on the number of pixels being rendered in the output, so as resolution increases rendering time will as well. Initialization time is slower for unwarping than forward warping. On my machine (Intel Core i7 4770K, overclocked to 4.0 GHz) 1080p videos for DK2 unwarp at about 27 frames per second (FPS), close to real-time. Others report rates closer to 10 FPS. A GPU-based OpenCL implementation to dramatically improve performance is planned.
