Using Apple File System (APFS) with your virtualized Mac

Apple has just released the macOS High Sierra with new features, one of them is the brand-new Apple File System (APFS) that is optimized for flash storage which newer Macs enjoy. If you happen to be using macOS in a virtualized way, e.g. with VMware, you may have trouble getting the new OS to work as the upgrade forces conversion of the boot partition to APFS which the VMware UEFI does not support.

To solve the problem, we need to let the VMware UEFI know APFS and luckily the APFS driver can be extracted from the High Sierra installer as a UEFI driver executable. We can then slip the driver to the UEFI BIOS that bundles with VMware Player itself and everything should work.

Getting Started

We’ll need 3 things before modifying the VMware UEFI BIOS. They are listed below:

To simplify things, you can download my modified UEFI BIOS (tested on VMware Workstation Pro 14, may work for other versions too). If that ROM doesn’t work for you, go after these steps to get a modified BIOS with APFS support.

Use UEFITool to open EFI64.rom located at [VMware Installation Folder]/x64/, select File > Search and choose GUID tab. Type in 961578FE-B6B7-44C3-AF35-6BC705CD2B1F and double click the result inside Message section. Leave this screen for now.

Extract the FFS tool to the same directory as the APFS driver file. Open your command prompt, change directory to that place and run this command: GenMod apfs.efi .

 

Go back to UEFITool, right-click the selected item and choose Insert After, then select apfs.ffs from the FFS directory. The screen should look like this.

 

Save the modified ROM with the name efi64_apfs.rom to your VM directory.

Applying the new UEFI BIOS

To get the modified UEFI BIOS to work, use a text editor to open the VMX file. Ensure the file contains the following lines.

firmware = "efi"
efi64.filename = "efi64_apfs.rom"

Save the VMX file and start your VM, your macOS High Sierra will now boot as expected with an APFS volume. Voila!

Installing NVENC SDK and CUDA SDK on Ubuntu 14.04

After I set up my streaming server, there are some problems brought by the design. Using CPU to process the streams will consume lots of CPU cycles and if the streaming server have lots of connections, resource to handle them will run low if the machine itself does not have strong CPUs. NVIDIA’s NVENC is a way of offload the transcoding to GPUs that is dedicated to such processing and leaves much more CPU cycles for other purposes. However, installing NVIDIA’s driver is a nightmare, which is why I decided to write it down for future reference.

# Fetch system updates (if it is a fresh install)
apt-get install update && apt-get install upgrade

# Install required packages for NVIDIA driver installation
apt-get install build-essential linux-source linux-headers-3.13.0-68-generic linux linux-image-extra-virtual -y

# Get NVIDIA CUDA SDK (this is v7.5, for the latest version please visit NVIDIA's site)
wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/cuda-repo-ubuntu1404_7.5-18_amd64.deb
dpkg -i cuda-repo-ubuntu1404_7.5-18_amd64.deb

# Install CUDA SDK (which includes the NVIDIA graphics driver)
apt-get update
apt-get install cuda -y

# Check whether NVIDIA kernel module is loaded
modprobe nvidia

# If the above fails, try installing NVIDIA graphics drive seperately (this is drier version 352.63, for the latest one go check NVIDIA's website)
wget http://us.download.nvidia.com/XFree86/Linux-x86_64/352.63/NVIDIA-Linux-x86_64-352.63.run
chmod +x NVIDIA-Linux-x86_64-352.63.run

# Run the installer. Accept the EULA and it will ask you whether to overwrite the previously installed driver, choose continue. The installation here should complete.
./NVIDIA-Linux-x86_64-352.63.run

# Check whether NVIDIA kernel module is loaded
modprobe nvidia

# If all things go well here, this command will show detailed information about your GPU
nvidia-smi

Then the below installs NVENC SDK’s header into your system.

# Fetch NVENC SDK
wget http://developer.download.nvidia.com/compute/nvenc/v5.0/nvenc_5.0.1_sdk.zip

# Uncompress the zip and copy the headers to /usr/local/include/
unzip -q nvenc_5.0.1_sdk.zip
mv nvenc_5.0.1_sdk/Samples/common/inc/*.h /usr/local/include/

You can then now compile programs that uses NVIDIA’s NVENC to speed up video processing, including ffmpeg.

Setting Up Adaptive Streaming with Nginx

Recently, I’m working out a system to smoothly stream live events for an organization. That is pretty new to me and, after a bunch of research, found that Nginx with the RTMP module seems to be a good choice. There are many difficulties when setting all this up and after several days of testing, I found a good setting that is worth a post.

Setup Nginx and RTMP module

First, let’s get Nginx set up. In order to use the RTMP module, we need to compile that as an Nginx module. It would look something like this:

# Installing requirements in Ubuntu/Debian
apt-get install git gcc make libaio1 libpcre3-dev openssl libssl-dev ffmpeg -y

# Installing the same thing in RHEL/CentOS
yum install git gcc make libaio libaio-devel openssl libssl-devel pcre-devel ffmpeg -y

# Download nginx and nginx-rtmp-module
wget http://nginx.org/download/nginx-1.9.4.tar.gz
git clone https://github.com/arut/nginx-rtmp-module.git

# Compile nginx with nginx-rtmp and libaio
tar zxvf nginx-1.9.4.tar.gz

./configure --prefix=/usr/local/nginx --with-file-aio --add-module=/path/to/nginx-rtmp/
make
make install

# Link nginx
ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

nginx # Start Nginx
nginx -s stop # Stop Nginx

After all things are done, check whether nginx is compiled properly.

Capture

If you can see that Nginx RTMP is included, you can go to the next step. Before we proceed to configuring Nginx for live streaming, we should confirm what kind of resolution we should provide for live streams and how much hardware power you have.

Prerequisites

For converting live streams into several streams for adaptive streaming, you need to make sure your server have enough CPU for such workload. Otherwise, the live stream will suffer from continuous delays and/or server becomes unresponsive. I have spawn some EC2 c3.large and c3.xlarge instances, test with them and I found out their optimized CPU can handle such workload with ease. Something that also worth mention is about the I/O limits of the disks. If possible, store the HLS fragments generated to an high-speed SSD helps maintain smooth streaming experiences.

ec2CPU Usage when using an EC2 c3.xlarge instance.

Then, you also need to think about what kind of resolutions you will be offering for adaptive streaming. Generally about 4-5 variants are good enough to provide great loading speeds for different network speeds and devices. Here’s my recommended list of variants used for live streaming:

  1. 240p Low Definition stream at 288kbps
  2. 480p Standard Definition stream at 448kbps
  3. 540p Standard Definition stream at 1152kbps
  4. 720p High Definition stream at 2048kbps
  5. Source resolution, source bitrate

Configuring nginx for live streaming

Here is my own nginx.conf with comments that you can have references on.

worker_processes  auto;
events {
    # Allows up to 1024 connections, can be adjusted
    worker_connections  1024;
}

# RTMP configuration
rtmp {
    server {
        listen 1935; # Listen on standard RTMP port
        chunk_size 4000; 
        
        # This application is to accept incoming stream
        application live {
            live on; # Allows live input
            
            # Once receive stream, transcode for adaptive streaming
            # This single ffmpeg command takes the input and transforms
            # the source into 4 different streams with different bitrate
            # and quality. P.S. The scaling done here respects the aspect
            # ratio of the input.
            exec ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1
                        -c:v libx264 -c:a libvo_aacenc -b:v 256k -b:a 32k -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_low
                        -c:v libx264 -c:a libvo_aacenc -b:v 768k -b:a 96k -vf "scale=720:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_mid
                        -c:v libx264 -c:a libvo_aacenc -b:v 1024k -b:a 128k -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_high
                        -c:v libx264 -c:a libvo_aacenc -b:v 1920k -b:a 128k -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_hd720
                        -c copy -f flv rtmp://localhost/show/$name_src;
        }
        
        # This application is for splitting the stream into HLS fragments
        application show {
            live on; # Allows live input from above
            hls on; # Enable HTTP Live Streaming
            
            # Pointing this to an SSD is better as this involves lots of IO
            hls_path /mnt/hls/;
            
            # Instruct clients to adjust resolution according to bandwidth
            hls_variant _low BANDWIDTH=288000; # Low bitrate, sub-SD resolution
            hls_variant _mid BANDWIDTH=448000; # Medium bitrate, SD resolution
            hls_variant _high BANDWIDTH=1152000; # High bitrate, higher-than-SD resolution
            hls_variant _hd720 BANDWIDTH=2048000; # High bitrate, HD 720p resolution
            hls_variant _src BANDWIDTH=4096000; # Source bitrate, source resolution
        }
    }
}

http {
    # See http://licson.net/post/optimizing-nginx-for-large-file-delivery/ for more detail
    # This optimizes the server for HLS fragment delivery
    sendfile off;
    tcp_nopush on;
    aio on;
    directio 512;
    
    # HTTP server required to serve the player and HLS fragments
    server {
        listen 80;
        
        location / {
            root /path/to/web_player/;
        }
        
        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
            }
            
            root /mnt/;
            add_header Cache-Control no-cache; # Prevent caching of HLS fragments
            add_header Access-Control-Allow-Origin *; # Allow web player to access our playlist
        }
    }
}
Then, configure your live encoder to use these settings to stream into the server:
  • RTMP Endpoint: rtmp://yourserver/live/
  • RTMP Stream Name: [Whatever name you like]
Finally, configure your player for live playback. The HLS URL would look like this:
http://yourserver/hls/[The stream name above].m3u8

Recommended encoder settings for live events

If you can adjust the encoder, the following settings can help to gain better experiences.

  • Full HD Resolution (1920×1080) is recommended
  • H.264 Main profile, with target bitrate of 4000Kbps, maximum 6000Kbps
  • 25fps, 2 second keyframe interval
  • AAC audio at 128Kbps, 44.1kHz sample rate

And that’s all! I hope you can enjoy doing live events with these techniques.

Auto updates is out with WordPress 3.7!

You know, almost 20% of all websites are created with WordPress. Many of them have security risks as their version of WordPress is not updated and old versions usually will have lots of vulnerabilities and make them easy to be hacked in. Cases of WordPress sites getting hacked is getting more serious today.

Finally, in the WordPress 3.7 update, it introduces a new feature that is quite attractive to me and solve the problem of using old versions of the WordPress core. Now, the WordPress system will check for core updates, plugins updates and themes updates & automatically install them when they have new versions.

For me, this feature has a great potential that I don’t need to check and install updates as often as what I’ve done in this blog. Also for blogs & sites that don’t need to update so often, this feature reduces the work to keep up the site.

However, as I tried, I can’t see any controls to fine tune the behaviour of this great auto update feature as some people may not want auto updates or they want to control how it should behave.

Overall, I’ve waited this feature to be available in WordPress for a long time and it has come true!