Click here to Skip to main content
Click here to Skip to main content

Film App for Windows 8 Desktop

, 26 Oct 2012
Rate this:
Please Sign up or sign in to vote.
Watch public domain and Indie films in an ultrabook packaged desktop webapp. Use Film App as an offline scheduler for local Film Festivals to support Indie film screenings.

Introduction

I am building Film App packaged with encapsulator 2 for Ultrabook, as a desktop App. Film App delivers over 150 public domain and Independent (Indie) films in a desktop web app, showcasing touch based responsiveness, html5/CSS3 capabilities, and the video encoding power of Ultrabook. While most real world activities are moving exclusively online, Indie Film has fans who enjoy attending real film festivals to support local filmmakers. Film App allows fans to connect with the work of local filmamkers online, support the craft by attending local film festivals, and discover the next great film by using offline schedules. Filmmakers can use this powerful video processing tool to encode thier creative projects much faster. Film App: Watch, Share, Post, Attend.

Code snippets listed below showcase touch as a first class citizen. Swipe gestures are used to directly manipulate large touch targets which throw pixels across the screen. The streamlined code emphasizes power savings by not relying on wifi but instead using browser based local sqlite database for data storage of comments and schedules to bypass intermittent, expensive, power hungry online lookups.

Skeuomorphism vs. Digital User Interfaces

Web development after the dot.com crash of the late 90s attracted designers who relied heavily on skeuomorphism to convey their real world ideas online. Skeuomorphism is taking a familiar real world object, then applying it to new environments to keep a nostalgic comfortable idea. For example, website User Interfaces (UI) used crumpled textured backgrounds to make sites look like old yellowing paper; dark wood textures to convey hand crafted roughness; brushed metal for a highly polished feel. A more recent trend has been to use letterpress text with muted textures in digital UI. These are artificial devices which pretend to bring warmth and familiarity, but digital interfaces have no need for old world references, and can be so much more efficient by removing fake textures or other skeuomorphs. Windows 8 takes this to an extreme level by removing all interface elements unless absolutely necessary. Instead of relying on buttons reflections, wood grain, or brushed metal interfaces Windows 8 demands heavy emphasis be placed on content, and that fonts with height and color variations be used creatively for design.

Background

Building Film App, I wanted to preserve and deliver classical public domain films in an organized, efficient way for modern online audiences. When I watch the films I transcode, I see artifacts and screen grain from early movies. These are not artificial skeuomorphic elements, they are actual dust and grain in movies shot on actual film in the early 1900s.

Because skeuomorphism became such a large part of interface design well into 2010, I absorbed these into my design. My early design for Film App had heavy textures and drop shadows.

And very heavy use of the Letterpress effect, as you can see in the logo below. Letterpress exists in traditional print. Letterpress creates a very deep impression in thick paper. This conveys expensive attention to detail on paper. You can see in my January 2011 logo that I created this impression of a heavy stamp onto oddly lit texture. The word 'Film' and the film sprockets are supposed to look as if they have been heavily etched onto this surface. Although a lot is going on in the background, I did keep the logo very simple. It still conveys film to me. A lot of this detailing is lost when the logo is resized to be displayed as a small app icon.

I have had to iteratively remove from the original design of Film App as I recognized unnecessary extra elements in my design. My original design placed everything in front of your eyes: Title, director, date, duration, description, and poster in a long vertical swipe. Giant headers and footers led to other sections. Clicking the poster would lead to a movie. Heavily expensive, because I'm presenting all the data upfront with lots of text and expecting the user to absorb everything all at once.

I have redesigned it simply to a poster, with year and duration overlay. Movie posters, no matter when they were designed, contain the title and all the emotional ideas of the film; each viewer can see from a single glance at my stripped down essentials, whether they would be interested in watching based on emotional response, time constraints, and year of interest. Giant headers and footers have been replaced by the logo. Very large text delineates genres. Subtle color variations define the type of genre. And this is where touch gets to shine. Upon starting this app, the first required user input is touch gesture. Swipe left or right to see other films in this genre. Swipe up or down to see more genres. Quick glance at times and dates, swipe to find an item of interest, click to start watching. Touch, the first class citizen, is given the most important role: Usage upon first screen.

