How to Stress Test RTMP Server using Flazr

Flazr is a collection of multimedia streaming tools implemented in Java, including a RTMP server, a RTMP client and some other utilities. It runs on both Linux and Windows.

One of the things that Flazr can do is to stress test your RTMP server. In a testing environment, your bandwidth (hundreds of clients receiving video streams simultaneously requires high bandwidth) or computing power (hundreds of clients playing/decoding video requires a lot of CPU power) are normally limited so that you won’t have enough resources to stress test your RTMP server exactly as it’s used in production.

Flazr can create lots of connections with a third-party RTMP streaming server and constantly receive video data without playing it, so it doesn’t have the CPU power issue. If you place the Flazr client at the same machine as your RTMP server, you won’t have the bandwidth issue also.

To use Flazr for RTMP stress testing is quite simple. Suppose your RTMP url is rtmp://173.23.24.25:80/live/test.stream, you can simply go Flazr directory and type the following command,

./client.sh -version 00000000 -load 20 -threads 15 -host 173.23.24.25 -port 80     -app live test.stream

This command will create 20 connections (-load 20) to receive data from the RTMP server.

If you want more options, you can just type ./client.sh to view a list of supported configurations.

References:

Flazr Official Website: http://flazr.com/

Youtube Video Tutorial: http://www.youtube.com/watch?v=Wa2bDQLKf8s

Assume People do Simple Stuff Right–How Bookmark Work on Different Browsers

Every browser has bookmark/favourite function, but different browsers implement the workflow differently. Let’s look at them one by one.

IE 9

At IE9, user needs do the following steps.

1.  Click the star icon at the right side corner. Then a window pops up as below.

ie9

2. Users need to select the Favorites tab if it’s not the current tab, then click the “Add to favorites” button.

3. I thought that’s done. NO! A new window pops out.

ie92

4. Select folder, or new new folder (another window pops out).

5. Finally, click Add to add the page to favourite.

Firefox 4

1. Click the right side star icon at the Location bar. Guess what? That’s all. (The “Bookmark this page” is a tip popped out when I place my mouse over the start icon.)

image

One can find the bookmark as shown in the figure below,

firefox2

2. Of course, some people are more organized and they want to put bookmarks into different folders. Simple, just click the star icon again. Then you’ll see this,

image

The interface is quite intuitive and somehow similar across all browsers. Compared with IE9, it also allows you to remove the bookmark previously added.

Safari 5.1

1. Click the left side “+” icon of the Location bar, you’ll see window below,

safari2

2. Well, my first impression was “where is the new folder option?”.

2.1 Never mind, just click “Add”.

3. Later on, I figured out right click the empty space of the Location bar and we’ll be able to create a new folder.

Opera 11.50

0. Look around, but I cannot find any icon for bookmarks.

1. Click the upper left corner, then I find the place,

opera1

2. Click “Bookmark Page”, the familiar dialog pops out,

opera2

3. Well, we all know what to do next.

Chrome 12.0

1. Similar to firefox, it has a star icon at the right side of the Location bar, again the “Bookmark this page” is the tooltip text when I placed my mouse over the star icon.

image

Click the icon, you’ll see this,

chrome

2. Click Done, then done. If I want to remove the bookmark, click Remove. If I want to be more organized, click Edit. Then another window is shown,

image

Ok. This is also similar to all other browsers, except Chrome shows more bookmark folders than all other browsers. (All other browsers use a dropdown list.)

My Personal Preference and Why

This is purely subjective. But I will give my preference order as Chrome > Firefox > IE 9 = Opera > Safari, in terms of the bookmark function. Why?

Both Chrome and Firefox assume users will do the right thing, in other words, they think people click  the star icon because he/she actually wants to bookmark a page. So once users click the star icon, they add the bookmark.

Besides, they offer obvious and easy to use interface for users to further organize the bookmarks. (both requires two clicks. For firefox, it’s two clicks of the star icon; for Chrome, it’s one click of the star icon, one click of the Edit button.) Users also have the option to quickly remove the bookmark if he/she changes his/her mind or finishes the reading of the page.

Why Chrome is sightly better than Firefox in my preference? The “Edit Bookmark” window. I like it a lot more than all the other browsers offer. It’s much easier to choose a folder or organize folder structure.

IE9, Opera and Safari takes another approach. They tried to guide users through some process. First this, then that, do you confirm? Yes, then the action is taken.

Come on, I know what I’m doing, I simply want to save a page to read it later. Why so many steps? And Safari, please let me create new folder when I add a bookmark so I can be organized if I want to.

So lesson learned: assume your users are smart and execute their instructions as quick as possible, and always give them the choice to refine the instructions and rollback.

Simple HTML Modal Window Based on jQuery

A modal window is a pop up window which requires user to interact with it before continuing operation on its parent window. This post covers how to construct a basic modal window based on jQuery and a third-party javascript.

First, in the parent page, we’ll need to provide a link to the modal window, which is basically what we want to show on the modal window. The link can be a static link, or we can use javascript to pass the url of the link. The sample page is given below,

<!DOCTYPE html>

<html lang="en">

    <head>

        <meta charset="utf-8" />

        <title>Simple Modal Window Test</title>

        <link rel="stylesheet" href="styles/modal-window.css" />

        <script type="text/javascript" language="javascript" src="scripts/jquery-1.3.2.min.js"></script>

        <script type="text/javascript" language="javascript" src="scripts/modal-window.min.js"></script>

        <script type="text/javascript">

            function open_modal() {

                $(this).modal({width:533, height:153, src:"modal.html"}).open();

            }

        </script>

    </head>

    <body>

        <div style="position:relative; margin-left: 200px; top:80px">

            <h3 style="margin-top:50px;"><a href="modal.html" onclick="$(this).modal({width:833, height:453}).open(); return false;">&rarr; Modal iFrame</a></h3>

            <h3 style="margin-top:50px;"><a href="images/test.jpg" onclick="$(this).modal({width:500, height:375}).open(); return false;">&rarr; Modal Image</a></h3>

            <input type="button" value="open modal" onclick="open_modal();">

        </div>

    </body>

