After some holiday time off I returned to a work project that uses Angular, started it up, and got this error.
An unhandled exception occurred: listen EACCES: permission denied 127.0.0.1:443
I’ve seen this error before, but did not immediately realized what caused it. It took me a few minutes to recall the source of the problem. This error occurs when there is another application that is already using the port that Angular is trying to open. In my case, it was a VMWare service that was occupying the port. I stopped the service and my project started up. If it happened to you, how would you know what process is using the port?
On Windows, you can list which processes are using which port with the following command
netstat -aon
You’ll get a full list of ports, addresses, and process IDs.
If you wanted to filter those results, you can pass the output through “findstr” using a port number as the string to filter by.
C:\Users\Joel>netstat -aon | findstr 443
TCP 0.0.0.0:443 0.0.0.0:0 LISTENING 38880
TCP 0.0.0.0:44367 0.0.0.0:0 LISTENING 4
TCP 192.168.1.81:49166 72.21.81.200:443 TIME_WAIT 0
TCP 192.168.1.81:49169 64.233.177.101:443 TIME_WAIT 0
TCP 192.168.1.81:49206 13.249.111.97:443 ESTABLISHED 24324
TCP 192.168.1.81:49209 52.167.253.237:443 ESTABLISHED 1996
TCP 192.168.1.81:49220 52.184.216.246:443 ESTABLISHED 37976
TCP 192.168.1.81:49222 168.62.57.154:443 ESTABLISHED 24324
TCP 192.168.1.81:49224 52.114.74.45:443 ESTABLISHED 13304
TCP 192.168.1.81:49227 52.113.194.132:443 ESTABLISHED 10376
TCP 192.168.1.81:49228 184.24.37.85:443 ESTABLISHED 27828
TCP 192.168.1.81:49231 13.92.225.245:443 ESTABLISHED 27828
TCP 192.168.1.81:49233 140.82.113.3:443 ESTABLISHED 24324
TCP 192.168.1.81:49234 20.190.133.75:443 ESTABLISHED 39168
TCP 192.168.1.81:49236 204.79.197.203:443 ESTABLISHED 27828
TCP 192.168.1.81:49238 52.96.104.18:443 ESTABLISHED 12440
TCP 192.168.1.81:49239 52.96.104.18:443 ESTABLISHED 12440
You will be more interested in matches from the left column, since that is the port number being used on your machine. Right now, I can see that on my machine the process occupying port 443 is process 38,880. Great, I have a process number. But what can I do with it. There is another command named “tasklist” that list processes names and their process ID. Combined with findstr, I can get the name of the process using the specific port.
C:\Users\Joel>tasklist | findstr 38880
vmware-hostd.exe 38880 Services 0 32,084 K
I’ve got a range of media that I’m moving from its original storage to hard drives. Among this media are some DVDs that I’ve collected over time. It took a while, but I managed to convert the collection of movies and TV shows to video files on my hard drive. Now that they are converted, I wanted to build a solution for browsing and playing them. I tried using a drive with DLNA built in, but the DLNA clients I have appear to have been built with a smaller collection of videos in mind. They present an alphabetical list of the video files. Not the way I want to navigate.
I decided to instead make my own solution. To start though, I wanted to make a solution that would stream a file video file. Unlike most HTML resources, which are relatively small, video files can be several gigabytes. Rather than have the web server present the file in its entirety I need for the web server to present the file in chunks. My starting point is a simple NodeJS project that is presenting HTML pages through Express.
With the above application, static content any files that are put in the folder named “public” will be served when requested. In that folder, the stylesheet, JavaScript, HTML, and other static content will be placed. The videos will be in another folder that is not part of the project. The path to this folder is specified by the setting VIDEO_ROOT in the .env file.
For this to stream files, there are two additional routes that I am going to add. One route will return a list of all of the video IDs. The other route will return the video itself.
For this first iteration for video streaming, I’m going to return file names as video IDs. At some point during the development of my solution this may change. But for testing streaming the file name is sufficient. The route handler for the library will get a list of the files and return it in a structure that is marked with a date. The files it returns are filtered to only include those with an .mp4 extension.
The video element in an HTML page will download a video in chunks (if the server supports range headers). The video element sends a request with a header stating the byte range being requested. In the response, the header will also state the byte range that is being sent. Our express application must read the range headers and parse out the range being request. The range header will contain a starting byte offset and may or may not contain an ending byte offset. The value in the content range may look something like the following.
byte=0-270 byte=500-
In the first example, there is a starting and ending byte range. In the second, the request only specifies a starting byte. It is up to the server to decide how many bytes to send. This header is easily parsed with a couple of String.split operations and integer parsing.
function getByteRange(rangeHeader) {
var byteRangeString = rangeHeader.split('=')[1];
byteParts = byteRangeString.split('-');
var range = [];
range.push(Number.parseInt(byteParts[0]));
if(byteParts[1].length == 0 ) {
range.push(null);
} else {
range.push(Number.parseInt(byteParts[1]))
}
return range;
}
There is the possibility that the second number in the range is not there, or is present but is outside of the range of bytes for the file. To handle this, there’s a default chunk size defined that will be used when the byte range is not specified. But the range is also checked against the file size and clamped to ensure that there is no attempt to read past the end of the file.
const CHUNK_SIZE = 2 ** 18;
//...
var start = range[0];
if(range[1]==null)
range[1] = Math.min(fileSize, start+CHUNK_SIZE);
var end = range[1] ;
end = Math.min(end, fileSize);
In the response, the header contains a header defining the range of bytes in the response and it’s length. We build out those headers, set them on the response header, and then write the range of bytes. To write out the bytes, a read stream from the video file and piped to the response stream.
The server can now serve video files for streaming. For the client side, some HTML and JavaScript is needed. The HTML contains a video element and a <div/> element that will be populated with a list of the videos.
The JavaScript will request a list of the videos from the /library route. For each video file, it will create a text element containing the name of the video. Clicking on the text will set the src element on the video.
function start() {
fetch('/library')
.then(data=>data.json())
.then(data=> {
console.log(data);
var elementRoot = $('#videoBrowser');
data.fileList.forEach(x=>{
var videoElement = $(`<div>${x}</div>`);
$(elementRoot).append(videoElement);
$(videoElement).click(()=>{
var videoURL = `/video/${x}`;
console.log(videoURL);
$('#videoPlayer').attr('src', videoURL );
})
});
});
}
$(document).ready(start());
Almost done! The only thing missing is adding these routes to the source of app.js. As it stands now, app.js will only serve static HTML file.
I started the application (npm start) and at first, I thought that the application was not working. The problem was in the encoding of the first MP4 file that I tried. There are a range of different video encoding options that one can use for MP4 files. Looking at the encoding properties of two MP4 files (one file streamed successfully, the other did not) there was no obvious difference at first.
The problem was with metadata stored in the file. A discussion of video encodings is a topic that could be several posts of its own. But the short explanation is that we need to ensure that the metadata is at the begining of the file. We can use ffmpeg to write a new file. Unlike the process of re-encoding a file, for this process the video data is untouched. I used the tool on a movie and it completed within a few seconds.