Unknownpgr

Implementing Live Webcam Streaming over HTTP

2020-08-11 04:03:18 | English, Korean

This post was translated from Korean into English by AI.

While participating in a project recently, I needed to implement live streaming. The goal was to receive video from a camera and make it viewable in a web browser. After searching around on Google, I found that there was no suitable solution that implemented this cleanly. So I decided to build it myself.

The biggest problem with video, especially live streaming, is figuring out how to receive the video in the first place and how to stream it. Fortunately, I had done something similar a few times before, so I knew of two solutions.

  1. Use OpenCV or a similar tool to convert the camera feed into images and send them to the client. The client displays the images by continuously updating them. In reality, then, it is not a video but merely a series of rapidly changing images.
  2. Use FFmpeg.

At first glance, solution 1 may seem rather crude. In practice, however, it provides quite decent video quality and FPS, is easy to implement (there is no need to struggle with encoding), and is particularly useful when the video needs to be processed before it is sent. Its drawback is that it cannot transmit audio. So this time, I decided to implement solution 2.

What I Did Before

When I used FFmpeg in the past, I did not know much about protocols capable of transmitting video in real time. As a result, I had to take a fairly low-level approach, using the following method.

  1. Use FFmpeg to convert the webcam feed to WebM, pipe the output stream to stdout, and receive it in the Node.js server.
  2. The Node.js server stores the received stream in a buffer.
  3. Once the buffer is full, it is placed in a queue.
  4. If a client requests the video, the server takes a buffer from the queue and sends it to the client.
  5. The client uses the HTML5 Media Source API to append the buffer to a video tag.

According to the commit history, I implemented this in February 2019, so it was just before I graduated from high school. Looking back now, it is a rather inefficient implementation. The architecture cannot even serve a stream to two people at the same time.

What I Did This Time

This time, instead of doing it that way, I decided to make full use of FFmpeg's features. The code is so simple that you may understand it more easily by looking directly at the source code.

Below is the exact FFmpeg command run by Node.js. If FFmpeg is installed, you can enter it directly in a terminal and it will work.

ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -crf 23 -pix_fmt yuv420p -hls_time 2 -hls_list_size 5 -hls_delete_threshold 1 -hls_flags delete_segments -f hls public/video.m3u8

Let us go through the arguments one by one. They are also explained in the source code.

Of the options above, -hls_list_size, -hls_delete_threshold, and -hls_flags delete_segments form a set. The explanation is a little complicated: as mentioned above, -hls_flags delete_segments deletes old segments, while -hls_delete_threshold specifies how many segments not included in the .m3u8 file should still be considered not old.

For example, suppose that hls_list_size is 7 and hls_delete_threshold is 3. The .m3u8 file would then contain information about seven segments in total, from n through n+6: n, n+1, n+2...n+6. Because hls_delete_threshold is 3, segments n-1, n-2, and n-3 are not considered old; segments starting with n-4 are. Consequently, there will always be ten .ts files.

The hls_delete_threshold option is necessary because, even though a particular segment has been removed from the segment list in the .m3u8 file, a client may still be downloading that segment because of its internet speed or some other issue.

With these options, the files are updated in real time, and the client can play the live video just as it would play a regular HLS file. Below is a screenshot.

screenshot

You can stream and watch video captured by a webcam in HTML. (I removed the URL because it was using my personal server.)

References


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -