Author: Jiang Minxibei chat front-end development engineer this article is published at the same time in personal blog

preface

On the homepage of our company’s official website — baychat official website, the first screen has a background video that plays automatically, which has been criticized for slow video loading and playing card. At first, I thought the file was too big or the Internet speed was too slow, but when I tried to optimize it, I found it was not as easy as EXPECTED. This paper records the optimization process and experience summary, hoping to be helpful to readers.

The status quo

The homepage of the official website is composed of six screens. The first screen is mainly a background video that plays automatically in a loop. If the page is not cached:

  • It takes several seconds for the video picture to appear, during which only the background color of the page can be seen. Moreover, after the first frame appears, the picture will remain stuck for a long time, sometimes even more than 10 seconds. This situation is referred to as the first frame card in the following paragraphs
  • The first frame of the screen after the card is not smooth playback, the experience is like a slideshow

A preliminary study

To optimize the video lag, I started with the file size of the video. Download MediaInfo to view the video file:

  • Format: mp4
  • Resolution: 1080P
  • Rate: 4 k
  • Size: 7.3 M
  • Duration: 15 seconds

The 4K bit rate is very high for online video, and I used the video compression tool Format Factory to adjust it to an acceptable 2400 bit rate for the file size of 4.4m. Since the file size has been reduced to 60% of its original size, there should be a significant optimization effect.

Weird first frame

Packing up the test environment, I got a sharp drop in my glasses: the video stuck a little less than before, but it was still noticeably stagnant, and the first frame of the video barely improved. It seems that reducing file size by reducing bitrate is a drop in the ocean.

explore

Realizing that a simple method of reducing the size of the resource file was not working, I searched the Internet for a solution, but found few articles related to it and did not find the reason for the first frame card. Unable to find a solution, I had to fumble with it, and I began to wonder if splitting the video into multiple portions and allowing the browser to play the first one loaded would alleviate the first frame card problem.

Videos are loaded in blocks

I cut the original video into two segments, and listened to the ended event of the video tag. After the first segment, I changed the SRC to switch to the second segment, and then switched back to the first segment, and the process was looped.

This improved the problem of the first frame card, but the side effect was that the switch between frames was not seamless, and each switch would be stuck for about a second.

reflection

I finally gave up the idea of blocking the video. On the one hand, the length of the video is only 15 seconds, so the significance of the blocks is not great. On the other hand, I don’t think it would be the best solution, even if it could be done seamlessly, because it doesn’t solve the fundamental problem (why the video is on the first frame card).

Moov position causes the first frame card? Break the legend

As to why the first frame loading was slow, I started to suspect that it had something to do with the MP4 format. I did a little searching, and there were several articles that mentioned mooV:

Although MP4 supports streaming, the “index” of the video is stored in the MOOV object, and the video will be played only after the MOOV downloads. Most MP4 files put the MOOV at the top of the file, but if you put the MOOV at the bottom, you need to download the entire file before you can play it. Reference blog.csdn.net/jinshelj/ar… .

I looked at the compressed MP4 file and the MOOv was indeed at the end. So I used Qt-FastStart (ffMPEg-based MOOV preprocessor) to preprocess the MOOV object. However, after my test, I found that the mooV in front did not optimize the problem of the first frame card, and the playback performance was the same as that in the tail. And when the file MOOv is in the tail, the video starts to play before the file is downloaded. There is no such thing as the video can be played after the file is downloaded.

So I’ll check data, finally found out the reason: if the server itself is to support the seek, mp4 video also can normal edge to down play, reference segmentfault.com/a/119000001…

Since mooV didn’t cause the first frame card, what did?

More suitable for network streaming format – FLV

So far the first frame card problem has not been solved, so I decided to try another video format, so is there a better video format than MP4 for online playback?

I did a lot of research on this, and FLV is a very simple, naturally streaming format, very suitable for network streaming. If the “index” of an MP4 video is stored as a whole, the “index” of an FLV is stored in segments. Take a simple example: at the beginning of a video, MP4 needs to download the entire “index” of the video to start playing, while FLV only needs to download the “index” of the beginning.