</html>

The page has two anchor elements that contains link to a html page and an image respectively. It also has a button which will trigger opening of a modal window once it’s clicked.

The effect can be seen below, or here as a separate page.

For the first option, we want to open another html page at the modal window, the sample html page is given below,

<!DOCTYPE html>

<html lang="en">

    <head>

        <title>Modal Window Sample</title>

        <script type="text/javascript">

            function finish() {

                parent.$.modal().close();

            }

        </script>

    </head>

    <body>

        <div style="margin-top:20px; text-align:center;">

            <h2>Modal Window Sample</h2>

            <button onclick="finish()">Submit and Close</button>

        </div>

    </body>

</html>

One can add more HTML tags.

This modal window works well for images and pop up window. However, if I want to pass info between parent window and child window, then it’s not so obvious how to handle it. I’ll continue to look for better solutions.

Note: this post is a study notes for the A Simple Modal – Redux. You can download the source file from the original article here.

How to Set up Reverse SSH

In my work, I used SSH a lot for remote access, including Amazon cloud server. It works with the following simple command,

ssh dest_user_name@dest_ip_or_domain_name

For example, the remote machine has an IP of 137.175.128.23, and the user name is test, then we simply enter the following command,

ssh test@137.175.128.23

This requires the remote side to have a public IP address. If we want to ssh to a computer which sits behind firewall and NAT, then the computer won’t be visible to us and SSH won’t work.

That’s where the reverse SSH comes into play.

Reverse SSH doesn’t require the destination to have a public IP address, instead the public IP address could be with the source side, or a middle machine between source and destination. We’ll cover both cases below.

(Throughout this tutorial, we’ll use 137.175.128.23 as public IP for our machine, no matter it’s at the source side or dest side, and “test” as the username for the machine with public IP.)

1. Access Remote Side from Source with Public IP

First, consider the case we have a public IP address at source, and we want to access a destination computer in a private network.

At the destination, type the command below,

ssh –R 12345:localhost:22 test@137.175.128.23

This command will create a tunnel between the source addr (137.175.128.23:12345) and the destination address (localhost[with private ip]:22).

Then at the source side, we ssh to local port 12345 by,

ssh dest_user_name@localhost –p 12345

This command will ssh to source port 12345, which has been connected with the remote side in previous step.

Therefore, the SSH tunnel is established as below,

dest addr (private_ip:22) <= source addr 1 (137.175.128.23:12345) <= source addr 2(137.175.128.23 : port_used_by_source_ssh_client)

Note that you’ll need both connections active to access the destination. In order to keep it alive, you can run command like “top” and “watch” to ensure there’s some data transmission.

Or you can add the following line to the /etc/ssh/ssh_config file:

ServerAliveInterval 60

The client will send keep alive message every 60 seconds in this case.

2. Connect Remote Side using Private IP, with Public IP as Middle Man

Now consider a situation where both source and dest have private IP addresses, then the public IP machine can serve as middle man.

We simply SSH to middle man first, then the reset is the same as previous case.

So first at source computer,

ssh test@137.175.128.23

After ssh to middle computer, at the middle computer,

ssh –R 12345:localhost:22 test@137.175.28.23

Finally, at source computer (probably you’ll need to open another console window),

ssh localhost –p 12345

How to Set up SSH Key Authentication

ssh is a great tool for remote access, especially under Linux. The command line based remote access can run smoothly on a slow network link.

Sometimes we access a remote machine so frequently that we want to get rid of the step of entering password every time. SSH actually allows key authentication, and it’s very easy to set up.

If you’re in a rush, a three-step quick guide is give below,

ssh-keygen –t dsa

cat ~/.ssh/id_dsa.pub | ssh remote_user_name@remote_ip “cat >> ~/.ssh/authorized_keys2”

ssh remote_user_name@remote _ip

For more details about each step, please read the explanation for each step below.

1. Generate Key Pair

Simple enter the following command,

ssh-keygen –t dsa

or, if you prefer rsa encryption,

ssh-keygen –t rsa

This will generate a public key (the file with .pub extension) and a private key (without .pub extension). You’ll need to put the public key to the remote side and keep the private key in a safe place.

2. Copy Public Key to Remote .ssh Directory

If remote server doesn’t have .ssh folder at home directory, run ssh-keygen on remote side to create one.

If there’s no authorized_keys2 file under .ssh folder, copy the public key to the remote side.

scp ~/.ssh/id_dsa.pub remote_user_name@remote_ip:~/.ssh/authorized_keys2

If there’s already a authorized_keys2 file, append the public key file content to the end of the authorized_keys2 file.

One can use a single command,

cat ~/.ssh/id_dsa.pub | ssh remote_user_name@remote_ip “cat >> ~/.ssh/authorized_keys2”

or execute the following steps.

First, copy the public key file to a temporary file at remote side,

scp ~/.ssh/id_dsa.pub remote_user_name@remote_ip:~/.ssh/tmpfile

Next ssh to remote side,

ssh remote_user_name@remote_ip

Finally, append the temporary file content to the end of authorized_keys2 file,

cat ~/.ssh/tmpfile >> ~/.ssh/authorized_keys2

3. SSH to remote Server without Password

Now you can ssh to remote side without password.

Note that if the first time you failed to ssh to remote side, you can simply log out from your computer and log in again. I don’t know why this works, but it fixes the issue.

HTML Dropdown Menu–with CSS and JavaScript

