Saturday, August 1, 2020

Streaming Media using Http Live Streaming (HLS)


Streaming media is a process in which media is played at the same time as media is downloaded. 

Streaming can be made either from a file or directly from the devices. 

Examples of streaming from file include shows and content distribution from a recorded file. Examples of streaming from devices include creating a live feed from your cameras such as using Facebook or youtube app on your phone or more professionally, the high-end recording camera that is used in broadcasting sports, news events, etc. 

Streaming over the internet is commonly done using HTTP Live Streaming, commonly known as (HLS). Another alternative is to use MPEG-DASH or Smooth Streaming which are not subject of this post. In this article, I will go over a basic example of how to make a prototype HLS setup using commonly available tools and techniques from a general purpose computer using systems, namely Windows 10 and macOS Catalina.

Architecture


HLS uses a web server to distribute the media content. The media content is segemented into a multiple chunks or more commonly segments and an index file, often called playlist. The index file (with extension .m3u8) enumerate all the chunks. The segments are typically in MPEG-2 TS file (.ts files). The server accepts requests from the client for mime-type of mpegurl  matching the TS and delivers playlist and segments needed for streaming. The client, typically an HSL player requests and download the segments and plays the media file. 

The playback is driven by a playlist (file with extension .m3u8) and segments (file with extension .ts). For example, a playlist could look like the following where individual segments indicated by .ts are defined  
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:17
#EXTINF:16.733333,file000.ts
#EXTINF:8.333333,file001.ts
#EXTINF:5.033333,file002.ts
#EXT-X-ENDLIST
Tools and Software and Setup

FFmpeg, a common opensource software can be used to transcode a file and to generate content for HLS. One can chose to use Apache server for MacOS or Internet Information Services (IIS) server in Windows 10. One can always create server and serve the content, but this woudl be beyond the scope of this post.  

In order to be able to stream the content, we need to enable certain mpegurl mimetype (Multipurpose Internet Mail Extensions) type to the server so that .m3u8 can be served. In addition, the ts (segments) support must also be added to the server. 

I am using Apache server  in macOS and IIS in Windows machine. In macOS, in Apache, add the following in the /etc/apache2/mime.types:

  • application/vnd.apple.mpegurl m3u8
  • video/mp2t ts
For windows, in IIS Manager, support for m3u8 can be added by going to mimetypes. If the list already contains an entry for .m3u8, ignore this step.The .mp2 extension representing the mime-type of video/mpeg is inherited automatically in IIS. If .mp2 is present in the list of mime types, add one entry for the same. 

To install ffmpeg in mac OS, I used homebrew. First I installed dependencies and then ffmpeg using the following commands in the terminal.  
brew install nasm pkg-config texi2html aom fontconfig freetype frei0r gnutls lame libass libbluray libsoxr libvorbis libvpx opencore-amr openjpeg opus rtmpdump rubberband sdl2 snappy speex tesseract theora x264 x265 xvid xz
brew install ffmpeg

For Windows, an installer matching the specificaiton can be used. For example, I got the installer for windows 64 bit from https://ffmpeg.org/download.html and worked through it.


Create playlist and segments

ffmpeg can be used to encode the media and create segments. This also creates the playlist


ffmpeg
   -i file.mp4   -map 0   -codec:v libx264   -codec:a aac   -f ssegment   -segment_list file.m3u8   -segment_list_flags +live   -segment_time 10   file%03d.ts

In the command above, the ffmpeg is called with parameters that specifies the input with -i file.mp4 (file name). Arguments -codec:v and -codec:a are for encoding video and audio respectively. It is often common to keep the audio and video codec same as original which can be achived by using 'copy' for -codec. The ssegement specifies the length of each segment. Typically, a 5-7 second chunks are ideal. However, for some adavance anlaysis, a longer may be suitable. Note that larger the chunk size, more time is needed to download the content. 

For example, I have a file named file.mp4 which is of 40 seconds duration and when I run a command to make HLS with chunk size of 5 secconds, the output a list of files where one is of extension .m3u8 and rest of the files are of .ts extension. For this case, we will have one m3u8 file and 8 .ts files. 

Serve Media
Now that the appropriate files have been created, it is time to put these files into server and serve up. 

Copy the generated files, i.e., the file with .m3u8 and all the .ts file to the document folder of the webserver. For simplicity, I am putting the file in the root folder.


Browse/Play Media

Use a client, an http live streaming capable player such as VLC or chrome browser extension and navigate to localhost/file.m3u8. you have now successfully started streaming the media using HSL.


Reference
  • FFmpeg. A complete, cross-platform solution to record, convert and stream audio and video


Wednesday, November 20, 2019

Working with shared files in remote server using SMB in Java