An interested user click will reveal the full poster, descriptive, director, along with the instantly playing film. Ultrabook's HTML5 shines here. Video is in h.264 format, fully controlled by the user. Multi column CSS3 breaks up large amounts of descriptive text, making content as visually appealing as traditional newsprint. Initial dropcaps, to delineate the start of this thought. Poster of the film placed inline within descriptive, another feature of CSS3. Just below, all related films. Again, the swipe gesture to call up more film posters. So instead of relying on Next buttons, I use touchmove swipes to get to the next set of movie options. Instead of using arrows to find the previous/next movies, Related Films again employ swipe gestures. Touch is made into a first class citizen. Although very small next/previous text is listed for non touch screen devices, the user is encouraged wherever possible to touch their ultrabook. To move to the next options, swipe large touch targets with a very natural hand gesture instead of clicking a tiny button or link target with mouse precision. The swiping gesture allows me to remove arrows, text, instructions, and other UI elements which distract from the actual content.

Using the code

Frameworks have greatly removed the building of repetitive, routine tasks away from programmers, at the cost of filesize. The current popular framework jQuery is sometimes used as a crutch to speed app delivery, instead of optimizing code. The unzipped, unminified latest version of jQuery clocks in at a very heavy 260kb. Built in functions handle ajax, multi browser compatibility, chaining, and hundreds of other framework capabilities. But in building for speedy touch response in Ultrabook I know the encapsulator incorporates the Chromium WebKit browser. I don't need to chain functions or worry about cross compatibility. I only need to use touchmove javascript functions for onscreen swipe. Brad Birdsall's beautiful swipejs.com flies at only 8kb without compression tricks. Swipejs takes multiple images and creates a touch based slider with 1:1 movement and resistant bounds. By removing my dependency on jQuery (260kb) as an encompassing solution and using swipejs (8kb) I have saved 32 times the weight of a single library, simply to throw pixels across the screen using a swipe gesture in the Chromium webkit based browser. The entirety of swipe.js is listed below, and this controls all pixels thrown around the screen.

/*
 * Swipe 1.0
 *
 * Brad Birdsall, Prime
 * Copyright 2011, Licensed GPL & MIT
 *
*/

window.Swipe = function(element, options) {

  // return immediately if element doesn't exist
  if (!element) return null;

  var _this = this;

  // retreive options
  this.options = options || {};
  this.index = this.options.startSlide || 0;
  this.speed = this.options.speed || 300;
  this.callback = this.options.callback || function() {};
  this.delay = this.options.auto || 0;

  // reference dom elements
  this.container = element;
  this.element = this.container.children[0]; // the slide pane

  // static css
  this.container.style.overflow = 'hidden';
  this.element.style.listStyle = 'none';
  this.element.style.margin = 0;

  // trigger slider initialization
  this.setup();

  // begin auto slideshow
  this.begin();

  // add event listeners
  if (this.element.addEventListener) {
    this.element.addEventListener('touchstart', this, false);
    this.element.addEventListener('touchmove', this, false);
    this.element.addEventListener('touchend', this, false);
    this.element.addEventListener('webkitTransitionEnd', this, false);
    this.element.addEventListener('msTransitionEnd', this, false);
    this.element.addEventListener('oTransitionEnd', this, false);
    this.element.addEventListener('transitionend', this, false);
    window.addEventListener('resize', this, false);
  }

};

Swipe.prototype = {

  setup: function() {

    // get and measure amt of slides
    this.slides = this.element.children;
    this.length = this.slides.length;

    // return immediately if their are less than two slides
    if (this.length < 2) return null;

    // determine width of each slide
    this.width = Math.ceil(("getBoundingClientRect" in this.container) ? this.container.getBoundingClientRect().width : this.container.offsetWidth);

    // return immediately if measurement fails
    if (!this.width) return null;

    // hide slider element but keep removeding during setup
    this.container.style.visibility = 'hidden';

    // dynamic css
    this.element.style.width = Math.ceil(this.slides.length * this.width) + 'px';
    var index = this.slides.length;
    while (index--) {
      var el = this.slides[index];
      el.style.width = this.width + 'px';
      el.style.display = 'table-cell';
      el.style.verticalAlign = 'top';
    }

    // set start removed and force translate to remove initial flickering
    this.slide(this.index, 0); 

    // show slider element
    this.container.style.visibility = 'visible';

  },

  slide: function(index, duration) {

    var style = this.element.style;

    // fallback to default speed
    if (duration == undefined) {
        duration = this.speed;
    }

    // set duration speed (0 represents 1-to-1 scrolling)
    style.webkitTransitionDuration = style.MozTransitionDuration = style.msTransitionDuration = style.OTransitionDuration = style.transitionDuration = duration + 'ms';

    // translate to given index position
    style.MozTransform = style.webkitTransform = 'translate3d(' + -(index * this.width) + 'px,0,0)';
    style.msTransform = style.OTransform = 'translateX(' + -(index * this.width) + 'px)';

    // set new index to allow for expression arguments
    this.index = index;

  },

  getPos: function() {
    
    // return current index position
    return this.index;

  },

  prev: function(delay) {

    // cancel next scheduled automatic transition, if any
    this.delay = delay || 0;
    clearTimeout(this.interval);

    // if not at first slide
    if (this.index) this.slide(this.index-1, this.speed);

  },

  next: function(delay) {

    // cancel next scheduled automatic transition, if any
    this.delay = delay || 0;
    clearTimeout(this.interval);

    if (this.index < this.length - 1) this.slide(this.index+1, this.speed); // if not last slide
    else this.slide(0, this.speed); //if last slide return to start

  },

  begin: function() {

    var _this = this;

    this.interval = (this.delay)
      ? setTimeout(function() { 
        _this.next(_this.delay);
      }, this.delay)
      : 0;
  
  },
  
  stop: function() {
    this.delay = 0;
    clearTimeout(this.interval);
  },
  
  resume: function() {
    this.delay = this.options.auto || 0;
    this.begin();
  },

  handleEvent: function(e) {
    switch (e.type) {
      case 'touchstart': this.onTouchStart(e); break;
      case 'touchmove': this.onTouchMove(e); break;
      case 'touchend': this.onTouchEnd(e); break;
      case 'webkitTransitionEnd':
      case 'msTransitionEnd':
      case 'oTransitionEnd':
      case 'transitionend': this.transitionEnd(e); break;
      case 'resize': this.setup(); break;
    }
  },

  transitionEnd: function(e) {
    
    if (this.delay) this.begin();

    this.callback(e, this.index, this.slides[this.index]);

  },

  onTouchStart: function(e) {
    
    this.start = {

      // get touch coordinates for delta calculations in onTouchMove
      pageX: e.touches[0].pageX,
      pageY: e.touches[0].pageY,

      // set initial timestamp of touch sequence
      time: Number( new Date() )

    };

    // used for testing first onTouchMove event
    this.isScrolling = undefined;
    
    // reset deltaX
    this.deltaX = 0;

    // set transition time to 0 for 1-to-1 touch movement
    this.element.style.MozTransitionDuration = this.element.style.webkitTransitionDuration = 0;
    
    e.stopPropagation();
  },

  onTouchMove: function(e) {

    // ensure swiping with one touch and not pinching
    if(e.touches.length > 1 || e.scale && e.scale !== 1) return;

    this.deltaX = e.touches[0].pageX - this.start.pageX;

    // determine if scrolling test has run - one time test
    if ( typeof this.isScrolling == 'undefined') {
      this.isScrolling = !!( this.isScrolling || Math.abs(this.deltaX) < Math.abs(e.touches[0].pageY - this.start.pageY) );
    }

    // if user is not trying to scroll vertically
    if (!this.isScrolling) {

      // prevent native scrolling 
      e.preventDefault();

      // cancel slideshow
      clearTimeout(this.interval);

      // increase resistance if first or last slide
      this.deltaX = 
        this.deltaX / 
          ( (!this.index && this.deltaX > 0               // if first slide and sliding left
            || this.index == this.length - 1              // or if last slide and sliding right
            && this.deltaX < 0                            // and if sliding at all
          ) ?                      
          ( Math.abs(this.deltaX) / this.width + 1 )      // determine resistance level
          : 1 );                                          // no resistance if false
      
      // translate immediately 1-to-1
      this.element.style.MozTransform = this.element.style.webkitTransform = 'translate3d(' + (this.deltaX - this.index * this.width) + 'px,0,0)';
      
      e.stopPropagation();
    }

  },

  onTouchEnd: function(e) {

    // determine if slide attempt triggers next/prev slide
    var isValidSlide = 
          Number(new Date()) - this.start.time < 250      // if slide duration is less than 250ms
          && Math.abs(this.deltaX) > 20                   // and if slide amt is greater than 20px
          || Math.abs(this.deltaX) > this.width/2,        // or if slide amt is greater than half the width

    // determine if slide attempt is past start and end
        isPastBounds = 
          !this.index && this.deltaX > 0                          // if first slide and slide amt is greater than 0
          || this.index == this.length - 1 && this.deltaX < 0;    // or if last slide and slide amt is less than 0

    // if not scrolling vertically
    if (!this.isScrolling) {

      // call slide function with slide end value based on isValidSlide and isPastBounds tests
      this.slide( this.index + ( isValidSlide && !isPastBounds ? (this.deltaX < 0 ? 1 : -1) : 0 ), this.speed );

    }
    
    e.stopPropagation();
  }

};