Browsers do not have native support for FLV decoding, it is generally done through Flash. However, flv.js, an open source plugin from Bilibili, enables the video tag to support FLV playback. To see if FLV could improve the first frame card, I introduced FLV.js and converted the original video to FLV format. Flv.js is compressed to only 100KB+, it is very convenient to use, the code example is as follows:

const flvjs = require('.. /fiv.js');
if (flvjs.isSupported()) {
    var videoElement = $bgVideo[0];
    var flvPlayer = flvjs.createPlayer({
        type: 'flv', url: SRC // video address}); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); }Copy the code

I tested it, and I was pleasantly surprised. The problem with the first frame card has been resolved, but the playback is still not smooth and the experience is like a slideshow. While the problem is not completely resolved and flv.js is not compatible with browsers below IE10, at least substantial progress has been made. Then just focus on the problem of the video not playing smoothly.

Find out who stole the Internet speed

The problem is relatively simple, either because the download is too slow or the file is too large. The file is already compressed, so you just need to figure out why the download is slow.

At this point, I started to focus on the static resources of the page to see if I could optimize some of the resources that take up a lot of money. Static resources have been compressed by the tools at release time and there is no room for further compression. My idea is to try to have the resources visible on the first screen load immediately, while the resources off the first screen load lazily. When I open the developer tools in my browser, I find that most of the resource files are very small. There is only one very large file, which is up to 900+KB. When I open it, I find that it is a GIF and not in the first screen. This is the time to think about how to load the image after the video has been downloaded. The basic idea is to dynamically add the picture tag to the page after the video is downloaded.

I looked up native events on the video TAB. Among the many events, suspend is more suitable for the current scene, which is triggered when the video download is complete. Suspend, however, is not compatible and cannot be triggered on lower versions of browsers such as IE 9. The Progress event does not have this problem. The progress event is triggered during the video download. If a video download takes 10 seconds, progress will be triggered every second during those 10 seconds.

The progress event is monitored and the timer is set to determine whether the video is loaded, so as to achieve the purpose of delaying the loading of THE GIF. The specific code is as follows:

/** Listen for event progress and set the timer. Each time the progress event is triggered, the previous timer is cleared. If progress does not fire within a second, the download is considered complete and the callback is triggered and the binding is removed. * * / / /$bgVideo[0] is the dom node of the video$bgVideo[0].function() {
	$bgImg.removeClass('hiden');
});

functionAfterDownload (video, callback) {var callbackTimer = null; // The progress event callback function. Callback var progressCallBack = is not executed until progress is not triggered for one secondfunction() { clearTimeout(callbackTimer); // Set a timer of 1 second. After the binding is triggered, delete the timer callbackTimer =setTimeout(function() {
			callback();
			video.removeEventListener('progress', progressCallBack); }, 1000); }; // Bind the event video.addeventListener ('progress', progressCallBack);
}
Copy the code

In the process of debugging, the above code has a little problem. The Progress event sometimes does not fire if the video has been cached, and the same is true of the Suspend event. My guess is that the video was downloaded too quickly and the addEventListener was finished before it even executed. I tried to write the binding to the event in HTML:

<video onprogress="progressCallBack". />Copy the code

After doing this, pressing F5 repeatedly to refresh during local debugging would not cause problems, but publishing to the server would occasionally cause problems, and the progress event would not be triggered. In the end, I chose a simple, crude approach and set a 3-second timer during page initialization:

function afterDownload(video, callback) {
	var callbackTimer = null;
	var progressCallBack = function() {... }; // Prevent Chrome from triggering progress callbackTimer = when cachedsetTimeout(function() {
		callback();
		clearTimeout(callbackTimer);
		video.removeEventListener('progress', progressCallBack);
	}, 3000);
	
	video.addEventListener('progress', progressCallBack);
}
Copy the code

If the progress event is not triggered within 3 seconds after the page is entered, the video is considered cached and the callback is executed directly and the binding and timer are deleted. The problem was solved.

While this approach can solve the problem, I don’t think the implementation is perfect. If you have a better method ☺, leave a comment below

Easy to use and free video compression tool – small ball toolbox

After the above optimization, the video playback effect of the home page has been much better, but there are still occasional cases of slow. Although the format factory has been used to compress the video, but considering that there are many other video compression tools on the market, then go to Baidu to find more, found a good word of mouth tool, called “small ball toolbox”.

CRF (Const Quality, fixed Quality), this bit rate control is very excellent, so that it can be suppressed without 2pass, that is, even 1pass can achieve very good bit rate allocation utilization. Other encoders also have quality mode suppression (such as XVID or ERP to suppress RMVB), but as far as I know, it is only “Const Quantization” to THE CRF of X264 to allocate the bit rate more reasonably based on the human visual psychology on the basis of Quantization. The goal is to make the quality of the video as uniform as possible while using the bit rate as efficiently as possible. CRF mode also has the advantage that many people do not know how much bit rate should be pressed to the video when pressing. CRF is to allocate the bit rate according to the need, so in fact to save the agony of how much bit rate.

An introductory tutorial for the pellet kit is attached.

Finally, I tried different CRF values using the pellet toolbox. After pressing and comparing with the naked eye, I found a balance between the image quality and the file size. I managed to get the video down to 3.4m at 1080P resolution. I tried to lower the resolution again and found that the difference in quality was not significant when the resolution was reduced to 720P. Finally, the compression parameter of resolution 720p and CRF23 was selected. At this time, the video was compressed to 2M, which was extremely thin compared to the 7.3m at the beginning.

conclusion

So far, the video can be presented quickly and play smoothly. At the same time, whether using MP4 format, or FLV format, the first frame card problems have not existed. For this case, I tested Chrome’s speed limit feature, and the first frame card problem for MP4 videos only occurred when the Internet speed was low (either too slow or the video file was too large). Since we’ve made the video size small enough and delayed loading the larger images, the difference between FLV and MP4 is negligible. Eventually I took down flv.js and used mp4 files for playback.

The end of the

This article records my entire optimization process for the first screen and some experience gained from it. The process is bumpy and bumpy, hoping to help readers take less detours. At the same time, I think optimization is endless, especially my knowledge of video compression is relatively weak, if there is something wrong in the article, or good suggestions, please give me your comments.

supplement

After enthusiastic netizens’ feedback, I found that my understanding of MOOV was a little biased, and there were two problems when the first screen video was played on Chrome:

  • An MP4 file is requested three times
  • Sometimes the GIF will start loading before the video is finished (failed to listen for progress event)

In IE9 and Firefox, these two problems can not be repeated, because I mainly use Firefox during the development, so the above two problems are omitted. I searched for relevant information and compared three MP4 files with different structure order, and now I will share the results with you:

Attached mp4 file structure diagram with MOOV rear, MOOV front without Meta, MOOV front with Meta

The mooV. udta.meta contains the metadata of the video. If there is no MOOv. udta.meta, then three requests are required. In the testing process, I found that there is no MOOv.udta. meta, but the moov pre-file needs to be requested three times in Chrome, while only one time in Firefox. I did not find relevant information about this situation, and temporarily think that different browsers adopt different strategies.

Since the file with moov and no meta after moov was suppressed by using small pellet toolbox, the file with Qt-faststart only preposes MOOv but does not generate Moov. udta. Meta, so three requests appear on Chrome and affect the triggering of progress event. So that leads to the above two problems. Mp4-movflags faststart -acodec copy – vCOdec copy output.mp4) These two problems disappear.

Reference:

Optimizing MP4 Video for Fast Streaming

Understanding the MPEG-4 movie atom | Adobe Developer Connection

Detailed description of MP4 file format – Structure overview

Media file format analysis MP4

Using FFMPEG to modify the MP4 file header information, so that it supports streaming loading and playing