Tuesday, July 16, 2013

HTTP Live Streaming Tutorial With HTML5 and Ruby Sinatra

HTTP Live Streaming is a way to send audio and video over HTTP from a web server to client software on the desktop or to iOS-based devices. HTTP Live Streaming sends audio and video as a series of small files, typically of about 10 seconds duration, called media segment files. An index file, or playlist, gives the clients the URLs of the media segment files. The playlist can be periodically refreshed to accommodate live broadcasts, where media segment files are constantly being produced.

Full Source Code: http://bit.ly/12EdHal

1. Download Apple HTTP Live Streaming Command Line Tools: https://developer.apple.com/streaming/

2. With the MP4 video (mine is Iron-Man-3.mp4) in the same directory:


$ mediafilesegmenter Iron-Man-3.mp4

dyn-209-2-221-215:tutorial ktran$ mediafilesegmenter Iron-Man-3.mp4
Jul 16 2013 14:25:35.304: Using floating point is not backward compatible to iOS 4.1 or earlier devices
Jul 16 2013 14:25:35.304: Processing file /Users/ktran/Desktop/tutorial/Iron-Man-3.mp4
Jul 16 2013 14:25:35.338: Finalized fileSequence0.ts
Jul 16 2013 14:25:35.343: segment bitrate 1.98 Mbits/sec is new max
Jul 16 2013 14:25:35.370: Finalized fileSequence1.ts
Jul 16 2013 14:25:35.377: segment bitrate 4.38 Mbits/sec is new max
Jul 16 2013 14:25:35.401: Finalized fileSequence2.ts
Jul 16 2013 14:25:35.401: segment bitrate 5.24 Mbits/sec is new max
Jul 16 2013 14:25:35.420: Finalized fileSequence3.ts
Jul 16 2013 14:25:35.440: Finalized fileSequence4.ts
Jul 16 2013 14:25:35.470: Finalized fileSequence5.ts
Jul 16 2013 14:25:35.471: segment bitrate 6.56 Mbits/sec is new max
Jul 16 2013 14:25:35.490: Finalized fileSequence6.ts
Jul 16 2013 14:25:35.514: Finalized fileSequence7.ts
Jul 16 2013 14:25:35.549: Finalized fileSequence8.ts
Jul 16 2013 14:25:35.550: segment bitrate 8.19 Mbits/sec is new max
Jul 16 2013 14:25:35.586: Finalized fileSequence9.ts
Jul 16 2013 14:25:35.613: Finalized fileSequence10.ts
Jul 16 2013 14:25:35.653: Finalized fileSequence11.ts
Jul 16 2013 14:25:35.703: Finalized fileSequence12.ts
Jul 16 2013 14:25:35.739: Finalized fileSequence13.ts
Jul 16 2013 14:25:35.765: Finalized fileSequence14.ts
Jul 16 2013 14:25:35.766: Finalized fileSequence15.ts
Jul 16 2013 14:25:35.766: segment does not contain sync frame
Jul 16 2013 14:25:35.767: average bit rate is 5.28 Mbits/sec - max file bit rate is 8.19 Mbits/sec
dyn-209-2-221-215:tutorial ktran$

3. Install Ruby Sinatra and Thin (if not installed)

$ gem install sinatra
$ gem install thin


dyn-209-2-214-100:tutorial ktran$ gem install sinatra
Successfully installed sinatra-1.4.3
1 gem installed
Installing ri documentation for sinatra-1.4.3...
Installing RDoc documentation for sinatra-1.4.3...
dyn-209-2-214-100:tutorial ktran$
dyn-209-2-214-100:tutorial ktran$ gem install thin
Building native extensions. This could take a while...
Successfully installed thin-1.5.1
1 gem installed
Installing ri documentation for thin-1.5.1...
Installing RDoc documentation for thin-1.5.1...
dyn-209-2-214-100:tutorial ktran$

3. Create the server.rb file and index.html file in the same directory as your video segments. 

 server.rb


require 'sinatra'
require 'rubygems'

get '/' do
File.read(File.join('.', 'index.html'))
end

get '/:filename' do
File.read(File.join('.', "#{params[:filename]}"))
end

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HTTP Live Streaming Example</title>
<link href="http://vjs.zencdn.net/c/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/c/video.js"></script>
</head>
<body>
<video class="video-js vjs-default-skin"
controls preload="auto" width="1280" height="720"
data-setup='{"example_option":true}'>
<source src="http://localhost:4567/prog_index.m3u8" type='application/x-mpegURL'/ >
</video>

</body>
</html>

3. Start the server.

$ ruby server.rb


4. Go to a web browser and go to the address: http://localhost:4597.

 First Mistake - I Didn't Create a General Route to Send Files - But At Least I Know It's Correctly Requesting All of the Segments