In the html doc I simply list the slider as an id, with the swipe class. Fill the unordered list with poster images of films to be swiped. At this point I have simplified my pixel throwing across the screen from a heavy 250+kb library, and slimmed it down to just over 8kb. The power of intel Ultrabook processors should make throwing lots of these pixels in horizontal and vertical directions very smooth.

<h2>Classics</h2>

<div id='slider' class='swipe'>
  <ul>
    <li style='display:block'><div><img src="images/classicfilm001.jpg"></div></li>
    <li style='display:none'><div><img src="images/classicfilm002.jpg"></div></li>
    <li style='display:none'><div><img src="images/classicfilm003.jpg"></div></li>
    <li style='display:none'><div><img src="images/classicfilm004.jpg"></div></li>
    <li style='display:none'><div><img src="images/classicfilm005.jpg"></div></li>
  </ul>
</div>

<a href='#' onclick='slider.prev();return false;'>prev</a> 
<a href='#' onclick='slider.next();return false;'>next</a>
<br><br>

At the very bottom of the page, to keep up speed of loading, add a reference to swipe.js; below that, add more javascript to specify which ID to act upon, control duration of each slide if a user is not swiping, and speed of next slide.

script src='js/swipe.js' /script

// slider
var slider = new Swipe(document.getElementById('slider'), {
    //startSlide: 1,
    speed: 400,
    auto: 10000,
      callback: function(e, pos) {
        
        var i = bullets.length;
        while (i--) {
          bullets[i].className = ' ';
        }
        bullets[pos].className = 'on';

      }
    }),
    bullets = document.getElementById('position').getElementsByTagName('em'),

// tabs
    tabs = new Swipe(document.getElementById('tabs'), {
      callback: function(event,index,elem) {
        setTab(selectors[index]);
      }
    }),
    selectors = document.getElementById('tabSelector').children;

for (var i = 0; i < selectors.length; i++) {
  var elem = selectors[i];
  elem.setAttribute('data-tab', i);
  elem. önclick = function(e) {
    e.preventDefault();
    setTab(this);
    tabs.slide(parseInt(this.getAttribute('data-tab'),10),300);
  }
}

function setTab(elem) {
  for (var i = 0; i < selectors.length; i++) {
    selectors[i].className = selectors[i].className.replace('on',' ');
  }
  elem.className += ' on';
}

// url bar hiding
(function() {
    
  var win = window,
      doc = win.document;

  // If there's a hash, or addEventListener is undefined, stop here
  if ( !location.hash || !win.addEventListener ) {

    //scroll to 1
    window.scrollTo( 0, 1 );
    var scrollTop = 1,

    //reset to 0 on bodyready, if needed
    bodycheck = setInterval(function(){
      if( doc.body ){
        clearInterval( bodycheck );
        scrollTop = "scrollTop" in doc.body ? doc.body.scrollTop : 1;
        win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
      } 
    }, 15 );

    if (win.addEventListener) {
      win.addEventListener("load", function(){
        setTimeout(function(){
          //reset to hide addr bar at onload
          win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
        }, 0);
      }, false );
    }
  }

})();


