On Black Friday last year, I got a pretty nice deal on an ultrawide HDR10 supported monitor by LG... so I bought two of them. One for my office at work, and one for home. It's definitely not a monitor used for mastering content, but it's a nice way to get into the world of HDR content creation. So, what can we do?
Well, as it turns out, there isn't a real way to record HDR at the moment via software (EDIT: Ok, maybe "Action!" can do it. I'll test that later). Content creators out there resort to using high quality capture cards to capture HDR10 gameplay. But why is there no software solution? Well, there is... sorta. But you have to get creative with it and manually do everything. For me, this is fine. I prefer being in full control in post. Let's experiment.
Extracting HDR Metadata from the monitor
Before mastering can be done on this monitor, we need to extract Colour Primaries and Display Luminance values from the monitor. Windows doesn't make this very obvious, but you can run dxdiag.exe
via the command line and extract it from there. This can be done via:
dxdiag.exe -t diagnostic.txt
Next, open up diagnostic.txt
and search for "Display Luminance". If you have multiple monitors, this will appear multiple times. You can easily tell which is which by looking at nearby lines like "HDR Support", "Current Mode" (Screen Resolution), etc.
Generating HDR metadata string for FFmpeg
Now we have exactly what we need to embed HDR metadata into a final video file if gameplay is recorded onto this monitor. However, FFmpeg requires it to be in a specific format. Specifically this:
Color Primaries: Red(0.651367,0.332031), Green(0.306641,0.630859), Blue(0.150391,0.059570), White Point(0.313477,0.329102)
Display Luminance: Min Luminance = 0.010000, Max Luminance = 1499.000000, MaxFullFrameLuminance = 799.000000
will become this:
G(15332,31543)B(7520,2978)R(32568,16602)WP(15674,16455)L(14990000,100)
Yikes!! What math is required to get those numbers? Well, don't worry about it. Instead, I wrote a nice little tool to generate the string for you with the dxdiag information. It's in my render_tools
GitHub repo here. If you want to run this online (where you can put your monitor information in), here's a cpp.sh link.
Recording gameplay
We'll get back to FFmpeg in a bit. For now, we need footage to encode. I use Dxtory to record my gameplays. It is the closest a software recorder gets to capturing the original footage correctly. For video codec, I use UtVideo for "lossless" capture (more on this later). I use the default settings on it. For audio, I have a 7.1 setup, and a lot of games out there utilise all 8 channels (Modern Warfare supports up to 16). Dxtory can capture this losslessly as well via IEEE Float. Set it and you're good to go.
Here's my configuration. You don't need to understand the language. The UI is the same in English.
Now just go into a game and record some test footage. You'll get something that looks like this:
Does something look a little off to you? Maybe the footage is a little washed out? This is normal. In fact, it's the raw stream that is being sent to your monitor. The only problem is that it's lacking the HDR metadata, hence why it looks this way. Gee, don't we have that information? Yes we do. Let's embed it via FFmpeg.
Encoding via FFmpeg
Now for the fun part. Let's generate a MKV file straight from the AVI master with the HDR metadata embedded. For reference, this is all open source and available in my render_tools
repo on GitHub here. If you don't want to hear an explanation, just scroll to the end of this section. I post the final command there that you can modify for your monitor.
This is not an FFmpeg tutorial. But for those who don't know how FFmpeg works, it's a command line swiss-army knife for media streams like video and audio. It supports basically every format I've thrown at it. If you need an AVI file converted to MP4, consider it done. WAV to MP3? Done. In a basic sense, it looks like this:
UNIX> ffmpeg -i video.avi video.mp4
For archival of gameplay, I use the MKV container and encode videos via libx265 at CRF 16 with FLAC audio. YouTube accepts these. For that, you'll want a command that looks like this:
UNIX> ffmpeg -i video.avi -c:v libx265 -pix_fmt yuv420p10le -crf 16 -c:a flac video.mkv
This command has a problem... it doesn't embed the HDR metadata. Recall that string we generated earlier? Now is the time to use it. That FFmpeg command is about to get very lengthy.
First, the HDR10 standard requires BT.2020 with a bit depth of 10. So we need to make sure the video outputs proper BT.2020 instead of BT.709. For this, we'll need the following arguments added to the ffmpeg command (put it after -pix_fmt yuv420p10le
):
-vf scale=out_color_matrix=bt2020:out_h_chr_pos=0:out_v_chr_pos=0,format=yuv420p10
Finally, we'll embed the HDR metadata extracted at the very beginning of this blog post. FFmpeg can pass parameters to the internal x265 encoder via -x265-params
. We'll pass in the metadata there. Add this in the FFmpeg command after the previous addition that converted the colour space to BT.2020:
-x265-params "colorprim=bt2020:colormatrix=bt2020nc:transfer=smpte2084:colormatrix=bt2020nc:hdr=1:info=1:repeat-headers=1:max-cll=0,0:master-display=G(15332,31543)B(7520,2978)R(32568,16602)WP(15674,16455)L(14990000,100)"
If you're lost, here is the final command:
UNIX> ffmpeg -i video.avi -c:v libx265 -pix_fmt yuv420p10le -vf scale=out_color_matrix=bt2020:out_h_chr_pos=0:out_v_chr_pos=0,format=yuv420p10 -x265-params "colorprim=bt2020:colormatrix=bt2020nc:transfer=smpte2084:colormatrix=bt2020nc:hdr=1:info=1:repeat-headers=1:max-cll=0,0:master-display=G(15332,31543)B(7520,2978)R(32568,16602)WP(15674,16455)L(14990000,100)" -crf 16 -c:a flac video.mkv
Quite lengthy huh? I warned you. However, let's look at the MKV file it exported on the HDR display. Because HDR screenshots can't be displayed properly on an SDR monitor, this was converted from HDR10 back into SDR. Definitely not a perfect conversion, but you can see the difference.
Only one problem...
This method of capturing and encoding HDR10 video via Dxtory is not perfect. Far from it. Dxtory reads the screen as an 8 bit buffer. Meanwhile, the output bit depth the monitor gives is 10 bit. So we're already losing a lot of detail, going from 1024 levels of colour per channel (red, green, and blue) down to 256. For scenes in-game where there's lots of colour variety, the trained eye is going to see banding of colours. This is why I encourage recording to UtVideo over something like x264vfw. We're losing a lot of detail. No need to lose even more. For a way to visually see this, here's a diagram for 8 bit going to 4 bit and back:
Obviously, with 10 bit going to 8 bit and back won't have much of a dramatic impact unlike the one shown above. But it's still a loss of colour information. If there's a software recorder out there that can capture the screen buffer in 10 bit, that'll solve this. Until then, the only proper solution I can think of is to use a dedicated capture card that was made to capture HDR signals. But until then, it's a valid solution to recording HDR video with software and not a dedicated capture card. I'll take it.
Example Clip
Here is a test clip I recorded from when I unlocked a gold Model 680 shotgun in Modern Warfare. It was encoded with the very command shown above. YouTube accepts HDR10 videos and will generate SDR versions for people without HDR displays. It also allows surround sound (It stores a 5.1 stream that plays on TVs). If you have an iPhone X (or more recent), or a Samsung Galaxy S8/Note 8 (or more recent), those have HDR certified displays. It does make a difference.