This post gives step-by-step instruction to build a cool HTML dropdown menu as below or here (as a separate page),

1. Structure the Menu by HTML

HTML is originally designed to be a markup language to indicate the different parts of a document. This is how HTML is used in our dropdown menu. Below is the HTML code,

<!doctype html>

<html>

    <head>

        <meta charset="utf-8" />

        <meta name="robots" content="noindex" />

        <title>Dropdown Menu Step 1</title>

    </head>

    <body>

        <div class="nav">

            <ul class="menu" id="menu">

                <li><a href="#">Home</a></li>

                <li><a href="#">Menu One</a>

                    <ul>

                        <li><a href="#">Menu Item 1</a></li>

                        <li><a href="#">Menu Item 2</a></li>

                        <li class="submenu">

                            <a href="#">Menu Item 3</a>

                            <ul>

                                <li><a href="#">Menu Item 31</a></li>

                                <li><a href="#">Menu Item 32</a></li>

                                <li><a href="#">Menu Item 33</a></li>

                            </ul>

                        </li>

                    </ul>

                </li>

                <li><a href="#">Menu Two</a>

                    <ul>

                        <li><a href="#">Menu Item 21</a></li>

                        <li><a href="#">Menu Item 22</a></li>

                        <li><a href="#">Menu Item 23</a></li>

                        <li class="submenu">

                            <a href="#">Menu Item 24</a>

                            <ul>

                                <li><a href="#">Menu Item 241</a></li>

                                <li><a href="#">Menu Item 242</a></li>

                                <li><a href="#">Menu Item 243</a></li>

                                <li><a href="#">Menu Item 244</a></li>

                                <li><a href="#">Menu Item 245</a></li>

                            </ul>                        

                        </li>

                        <li><a href="#">Menu Item 25</a></li>

                        <li><a href="#">Menu Item 26</a></li>

                    </ul>

                </li>

                <li><a href="#">Menu Three</a>

                    <ul>

                        <li><a href="#">Menu Item 31</a></li>

                        <li><a href="#">Menu Item 32</a></li>

                        <li><a href="#">Menu Item 33</a></li>

                    </ul>

                </li>

                <li><a href="#">Menu Four</a>

                </li>

            </ul>

        </div>

    </body>

</html>

The web page will look like below,

2. Apply CSS Style to HTML

After defining the structure of the dropdown menu, we’ll need to apply CSS style to make it looks like a dropdown menu, the html code is as below,

<!doctype html>

<html>

    <head>

        <meta charset="utf-8" />

        <meta name="robots" content="noindex" />

        <link rel="stylesheet" href="dropdown_menu_step2.css" />

        <title>Dropdown Menu Step 2</title>

    </head>

    <body>

        <div class="nav">

            <ul class="menu" id="menu">

                <li><a href="#">Home</a></li>

                <li><a href="#">Menu One</a>

                    <ul>

                        <li><a href="#">Menu Item 1</a></li>

                        <li><a href="#">Menu Item 2</a></li>

                        <li class="submenu">

                            <a href="#">Menu Item 3</a>

                            <ul>

                                <li><a href="#">Menu Item 31</a></li>

                                <li><a href="#">Menu Item 32</a></li>

                                <li><a href="#">Menu Item 33</a></li>

                            </ul>

                        </li>

                    </ul>

                </li>

                <li><a href="#">Menu Two</a>

                    <ul>

                        <li><a href="#">Menu Item 21</a></li>

                        <li><a href="#">Menu Item 22</a></li>

                        <li><a href="#">Menu Item 23</a></li>

                        <li class="submenu">

                            <a href="#">Menu Item 24</a>

                            <ul>

                                <li><a href="#">Menu Item 241</a></li>

                                <li><a href="#">Menu Item 242</a></li>

                                <li><a href="#">Menu Item 243</a></li>

                                <li><a href="#">Menu Item 244</a></li>

                                <li><a href="#">Menu Item 245</a></li>

                            </ul>                        

                        </li>

                        <li><a href="#">Menu Item 25</a></li>

                        <li><a href="#">Menu Item 26</a></li>

                    </ul>

                </li>

                <li><a href="#">Menu Three</a>

                    <ul>

                        <li><a href="#">Menu Item 31</a></li>

                        <li><a href="#">Menu Item 32</a></li>

                        <li><a href="#">Menu Item 33</a></li>

                    </ul>

                </li>

                <li><a href="#">Menu Four</a>

                </li>

            </ul>

        </div>

    </body>

</html>

The code is almost the same as the previous page, with the addition of

<link rel=”stylesheet” href=”dropdown_menu_step2.css” />

The CSS file dropdown_menu_step2.css is as below,

* {

    margin:0px;

    padding:0px;

}

.nav {

    height:36px;

    background:#aaaaaa;

    color:#ffffff;

    text-shadow:1px 1px #888888;

    z-index:1000;

    margin:0px;

}

 

.menu {

    list-style:none;

    font-family:Arial,Verdana,Serif;

    font-size:16px;

    text-align:center;

    width:600px;

    margin:0 auto;

}

 

.menu a {

    float:left;

    color: #eeeeee;

    text-decoration:none;

    width:120px;

    height:26px;

    padding:8px 0px 0px 0px;

}

 

.menu a:hover {

    color:#ffffff;

}

 

.menu li {

    position:relative;

    float:left;

    width:120px;

}

 

.menu ul {

    list-style:none;

    font-family:Arial,Verdana,Serif;

    font-size:13px;

    font-weight:normal;

    background:#aaaaaa;

    position:absolute;

    top:36px;

    left:0;

}

 

.menu ul ul {

    left:120px;

    top:0;

}

 

.menu li.submenu {

    font-weight:bold;

}

 