A similar size and complexity optimization can be applied to writing Ajax code. When users post comments on each movie, I don't want the movie to stop, or any other UI to reload while posting this user's review sentence or two. Instead of using jQuery, which checks for every possible use case, I can write the most basic ajax functionality to handle the comment post. In late 2005 after I read Jesse James Garret's paper " Ajax: A New Approach to Web Applications ", I used the code below when posting information from my comment forms.

   
   var http_request = false;
   function makePOSTRequest(url, parameters) {
      http_request = false;
      if (window.XMLHttpRequest) { // Mozilla, Safari,...</font>
         http_request = new XMLHttpRequest();
         if (http_request.overrideMimeType) {
         	// set type accordingly to anticipated content type
            //http_request.overrideMimeType('text/xml');
            http_request.overrideMimeType('text/html');
         }
      } else if (window.ActiveXObject) { // IE
         try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
         } catch (e) {
            try {
               http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
         }
      }
      if (!http_request) {
         alert('Cannot create XMLHTTP instance');
         return false;
      }
      
      http_request.onreadystatechange = alertContents;
      http_request.open('POST', url, true);
      http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      http_request.setRequestHeader("Content-length", parameters.length);
      http_request.setRequestHeader("Connection", "close");
      http_request.send(parameters);
   }

I greatly respect Microsoft's forward thinking in creating the XMLHTTP ActiveX control in 1999, and the fact that they quietly used it for Outlook web access in 2000 a full 5 years before it was appreciated by the wider world still astonishes me. But today I don't need to check for old IE implementations in my code or check for existence of XMLHttpRequest during every ajax request. Removing checks for the 2 versions of ActiveX objects required for IE, I can further optimize my ajax code, because again I know that only Chromium needs to be accounted for inside this encapsulator packaged Web App, and XMLHttpRequest does exist in this browser. The above code is the standard ajax we have all used since late 2005. First create a variable, then check for different browsers, set to html instead of xml, then check for multiple versions of IE ActiveXObjects, don't proceed if XMLHTTP doesn't exist. All the code which checks for IE browsers and the existence of the XMLHttpRequest can be deleted. So the simple ajax function to take and return my comment posts becomes a very nice and short few lines, listed below. It says assume you have all the Chromium WebKit tools available in the modern Ultrabook encapsulator packaged App, don't waste time checking to verify things you know exist. To make a POSTRequest, create new XMLHttpRequest. Then set the override mime type to accept returned html instead of xml. Done. The onreadystatechange, open, setRequestHeader lines, and send lines don't change.

	function makePOSTRequest(url, parameters) {
		http_request = new XMLHttpRequest();
		if (http_request.overrideMimeType) {
			http_request.overrideMimeType('text/html');
		}
	}

These are some of the optimizations I started thinking about, as I was trimming my UI. I identified and removed many skeuomorphs in my design and slimmed down giant unnecessary UI elements. Hunting for efficiency also resulted in trimming many lines of code. Free of all these nonessential checks and every possible outlier, code is actually fun to write. And it helps to have small codebases, because UI is super responsive.

CSS3 built into Ultrabook's Web App Chromium browser allows me to design very pleasing looking layouts. The dropcap is applied to the first letter of the first child in a paragraph, floated left, colored, sized, with some space around the character. Code is listed below. If you look at the poster image below the code, you'll see that the first character is giant, delineating a new thought for each film.

	p:first-child:first-letter {
		float: left;
		color: #5499c5;
		font-size: 5em;
		line-height:0.5em;
		display: inline;
		padding: 0.2em 0.1em 0 0.2em;
		text-transform: capitalize;
	}

	#multicolumnElement {
		width:960px;
		-webkit-column-width:300px;
		-webkit-column-gap:30px;
		margin-left:auto;
		margin-right:auto;
		height: 600px;
	}

Another similar nice CSS3 addition: multi column layout. Listed above, the CSS3 code gives me columns with a width of 300 pixels and a gap of 30 pixels. Centered on the page, no matter what the width of your device, columns should be 600 pixels high and fairly equally balanced. It took so many workarounds in previous generations of HTML and CSS to do layouts like this.