dyn-209-2-214-100:tutorial ktran$ ruby server.rb
== Sinatra/1.4.3 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.1 codename Straight Razor)
>> Maximum connections set to 1024
>> Listening on localhost:4567, CTRL+C to stop
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET / HTTP/1.1" 200 1204 0.0040
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /prog_index.m3u8 HTTP/1.1" 200 680 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /prog_index.m3u8 HTTP/1.1" 200 680 0.0010
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence0.ts HTTP/1.1" 404 453 0.0011
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence1.ts HTTP/1.1" 404 453 0.0013
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence2.ts HTTP/1.1" 404 453 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence3.ts HTTP/1.1" 404 453 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence4.ts HTTP/1.1" 404 453 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence5.ts HTTP/1.1" 404 453 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence6.ts HTTP/1.1" 404 453 0.0008
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence7.ts HTTP/1.1" 404 453 0.0010
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence8.ts HTTP/1.1" 404 453 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence9.ts HTTP/1.1" 404 453 0.0008
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence10.ts HTTP/1.1" 404 454 0.0008
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence11.ts HTTP/1.1" 404 454 0.0009
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence12.ts HTTP/1.1" 404 454 0.0011
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence13.ts HTTP/1.1" 404 454 0.0011
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence14.ts HTTP/1.1" 404 454 0.0008
127.0.0.1 - - [16/Jul/2013 14:59:33] "GET /fileSequence15.ts HTTP/1.1" 404 454 0.0009

Success




dyn-209-2-214-100:tutorial ktran$ ruby server.rb
== Sinatra/1.4.3 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.1 codename Straight Razor)
>> Maximum connections set to 1024
>> Listening on localhost:4567, CTRL+C to stop
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET / HTTP/1.1" 200 1204 0.0039
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /prog_index.m3u8 HTTP/1.1" 200 680 0.0011
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /prog_index.m3u8 HTTP/1.1" 200 680 0.0008
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence0.ts HTTP/1.1" 200 2468252 0.0208
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence1.ts HTTP/1.1" 200 5461964 0.0228
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence2.ts HTTP/1.1" 200 6529428 0.0398
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence3.ts HTTP/1.1" 200 5280732 0.0163
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence4.ts HTTP/1.1" 200 5495616 0.0246
127.0.0.1 - - [16/Jul/2013 15:04:18] "GET /fileSequence5.ts HTTP/1.1" 200 8170480 0.0412
127.0.0.1 - - [16/Jul/2013 15:04:33] "GET /fileSequence6.ts HTTP/1.1" 200 4904168 0.0152
127.0.0.1 - - [16/Jul/2013 15:04:43] "GET /fileSequence7.ts HTTP/1.1" 200 6491452 0.0148
127.0.0.1 - - [16/Jul/2013 15:04:53] "GET /fileSequence8.ts HTTP/1.1" 200 10209716 0.0429
127.0.0.1 - - [16/Jul/2013 15:05:03] "GET /fileSequence9.ts HTTP/1.1" 200 9690648 0.0372
127.0.0.1 - - [16/Jul/2013 15:05:13] "GET /fileSequence10.ts HTTP/1.1" 200 6523036 0.0215
127.0.0.1 - - [16/Jul/2013 15:05:22] "GET /fileSequence11.ts HTTP/1.1" 200 5998328 0.0246
127.0.0.1 - - [16/Jul/2013 15:05:33] "GET /fileSequence12.ts HTTP/1.1" 200 8308284 0.0357
127.0.0.1 - - [16/Jul/2013 15:05:42] "GET /fileSequence13.ts HTTP/1.1" 200 6363236 0.0265
127.0.0.1 - - [16/Jul/2013 15:05:53] "GET /fileSequence14.ts HTTP/1.1" 200 7246648 0.0295
127.0.0.1 - - [16/Jul/2013 15:06:02] "GET /fileSequence15.ts HTTP/1.1" 200 255492 0.0037
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence0.ts HTTP/1.1" 200 2468252 0.0124
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence1.ts HTTP/1.1" 200 5461964 0.0062
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence2.ts HTTP/1.1" 200 6529428 0.0151
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence3.ts HTTP/1.1" 200 5280732 0.0034
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence4.ts HTTP/1.1" 200 5495616 0.0052
127.0.0.1 - - [16/Jul/2013 15:06:54] "GET /fileSequence5.ts HTTP/1.1" 200 8170480 0.0167
Note: You can't view the movie on multiple browsers at once.

Sources: http://www.christopherlavender.com/2012/07/14/http-live-streaming-part-1-setup-segment-stream/

Monday, July 8, 2013

Recursive Interview Questions Tutorial (WIP)


The most important thing to learn is how to think recursively. This allows you to solve a large class of problems relatively simply. It's important to know for job interviews. I had interviewed at Google six months ago and because I botched an interview question that could have been solved recursively, the interviewer told the recruiter I "lacked fundamental understanding of computer science". 

To solve a problem recursively:

1)  Think about how to solve the problem for simple cases (base case). 
2)  Determine how to break down larger cases into smaller instances (recursive decomposition).

Remember that every recursive algorithm has two components: the base case and the recursive decomposition. 

Problem #1 Reverse A String
Base Case:  When the string is the empty string is it the same backwards as it is forwards
Recursive Decomposition: For strings, to shrink the string to make forward progress is the same as solving the problem for substrings of the string, and then combining the sub-solutions.


/* Returns the reverse of the indicated string. */
string reverseString(string line) {
if (line == "")
return "";
else
return reverseString(line.substr(1)) + line[0];
}

Problem #2 Count Spaces in A String
Base Case:  When the string is empty is cannot have a space so the count of spaces is 0.
Recursive Decomposition: Check character by character by working with substrings of the string, and then sum the counts of the strings. 


/* Returns the count of white spaces in a string*/
int countSpaces(string line){
if (line.length() == 0)
return 0;
else
return ((isspace(line[0]) ? 1 : 0) + countSpaces(line.substr(1)));
}
Problem #3 Find Last Occurrence of Character in String
Base Case: Well if you are at the end of the string and no characters follow, it must be the last occurrence if there is a match. 
Recursive Decomposition: Start at the end of the string and after each call remove the last character. 


/* Return the index of the last occurence of a character in the string */
int find_last_occurence(string line, char character){
if (line[line.length()-1] == character)
return (int)line.length();
else if(line.length() == 0)
return -1;
else
return find_last_occurence(line.substr(0,line.length()-1), character);
}

Spark Notes Article That I Thought Was Useful: http://www.sparknotes.com/cs/recursion/whatisrecursion/section2.rhtml

Problem #4 Traversing a Binary Tree. Inorder, preorder, postorder.

Problem #5 Print all subsets of a set. 

Problem #6 Print all alphanumeric combinations of a phone number. 

These are the slides that I found on the Stanford Engineering Everywhere site that I found useful to learn more about recursion. 
























Saturday, July 6, 2013

A Simple Server in C++

Makefile


default:
g++ server.cpp
./a.out
clean:
rm -f a.out

server.cpp


//
// server.cpp
// SimpleServer
//
// Created by Kurry Tran on 7/3/13.
// Copyright (c) 2013 Kurry Tran. All rights reserved.
//

#include <iostream>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <netdb.h>

void* SocketHandler(void*);

int main(int argc, const char * argv[])
{
int host_port= 1101;
struct sockaddr_in my_addr;
int hsock;
int *p_int;

socklen_t addr_size = 0;
int* csock;
sockaddr_in sadr;
pthread_t thread_id=0;

hsock = socket(AF_INET, SOCK_STREAM, 0);
if(hsock == -1){
std::cout <<"Error initializing socket " << errno << std::endl;
goto FINISH;
}
p_int = (int*)malloc(sizeof(int));
*p_int = 1;

if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 )||
(setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
std::cout <<"Error setting options " << errno << std::endl;
free(p_int);
goto FINISH;
}
free(p_int);

my_addr.sin_family = AF_INET ;
my_addr.sin_port = htons(host_port);

memset(&(my_addr.sin_zero), 0, 8);
my_addr.sin_addr.s_addr = INADDR_ANY ;

if( bind( hsock, (sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
std::cerr << "Error binding to socket, make sure nothing else is listening on this port " << errno << std::endl;
goto FINISH;
}

if(listen( hsock, 10) == -1 ){
std::cerr << "Error listening " << errno << std::endl;
goto FINISH;
}

addr_size = sizeof(sockaddr_in);

while(true){
std::cout <<"waiting for a connection on localhost:"<<host_port<< std::endl;
csock = (int*)malloc(sizeof(int));
if((*csock = accept( hsock, (sockaddr*)&sadr, &addr_size))!= -1){
std::cout <<"---------------------\nReceived connection from "<< inet_ntoa(sadr.sin_addr) << std::endl;
pthread_create(&thread_id,0,&SocketHandler, (void*)csock );
pthread_detach(thread_id);
}
else{
std::cerr << "Error accepting " << errno << std::endl;
}
}

FINISH:
;
}

void *SocketHandler(void *lp){
int *csock = (int *)lp;

char buffer[1024];
int buffer_len = 1024;
ssize_t bytecount;

memset(buffer, 0, buffer_len);
if((bytecount = recv(*csock, buffer, buffer_len, 0))== -1){
std::cerr << "Error receiving data " << errno << std::endl;
goto FINISH;
}
std::cout << "Received bytes " << bytecount << std::endl << "Received string "<< buffer << std::endl;

if((bytecount = send(*csock, buffer, strlen(buffer), 0))== -1){
std::cerr << "Error sending data " << errno << std::endl;
goto FINISH;
}
std::cout << "Sent bytes " << bytecount << std::endl;

FINISH:
close(*csock);
free(csock);
return 0;
}