.menu li.submenu li {

    z-index:1001;    /*uncomment this line to see the overlapping effect of menu item 31, 32, 33 with 23, 24, 25*/

}

The key idea is to set the list-style as none so the menu items won’t appear as menu items. The dropdown menu items and the submenu are put into relative layout, so they will appear right under the top menu and at the right side of the menu item respectively. The page will look like below,

3. Add JavaScript for Dynamic Effect and Update CSS

There is only one simple change to the CSS file, which is simply set the drop down menu item display as display:none.

The CSS file dropdown_menu_step3.css is as below,

* {

    margin:0px;

    padding:0px;

}

.nav {

    height:36px;

    background:#aaaaaa;

    color:#ffffff;

    text-shadow:1px 1px #888888;

    z-index:1000;

    margin:0px;

}

 

.menu {

    list-style:none;

    font-family:Arial,Verdana,Serif;

    font-size:16px;

    text-align:center;

    width:600px;

    margin:0 auto;

}

 

.menu a {

    float:left;

    color: #eeeeee;

    text-decoration:none;

    width:120px;

    height:26px;

    padding:8px 0px 0px 0px;

}

 

.menu a:hover {

    color:#ffffff;

}

 

.menu li {

    position:relative;

    float:left;

    width:120px;

}

 

.menu ul {

    list-style:none;

    font-family:Arial,Verdana,Serif;

    font-size:13px;

    font-weight:normal;

    background:#aaaaaa;

    position:absolute;

    top:36px;

    left:0;

    display:none;              /*only this line is added compared with step 2*/

}

 

.menu ul ul {

    left:120px;

    top:0;

}

 

.menu li.submenu {

    font-weight:bold;

}

 

.menu li.submenu li {

    z-index:1001;    /*uncomment this line to see the overlapping effect of menu item 31, 32, 33 with 23, 24, 25*/

}

The dynamic effects are programmed using JavaScript, the code is as below,

var TINY = {};

 

TINY.dropdown = function() {

    var p = {fade:1, slide:1, active:0, timeout:200};

    var init = function(n, o) {

        for(s in o) {

            p[s] = o[s];            //p.id = "menu", active = "menuhover"

        }        

        p.n = n;                //p.n = "dropdown"

        this.build();

    };

    init.prototype.build = function() {

        this.h = [];

        this.c = [];

        this.z = 1000;                            //the initial z-index value

        var s=document.getElementById(p.id).getElementsByTagName('ul');    //get all ul elements

        var l = s.length;

        var i = 0;

        p.speed = p.speed? p.speed*.1:.75;     //note that the speed is slower as the value increase

        for (; i < l; ++i) {

            var h = s[i].parentNode;                 //get the li element for each ul element

            this.h[i] = h;

            this.c[i] = s[i];

            h.onmouseover = new Function(p.n + '.show(' + i + ', 1)');

            h.onmouseout = new Function(p.n + '.show(' + i + ')');

        }

    };

    init.prototype.show = function(x, d) {        //the element index and the direction

        var c = this.c[x];        //get the sub menu ul

        var h = this.h[x];        //get the top level menu item li

        clearInterval(c.t);

        clearInterval(c.i);

        c.style.overflow = 'hidden';    //The overflow is clipped, and the rest of the content will be invisible

        if (d) {            //show the dropdown menu        

            if (p.active && h.className.indexOf(p.active) == -1) {

                h.className += ' ' + p.active;

            }

            if (p.fade || p.slide) {

                c.style.display = 'block';

                if (!c.m) {

                //the menu is mouse over for the first time, we do some initializaition here.

                    if (p.slide) {

                        c.style.visibility = 'hidden';

                        c.m = c.offsetHeight;

                        c.style.height = '0';

                        c.style.visibility = '';

                    } else {

                        c.m = 100;                                            

                        c.style.opacity = 0;

                        c.style.filter = 'alpha(opacity=0)';

                    }

                    c.v = 0;

                }

                if (p.slide) {

                    if (c.m == c.v) {                            //if the element is fully shown

                        c.style.overflow = 'visible';        //The overflow is not clipped. It renders outside the element's box.    

                    } else {

                        c.style.zIndex = this.z;

                        ++this.z;

                        c.i = setInterval(function() {

                            slide(c, c.m, 1)            //open up the submenu

                        }, 20);

                    }

                } else {

                    c.style.zIndex = this.z;

                    ++this.z;

                    c.i = setInterval(function() {

                        slide(c, c.m, 1);                //open up the submenu

                    }, 20);

                }

            } else {

                c.style.zIndex = this.z;

                c.style.display = 'block';

            }

        } else {

            c.t = setTimeout(function() {

                hide(c, p.fade||p.slide? 1:0, h, p.active);    

            }, p.timeout);

        }

    }

    function hide(c, t, h, s) {

        if (s) {

            h.className = h.className.replace(s, '');        

        }

        if (t) {                                                //if fade or slide effect is defined

            c.i = setInterval(function() {

                slide(c, 0, -1);                            //close up the sub menu

            }, 20);

        } else {                                                //if no special effect, just make the submenu not visible

            c.style.display = 'none';

        }

    }

    function slide(c, t, d) {                        //d is the slide direction

        if (c.v == t) {        //when the visible part (c.v) reached the preset limit (t), we stop the function

            clearInterval(c.i); 

            c.i = 0;

            if (d == 1) {

                if (p.fade) {

                    c.style.filter = '';

                    c.style.opacity = 1;

                } 

                c.style.overflow = 'visible';

            }

        } else {

            c.v = (t - Math.floor(Math.abs(t - c.v) * p.speed) * d);    

            if (p.slide) {

                c.style.height = c.v + 'px';

            }

            if (p.fade) {

                var o = c.v/c.m;

                c.style.opacity = o;

                c.style.filter = 'alpha(opacity=' + (o*100) + ')';

            }

        }

     }

     return {init:init}

}();