The Intel App Up encapsulator incorporates the Chromium WebKit Browser, which allows lots of CSS3 updates. I use multicolumns to beautifully break up my text in landscape mode. I will have to find out how to use CSS3 Media Queries to check for a different number of columns, at a greater height, when the device is in portrait mode.

Points of Interest

In building Film App my first thought was simply to collect public domain film and distribute it for pay in mobile app stores. So earlier this year I got my devices ready, got a booth at South by Southwest (SXSW), and hung out for 10 days, in Austin, Texas. My vendor badge is shown, on top of bags of information for music, film, and tech for SXSW 2012

South by Southwest is a creative environment where tech, film, and music intersect. And the resulting relationships which come from being in that space are absolutely amazing. I designed banners, a table runner, postcards.

And beautiful letterpress business cards. In this case I actually looked at all my year old skeuomorphic logo designs and created a template to show Rohner Letterpress in Chicago. They took my illustrations, cut some dies, and deeply press ed these designs into very thick paper. Letterpress looks amazing in real life. You can actually feel the indentations as you run a thumb across the card. It's an immediate attention getter, and everyone glances at the logo. Everyone. I can see now the distinction between a real letterpress design, where letters are actually pressed in thick paper and my skeuomorphic mostly fake looking 3d effects on flat digital screens. Windows 8 design standards demand doing away with the pretense of texture, shadow, reflections, and gradient effects in favor of optimizing for a modern, digital screen.

My hope was to display in March at Austin, and get fans for Film App. It worked; I did well. A few bloggers liked it, downloaded, and stopped by later to say they liked the simplicity of Film App.

I was prepared for that; ready to hand out cards, and have people appreciate my App. I wasn't prepared for suggestions from filmmakers and film festival directors. So while I spent the first 2 hours demoing and having a lot of fun showing my public domain film collection, I failed to realize that almost all the attendees at Austin are creators, not just consumers. I had accidentally stumbled upon an interested new set of markets. Instead of just finding consumers who wanted to watch, I ran across creators of Indie film. And organizers of film festivals who showcase these Indie films. They gave me tons of suggestions for how I could include their work in my app. In many cases like this, I have previously just taken down some info, and said I'd get back with some updates in a few weeks. But there was a particularly interested, persistent filmmaker who really wanted to have his film be seen digitally. He handed me a DVD of his 25 minute film, and asked how long I would be at SXSW. He also wanted to know if I could transcode and showcase his film in my Film App. He was standing with a friend, who ran a local festival, and this festival director wanted to know if I could show a localized film schedule with bios of filmmakers so she could distribute Film App at her festival to her tech savvy attendees.

Most App devs don't get an opportunity to recognize an emerging market realtime, forming in front of their eyes. And here at SXSW, within the first 2 hours of the show I got to see and began solving a much larger problem.

There was a break in the foot traffic to the showroom floor. A fellow booth vendor told me the Vimeo sponsored theaters started the machinery to show films back to back, in earnest. And tech heads were busy at conferences. The vendor said that there would be a break in the action; foot traffic would be like this, almost no attendee visitors for about 2 hours. So I pulled out the laptop and made simple, on the fly decisions of what I could reasonably build in an hour to show the next groups of filmmakers and festival directors who stopped by.

First a new screen, standard ajax polling to bring in realtime scheduling info and pull it down over wifi for each festival. Show this to the film festival director next time she stopped by - Simple schedule, ajax updated over wifi in case film showing times changed on the fly at the last minute.

Then open up HandBrake, to rip this DVD into h.264 so I could show the filmmaker. This 25 minute piece would take a few minutes to move onto my hard drive from DVD, then about 25 minutes to convert so I could include his film in my demo the next time he stopped by.

So while it's encoding on my laptop, I took a quick walk around this giant 500 vendor booth area, to get an idea of some of what else is around here. Lots of location aware apps. Lots of social networks. Almost everyone is having problems, because among this tech aware audience the wifi is maxed out. The social apps were not able to connect, and the few demos I saw were glossed over, because there was little access in this hallway where everyone was constantly on the web using their devices. The best parts of these social apps could not be demoed because they were dead without a connection. Location aware apps were draining device batteries, and these vendors were looking for outlets and time to charge up the few devices they brought to showcase their work. Because there's too much demand for wifi access in this hipster tech crowd, the location awareness polling over wifi was killing batteries. Users downloading these apps were deleting them as they visited booths, even though the concepts behind them were really thoughtful Apps.