Introduction

We are used to working with shared files where sharing is limited to users either in some domain to certain users. The remote application needs to access the shared resource and take actions such as add, delete resources in the shared location.

A server has shared resources that typically uses the Server Message Block (SMB) protocol (Server Message Block)in the Windows machine. The SMB protocol enables an application to access files on the remote servers, as well as other resources allowing a client application to open, read, move, update files on the remote server. The resource is limited to certain users. The user needs to pass credentials to access the shared resource. The activities that the user can do on the shared resource is defined in the sharing permission. Let's assume that the user has both read and write permission.

In this article, I am demonstrating a use case where an application needs to interact with shared files in Windows server machines using a simple java application. The use case makes the following assumption:

  • The server is a windows machine
  • There exists a local user named 'Test' and the password for the user is 'Password'. 
  • The shared location is '127.0.0.0.1\temp' 
  • The user 'Test' has access to the shared location 'temp' which can be anywhere in the machine that has IP of '127.0.0.1'.
  • The client application has access to the network

CodeBase:

The application that we are designing is in Java using Maven for build and dependency management. I am using JCIFS, an open-source client library that implements the CIFS/SMB networking protocol (https://www.jcifs.org/). The library is available from Maven Repository (https://mvnrepository.com/artifact/jcifs/jcifs/1.3.17).

The dependencies are shown in the following pom.xml file:
Currently, the application has one file Main.java with the following content:

Here, the main method uses a url pattern of for smb protocol and points to shared location at line 8.
Local variables for userName, password and domains are initialized in lines 9 through 11 and authenticates the user in line 13 at which time, the application has successfully connected to the shared folder.

The application creates a SmbFile object in line 15 and passes that folder to a method called doRecursiveLookup(..) in line 24. The method simply iterates through all the files and folders within the shared location and prints the names in the console in a recursive fashion. Note that the SmbFile object is the same for file or folders and .isDirectory() method was used to test if the object in scope is file or folder.

In the demo, I showed reading the resources in the remote server using SMB protocol.

Update:

Some institutions tend to disable SMB 1.0 in which case we need to use library from https://repo1.maven.org/maven2/org/codelibs/jcifs/ for newer version of the protocol. 

Additional Resources


Tuesday, August 6, 2019

Using EntityFramework in a .Net Core Project

Introduction

While I work with EntityFramework every day, I do not add an EntityFramework (EF) to an existing project every day. Time and again, I have to create a new project or add EntityFramework to the existing project and I have to re-do many steps that are hard to remember. I decided to document the steps in the form of this article.

In this article, I am going to show how to get started with EntityFramework. I will start a project without support to EntityFramework and add the EF support to the project and use a migration to update the database. The code related to this article is available at GitHub. The sample project uses in Visual Studio 2017 IDE using C#, MS SQL Server with SQL Server Management Studio (SSMS) and EntityFrameworkCore with .Net Core SDK 2.2.


Create Solution and Project

I started with the creation of an empty solution and added a WebAPI project targetting .NetCore 2.2.





As I am using MS SQL Server, I added a reference to

Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Design using Nuget Package Manager.


I updated the appsetting.json file for the connectionstring by adding the following property:

 "ConnectionStrings": { 
     "DefaultConnection":
        "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;" }



Configure Database and Models

I have created models for Customer, Contact, and CustomerContact. The code example has these models in DBContext folder. In addition, there are classes for IAuidtable and Audit and some enums which can be ignored for now. Customer and Contact entity have many to many relationships and thus I added CustomerContact entity to store the relationship.


public class Customer : IAuditable
    {
        public Guid Id { get; set; }
        public String AccountNumber { get; set; }
        public String Name { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public String LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public ICollection<CustomerContact> CustomerContacts { get; set; }
    }

    public class Contact : IAuditable
    {
        public Guid Id { get; set; }
        public String Name { get; set; }
        public String Title { get; set; }
        public String Phone { get; set; }
        public String Email { get; set; }
        public ContactTypeEnum ContactType { get; set; }
        public String Note { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public String LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public ICollection<CustomerContact> CustomerContacts { get; set; }

    }

    public class CustomerContact:IAuditable
    {
        public Guid Id { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public string LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public Guid CustomerId { get; set; }
        public Customer Customer { get; set; }
        public Guid ContactId { get; set; }
        public Contact Contact { get; set; }
    }

    public class Audit
    {
        public Guid Id { get; set; }
        public Guid? EntityId { get; set; }
        public string User { get; set; }
        public String Entity { get; set; }
        public DateTime DateTime { get; set; }
        public string ColumnName { get; set; }
        public String OldValue { get; set; }
        public String NewValue { get; set; }
        public EntityStateChangeTypeEnum ChangeType { get; set; }

    }
    /// <summary>
    /// This interface determines what will be automatically tracked.
    /// </summary>
    interface IAuditable
    {
        Guid Id { get; set; }
        DateTime? CreatedDate { get; set; }
        DateTime? ModifiedDate { get; set; }
        String LastModifiedBy { get; set; }
        bool IsInactive { get; set; }
    }

    public enum EntityStateChangeTypeEnum
    {
        Added,
        Deleted,
        Modified,
    }

    public enum ContactTypeEnum
    {
        Primary,
        Secondary,
        Emergency,
    }


With these changes, I am ready to set up code-first logic for creating database tables. I am going to create a class ApplicationDbContext that derives from DbContext of EntityFrameworkCore. In this class, I create DbSet objects of the type of Customer, Contact, CustomerContact, and Audit. This is what makes EF aware of the tables I need to create.


using Microsoft.EntityFrameworkCore;

namespace AutotrackEntityChange.DBContext
{
    public class ApplicationDbContext: DbContext
    {
        //Define DbSets
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Contact> Contacts { get; set; }

        public DbSet<CustomerContact> CustomerContacts { get; set; }
        public DbSet<Audit> Audits { get; set; }

        public ApplicationDbContext(
          DbContextOptions<ApplicationDbContext> options)
            : base(options) {}
    }
}


Now I need to add service in the Startup.cs class to use ApplicationDbContext within the application. The service can be added inside the ConfigureService method using the following code:


 services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration
            .GetConnectionString("DefaultConnection")));


Note that the service addition above uses the same DefaultConnection that was defined in the appsetting.json earlier.


Migration and Database Update

I can run the 'Add-Migration 'InitialCreate'' command in the Package Manager Console within the Visual Studio to generate necessary migration scripts.


This will create necessary Migration files in the solution under Migration folder.


Now I can run 'Update-Database' command in the Package Manager Console to apply the migration. 

Note that at this time, the migration code will generate the database and the tables inside 'ApplicationDb' database. I can now open SSMS and navigate to the database and see the tables.



Summary

In this article, I showed steps to add EntityFramework support to a .Net Core project using Code-First approach and migrations. I provided an example using a WebAPI project with SQL Server, but the steps mentioned here can be used for other database and other project types. The migration I used was really for creating the database and entities, but a similar approach can be used to update the entities. am hoping this will help someone and I welcome comments and suggestions.

References

Sunday, August 4, 2019

Remove commit history in git

Have you ever had a need to clean up all the history in the git repository? I have. I dealt with this old fashioned way until I started interacting with the command line.  The old fashioned way is to create a new repository and update files and replace the old with a new one. In this article, I am going to show how to clean the commit history in git using tools and technique using command lines.  

Method 1: Re-init the repository

In this method, I remove and recreate the git and modify the origin and then I can push the code to remote. The following set of commands can be used:
$ rm -rf .git
$ git init
$ git add .
$ git commit -m "Starting Commit"
$ git remote add origin [......]
$ git push -u --force origin master

Method 2: Alter the branch


In this method, I initially create a temp branch, add all the files to that branch, delete the old branch and then rename the current branch to master and then push the changes to remote. Following sets of commands can do the job. 
$ git checkout --orphan temp
$ git add -A
$ git commit -am "Starting Commit"
$ git branch -D master
$ git branch -m master
$ git push -f origin master

Method 3:  Squashing the commits 

In this method, I combine all the commits into one which is known as squashing. For example, if I have 25 commits since the beginning, then I can combine all commits into one by using the following steps for squashing commits into one and amending the commit message:
$ git rebase -i Head~25
$ #edit the commits mannually
$ git commit –amend

Wednesday, July 31, 2019

Unit Testing C++ Code with Visual Studio

Unit Testing has been an inherent part of software development today. In Unit Testing, individual units of software are tested. While manual testing of individual functionality has been part of the development cycle, more recently these testing is automated with a testing framework.

In this article, I am going to illustrate the unit testing example in C++ using Visual Studio 2017 IDE and Native Unit Test Project. For illustration, unit test and actual software are contained within a solution. The code is contained in the basiccpp solution which currently includes one project and one test project and can be obtained from GitHub.

To get started, I have a solution named ‘basiccpp’ solution. The solution currently contains a console application, basiccpp, written in C++.

Step 1: Add a test project.
In the solution explorer, right-click to get the context menu and click ‘Add’ and select Test. Select the test framework. In this example, I am using ‘Native Unit Test Project’. The native unit test project resides in namespace “Microsoft::VisualStudio::CppUnitTestFramework”.


The test project created will test the functions within the basiccpp project. The basiccpp project is a window console application written in C++. 


Step 2: Add a reference

For the test project to get access to the basiccpp project, I will add a reference to the project.
Right-click the ‘References’ in the test project, and click Add Reference and in the window displayed, check the basiccpp project. If a single test project needs to test multiple projects, then I would select all the projects I am testing. 


Now the reference is added, I can start creating tests for the projects.

Step 3: Add Test
By default, Visual Studio template provides us with a unittest1.cpp file. I am going to rename this such that I know by looking at the file name the behavior I am testing. In this example, I am going to test Array.cpp file. Thus, I would name it as ‘ArrayTest.cpp’.

The specification of Array is in Array.hpp file and actual impelementaiton is in Array.cpp file. The content of this Array.hpp file is shown below:

#pragma once
#ifndef ARRAY_HPP_INCLUDED
#define ARRAY_HPP_INCLUDED
#endif

#include <cassert>
#include <ostream>
#include <utility>

template <typename T>
class Array {
private:
       T* m_ptr{ nullptr };
       int m_size{ 0 };
public:
       Array() = default;
       ~Array();
       //Constructor to create an array of defined size
       explicit Array(int size);
       //copy constructor
       Array(const Array& source);
       T& operator[](int index);
       T operator[](int index) const;
       void print() const;
       int Size() const;
       bool IsValidIndex(int index) const;
};

For testing, I will add test targeting publicly exposed the function of Array<T> class.
Since I am targeting functions defined in Array.hpp in basiccpp project, I am going to include Array.cpp in the ArrayTest.cpp by adding the following line:

#include "..\basiccpp\Array.cpp"

The Visual Studio will provide the IntelliSense as I am typing to the referenced project and I can select the correct include file.  




Step 4: Create tests

I am going to update the content of ‘ArrayTest.cpp’. In minimum, the structure of the test class one method with the following structure: 

namespace basicTest
{
       TEST_CLASS(my_test_class)
       {
       public:

              //test creation, size and [] operators.
              TEST_METHOD(my_test_method)
              {
                      //TODO: add test logic here.
              }
}
}

I can create as many methods within the class. The full content of the file test class for ArrayTest is shown below:

#include "stdafx.h"
#include "CppUnitTest.h"
#include "..\basiccpp\Array.cpp"
#include <functional>
#include <assert.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace basicTest
{
       TEST_CLASS(ArrayTest)
       {
       public:

              //test creation, size and [] operators.
              TEST_METHOD(ArrayTest_Size)
              {
                     int size = 5;
                     Array<int> mArray{ size };
                     for (int i = 0; i < size; i++) {
                           mArray[i] = i;
                     }
                     Assert::AreEqual(size, mArray.Size());
                     Assert::IsTrue(mArray[0] == 0);
                     Assert::IsTrue(mArray[4] == 4);
              }

              //test the empty function
              TEST_METHOD(ArrayTest_Empty)
              {
                     int size = 5;
                     Array<int> mArray0{ 0 };
                     Assert::IsTrue(mArray0.IsEmpty());

                     Array<int> mArray{ size };
                     for (int i = 0; i < size; i++) {
                           mArray[i] = i;
                     }
                     Assert::IsFalse(mArray.IsEmpty());
              }

              //test  isValidIndex function
              TEST_METHOD(ArrayTest_IsValidIndex)
              {
                     int size = 5;
                     Array<int> mArray{ size };
                     Assert::AreEqual(size, mArray.Size());
                     Assert::IsTrue(mArray.IsValidIndex(1));
                     Assert::IsFalse(mArray.IsValidIndex(5));
              };

              //Test the method indexoutofbount by passing the invalid index and checking
              //if the exception is thrown
              TEST_METHOD(ArrayTest_IndexoutOfBound)
              {
                     int size = 5;
                     Array<int> mArray{ size };
                     auto func = [mArray, size] {
                           mArray[size + 100]; };
                     Assert::ExpectException<IndexOutOfBoundsException>(func);
              }

              //test copy constructor
              TEST_METHOD(ArrayTest_CopyConstructor)
              {
                     int size = 5;
                     Array<int> mArray{ size };
                     for (int i = 0; i < size; i++) {
                           mArray[i] = i;
                     }
                     Array<int> cArray = mArray;
                     cArray[0] = 100;
                     Assert::IsTrue(cArray[0] != mArray[0]);
              }
       };
}

Step 5: Run the test using Test Explorer
Once I have written the test, I can build and run the test using Test Explorer. In addition, by adding breakpoints to the method as well as in the test, I can debug the test. 


Additional Reference:
Write unit tests for C/C++ in Visual Studio
Using Assert::ExpectException with Native Unit Testing in VS11
GitHub page for the code
GitHub Link for Code