The dynamic effects are using event handler for onmouseover and onmouseout events of the menu, including the submenu.

The basic idea for slide effect is to use the overflow property. When set to hidden, the content of the html element will be clipped. In our case, the menu item will be shown partially. As we increase the html element height, more and more content will be shown, therefore a slide effect is seen.

For the fade effect, the opacity property is used. For IE, one shall use filter:alpha(opacity=x), where x is in the range of 0 to 100. For other browsers, use opacity:x, where x is from 0.0 to 1.0.

The web page is also similar to step 2. The main difference is the line that links to javascript file,

<script type=”text/javascript” src=”dropdown_menu.js”></script>

and the script to call the function defined in dropdown_menu.js right before the closing tag of body element,

<script type=”text/javascript”>
            var dropdown=new TINY.dropdown.init(“dropdown”, {id:’menu’, active:’menuhover’});
</script>

The dropdown menu will be like what you saw at the beginning of this post.

Note: This post is the study notes of the HTML dropdown menu written by Michael Leigeber. One can find the original post at here.

Android Tutorial–Programming with Calendar

One of the popular apps on Android device is the Calendar application. As one of the android app development philosophy is to make use of functionalities of other apps, it is expected by many developers that we can access the calendar data easily. However, the truth is NO!

Android doesn’t provide an official API for read/write Calendar data, probably because Android is going to change the calendar data format in future release.

The good news is there’re workarounds, more than one. This tutorial will list out those methods and provide an example at the end of the tutorial.

1. Google Calendar APIs

Google doesn’t provide Calendar API specifically for Android, but it does provide an Calendar API for more general use. The API supports .NET, Java and Python, so it is expected that it can be used in Android.

However, this method requires the app to establish a network connection through Internet (probably requires some kind of authentication), and then access the calendar data. As android already has Calendar app built in, it sounds dumb that we’ll need to connect to Internet, create a connection and access the data.

This approach is not explored in detail here. Interested readers could refer reference 2 for more details.

2. Android Calendar Private APIs

Android is open source, so it’s not difficult to find out that android actually has undocumented APIs for calendar (it’s just not public APIs).

This part will cover how to get list of calendars, how to read event, and how to add event.

2.0 Permission Declaration

In order to read calendar data, one needs to declare the “READ_CALENDAR” permission in AndroidManifest.xml file.

<uses-permission android:name="android.permission.READ_CALENDAR"></uses-permission>

 

For write to Calendar, there’s a another “WRITE_CALENDAR” permission.

<uses-permission android:name="android.permission.WRITE_CALENDAR"></uses-permission>

 

You can refer to the complete code at the end of the tutorial for exact position of the declaration.

2.1 List Calendars

One may have multiple calendars created in Calendar application. For me, I have the default calendar, a Singapore Holiday Calendar, and a third calendar from my other Gmail account.

The code to get a list of all calendars are as below,

class MyCalendar {
    public String name;
    public String id;
    public MyCalendar(String _name, String _id) {
        name = _name;
        id = _id;
    }
    @Override
    public String toString() {
        return name;
    }
}
private MyCalendar m_calendars[];
private void getCalendars() {
    String[] l_projection = new String[]{"_id", "displayName"};
    Uri l_calendars;
    if (Build.VERSION.SDK_INT >= 8 ) {
        l_calendars = Uri.parse("content://com.android.calendar/calendars");
    } else {
        l_calendars = Uri.parse("content://calendar/calendars");
    }
    Cursor l_managedCursor = this.managedQuery(l_calendars, l_projection, null, null, null);    //all calendars
    //Cursor l_managedCursor = this.managedQuery(l_calendars, l_projection, "selected=1", null, null);   //active calendars
    if (l_managedCursor.moveToFirst()) {
        m_calendars = new MyCalendar[l_managedCursor.getCount()];
        String l_calName;
        String l_calId;
        int l_cnt = 0;
        int l_nameCol = l_managedCursor.getColumnIndex(l_projection[1]);
        int l_idCol = l_managedCursor.getColumnIndex(l_projection[0]);
        do {
            l_calName = l_managedCursor.getString(l_nameCol);
            l_calId = l_managedCursor.getString(l_idCol);
            m_calendars[l_cnt] = new MyCalendar(l_calName, l_calId);
            ++l_cnt;
        } while (l_managedCursor.moveToNext());
    }
}

The code essentially queries the android calendar content provider and goes through the calendar list returned. The name and calendar id are stored in m_calendars[].

For android version less than 8, the uri for query calendars is content://calendar/*, while content://com.android.calendar/* is used for android version 8 and above.

2.2 Read Events

To read a list of Calendar events, one can create another query as illustrated below,

private void getLastThreeEvents() {
    Uri l_eventUri;
    if (Build.VERSION.SDK_INT >= 8 ) {
        l_eventUri = Uri.parse("content://com.android.calendar/events");
    } else {
        l_eventUri = Uri.parse("content://calendar/events");
    }
    String[] l_projection = new String[]{"title", "dtstart", "dtend"};
    Cursor l_managedCursor = this.managedQuery(l_eventUri, l_projection, "calendar_id=" + m_selectedCalendarId, null, "dtstart DESC, dtend DESC");
    //Cursor l_managedCursor = this.managedQuery(l_eventUri, l_projection, null, null, null);
    if (l_managedCursor.moveToFirst()) {
        int l_cnt = 0;
        String l_title;
        String l_begin;
        String l_end;
        StringBuilder l_displayText = new StringBuilder();
        int l_colTitle = l_managedCursor.getColumnIndex(l_projection[0]);
        int l_colBegin = l_managedCursor.getColumnIndex(l_projection[1]);
        int l_colEnd = l_managedCursor.getColumnIndex(l_projection[1]);
        do {
            l_title = l_managedCursor.getString(l_colTitle);
            l_begin = getDateTimeStr(l_managedCursor.getString(l_colBegin));
            l_end = getDateTimeStr(l_managedCursor.getString(l_colEnd));
            l_displayText.append(l_title + "n" + l_begin + "n" + l_end + "n----------------n");
            ++l_cnt;
        } while (l_managedCursor.moveToNext() && l_cnt < 3);
        m_text_event.setText(l_displayText.toString());
    }
}