Immediate decision time. As I walked back to my booth, with about an hour left before traffic picked up on my side of the floor, I had to reduce battery waste and allow access to crucial data without being completely reliant on wifi. Get rid of my ajax polling scheduler, Chromium browser has a bundled sqllite db, store data locally on device. Get rid of repeated polling and store data locally, to save batteries. If movie theaters are anything like SXSW showroom floors, attendees using Film App would probably run into the same lack of wifi and battery drain, so rethink and optimize Film App to prevent battery drain.

Instead of ajax polling for comments from the cloud database for each film, I set up a php script on the remote server to export all comments for that film into a text file . This text file was imported into a div in the app comment section. A user who posted her comments went directly into local sqllite storage, then displayed the comment in a div directly above the downloaded reverse chronological comments. Check for wifi connection. If it exists, post this comment to the cloud db.

If there isn't wifi from the previous line, post a flag in the local sqllite storage that the comment didn't make it to the cloud. The next film she clicked on, her sqllite db comments are extracted, then posted in the background to the cloud.

This changed my App from polling to get realtime comment info and draining battery, to only retrieving when a film is clicked on, posting only locally to the sqllite db, then posting user comments in the background to the cloud.

Another optimization: Remove repeated ajax polling to get scheduling info.

Same optimization used. Check all the cloud stored date of film, venue playing, then export to a text file. When a user opens up the app, check for wifi. Import latest text file of schedules. Don't poll anymore.

The file had finished encoding by this time, I wrote very simple insertion, update codes into my demo, packaged the encoded film in App, then deployed to tablets and phones.

Within minutes the filmmaker and festival director stopped by to get another look at the App on their way to get some more ribs. I showed them his film in App, then ran the localized schedule. No wifi the whole time. No worries, everything for this demo was packaged in the app. I asked about scheduling, and how often they worry about last minute changes. The Festival director said there are always changes last minute, but the vast majority of these Indie film festivals are planned months in advance. Small film festivals' largest worries are attendee numbers. But in order to have an excellent program, schedules are cemented months in advance. I asked about battery life vs. real time schedule, and both assured me it's a lot easier to get 95% correct data in App and let attendees know in person at the event about major changes. I expressed concern about polling for the latest data and not being up to the minute. The reply was, pressures on the floor of small understaffed Film Festivals prevent a dedicated staff member from posting changes online. All emergency changes are repeated endlessly in person over very loud microphones, and could I please package this up, so they could use this for their festival within a few weeks? Sales of Film App began in earnest. My original idea of preserving public domain films was an ok sales tactic. But delivering current Indie Films and listing offline schedules was much more compelling to this set of filmmakers and festival directors.

Unfortunately I had too many devices to keep track of, and could not encode enough filmmakers work while at SXSW. 7 months later, I am still trading emails and transcoding films from filmakers I met at SXSW 2012, as we get the time to follow up with each other.

I should mention that the major drawback of being a 1 person independent App Developer is the inability to be everywhere. Intel was actually demoing Ultrabooks during SXSW. In the very short time I had to visit other booths, I did not get to see their work. As a developer I was struggling with my laptop, devices, tablets, and phones. Any changes to my Film App, and I had to go to find power, boot up my laptop, compile, deploy to multiple devices, then secure my laptop. Not optimal when I'm trying to promote Film App at a crowded booth in a distant city.

Having a touch enabled Ultrabook would have prevented my having to bring along all these other demo devices. Ultrabook's light weight would allow me to demo on its touch enabled interface. Most tradeshows require me as a vendor to be on and engaged for a full 8 hours. Power is provided, but not having to worry about running out of charge is luxurious. Ultrabook battery life should give me enough power to demo videos in Film App, use swipe gestures to throw pixels across the screen, and be on all day.

