Dual Boot Clock Shenanigans

Probably the most annoying thing when dual booting Windows and Linux is the clock. For the various reasons, Windows keeps BIOS clock in local time-zone while Linux prefers it as UTC. While this is not a problem in Reykjavík, it surely is everywhere else.

There are ways to make Windows run in UTC but they either don’t work with the latest Windows 10 or they require time synchronization to be turned off. As I value precise time, a solution on Linux side was needed.

Fortunately, Linux does offer setting for just this case. Just run the following command:

sudo timedatectl set-local-rtc 1 --adjust-system-clock

This will tell Linux to keep local clock in RTC. While this is not necessarily fully supported, I found it’s actually the only setting that reliably works when dual booting Windows 10.

PS: You might need a reboot or two before this takes effect.

My Resolve Dashcam Workflow

As I moved to Resolve I was forced to change my Vegas Movie Studio dashcam processing workflow a bit. Not only you cannot use MP4 under Linux at all, but MP4 presents challenges to the free Resolve under Windows too.

The first step I take for all dashcam footage is to convert it using ffmpeg to DNxHR LB. Not only it’s a well-supported intermediary codec that increases performance significantly, but it also get’s rid of any nonsense my dashcam puts in the clip. And 36 Mbps is more than enough for anything my dashcam can throw at it. Instead of converting clip-by-clip, I opted to merge them all into a single file - that’s the reason behind weird syntax:

ls *.MP4 | awk '{print "file \x27" $1 "\x27"}' | ffmpeg \
    -f concat -safe 0 -protocol_whitelist pipe,file -i - \
    -c:v dnxhd -profile:v dnxhr_lb -q:v 1 -pix_fmt yuv422p -an \
    ^^dashcam.mov^^

Once all these videos are imported into Resolve, I go over them removing any clip portions when car is not moving. For any stops where state around car changes (e.g. waiting for traffic light), I use smooth cut to transition from one state to another. Other than that, I leave footage as is.

Once I’m done with editing I export the whole video into DNxHR SQ VBR. If I hadn’t done any editing, exporting to DNxHR LB would be fine as generational loss is quite acceptable. However, with all smooth cuts I’ve made, a temporary bump in video quality is beneficial. Especially since this is not the final output.

As I don’t expect to edit these clips again, the final output is H.264 as it’s size savings cannot be ignored. I usually use two-pass encoding with 6 Mbps average rate. You can use veryslow preset to increase quality at the cost of speed but improvement is minimal so I simply go with the default of medium:

ffmpeg -i ^^render.mov^^ \
   -c:v libx264 -pix_fmt yuv420p -b:v 6M \
   -an -y -pass 1 -f mp4 ^^render^^.mp4

ffmpeg -i ^^render.mov^^ \
   -c:v libx264 -pix_fmt yuv420p -b:v 6M \
   -an -y -pass 2 -f mp4 ^^render^^.mp4

rm ffmpeg2pass-0.log*

And that’s it - final video is similar enough in quality while not taking extreme amounts of disk space.

PS: I am not using H.265 at this time because I find it even more trouble to work with than H.264 is. I might think about it in the future as support for it increases.

Implementing Global Hotkey Support in QT under X11

High-level description of global hotkey support is easy enough:

bool registerHotkey(QKeySequence keySequence) {
  auto key = Qt::Key(keySequence[0] & static_cast<int>(~Qt::KeyboardModifierMask));
  auto modifiers = Qt::KeyboardModifiers(keySequence[0] & static_cast<int>(Qt::KeyboardModifierMask));

  return nativeRegisterHotkey(key, modifiers);
}

Essentially one has to split key sequence into a key and modifiers and get platform-specific code to do the actual work. For X11 this is a bit more involved and full of traps.

Inevitably, X11-specific code will have a section with conversion of key and modifiers into a X11-compatible values. For key value this has to be additionally converted from key symbols into 8-bit key codes:

bool Hotkey::nativeRegisterHotkey(Qt::Key key, Qt::KeyboardModifiers modifiers) {
  uint16_t modValue = 0;
  if (modifiers & Qt::AltModifier)     { modValue |= XCB_MOD_MASK_1; }
  if (modifiers & Qt::ControlModifier) { modValue |= XCB_MOD_MASK_CONTROL; }
  if (modifiers & Qt::ShiftModifier)   { modValue |= XCB_MOD_MASK_SHIFT; }

  KeySym keySymbol;
  if (((key >= Qt::Key_A) && (key <= Qt::Key_Z)) || ((key >= Qt::Key_0) && (key <= Qt::Key_9))) {
    keySymbol = key;
  } else if ((key >= Qt::Key_F1) && (key <= Qt::Key_F35)) {
    keySymbol = XK_F1 + (key - Qt::Key_F1);
  } else {
    return false; //unsupported key
  }
  xcb_keycode_t keyValue = XKeysymToKeycode(QX11Info::display(), keySymbol);

  xcb_connection_t* connection = QX11Info::connection();
  auto cookie = xcb_grab_key_checked(connection, 1,
                static_cast<xcb_window_t>(QX11Info::appRootWindow()),
                modValue, keyValue, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
  auto cookieError = xcb_request_check(connection, cookie);
  if (cookieError == nullptr) {
    return true;
  } else {
    free(cookieError);
    return false;
  }
}

With key code and modifier bitmask ready, a call to xcb_grab_key_checked will actually do the deed, followed by some boiler plate code for error detection.

At last, we can use event filter to actually capture the key press and emit activated signal:

bool Hotkey::nativeEventFilter(const QByteArray&, void* message, long*) {
  xcb_generic_event_t* e = static_cast<xcb_generic_event_t*>(message);
  if ((e->response_type & ~0x80) == XCB_KEY_PRESS) {
    emit activated();
    return true;
  }
  return false;
}

Mind you, this is a rather incomplete and simplified example. Full code (supporting both Windows and Linux) is available for download.

To use it, just assign instance to a long living variable, register a key sequence, and hook into activated signal:

_hotkey = new Hotkey();
_hotkey->registerHotkey(QKeySequence { "Ctrl+Shift+F1" });
connect(_hotkey, SIGNAL(activated()), this, SLOT(^^onActivated()^^));

PS: Windows variant of this code is available here.

Implementing Global Hotkey Support in QT on Windows

If your application needs a global hotkey support, QT will leave you hanging. Fortunately, on high-level this is a rather simple function:

bool registerHotkey(QKeySequence keySequence) {
  auto key = Qt::Key(keySequence[0] & static_cast<int>(~Qt::KeyboardModifierMask));
  auto modifiers = Qt::KeyboardModifiers(keySequence[0] & static_cast<int>(Qt::KeyboardModifierMask));

  return nativeRegisterHotkey(key, modifiers);
}

Essentially the only work is splitting key and modifiers into their own variables and then having platform-specific code handling the nasty bits.

For Windows, this is just a simple conversion of modifiers and keys followed by a call to RegisterHotKey API:

bool nativeRegisterHotkey(Qt::Key key, Qt::KeyboardModifiers modifiers) {
  uint modValue = 0;
  if (modifiers &  Qt::AltModifier) { modValue += MOD_ALT; }
  if (modifiers &  Qt::ControlModifier) { modValue += MOD_CONTROL; }
  if (modifiers &  Qt::ShiftModifier) { modValue += MOD_SHIFT; }

  uint keyValue;
  if (((key >= Qt::Key_A) && (key <= Qt::Key_Z)) || ((key >= Qt::Key_0) && (key <= Qt::Key_9))) {
    keyValue = key;
  } else if ((key >= Qt::Key_F1) && (key <= Qt::Key_F24)) {
    keyValue = VK_F1 + (key - Qt::Key_F1);
  } else {
    return false; //unsupported key
  }

  return RegisterHotKey(nullptr, _hotkeyId, modValue, keyValue);
}

But this alone is nothing without actual QAbstractNativeEventFilter-based event filter. Here we need to intercept message and emit the signal:

bool nativeEventFilter(const QByteArray&, void* message, long*) {
  MSG* msg = static_cast<MSG*>(message);
  if (msg->message == WM_HOTKEY) {
    if (msg->wParam == static_cast<WPARAM>(_hotkeyId)) {
      emit activated();
      return true;
    }
  }
  return false;
}

Mind you, this is a rather incomplete and simplified example. Full code (supporting both Windows and Linux) is available for download.

To use it, just assign instance to a long living variable, register a key sequence, and hook into activated signal:

_hotkey = new Hotkey();
_hotkey->registerHotkey(QKeySequence { "Ctrl+Shift+F1" });
connect(_hotkey, SIGNAL(activated()), this, SLOT(^^onActivated()^^));

PS: X11 variant of this code is available here.

Black Frames in Garmin Dashcam Video

Illustration

Editing video files produced by my Garmin dashcam is sometimes really annoying. Not only it splits every darn trip into 60 second chunks forcing you to edit bunch of small files, but it will also occasionally produce video with the first frame completely black. At least that’s how it looks in Vegas Movie Studio 15 and, while annoying, was easy enough to remove. In DaVinci Resolve these videos would have a few seconds worth of corrupted data and that’s a bigger problem.

As I’m moving most of my video editing to Resolve due to it’s cross-platform capabilities, I decided to figure it out. One way to see what’s going on with MP4 is by looking at video file in MediaInfo.

The first confusing thing was that MP4 contained only streams with ID 2 and 3. What happened to stream with ID 1 is anybody’s guess. The second source of confusion was that all streams combined amounted to smidgen over 60 MB. The whole video file was more than 70 MB. While one can expect MP4 container format to take some space, overhead is generally measured in KB - not MB.

However, both these things were present in both valid and invalid video file. It took going into Advanced mode to reveal more curiosities. At last it let me know where remaining 10 MB were - in the header. And more interestingly it has shown multiple stream size calculations for video stream. File that contained black frame had one of it’s six video stream sizes listed as 59.97 MB while fully working file had all video stream sizes set to 60.00 MB.

Either due to crappy encoder or bad coding, Garmin not only bloats header to unreasonable level but it can also miscalculate stream stream sizes. Because MP4 contains stream size data at multiple places, it was dependent on decoder whether error would be noticeable or not.

Knowing I am dealing with the corrupt container and seemingly correct stream (albeit one frame shorter), I decided to simply repackage MP4 without recompression using ffmpeg:

ffmpeg -i ^^input.mp4^^ -c:v copy -c:a copy ^^fixed.mp4^^

This copies both video and audio stream (irrelevant if there is no audio stream) into a new file. For normal videos this results in a direct stream copy. Videos where one frame was corrupted end up with 59.967 seconds worth of frames. Essentially the broken frame will be removed. And this repackaging solved the black frame issue for both Vegas Movie Studio and DaVinci Resolve.

Unfortunately, while DaVinci Resolve did recognize files now, exported result had a stutter. For some reason all these files were recognized as 15 fps. And no, this wasn’t due to stream copy as original videos were misidentified too. It took me a while to give up and ask the question about it on Blackmagic forum only to find out I stumbled upon a bug.

As a workaround before bug is resolved, I went onto converting the stream to DNxHD LB codec:

ffmpeg -i ^^input.mp4^^ -c:v dnxhd -profile:v dnxhr_lb -pix_fmt yuv420p -c:a copy ^^fixed.mov^^

Not only this also removes invalid frames but it also helps editing speed as DNxHD is much more CPU-friendly format.

Being too lazy to deal with these files on case-by-case basis, a bit of Bash magic to repackage multiple files comes in handy:

mkdir out
find . -type f -name '*.MP4' -exec \
    ffmpeg -i {} -c:v dnxhd -profile:v dnxhr_lb -q:v 1 -pix_fmt yuv422p -c:a copy \
    out/{}.mov \;