The method above get the last three events from the selected calendar. Note that m-selectedCalendarId variable and getDateTimeStr() method are defined by us. You can refer to code at the end of the tutorial for a complete picture.

2.3 Create an Event

Similarly, one can insert a new event into Calendar database.

/*add an event to calendar*/
private void addEvent() {
    ContentValues l_event = new ContentValues();
    l_event.put("calendar_id", m_selectedCalendarId);
    l_event.put("title", "roman10 calendar tutorial test");
    l_event.put("description", "This is a simple test for calendar api");
    l_event.put("eventLocation", "@home");
    l_event.put("dtstart", System.currentTimeMillis());
    l_event.put("dtend", System.currentTimeMillis() + 1800*1000);
    l_event.put("allDay", 0);
    //status: 0~ tentative; 1~ confirmed; 2~ canceled
    l_event.put("eventStatus", 1);
    //0~ default; 1~ confidential; 2~ private; 3~ public
    l_event.put("visibility", 0);
    //0~ opaque, no timing conflict is allowed; 1~ transparency, allow overlap of scheduling
    l_event.put("transparency", 0);
    //0~ false; 1~ true
    l_event.put("hasAlarm", 1);
    Uri l_eventUri;
    if (Build.VERSION.SDK_INT >= 8 ) {
        l_eventUri = Uri.parse("content://com.android.calendar/events");
    } else {
        l_eventUri = Uri.parse("content://calendar/events");
    }
    Uri l_uri = this.getContentResolver().insert(l_eventUri, l_event);
    Log.v("++++++test", l_uri.toString());
}

The code above simply create a ContentValues and insert it into the database URI.

3. Sending Intent to Calendar

This method doesn’t require the READ/WRITE_CALENDAR permission. It sends out an intent to calendar, and users will be redirected to Calendar app to create the event. Note that this is not a documented method either.

Below is a method that adds an event to Android Calendar.

/*add an event through intent, this doesn't require any permission
* just send intent to android calendar
*/
private void addEvent2() {
   Intent l_intent = new Intent(Intent.ACTION_EDIT);
   l_intent.setType("vnd.android.cursor.item/event");
   //l_intent.putExtra("calendar_id", m_selectedCalendarId);  //this doesn't work
   l_intent.putExtra("title", "roman10 calendar tutorial test");
   l_intent.putExtra("description", "This is a simple test for calendar api");
   l_intent.putExtra("eventLocation", "@home");
   l_intent.putExtra("beginTime", System.currentTimeMillis());
   l_intent.putExtra("endTime", System.currentTimeMillis() + 1800*1000);
   l_intent.putExtra("allDay", 0);
   //status: 0~ tentative; 1~ confirmed; 2~ canceled
   l_intent.putExtra("eventStatus", 1);
   //0~ default; 1~ confidential; 2~ private; 3~ public
   l_intent.putExtra("visibility", 0);
   //0~ opaque, no timing conflict is allowed; 1~ transparency, allow overlap of scheduling
   l_intent.putExtra("transparency", 0);
   //0~ false; 1~ true
   l_intent.putExtra("hasAlarm", 1);
   try {
       startActivity(l_intent);
   } catch (Exception e) {
       Toast.makeText(this.getApplicationContext(), "Sorry, no compatible calendar is found!", Toast.LENGTH_LONG).show();
   }
}

This method is used in my android app, Advanced Phone Log.

4. A Complete and Runnable Example

The code snippet shown above are retrieved from an example. You can download the source code here, or find it at github.

Below are screenshots of the app.

234

Figure 1. Screenshots of Calendar Sample App

Reference:

1. Working with the Android Calendar: http://www.developer.com/ws/article.php/3850276/Working-with-the-Android-Calendar.htm

2. Google Calendar APIs and Tools: http://code.google.com/apis/calendar/data/2.0/developers_guide.html

YCbCr Color Space–An Intro and its Applications

Color space is a complicated topic. Colors don’t really exist, like dust does. We human being use colors to describe what we see. The most common way to describe what we see in terms of color is using combination of red, green and blue, which is referred as RGB color space.

A color space is simply a model of representing what we see in tuples. YCbCr is one of the popular color space in computing. It represents colors in terms of one luminance component/luma (Y), and two chrominance components/chroma(Cb and Cr).

Terms

YUV, Y’UV, YCbCr, Y’CbCr, YPbPr… All these terms cause lots of confusion. YUV and Y’UV are traditionally for analog encoding of color information in television system, it uses different conversion constants from YCbCr; while YCbCr or Y’CbCr are used for digital encoding of color information in computing systems; and YPbPr is the analog counterpart of YCbCr. Nowadays, YUV is also used in the digital video context, in which case it is almost equivalent to YCbCr.

Here we negnect the terminologies and focuses on understanding the colorspace and its usage in the context of computing world. From this point onwards, we’ll call it YCbCr.

Why YCbCr Color Space?

Study shows human eyes are sensitive to luminance, but not so sensitive to chrominance. For example, given an image below,

test2

Figure 1. A Color Image

One can use the matlab code below to display its Y, Cb, Cr component as color images or gray scale images.

%a program to display the image's ycbcr component

function [] = ycctest(imageName)

    rgb = imread(imageName);

    ycbcr = rgb2ycbcr(rgb);   %convert to yuv space

    %display the y component as a color image

    ycbcry = ycbcr;

    ycbcry(:,:,2) = 0;

    ycbcry(:,:,3) = 0;

    rgb1 = ycbcr2rgb(ycbcry);

    figure, imshow(rgb1);

    % display the cb component as a color image

    ycbcru = ycbcr;

    ycbcru(:,:,1) = 0;

    ycbcru(:,:,3) = 0;

    rgb2 = ycbcr2rgb(ycbcru);

    figure, imshow(rgb2);

    % display the cr component as a color image

    ycbcrv = ycbcr;

    ycbcrv(:,:,1) = 0;

    ycbcrv(:,:,2) = 0;

    rgb3 = ycbcr2rgb(ycbcrv);

    figure, imshow(rgb3);

    %display the y, cb, cr component as gray scale image

    figure,imshow(ycbcr(:,:,1));

    figure,imshow(ycbcr(:,:,2));

    figure,imshow(ycbcr(:,:,3));

end

Save the program as ycctest.m, and save the image as test.jpg. Then execute the program in matlab by typing command,

ycctest(‘./test.jpg’);

You’ll get the 3 color images for Y, Cb and Cr component from left to right as,

cyuv

Figure 2. Color Images formed by Y, Cb and Cr Components

You’ll also get another 3 gray-scale images for Y, Cb and Cr component from left to right,

gyuv

Figure 3. Gray-scale Images formed by Y, Cb and Cr Components

It’s not difficult to tell that our eyes perceive more info from the left most images in figure 2 and figure 3, which is formed by Y component of figure 1.

YCbCr color space makes use of this fact to achieve more efficient representation of scenes/images. It does so by separating the luminance and chrominance components of a scene, and use less bits for chrominance than luminance. The details of how to use less bits to represent chrominance is covered in Sub-sampling section below.

How does the Conversion Work?

The YCbCr image can converted to/from RGB image. There’re several standards defined for the conversion at different context. The conversion below is based on the conversion used in JPEG image compression.

The conversion can be expressed as equations below.

From 8-bit RGB to 8-bit YCbCr:

Y = 0.299R + 0.587G + 0.114B

Cb = 128 – 0.168736R – 0.331264G + 0.5B

Cr = 128 + 0.5R – 0.418688G – 0.081312B

From 8-bit YCbCr to 8-bit RGB:

R = Y + 1.402 (Cr – 128)

G = Y – 0.34414 (Cb – 128) – 0.71414(Cr – 128)

B = Y + 1.772 (Cb – 128)

Color Sub-sampling

The representation of YCbCr separates the luminance and chrominance, so the computing system can encode the image in a way that less bits are allocated for chrominance . This is done through color subsampling, which simply encodes chrominance components with lower resolution.

Here we cover four commonly used subsampling schemes: 4:4:4, 4:2:2, 4:2:0, and 4:1:1.

These four schemes are illustrated by the figure below [1],

subsample

Figure 4. Color Subsampling

The 4:4:4 is actually full resolution in both horizontal and vertical directions, there’s no subsampling done. 4:2:2 requires 1/2 resolution in horizontal direction; 4:1:1 requires 1/4 resolution in horizontal direction; and 4:2:0 means 1/2 resolution in both horizontal and vertical directions.

There’re also 4:4:0 and 4:1:0 schemes, interested readers could refer to reference 2.

Note that the sub-sampling process is lossy when comparing the processed image with the original image. The amount of info left can be calculated by summing up all components and divide by 12 (or 16, if alpha/transparency component exists). For the four subsampling schemes we mentioned, the amount of info left is 100%, 8/16=50%, 6/16 = 37.5%, 6/16 = 37.5%. In this way, the computing system could represent the image with less bits.

But how the image is reconstructed for display? Normally the image requires a Y, Cb, Cr component at every pixel. The reconstruction is done through interpolation or simply use the CbCr value of nearby pixel if the pixel doesn’t have CbCr values. The details are not covered in this post.

Applications

YCbCr is a commonly used color space in digital video domain. Because the representation makes it easy to get rid of some redundant color information, it is used in image and video compression standards like JPEG, MPEG1, MPEG2 and MPEG4.

References:

Basics of Video: http://lea.hamradio.si/~s51kq/V-BAS.HTM

Chrominance Subsampling: http://dougkerr.net/pumpkin/articles/Subsampling.pdf

JPEG File Interchange Format: http://www.w3.org/Graphics/JPEG/jfif3.pdf

JPEG Standard–A Tutorial Based on Analysis of Sample Picture–Part 2. JPEG File

This is a follow up of the previous blog: JPEG Standard, A Tutorial Based on Analysis of Sample Picture – Part 1. Coding of a 8X8 Block.

The sample jpeg image used for analysis in this tutorial is below,

sample

Figure 1. Sample JPEG Image for Analysis of JPEG File

The most common name for a jpeg image file is .jpg and .jpeg. A JPEG file consists of many segments, each begin with a marker. A marker contains two or more bytes. The first byte is 0xFF, the second byte indicates what marker it is. The optional length bytes indicates the size of the payload data of the marker (including the length bytes, excluding the first two marker bytes.) In case the marker payload data doesn’t align with byte boundary, the left bits are set to 1.

SOI

Given the sample image, the first two bytes are:  (You can save the sample image and view it using a hex editor.)

ff d8: it’s the SOI (Start Of Image) marker. As its name suggests, it indicates the start of jpeg image file. This marker has no payload data.

APPn

The next two bytes are:

ff e0: all marker in ff En (It’s called APPn marker) form indicates application specific section. It means some metadata follows.

The next two bytes indicates the length of the payload for the marker:

00 10: It means the data are 16 bytes, including 00 10. See below for the rest of 16 bytes.

4a 46 49 46 00 01 01 01 00 60 00 60 00 00

DQT

The next two bytes starts a new marker:

ff db: it’s the DQT (Define Quantization Table). It is follows by one or more quantization tables.

In the sample image, the following bytes are:

00 43: it indicates the payload data is 67 bytes (including 00 43).

01: quantization table info.  Bit 0..3: QT number. Bit 4..7: QT precision.

Then the quantization table:

02 02 02 03 03 03 06 03

03 06 0c 08 07 08 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

0c 0c 0c 0c 0c 0c 0c 0c

 

SOF0

The next two bytes:

ff c0: SOF0 (Start of Frame, Baseline DCT). Indicates the image is a baseline DCT-based JPEG image.

The bytes followed,

00 11: 17 bytes of data. The rest of 15 bytes are:

08 01 20 01 ba 03 01 22 00 02 11 01 03 11 01

08: 8 bits per sample. JPEG also specifies 12 bits and 16 bits per sample. But most of the jpeg image will be 8 bits per sample.

01 20: 288. The height of the image.

01 ba: 442. The width of the image.

03: number of components. Gray image will be one. RGB or YCbCr image will be 3.

For every component, there’ll be 3 bytes.

01 22 00: 01, component id; 22, component frequency, 0..3 bits (2) for vertical, 4..7 bits (2) for horizontal; 00, quantization table number.

02 11 01: 02, component id; 11, component frequency, 1 for vertical, 1 for horizontal; 01, quantization table number.

03 11 01: 03, component id; 11, component frequency, 1 for vertical, 1 for horizontal; 01, quantization table number.

Note: that’s why YCbCr has the sample ratio of 4 (2+2) : 2 (1+1) : 2 (1+1).

DHT

The next marker:

ff c4: DHT (Define Huffman Table). It specifies one or more Huffman tables.

The bytes that follows,

00 1f: 31 bytes.

00: HT info. 0..3 bits: HT number. 4th bit: HT type, 0 for DC table, 1 for AC table. 5..7 bits: must to 0.

Then there’re 16 bytes:

00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00: each byte represent the number of bytes for huffman code of length a particular length. For example, 00 means there’s no bytes for huffman code of length 1.

As 00 + 01 + 05 + 01 + …. + 00 = 12

The 12 bytes that follows are:

00 01 02 03 04 05 06 07 08 09 0a 0b

The next 3 markers are all DHT. With length of 181 bytes, 31 bytes and 181 bytes respectively.

SOS

After these three DHT markers, the next marker is:

ff  da: SOS (Start of Scan). Begins a top-to-bottom scan of the image. In baseline JPEG, there’s usually a single scan.

The bytes that follows,

00 0c: 12 bytes. With the rest of  the 10 bytes as below,

03 01 00 02 11 03 11 00 3f 00

03: number of components in the scan. Normally is 3 for color image.

01 00: component id, 01; Huffman table used: bits 0..3: AC table, here is 0. bits 4..7: DC table, which is 0.

02 11: component id, 02; Huffman table used: bits 0..3: AC table, here is 1. bits 4..7: DC table, which is 1.

03 11: component id, 03; Huffman table: AC table 1, DC table 1.

00 3f 00: ignored.

Entropy-encoded Data

The the entropy-encoded jpeg image data (check out part 1 for more detail) follow. One thing one needs to take note is for entropy data, there’s 0x00 byte follows any 0xff byte. This is to avoid the confusion with marker bytes. This technique is called byte stuffing.

EOI

The next marker is at the end of the jpeg file,

ff d9: EOI (End Of Image). It’s also the last two bytes of the jpeg file. As its name suggests, it indicates the end of the jpeg image.

There’re some other markers that are not used in the sample image, one can refer to reference part for more information.

Reference:

Wikipedia JPEG: http://en.wikipedia.org/wiki/JPEG

Reading Plan for Web Dev

Well, web app is going to be hot! I have no doubt about it. As the speed of internet progresses constantly, the ability of the browser advances, web apps are going to be fancier and fancier. Eventually web app will be able to compete with native apps in terms of features, functionalities and responsiveness. 

So I decided to start picking up web app development skills. Below is a list of books I’m going to read.

HTML: Dive into HTML5, by Mark Pilgrim [also known as HTML5, Up and Running, finished on 13 Aug 2011]

html5This book doesn’t introduce HTML from ground up. It focuses on the new and exciting features that HTML5 is/will bring to the web.

The books includes detailed explanation for <video><audio><cavans>, new input types, multimedia formats for html5, geolocation, offline application, and html feature detection.

It gives lots of mark up examples and illustrate how easy it is to update the old html pages to html5 page without breaking anything.

CSS: CSS: The Definitive Guide, by Eric Meyer [finished on 24 Aug 2011]

css

This is also a good book. It covers CSS in a great detail. But the book is a bit too detailed from my personl POV. It states a lot of points repeatedly, which I don’t like so much. Also the book doesn’t talks a lot about how to design a really complicated style in a step-by-step manner, which I really want to know.

I read the old version of the book, hopefully the latest edition could have some improvements.

Other than that, the book is pretty good reference.

JavaScript: Javascript: the Definitive Guide, by David Flanagan [finished on 18 Aug 2011]

javascript

I read an old version of this book, published at 1998. I think both javascript and the book have changed a lot since then. But it’s still a good book to read.

It doesn’t only explain the javascript language well, but also illustrates the js in the context of web browsers. Actually a lot of js we’re writing in web apps also need understanding of web browsers, and the predefined javascript objects used to manipulate different parts of the browsers.

I’ll probably buy a latest version of this book and read it again another time.