The power of Intel video encoding optimizations would help me bring on board a lot more filmmakers. The Windows team has spent lots of time optimizing the way Windows 8 encodes video, as opposed to Windows 7. Check the "From the Building Windows 8 blog" link below, where the Windows 8 team lists extensive media capabilities they built into Windows 8. Check out especially the 8 minute video they have at the bottom. They talk about encoding video files at 6:30 - 6:50, just 20 seconds devoted to such amazing performance improvements.

In many tests I ran, I got almost 25% increase in speed of encode, for the same files on a dual boot Win8 / Win7 machine. The Ultrabooks processor may help me further cut down on video encode processing time. To clarify, I'm encoding 150 files, almost 2 hours each on average. A savings of 30 minutes, 150 times means almost 75 hours of saved time. This is so understated in all Windows 8 documentation. Filmmakers who spend their entire existence waiting for encodes can now have 25% more time in their life. And Ultrabook optimizations should further decrease that encode time.

If I attend SXSW as a vendor next year, I'll have a couple of Ultrabooks demoing the speed of film encodes. For filmmakers a minute or 2 saved means more attention can be paid to getting perfect sound, lighting, or a better performance from their cast. On average, video encoding for an hour long film in Windows 8 has saved me 15 minutes, over encoding the same film using Windows 7 on desktops. That's real power: Getting more done in the same amount of time.

Initially I hoped to build Film App to preserve public domain movies. I ran demos at SXSW in March 2012, and found out that 2 entirely new opportunities presented themselves: Filmmakers who want to showcase their work in Film App, and small local Film Festival directors who want to provide minimal offline schedules.

This Ultrabook Windows 8 Desktop version of Film App takes advantage of incredibly powerful HTML5 and CSS3 capabilities. I can optimize battery life, provide offline data, and most importantly, showcase how to encode video files 25% faster simply by using optimizations of modern Intel Ultrabook instrumentation. At my optimal video bitrates, I'm getting about 500Mb per hour of encoded film. I am currently getting ready to demo 150 films in Film App, at next year's 2013 South by Southwest festival.

This time around I can encourage filmmakers to encode and upload their work, so I can showcase their work in Film App while we are at the festival. Instead of waiting for weeks or months afterwards.

Film App: Watch, Share, Post, Attend

References

History

10:47pm, Chicago: Modified a title section, added a reference section near the end, extensive text revisions to reinforce CSS3 code, remove duplicate text 'playing', reduce sentence size, reorder for readability.

2:00am, Chicago: Multiple paragraph edits to very clearly identify that the Intel App Up encapsulator incorporates the Chromium WebKit browser for use of HTML5, CSS3 functionality.

9:30pm, Chicago: Added an image showing local filmmaker features and offline schedules, changed the plural word 'exists' to 'exist' for correctness, clarified 1 sentence into 2, to reinforce their meaning.

7:15pm, Chicago: Added an image references from website to show how CSS3 Dropcap and Multicolumn are being used in landscape mode. Switched code snippets and added an image to point out how and where CSS3 Drop Cap/Column code is used. Fixed a sloppy section where gender of a user was changed within the same sentence. Added a period in the second paragraph. Added more javascript to identify how swipe gesture was being used. . Removed red color formatting within PRE tags.

3pm, Chicago: Added 3 more image references from website, added a few more CSS3 code listings.

10am, Chicago: Added 9 references from website, because zipped image upload was not visible in article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Milan Shah
Web Developer Virtual Scribe
United States United States
No Biography provided

Comments and Discussions

 
Question"Ultrabook packages the Chromium Browser" PinadminChris Maunder25-Oct-12 16:08 
AnswerRe: "Ultrabook packages the Chromium Browser" PinmemberMilan Shah25-Oct-12 17:22 
GeneralRe: "Ultrabook packages the Chromium Browser" PinadminChris Maunder25-Oct-12 18:00 
GeneralRe: "Ultrabook packages the Chromium Browser" PinmemberMilan Shah25-Oct-12 20:30 
QuestionImages not added PinmemberMilan Shah25-Oct-12 5:03 
QuestionScreenshot? PinadminChris Maunder25-Oct-12 4:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140821.2 | Last Updated 27 Oct 2012
Article Copyright 2012 by Milan Shah
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid