Bug 862586 - Update webvtt parser to v0.4. r=cpearce
authorRalph Giles <giles@mozilla.com>
Tue, 16 Apr 2013 15:53:00 -0700
changeset 140066 795f8cd8ab328f35fc968c31d9a54ff8ad68014a
parent 140065 c42747a5cd4b437b5ca7e7d9674712acb97ebcb3
child 140067 dd6946291f4e5a7f7ed6017b183d862107347664
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs862586
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 862586 - Update webvtt parser to v0.4. r=cpearce Update media/webvtt to the v0.4 tag of https://github.com/mozilla/webvtt.git master
media/webvtt/README_MOZILLA
media/webvtt/alloc.c
media/webvtt/cue.c
media/webvtt/cue_internal.h
media/webvtt/cuetext.c
media/webvtt/include/webvtt/cue.h
media/webvtt/include/webvtt/node.h
media/webvtt/include/webvtt/string.h
media/webvtt/lexer.c
media/webvtt/node.c
media/webvtt/parser.c
media/webvtt/parser_internal.h
media/webvtt/string.c
media/webvtt/string_internal.h
--- a/media/webvtt/README_MOZILLA
+++ b/media/webvtt/README_MOZILLA
@@ -1,7 +1,7 @@
 These files are from the WebVTT library, and are extracted from rev
-6b637c9f30911433e6d5a5be5d8512317a0d8a14 of the git repository at
+c93accafb2087df7c678748e25bca21f1173979f of the git repository at
 https://github.com/mozilla/webvtt.
 
 The following CPPFLAGS are used in order to build and link in Mozilla
 -DWEBVTT_NO_CONFIG_H=1 -- Prevent inclusion of generated headers
 -DWEBVTT_STATIC=1      -- Compile as a static library
--- a/media/webvtt/alloc.c
+++ b/media/webvtt/alloc.c
@@ -41,22 +41,24 @@ struct {
   webvtt_alloc_fn_ptr alloc;
   webvtt_free_fn_ptr free;
   void *alloc_data;
 } allocator = { 0, default_alloc, default_free, 0 };
 
 static void *WEBVTT_CALLBACK
 default_alloc( void *unused, webvtt_uint nb )
 {
+  (void)unused;
   return malloc( nb );
 }
 
 static void WEBVTT_CALLBACK
 default_free( void *unused, void *ptr )
 {
+  (void)unused;
   free( ptr );
 }
 
 WEBVTT_EXPORT void
 webvtt_set_allocator( webvtt_alloc_fn_ptr alloc, webvtt_free_fn_ptr free, void *userdata )
 {
   /**
    * TODO:
--- a/media/webvtt/cue.c
+++ b/media/webvtt/cue.c
@@ -49,16 +49,17 @@ webvtt_create_cue( webvtt_cue **pcue )
    * Let cue's text track cue text position be 50.
    *
    * Let cue's text track cue size be 100.
    *
    * Let cue's text track cue alignment be middle alignment.
    */
   webvtt_ref( &cue->refs );
   webvtt_init_string( &cue->id );
+  webvtt_init_string( &cue->body );
   cue->from = 0xFFFFFFFFFFFFFFFF;
   cue->until = 0xFFFFFFFFFFFFFFFF;
   cue->snap_to_lines = 1;
   cue->settings.position = 50;
   cue->settings.size = 100;
   cue->settings.align = WEBVTT_ALIGN_MIDDLE;
   cue->settings.line = WEBVTT_AUTO;
   cue->settings.vertical = WEBVTT_HORIZONTAL;
@@ -78,16 +79,17 @@ webvtt_ref_cue( webvtt_cue *cue )
 WEBVTT_EXPORT void
 webvtt_release_cue( webvtt_cue **pcue )
 {
   if( pcue && *pcue ) {
     webvtt_cue *cue = *pcue;
     *pcue = 0;
     if( webvtt_deref( &cue->refs ) == 0 ) {
       webvtt_release_string( &cue->id );
+      webvtt_release_string( &cue->body );
       webvtt_release_node( &cue->node_head );
       webvtt_free( cue );
     }
   }
 }
 
 WEBVTT_EXPORT int
 webvtt_validate_cue( webvtt_cue *cue )
@@ -110,8 +112,14 @@ webvtt_validate_cue( webvtt_cue *cue )
      * payload, which is optional.
      */
     return 1;
   }
 
 error:
   return 0;
 }
+
+WEBVTT_INTERN webvtt_bool
+cue_is_incomplete( const webvtt_cue *cue ) {
+  return !cue || ( cue->flags & CUE_HEADER_MASK ) == CUE_HAVE_ID;
+}
+
--- a/media/webvtt/cue_internal.h
+++ b/media/webvtt/cue_internal.h
@@ -42,14 +42,11 @@ enum {
   CUE_HAVE_SETTINGS = (CUE_HAVE_VERTICAL | CUE_HAVE_SIZE
     | CUE_HAVE_POSITION | CUE_HAVE_LINE | CUE_HAVE_ALIGN),
 
   CUE_HAVE_CUEPARAMS = 0x40000000,
   CUE_HAVE_ID = 0x80000000,
   CUE_HEADER_MASK = CUE_HAVE_CUEPARAMS|CUE_HAVE_ID,
 };
 
-static webvtt_bool
-cue_is_incomplete( const webvtt_cue *cue ) {
-  return !cue || ( cue->flags & CUE_HEADER_MASK ) == CUE_HAVE_ID;
-}
+WEBVTT_INTERN webvtt_bool cue_is_incomplete( const webvtt_cue *cue );
 
 #endif
--- a/media/webvtt/cuetext.c
+++ b/media/webvtt/cuetext.c
@@ -24,21 +24,20 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <stdlib.h>
 #include <string.h>
 #include "parser_internal.h"
 #include "cuetext_internal.h"
+#include "node_internal.h"
 #include "cue_internal.h"
 #include "string_internal.h"
 
-static void webvtt_skipwhite( webvtt_byte **position );
-
 #ifdef min
 # undef min
 #endif
 #define min(a,b) ( (a) < (b) ? (a) : (b) )
 
 /**
  * ERROR macro used for webvtt_parse_cuetext
  */
@@ -62,29 +61,16 @@ do \
 
 #define CHECK_MEMORY_OP_JUMP(status_var, returned_status) \
   if( returned_status != WEBVTT_SUCCESS) \
   { \
     status_var = returned_status; \
     goto dealloc; \
   } \
 
-/**
- * This will only work on null-terminated strings, remember that!
- */
-static void
-webvtt_skipwhite( webvtt_byte **position )
-{
-  webvtt_byte *p = *position;
-  while( *p && webvtt_iswhite(*p) ) {
-    ++p;
-  }
-  *position = p;
-}
-
 WEBVTT_INTERN webvtt_status
 webvtt_create_token( webvtt_cuetext_token **token, webvtt_token_type token_type )
 {
   webvtt_cuetext_token *temp_token = (webvtt_cuetext_token *)webvtt_alloc0( sizeof(*temp_token) );
 
   if( !temp_token ) {
     return WEBVTT_OUT_OF_MEMORY;
   }
@@ -170,68 +156,64 @@ webvtt_delete_token( webvtt_cuetext_toke
     return;
   }  
   t = *token;
   
   /** 
    * Note that time stamp tokens do not need to free any internal data because 
    * they do not allocate anything. 
    */
-  switch( t->token_type ) {
-    case START_TOKEN:
-      data = t->start_token_data;
-      webvtt_release_stringlist( &data.css_classes );
-      webvtt_release_string( &data.annotations );
-      webvtt_release_string( &t->tag_name );
-      break;
-    case END_TOKEN:
-      webvtt_release_string( &t->tag_name );
-      break;
-    case TEXT_TOKEN:
-      webvtt_release_string( &t->text );
-      break;
+  if( t->token_type == START_TOKEN ) {
+    data = t->start_token_data;
+    webvtt_release_stringlist( &data.css_classes );
+    webvtt_release_string( &data.annotations );
+    webvtt_release_string( &t->tag_name );
+  } else if( t->token_type == END_TOKEN ) {
+    webvtt_release_string( &t->tag_name );
+  } else if( t->token_type == TEXT_TOKEN ) {
+    webvtt_release_string( &t->text );
   }
   webvtt_free( t );
   *token = 0;
 }
 
 WEBVTT_INTERN int
 tag_accepts_annotation( webvtt_string *tag_name )
 {
-  return webvtt_string_is_equal( tag_name, "v", 1 );
+  return webvtt_string_is_equal( tag_name, ( webvtt_byte * )"v", 1 );
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_node_kind_from_tag_name( webvtt_string *tag_name, webvtt_node_kind *kind )
 {
   if( !tag_name || !kind ) {
     return WEBVTT_INVALID_PARAM;
   }
 
   if( webvtt_string_length(tag_name) == 1 ) {
     switch( webvtt_string_text(tag_name)[0] ) {
-      case( UTF8_B ):
+      case( 'b' ):
         *kind = WEBVTT_BOLD;
         break;
-      case( UTF8_I ):
+      case( 'i' ):
         *kind = WEBVTT_ITALIC;
         break;
-      case( UTF8_U ):
+      case( 'u' ):
         *kind = WEBVTT_UNDERLINE;
         break;
-      case( UTF8_C ):
+      case( 'c' ):
         *kind = WEBVTT_CLASS;
         break;
-      case( UTF8_V ):
+      case( 'v' ):
         *kind = WEBVTT_VOICE;
         break;
     }
-  } else if( webvtt_string_is_equal( tag_name, "ruby", 4 ) ) {
+  } else if( webvtt_string_is_equal( tag_name, ( webvtt_byte * )"ruby", 4 ) ) {
     *kind = WEBVTT_RUBY;
-  } else if( webvtt_string_is_equal( tag_name, "rt", 2 ) ) {
+  } else if( webvtt_string_is_equal( tag_name, ( webvtt_byte * )"rt", 2 ) ) {
     *kind = WEBVTT_RUBY_TEXT;
   } else {
     return WEBVTT_INVALID_TAG_NAME;
   }
 
   return WEBVTT_SUCCESS;
 }
 
@@ -274,27 +256,27 @@ webvtt_create_node_from_token( webvtt_cu
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_data_state( webvtt_byte **position, webvtt_token_state *token_state, 
                    webvtt_string *result )
 {
   for ( ; *token_state == DATA; (*position)++ ) {
     switch( **position ) {
-      case UTF8_AMPERSAND:
+      case '&':
         *token_state = ESCAPE;
         break;
-      case UTF8_LESS_THAN:
+      case '<':
         if( webvtt_string_length(result) == 0 ) {
           *token_state = TAG;
         } else {
           return WEBVTT_SUCCESS;
         }
         break;
-      case UTF8_NULL_BYTE:
+      case '\0':
         return WEBVTT_SUCCESS;
         break;
       default:
         CHECK_MEMORY_OP( webvtt_string_putc( result, *position[0] ) );
         break;
     }
   }
 
@@ -323,55 +305,55 @@ webvtt_escape_state( webvtt_byte **posit
   webvtt_status status = WEBVTT_SUCCESS;
 
   CHECK_MEMORY_OP_JUMP( status, webvtt_create_string( 1, &buffer ) );
 
   /**
    * Append ampersand here because the algorithm is not able to add it to the
    * buffer when it reads it in the DATA state tokenizer.
    */
-  CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, UTF8_AMPERSAND ) );
+  CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, '&' ) );
 
   for( ; *token_state == ESCAPE; (*position)++ ) {
     /**
      * We have encountered a token termination point.
      * Append buffer to result and return success.
      */
-    if( **position == UTF8_NULL_BYTE || **position == UTF8_LESS_THAN ) {
+    if( **position == '\0' || **position == '<' ) {
       CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) );
       goto dealloc;
     }
     /**
      * This means we have enocuntered a malformed escape character sequence.
      * This means that we need to add that malformed text to the result and
      * recreate the buffer to prepare for a new escape sequence.
      */
-    else if( **position == UTF8_AMPERSAND ) {
+    else if( **position == '&' ) {
       CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) );
       webvtt_release_string( &buffer );
       CHECK_MEMORY_OP_JUMP( status, webvtt_create_string( 1, &buffer ) );
       CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, *position[0] ) );
     }
     /**
      * We've encountered the semicolon which is the end of an escape sequence.
      * Check if buffer contains a valid escape sequence and if it does append
      * the interpretation to result and change the state to DATA.
      */
-    else if( **position == UTF8_SEMI_COLON ) {
-      if( webvtt_string_is_equal( &buffer, "&amp", 4 ) ) {
+    else if( **position == ';' ) {
+      if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&amp", 4 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '&' ) );
-      } else if( webvtt_string_is_equal( &buffer, "&lt", 3 ) ) {
+      } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&lt", 3 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '<' ) );
-      } else if( webvtt_string_is_equal( &buffer, "&gt", 3 ) ) {
+      } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&gt", 3 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '>' ) );
-      } else if( webvtt_string_is_equal( &buffer, "&rlm", 4 ) ) {
+      } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&rlm", 4 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, rlm_replace, RLM_REPLACE_LENGTH ) );
-      } else if( webvtt_string_is_equal( &buffer, "&lrm", 4 ) ) {
+      } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&lrm", 4 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, lrm_replace, LRM_REPLACE_LENGTH ) );
-      } else if( webvtt_string_is_equal( &buffer, "&nbsp", 5 ) ) {
+      } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&nbsp", 5 ) ) {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, nbsp_replace, NBSP_REPLACE_LENGTH ) );
       } else {
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) );
         CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, **position ) );
       }
 
       *token_state = DATA;
       status = WEBVTT_UNFINISHED;
@@ -402,35 +384,35 @@ dealloc:
   return status;
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_tag_state( webvtt_byte **position, webvtt_token_state *token_state, 
                   webvtt_string *result )
 {
   for( ; *token_state == TAG; (*position)++ ) {
-    if( **position == UTF8_TAB || **position == UTF8_LINE_FEED ||
-        **position == UTF8_CARRIAGE_RETURN || **position == UTF8_FORM_FEED ||
-        **position == UTF8_SPACE ) {
+    if( **position == '\t' || **position == '\n' ||
+        **position == '\r' || **position == '\f' ||
+        **position == ' ' ) {
       *token_state = START_TAG_ANNOTATION;
     } else if( webvtt_isdigit( **position )  ) {
       CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) );
       *token_state = TIME_STAMP_TAG;
     } else {
       switch( **position ) {
-        case UTF8_FULL_STOP:
+        case '.':
           *token_state = START_TAG_CLASS;
           break;
-        case UTF8_SOLIDUS:
+        case '/':
           *token_state = END_TAG;
           break;
-        case UTF8_GREATER_THAN:
+        case '>':
           return WEBVTT_SUCCESS;
           break;
-        case UTF8_NULL_BYTE:
+        case '\0':
           return WEBVTT_SUCCESS;
           break;
         default:
           CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) );
           *token_state = START_TAG;
       }
     }
   }
@@ -438,29 +420,29 @@ webvtt_tag_state( webvtt_byte **position
   return WEBVTT_UNFINISHED;
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_start_tag_state( webvtt_byte **position, webvtt_token_state *token_state, 
                         webvtt_string *result )
 {
   for( ; *token_state == START_TAG; (*position)++ ) {
-    if( **position == UTF8_TAB || **position == UTF8_FORM_FEED ||
-        **position == UTF8_SPACE || **position == UTF8_LINE_FEED ||
-        **position == UTF8_CARRIAGE_RETURN ) {
+    if( **position == '\t' || **position == '\f' ||
+        **position == ' ' || **position == '\n' ||
+        **position == '\r' ) {
       *token_state = START_TAG_ANNOTATION;
     } else {
       switch( **position ) {
-        case UTF8_TAB:
+        case '\t':
           *token_state = START_TAG_ANNOTATION;
           break;
-        case UTF8_FULL_STOP:
+        case '.':
           *token_state = START_TAG_CLASS;
           break;
-        case UTF8_GREATER_THAN:
+        case '>':
           return WEBVTT_SUCCESS;
           break;
         default:
           CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) );
           break;
       }
     }
   }
@@ -473,27 +455,27 @@ webvtt_class_state( webvtt_byte **positi
                     webvtt_stringlist *css_classes )
 {
   webvtt_string buffer;
   webvtt_status status = WEBVTT_SUCCESS;
 
   CHECK_MEMORY_OP( webvtt_create_string( 1, &buffer ) );
 
   for( ; *token_state == START_TAG_CLASS; (*position)++ ) {
-    if( **position == UTF8_TAB || **position == UTF8_FORM_FEED ||
-        **position == UTF8_SPACE || **position == UTF8_LINE_FEED ||
-        **position == UTF8_CARRIAGE_RETURN) {
+    if( **position == '\t' || **position == '\f' ||
+        **position == ' ' || **position == '\n' ||
+        **position == '\r') {
       CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) );
       *token_state = START_TAG_ANNOTATION;
       return WEBVTT_SUCCESS;
-    } else if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) {
+    } else if( **position == '>' || **position == '\0' ) {
       CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) );
       webvtt_release_string( &buffer );
       return WEBVTT_SUCCESS;
-    } else if( **position == UTF8_FULL_STOP ) {
+    } else if( **position == '.' ) {
       CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) );
       webvtt_release_string( &buffer );
       CHECK_MEMORY_OP( webvtt_create_string( 1, &buffer ) );
     } else {
       CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, **position ) );
     }
   }
 
@@ -503,45 +485,45 @@ dealloc:
   return status;
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_annotation_state( webvtt_byte **position, webvtt_token_state *token_state, 
                          webvtt_string *annotation )
 {
   for( ; *token_state == START_TAG_ANNOTATION; (*position)++ ) {
-    if( **position == UTF8_NULL_BYTE || **position == UTF8_GREATER_THAN ) {
+    if( **position == '\0' || **position == '>' ) {
       return WEBVTT_SUCCESS;
     }
     CHECK_MEMORY_OP( webvtt_string_putc( annotation, **position ) );
   }
 
   return WEBVTT_UNFINISHED;
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_end_tag_state( webvtt_byte **position, webvtt_token_state *token_state, 
                       webvtt_string *result )
 {
   for( ; *token_state == END_TAG; (*position)++ ) {
-    if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) {
+    if( **position == '>' || **position == '\0' ) {
       return WEBVTT_SUCCESS;
     }
     CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) );
   }
 
   return WEBVTT_UNFINISHED;
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_timestamp_state( webvtt_byte **position, webvtt_token_state *token_state, 
                         webvtt_string *result )
 {
   for( ; *token_state == TIME_STAMP_TAG; (*position)++ ) {
-    if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) {
+    if( **position == '>' || **position == '\0' ) {
       return WEBVTT_SUCCESS;
     }
     CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) );
   }
 
   return WEBVTT_UNFINISHED;
 }
 
@@ -594,23 +576,19 @@ webvtt_cuetext_tokenizer( webvtt_byte **
         break;
       case END_TAG:
         status = webvtt_end_tag_state( position, &token_state, &result );
         break;
       case TIME_STAMP_TAG:
         status = webvtt_timestamp_state( position, &token_state, &result );
         break;
     }
-
-    if( token_state == START_TAG_ANNOTATION ) {
-      webvtt_skipwhite( position );
-    }
   }
 
-  if( **position == UTF8_GREATER_THAN )
+  if( **position == '>' )
   { (*position)++; }
   
   if( status == WEBVTT_SUCCESS ) {
     /**
      * The state that the tokenizer left off on will tell us what kind of token
      * needs to be made.
      */
     if( token_state == DATA || token_state == ESCAPE ) {
@@ -656,16 +634,26 @@ webvtt_parse_cuetext( webvtt_parser self
   webvtt_status status;
   webvtt_byte *position;
   webvtt_node *node_head;
   webvtt_node *current_node;
   webvtt_node *temp_node;
   webvtt_cuetext_token *token;
   webvtt_node_kind kind;
 
+  /**
+   *  TODO: Use these parameters! 'finished' isn't really important
+   * here, but 'self' certainly is as it lets us report syntax errors.
+   *
+   * However, for the time being we can trick the compiler into not
+   * warning us about unused variables by doing this.
+   */
+  ( void )self;
+  ( void )finished;
+
   if( !cue ) {
     return WEBVTT_INVALID_PARAM;
   }
 
   cue_text = webvtt_string_text( payload );
 
   if( !cue_text ) {
     return WEBVTT_INVALID_PARAM;
@@ -680,82 +668,76 @@ webvtt_parse_cuetext( webvtt_parser self
   current_node = node_head;
   temp_node = NULL;
   token = NULL;
 
   /**
    * Routine taken from the W3C specification
    * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-parsing-rules
    */
-  while( *position != UTF8_NULL_BYTE ) {
-    
+  while( *position != '\0' ) {
+    webvtt_status status = WEBVTT_SUCCESS; 
     webvtt_delete_token( &token );
 
     /* Step 7. */
-    switch( webvtt_cuetext_tokenizer( &position, &token ) ) {
-      case( WEBVTT_UNFINISHED ):
-        /* Error here. */
-        break;
-        /* Step 8. */
-      case( WEBVTT_SUCCESS ):
-
+    if( WEBVTT_FAILED( status = webvtt_cuetext_tokenizer( &position, 
+                                                          &token ) ) ) {
+      /* Error here. */
+    } else {
+      /* Succeeded... Process token */
+      if( token->token_type == END_TOKEN ) {
         /**
          * If we've found an end token which has a valid end token tag name and
          * a tag name that is equal to the current node then set current to the
          * parent of current.
          */
-        if( token->token_type == END_TOKEN ) {
+       if( current_node->kind == WEBVTT_HEAD_NODE ) {
           /**
            * We have encountered an end token but we are at the top of the list
            * and thus have not encountered any start tokens yet, throw away the
            * token.
            */
-          if( current_node->kind == WEBVTT_HEAD_NODE ) {
-            continue;
-          }
+          continue;
+        }
 
+        if( webvtt_node_kind_from_tag_name( &token->tag_name, &kind ) == WEBVTT_INVALID_TAG_NAME ) {
           /**
            * We have encountered an end token but it is not in a format that is
            * supported, throw away the token.
            */
-          if( webvtt_node_kind_from_tag_name( &token->tag_name, &kind ) == WEBVTT_INVALID_TAG_NAME ) {
-            continue;
-          }
+          continue;
+        }
 
+        if( current_node->kind == kind ) {
           /**
            * We have encountered an end token and it matches the start token of
            * the node that we are currently on. Move back up the list of nodes
            * and continue parsing.
            */
-          if( current_node->kind == kind ) {
-            current_node = current_node->parent;
-          }
+          current_node = current_node->parent;
+        }
+      } else {
+        /**
+         * Attempt to create a valid node from the token.
+         * If successful then attach the node to the current nodes list and
+         * also set current to the newly created node if it is an internal
+         * node type.
+         */
+        if( webvtt_create_node_from_token( token, &temp_node, current_node ) != WEBVTT_SUCCESS ) { 
+          /* Do something here? */ 
         } else {
+          webvtt_attach_node( current_node, temp_node );
 
-          /**
-           * Attempt to create a valid node from the token.
-           * If successful then attach the node to the current nodes list and
-           * also set current to the newly created node if it is an internal
-           * node type.
-           */
-          if( webvtt_create_node_from_token( token, &temp_node, current_node ) != WEBVTT_SUCCESS ) { 
-            /* Do something here? */ 
+          if( WEBVTT_IS_VALID_INTERNAL_NODE( temp_node->kind ) ) { 
+            current_node = temp_node; 
           }
-          else {
-            webvtt_attach_node( current_node, temp_node );
             
-            if( WEBVTT_IS_VALID_INTERNAL_NODE( temp_node->kind ) ) { 
-              current_node = temp_node; 
-            }
-            
-            /* Release the node as attach internal node increases the count. */
-            webvtt_release_node( &temp_node );
-          }
+          /* Release the node as attach internal node increases the count. */
+          webvtt_release_node( &temp_node );
         }
-        break;
+      }
     }
-    webvtt_skipwhite( &position );
   }
   
   webvtt_delete_token( &token );
   
   return WEBVTT_SUCCESS;
 }
--- a/media/webvtt/include/webvtt/cue.h
+++ b/media/webvtt/include/webvtt/cue.h
@@ -76,16 +76,17 @@ webvtt_cue_t {
   /**
     * PUBLIC:
     */
   webvtt_timestamp from;
   webvtt_timestamp until;
   webvtt_cue_settings settings;
   webvtt_bool snap_to_lines;
   webvtt_string id;
+  webvtt_string body;
 
   /**
     * Parsed cue-text (NULL if has not been parsed)
     */
   webvtt_node *node_head;
 } webvtt_cue;
 
 WEBVTT_EXPORT webvtt_status webvtt_create_cue( webvtt_cue **pcue );
--- a/media/webvtt/include/webvtt/node.h
+++ b/media/webvtt/include/webvtt/node.h
@@ -66,21 +66,30 @@ webvtt_node_kind_t {
   WEBVTT_TIME_STAMP = 257 | WEBVTT_NODE_LEAF,
 
   WEBVTT_NODE_LEAF_END = 257,
 
   /* An empty initial state for a node */
   WEBVTT_EMPTY_NODE = 258
 } webvtt_node_kind;
 
-#define WEBVTT_IS_LEAF(Kind) ( ((Kind) & WEBVTT_NODE_LEAF) != 0 )
-#define WEBVTT_NODE_INDEX(Kind) ( (Kind) & ~WEBVTT_NODE_LEAF )
-#define WEBVTT_IS_VALID_LEAF_NODE(Kind) ( WEBVTT_IS_LEAF(Kind) && (WEBVTT_NODE_INDEX(Kind) >= WEBVTT_NODE_LEAF_START && WEBVTT_NODE_INDEX(Kind) <= WEBVTT_NODE_LEAF_END ) )
-#define WEBVTT_IS_VALID_INTERNAL_NODE(Kind) ( (!WEBVTT_IS_LEAF(Kind)) && (WEBVTT_NODE_INDEX(Kind) >= WEBVTT_NODE_INTERNAL_START && WEBVTT_NODE_INDEX(Kind) <= WEBVTT_NODE_INTERNAL_END) )
-#define WEBVTT_IS_VALID_NODE_KIND(Kind) ( WEBVTT_IS_VALID_INTERNAL_NODE(Kind) || WEBVTT_IS_VALID_LEAF_NODE(Kind) )
+#define WEBVTT_IS_LEAF( Kind ) ( ( ( Kind ) & WEBVTT_NODE_LEAF) != 0 )
+#define WEBVTT_NODE_INDEX( Kind ) ( ( Kind ) & ~WEBVTT_NODE_LEAF )
+
+#define WEBVTT_IS_VALID_LEAF_NODE( Kind ) \
+  ( WEBVTT_IS_LEAF( Kind ) && \
+  ( WEBVTT_NODE_INDEX( Kind ) >= WEBVTT_NODE_LEAF_START && \
+    WEBVTT_NODE_INDEX( Kind ) <= WEBVTT_NODE_LEAF_END ) )
+
+#define WEBVTT_IS_VALID_INTERNAL_NODE( Kind ) \
+  ( ( !WEBVTT_IS_LEAF( Kind ) ) && \
+    (  WEBVTT_NODE_INDEX( Kind ) <= WEBVTT_NODE_INTERNAL_END ) )
+
+#define WEBVTT_IS_VALID_NODE_KIND( Kind ) \
+  ( WEBVTT_IS_VALID_INTERNAL_NODE( Kind ) || WEBVTT_IS_VALID_LEAF_NODE( Kind ) )
 
 struct webvtt_internal_node_data_t;
 
 typedef struct
 webvtt_node_t {
 
   struct webvtt_refcount_t refs;
   /**
--- a/media/webvtt/include/webvtt/string.h
+++ b/media/webvtt/include/webvtt/string.h
@@ -121,49 +121,50 @@ WEBVTT_EXPORT void webvtt_copy_string( w
  */
 WEBVTT_EXPORT const webvtt_byte *webvtt_string_text( const webvtt_string *str );
 
 /**
  * webvtt_string_length
  *
  * return the length of a strings text
  */
-WEBVTT_EXPORT const webvtt_uint32 webvtt_string_length( const webvtt_string *str );
+WEBVTT_EXPORT webvtt_uint32 webvtt_string_length( const webvtt_string *str );
 
 /**
  * webvtt_string_capacity
  *
  * return the current capacity of a string
  */
-WEBVTT_EXPORT const webvtt_uint32 webvtt_string_capacity( const webvtt_string *str );
+WEBVTT_EXPORT webvtt_uint32 webvtt_string_capacity( const webvtt_string *str );
 
 /**
  * webvtt_string_getline
  *
  * collect a line of text (terminated by CR/LF/CRLF) from a buffer, without 
  * including the terminating character(s)
  */
 WEBVTT_EXPORT int webvtt_string_getline( webvtt_string *str, const webvtt_byte *buffer,
-    webvtt_uint *pos, webvtt_uint len, int *truncate, webvtt_bool finish, webvtt_bool retain_new_line );
+    webvtt_uint *pos, int len, int *truncate, webvtt_bool finish );
 
 /**
  * webvtt_string_putc
  *
  * append a single byte to a webvtt string
  */
 WEBVTT_EXPORT webvtt_status webvtt_string_putc( webvtt_string *str, webvtt_byte to_append );
 
 
 /**
  * webvtt_string_is_equal
  *
  * compare a string's text to a byte array
  *
  */
-WEBVTT_EXPORT webvtt_bool webvtt_string_is_equal( webvtt_string *str, webvtt_byte *to_compare, webvtt_uint len );
+WEBVTT_EXPORT webvtt_bool webvtt_string_is_equal( const webvtt_string *str, 
+    const webvtt_byte *to_compare, int len );
 
 /**
  * webvtt_string_append
  *
  * append a stream of bytes to the string.
  *
  * if 'len' is < 0, then buffer is expected to be null-terminated.
  */
--- a/media/webvtt/lexer.c
+++ b/media/webvtt/lexer.c
@@ -120,17 +120,17 @@
 #define DEFAULT default:
 
 /**
  * Just for semantic clarity
  */
 #define OR
 #define AND
 
-#define OVERFLOW(X) \
+#define IF_OVERFLOW(X) \
   if( self->token_pos >= (sizeof(self->token) - 1 ) ) \
   { \
     RETURN(X) \
   }
 
 #define BEGIN_STATE(state) case state: { switch(c) {
 #define END_STATE DEFAULT BACKUP return BADTOKEN; } } break;
 #define END_STATE_EX } } break;
@@ -151,17 +151,16 @@ if(self->token_pos == sizeof(self->token
   return BADTOKEN; \
 }
 
 WEBVTT_INTERN webvtt_status
 webvtt_lex_word( webvtt_parser self, webvtt_string *str, const webvtt_byte *buffer, webvtt_uint *ppos, webvtt_uint length, webvtt_bool finish )
 {
   webvtt_status status = WEBVTT_SUCCESS;
   webvtt_uint pos = *ppos;
-  int d = 0;
   if( !str ) {
     return WEBVTT_INVALID_PARAM;
   }
 
   webvtt_init_string( str );
 
 # define ASCII_DASH (0x2D)
 # define ASCII_GT (0x3E)
@@ -188,16 +187,93 @@ webvtt_lex_word( webvtt_parser self, web
     }
   }
 
 _finished:
   *ppos = pos;
   return status;
 }
 
+/**
+ * webvtt_lex_newline
+ *
+ * Get newline sequence in re-entrant fashion. self->tstate must be
+ * L_START or L_NEWLINE0 for this function to behave correctly.
+ */
+WEBVTT_INTERN webvtt_token
+webvtt_lex_newline( webvtt_parser self, const
+  webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length,
+  webvtt_bool finish )
+{
+  webvtt_uint p = *pos;
+
+  /* Ensure that we've got a valid token-state for this use-case. */
+  DIE_IF( self->tstate != L_START && self->tstate != L_NEWLINE0 );
+
+  while( p < length ) {
+    webvtt_byte c = buffer[ p++ ];
+    self->token[ self->token_pos++ ] = c;
+    self->token[ self->token_pos ] = 0;
+    self->bytes++;
+
+    switch( self->tstate ) {
+      case L_START:
+        if( c == '\n' ) {
+          *pos = p;
+          return NEWLINE;
+        } else if( c == '\r' ) {
+          self->tstate = L_NEWLINE0;
+        } else {
+          goto backup;
+        }
+        break;
+      case L_NEWLINE0:
+        if( c == '\n' ) {
+          *pos = p;
+          self->tstate = L_START;
+          return NEWLINE;
+        } else {
+          goto backup;
+        }
+        break;
+
+      default:
+        /**
+         * This should never happen if the function is called correctly
+         * (EG immediately following a successful call to webvtt_string_getline)
+         */
+       goto backup;
+    }
+  }
+
+  *pos = p;
+  if( finish && ( p >= length ) ) {
+    /* If pos >= length, it's and 'finish' is set, it's an automatic EOL */
+    self->tstate = L_START;
+    return NEWLINE;
+  }
+
+  if( self->tstate == L_NEWLINE0 ) {
+    return UNFINISHED;
+  } else {
+    /* This branch should never occur, if the function is used properly. */
+    *pos = --p;
+    return BADTOKEN;
+  }
+backup:
+  self->token[ --self->token_pos ] = 0;
+  --self->bytes;
+  *pos = --p;
+  if( self->tstate == L_NEWLINE0 ) {
+    self->tstate = L_START;
+    return NEWLINE;
+  }
+  return BADTOKEN;
+}
+
 WEBVTT_INTERN webvtt_token
 webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish )
 {
   while( *pos < length ) {
     webvtt_byte c = buffer[(*pos)++];
     self->token[ self->token_pos++ ] = c;
     self->token[ self->token_pos ] = 0;
     self->column++;
@@ -264,31 +340,38 @@ webvtt_lex( webvtt_parser self, const we
         END_STATE
 
         BEGIN_STATE(L_SEP1)
           U_GT { RETURN(SEPARATOR) }
         END_STATE
 
         BEGIN_STATE(L_DIGIT0)
           U_DIGIT {
-            OVERFLOW(INTEGER)
+            IF_OVERFLOW(INTEGER)
             SET_STATE(L_DIGIT0)
           }
-          U_COLON { SET_STATE(L_TIMESTAMP1) }
+          U_COLON {
+            /* Don't return a TIMESTAMP if we start with '-' */
+            if( self->token[0] == '-' ) {
+              RETURN(INTEGER);
+            } else {
+              SET_STATE(L_TIMESTAMP1)
+            }
+          }
           U_PERCENT { RETURN(PERCENTAGE) }
         DEFAULT { BACKUP AND RETURN(INTEGER) }
         END_STATE_EX
 
         BEGIN_STATE(L_NEWLINE0)
           U_LF { SET_NEWLINE }
         DEFAULT { BACKUP AND SET_NEWLINE }
         END_STATE_EX
 
         BEGIN_STATE(L_WHITESPACE)
-          U_SPACE OR U_TAB { OVERFLOW(WHITESPACE) SET_STATE(L_WHITESPACE) }
+          U_SPACE OR U_TAB { IF_OVERFLOW(WHITESPACE) SET_STATE(L_WHITESPACE) }
         DEFAULT { BACKUP RETURN(WHITESPACE) }
         END_STATE_EX
 
         BEGIN_STATE(L_POSITION0)
           U_o { SET_STATE(L_POSITION1) }
         END_STATE
 
         BEGIN_STATE(L_POSITION1)
@@ -448,47 +531,52 @@ webvtt_lex( webvtt_parser self, const we
         END_STATE
 
         BEGIN_STATE(L_END1)
           U_d { RETURN(END) }
         END_STATE
 
         BEGIN_STATE(L_TIMESTAMP1)
           U_DIGIT {
-          OVERFLOW(BADTOKEN)
+          IF_OVERFLOW(BADTOKEN)
           SET_STATE(L_TIMESTAMP1)
         }
           U_COLON {
-          OVERFLOW(BADTOKEN)
+          IF_OVERFLOW(BADTOKEN)
           SET_STATE(L_TIMESTAMP2)
         }
           U_PERIOD {
-          OVERFLOW(BADTOKEN)
+          IF_OVERFLOW(BADTOKEN)
           SET_STATE(L_TIMESTAMP3)
         }
         END_STATE
 
         BEGIN_STATE(L_TIMESTAMP2)
           U_DIGIT {
-          OVERFLOW(BADTOKEN)
+          IF_OVERFLOW(BADTOKEN)
           SET_STATE(L_TIMESTAMP2)
         }
           U_PERIOD {
-          OVERFLOW(BADTOKEN)
+          IF_OVERFLOW(BADTOKEN)
           SET_STATE(L_TIMESTAMP3)
         }
         END_STATE
 
         BEGIN_STATE(L_TIMESTAMP3)
           U_DIGIT {
-          OVERFLOW(TIMESTAMP)
+          IF_OVERFLOW(TIMESTAMP)
           BREAK
         }
         DEFAULT {
           BACKUP
+		  /* Don't return a TIMESTAMP if we don't have at least one
+		     millisecond */
+          if( !webvtt_isdigit( self->token[ self->token_pos - 1 ] ) ) {
+            RETURN(BADTOKEN);
+          }
           RETURN(TIMESTAMP)
           BREAK
         }
         END_STATE_EX
 
         BEGIN_STATE(L_NOTE1)
           U_O { SET_STATE(L_NOTE2) }
         END_STATE
--- a/media/webvtt/node.c
+++ b/media/webvtt/node.c
@@ -27,17 +27,18 @@
  
  #include <string.h>
  #include <stdlib.h>
  #include "node_internal.h"
  
  static webvtt_node empty_node = {
   { 1 }, /* init ref count */
   0, /* parent */
-  WEBVTT_EMPTY_NODE /* node kind */
+  WEBVTT_EMPTY_NODE, /* node kind */
+  { { 0 } } /* value */
 };
 
 WEBVTT_EXPORT void
 webvtt_ref_node( webvtt_node *node )
 {
   if( node ) {
     webvtt_ref( &node->refs );
   }
--- a/media/webvtt/parser.c
+++ b/media/webvtt/parser.c
@@ -28,27 +28,26 @@
 #include "parser_internal.h"
 #include "cuetext_internal.h"
 #include "cue_internal.h"
 #include <string.h>
 
 #define _ERROR(X) do { if( skip_error == 0 ) { ERROR(X); } } while(0)
 
 static const webvtt_byte separator[] = {
-  UTF8_HYPHEN_MINUS, UTF8_HYPHEN_MINUS, UTF8_GREATER_THAN
+  '-', '-', '>'
 };
 
 #define MSECS_PER_HOUR (3600000)
 #define MSECS_PER_MINUTE (60000)
 #define MSECS_PER_SECOND (1000)
 #define BUFFER (self->buffer + self->position)
 #define MALFORMED_TIME ((webvtt_timestamp_t)-1.0)
 
 static webvtt_status find_bytes( const webvtt_byte *buffer, webvtt_uint len, const webvtt_byte *sbytes, webvtt_uint slen );
-static webvtt_status webvtt_skipwhite( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len );
 static webvtt_int64 parse_int( const webvtt_byte **pb, int *pdigits );
 static void skip_spacetab( const webvtt_byte *text, webvtt_uint *pos,
   webvtt_uint len, webvtt_uint *column );
 static void skip_until_white( const webvtt_byte *text, webvtt_uint *pos,
   webvtt_uint len, webvtt_uint *column );
 
 WEBVTT_EXPORT webvtt_status
 webvtt_create_parser( webvtt_cue_fn on_read,
@@ -124,16 +123,18 @@ cleanup_stack( webvtt_parser self )
       case V_TEXT:
         webvtt_release_string( &st->v.text );
         break;
         /**
          * TODO: Clean up cuetext nodes as well.
          * Eventually the cuetext parser will probably be making use
          * of this stack, and will need to manage it well also.
          */
+      default:
+        break;
     }
     st->type = V_NONE;
     st->line = st->column = st->token = 0;
     st->v.cue = NULL;
     if( st > self->stack ) {
       --self->top;
     }
     --st;
@@ -150,55 +151,84 @@ cleanup_stack( webvtt_parser self )
     self->stack_alloc = sizeof( self->astack ) / sizeof( *( self->astack ) );
     webvtt_free( pst );
   }
 }
 
 /**
  *
  */
-WEBVTT_EXPORT webvtt_status 
-webvtt_finish_parsing( webvtt_parser self ) 
+WEBVTT_EXPORT webvtt_status
+webvtt_finish_parsing( webvtt_parser self )
 {
   webvtt_status status = WEBVTT_SUCCESS;
-  
+  const webvtt_byte buffer[] = "\0";
+  const webvtt_uint len = 0;
+  webvtt_uint pos = 0;
+
   if( !self->finished ) {
     self->finished = 1;
-    
+
+retry:
     switch( self->mode ) {
       /**
        * We've left off parsing cue settings and are not in the empty state,
        * return WEBVTT_CUE_INCOMPLETE.
        */
       case M_WEBVTT:
-        if( self->top->type != V_NONE ) {
-          ERROR( WEBVTT_CUE_INCOMPLETE );
+        if( self->top->state == T_CUEREAD ) {
+          SAFE_ASSERT( self->top != self->stack );
+          --self->top;
+          self->popped = 1;
+        }
+
+        if( self->top->state == T_CUE ) {
+          webvtt_string text;
+          webvtt_cue *cue;
+          if( self->top->type == V_NONE ) {
+            webvtt_create_cue( &self->top->v.cue );
+            self->top->type = V_CUE;
+          }
+          cue = self->top->v.cue;
+          SAFE_ASSERT( self->popped && (self->top+1)->state == T_CUEREAD );
+          SAFE_ASSERT( cue != 0 );
+          text.d = (self->top+1)->v.text.d;
+          (self->top+1)->v.text.d = 0;
+          (self->top+1)->type = V_NONE;
+          (self->top+1)->state = 0;
+          self->column = 1;
+          status = webvtt_proc_cueline( self, cue, &text );
+          if( cue_is_incomplete( cue ) ) {
+            ERROR( WEBVTT_CUE_INCOMPLETE );
+          }
+          ++self->line;
+          self->column = 1;
+          if( self->mode == M_CUETEXT ) {
+            goto retry;
+          }
         }
         break;
-      /** 
+      /**
        * We've left off on trying to read in a cue text.
-       * Parse the partial cue text read and pass the cue back to the 
+       * Parse the partial cue text read and pass the cue back to the
        * application if possible.
        */
       case M_CUETEXT:
-        status = webvtt_parse_cuetext( self, self->top->v.cue, 
-                                       &self->line_buffer, self->finished );
-        webvtt_release_string( &self->line_buffer );
-        finish_cue( self, &self->top->v.cue );
+        status = webvtt_proc_cuetext( self, buffer, &pos, len, self->finished );
         break;
       case M_SKIP_CUE:
         /* Nothing to do here. */
         break;
       case M_READ_LINE:
         /* Nothing to do here. */
         break;
     }
     cleanup_stack( self );
   }
-  
+
   return status;
 }
 
 WEBVTT_EXPORT void
 webvtt_delete_parser( webvtt_parser self )
 {
   if( self ) {
     cleanup_stack( self );
@@ -219,17 +249,17 @@ webvtt_delete_parser( webvtt_parser self
 #define ELIF_TRANSITION(Token,State) } else IF_TRANSITION(Token,State)
 #define ENDIF }
 #define ELSE } else {
 
 static int
 find_newline( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len )
 {
   while( *pos < len ) {
-    if( buffer[ *pos ] == UTF8_CARRIAGE_RETURN || buffer[ *pos ] == UTF8_LINE_FEED ) {
+    if( buffer[ *pos ] == '\r' || buffer[ *pos ] == '\n' ) {
       return 1;
     } else {
       ( *pos )++;
     }
   }
   return -1;
 }
 
@@ -238,17 +268,17 @@ skip_spacetab( const webvtt_byte *text, 
   webvtt_uint *column )
 {
   webvtt_uint c = 0;
   if( !column ) {
     column = &c;
   }
   while( *pos < len ) {
     webvtt_byte ch = text[ *pos ];
-    if( ch == 0x20 || ch == 0x09 ) {
+    if( ch == ' ' || ch == '\t' ) {
       ++( *pos );
       ++( *column );
     } else {
       break;
     }
   }
 }
 
@@ -257,53 +287,26 @@ skip_until_white( const webvtt_byte *tex
   webvtt_uint *column )
 {
   webvtt_uint c = 0;
   if( !column ) {
     column = &c;
   }
   while( *pos < len ) {
     webvtt_byte ch = text[ *pos ];
-    if( ch == 0x20 || ch == 0x09 || ch == 0x0A || ch == 0x0D ) {
+    if( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ) {
       break;
     } else {
       int length = webvtt_utf8_length( text + *pos );
       *pos += length;
       ++( *column );
     }
   }
 }
 
-static webvtt_status
-webvtt_skipwhite( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len )
-{
-  if( !buffer || !pos ) {
-    return WEBVTT_INVALID_PARAM;
-  }
-
-  for( ; *pos < len && webvtt_iswhite( buffer[ *pos ] ); (*pos)++ );
-
-  return WEBVTT_SUCCESS;
-}
-
-static void
-find_next_whitespace( const webvtt_byte *buffer, webvtt_uint *ppos, webvtt_uint len )
-{
-  webvtt_uint pos = *ppos;
-  while( pos < len ) {
-    webvtt_byte c = buffer[pos];
-    if( c == UTF8_CARRIAGE_RETURN || c == UTF8_LINE_FEED || c == UTF8_SPACE || c == UTF8_TAB ) {
-      break;
-    }
-
-    ++pos;
-  }
-  *ppos = pos;
-}
-
 /**
  * basic strnstr-ish routine
  */
 static webvtt_status
 find_bytes( const webvtt_byte *buffer, webvtt_uint len,
     const webvtt_byte *sbytes, webvtt_uint slen )
 {
   webvtt_uint slen2;
@@ -332,17 +335,17 @@ find_bytes( const webvtt_byte *buffer, w
 #define STACK_SIZE ((webvtt_uint)(self->top - self->stack))
 #define FRAME(i) (self->top - (i))
 #define FRAMEUP(i) (self->top + (i))
 #define RECHECK goto _recheck;
 #define BACK (SP->back)
 /**
  * More state stack helpers
  */
-static webvtt_status
+WEBVTT_INTERN webvtt_status
 do_push( webvtt_parser self, webvtt_uint token, webvtt_uint back, webvtt_uint state, void *data, webvtt_state_value_type type, webvtt_uint line, webvtt_uint column )
 {
   if( STACK_SIZE + 1 >= self->stack_alloc ) {
     webvtt_state *stack = ( webvtt_state * )webvtt_alloc0( sizeof( webvtt_state ) * ( self->stack_alloc << 1 ) ), *tmp;
     if( !stack ) {
       ERROR( WEBVTT_ALLOCATION_FAILED );
       return WEBVTT_OUT_OF_MEMORY;
     }
@@ -351,16 +354,17 @@ do_push( webvtt_parser self, webvtt_uint
     self->stack = stack;
     self->top = stack + ( self->top - tmp );
     if( tmp != self->astack ) {
       webvtt_free( tmp );
     }
   }
   ++self->top;
   self->top->state = state;
+  self->top->flags = 0;
   self->top->type = type;
   self->top->token = ( webvtt_token )token;
   self->top->line = line;
   self->top->back = back;
   self->top->column = column;
   self->top->v.cue = ( webvtt_cue * )data;
   return WEBVTT_SUCCESS;
 }
@@ -438,50 +442,48 @@ webvtt_parse_cuesetting( webvtt_parser s
             if( tk != keyword ) {
               *pos -= tp;
               self->column -= tp;
               return WEBVTT_NEXT_CUESETTING;
             }
             if( *pos < len ) {
               webvtt_uint column = last_column;
               webvtt_byte ch = text[ *pos ];
-              if( ch != 0x3A ) {
+              if( ch != ':' ) {
                 webvtt_error e = WEBVTT_INVALID_CUESETTING;
-                if( ch == 0x20 || ch == 0x09 ) {
+                if( ch == ' ' || ch == '\t' ) {
                   column = self->column;
                   e = WEBVTT_UNEXPECTED_WHITESPACE;
                   skip_spacetab( text, pos, len, &self->column );
-                  if( text[ *pos ] == 0x3A ) {
+                  if( text[ *pos ] == ':' ) {
                     skip_until_white( text, pos, len, &self->column );
                   }
                 } else {
                   skip_until_white( text, pos, len, &self->column );
                 }
                 ERROR_AT_COLUMN( e, column );
               } else {
                 mode = P_COLON;
                 keyword_column = last_column;
               }
             } else {
-              ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column );     
+              ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column );
             }
             break;
           case WHITESPACE:
             break;
           case NEWLINE:
             return WEBVTT_SUCCESS;
             break;
           default:
             ERROR_AT( WEBVTT_INVALID_CUESETTING, last_line,
               last_column );
             *pos = *pos + tp + 1;
- skip_param:
-            while( *pos < len && text[ *pos ] != 0x20
-              && text[ *pos ] != 0x09 ) {
-              if( text[ *pos ] == 0x0A || text[ *pos ] == 0x0D ) {
+            while( *pos < len && text[ *pos ] != ' ' && text[ *pos ] != '\t' ) {
+              if( text[ *pos ] == '\n' || text[ *pos ] == '\r' ) {
                 return WEBVTT_SUCCESS;
               }
               ++( *pos );
               ++self->column;
             }
             break;
         }
         break;
@@ -513,41 +515,42 @@ get_value:
           ERROR_AT( WEBVTT_UNEXPECTED_WHITESPACE, last_line,
             last_column );
         } else if( ( i = find_token( tk, values ) ) >= 0 ) {
           webvtt_token t = values[ i ] & TF_TOKEN_MASK;
           int flags = values[ i ] & TF_FLAGS_MASK;
           *value_column = last_column;
           if( *pos < len ) {
             webvtt_byte ch = text[ *pos ];
-            if( ch != 0x20 && ch != 0x09
-              && ch != 0x0D && ch != 0x0A ) {
+            if( ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n' ) {
               goto bad_value;
             }
           }
           switch( t ) {
             case INTEGER:
             case PERCENTAGE:
               if( ( flags & TF_SIGN_MASK ) != TF_SIGN_MASK ) {
                 const webvtt_byte p = self->token[ 0 ];
-                if( ( ( flags & TF_NEGATIVE ) && p != UTF8_HYPHEN_MINUS )
-                  || ( ( flags & TF_POSITIVE ) && p == UTF8_HYPHEN_MINUS
-) ) {
+                if( ( ( flags & TF_NEGATIVE ) && p != '-' )
+                  || ( ( flags & TF_POSITIVE ) && p == '-' ) ) {
                   goto bad_value;
                 }
               }
+              break;
+
+            default: /* Currently, other types don't need these checks */
+              break;
           }
           return i + 1;
         } else {
 bad_value:
           ERROR_AT( bv, last_line, last_column );
 bad_value_eol:
-          while( *pos < len && text[ *pos ] != 0x20
-            && text[ *pos ] != 0x09 ) {
-            if( text[ *pos ] == 0x0A || text[ *pos ] == 0x0D ) {
+          while( *pos < len && text[ *pos ] != ' ' && text[ *pos ] != '\t' ) {
+            if( text[ *pos ] == '\n' || text[ *pos ] == '\r' ) {
               return WEBVTT_SUCCESS;
             }
             ++( *pos );
             ++self->column;
           }
           if( *pos >= len ) {
             return WEBVTT_SUCCESS;
           }
@@ -558,56 +561,52 @@ bad_value_eol:
   if( mode == P_VALUE && *pos >= len ) {
     ERROR( bv );
     goto bad_value_eol;
   }
   return WEBVTT_NEXT_CUESETTING;
 }
 
 WEBVTT_INTERN webvtt_status
-webvtt_parse_align( webvtt_parser self, webvtt_cue *cue, const
-webvtt_byte *text,
-  webvtt_uint *pos, webvtt_uint len )
+webvtt_parse_align( webvtt_parser self, webvtt_cue *cue,
+                    const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len )
 {
   webvtt_uint last_line = self->line;
   webvtt_uint last_column = self->column;
   webvtt_status v;
   webvtt_uint vc;
-  webvtt_token values[] = { START, MIDDLE, END, LEFT, RIGHT, 0 };
+  webvtt_token tokens[] = { START, MIDDLE, END, LEFT, RIGHT, 0 };
+  webvtt_align_type values[] = {
+    WEBVTT_ALIGN_START, WEBVTT_ALIGN_MIDDLE, WEBVTT_ALIGN_END,
+    WEBVTT_ALIGN_LEFT, WEBVTT_ALIGN_RIGHT
+  };
   if( ( v = webvtt_parse_cuesetting( self, text, pos, len,
-    WEBVTT_ALIGN_BAD_VALUE, ALIGN, values, &vc ) ) > 0 ) {
+    WEBVTT_ALIGN_BAD_VALUE, ALIGN, tokens, &vc ) ) > 0 ) {
     if( cue->flags & CUE_HAVE_ALIGN ) {
       ERROR_AT( WEBVTT_ALIGN_ALREADY_SET, last_line, last_column );
     }
     cue->flags |= CUE_HAVE_ALIGN;
-    switch( values[ v - 1 ] ) {
-      case START: cue->settings.align = WEBVTT_ALIGN_START; break;
-      case MIDDLE: cue->settings.align = WEBVTT_ALIGN_MIDDLE; break;
-      case END: cue->settings.align = WEBVTT_ALIGN_END; break;
-      case LEFT: cue->settings.align = WEBVTT_ALIGN_LEFT; break;
-      case RIGHT: cue->settings.align = WEBVTT_ALIGN_RIGHT; break;
-    }
+    cue->settings.align = values[ v - 1 ];
   }
   return v >= 0 ? WEBVTT_SUCCESS : v;
 }
 
 WEBVTT_INTERN webvtt_status
-webvtt_parse_line( webvtt_parser self, webvtt_cue *cue, const
-webvtt_byte *text,
-  webvtt_uint *pos, webvtt_uint len )
+webvtt_parse_line( webvtt_parser self, webvtt_cue *cue, const webvtt_byte *text,
+                   webvtt_uint *pos, webvtt_uint len )
 {
   webvtt_uint last_line = self->line;
   webvtt_uint last_column = self->column;
   webvtt_status v;
   webvtt_uint vc;
   webvtt_bool first_flag = 0;
   webvtt_token values[] = { INTEGER, PERCENTAGE|TF_POSITIVE, 0 };
   if( ( v = webvtt_parse_cuesetting( self, text, pos, len,
     WEBVTT_LINE_BAD_VALUE, LINE, values, &vc ) ) > 0 ) {
-    webvtt_uint digits;
+    int digits;
     webvtt_int64 value;
     const webvtt_byte *t = self->token;
     if( cue->flags & CUE_HAVE_LINE ) {
       ERROR_AT( WEBVTT_LINE_ALREADY_SET, last_line, last_column );
     } else {
       first_flag = 1;
     }
     cue->flags |= CUE_HAVE_LINE;
@@ -630,518 +629,441 @@ webvtt_byte *text,
         cue->snap_to_lines = 0;
         cue->settings.line = ( int )value;
       } break;
     }
   }
   return v >= 0 ? WEBVTT_SUCCESS : v;
 }
 
+WEBVTT_INTERN webvtt_status
+webvtt_parse_position( webvtt_parser self, webvtt_cue *cue,
+                       const webvtt_byte *text, webvtt_uint *pos,
+                       webvtt_uint len )
+{
+  webvtt_uint last_line = self->line;
+  webvtt_uint last_column = self->column;
+  webvtt_status v;
+  webvtt_uint vc;
+  webvtt_bool first_flag = 0;
+  webvtt_token values[] = { PERCENTAGE|TF_POSITIVE, 0 };
+  if( ( v = webvtt_parse_cuesetting( self, text, pos, len,
+    WEBVTT_POSITION_BAD_VALUE, POSITION, values, &vc ) ) > 0 ) {
+    int digits;
+    webvtt_int64 value;
+    const webvtt_byte *t = self->token;
+    if( cue->flags & CUE_HAVE_LINE ) {
+      ERROR_AT( WEBVTT_POSITION_ALREADY_SET, last_line, last_column );
+    } else {
+      first_flag = 1;
+    }
+    cue->flags |= CUE_HAVE_POSITION;
+    value = parse_int( &t, &digits );
+    if( value < 0 || value > 100 ) {
+      if( first_flag ) {
+        cue->flags &= ~CUE_HAVE_POSITION;
+      }
+      ERROR_AT_COLUMN( WEBVTT_POSITION_BAD_VALUE, vc );
+      return WEBVTT_SUCCESS;
+    }
+    cue->settings.position = ( int )value;
+  }
+  return v >= 0 ? WEBVTT_SUCCESS : v;
+}
+
+WEBVTT_INTERN webvtt_status
+webvtt_parse_size( webvtt_parser self, webvtt_cue *cue, const webvtt_byte *text,
+                   webvtt_uint *pos, webvtt_uint len )
+{
+  webvtt_uint last_line = self->line;
+  webvtt_uint last_column = self->column;
+  webvtt_status v;
+  webvtt_uint vc;
+  webvtt_token tokens[] = { PERCENTAGE|TF_POSITIVE, 0 };
+  if( ( v = webvtt_parse_cuesetting( self, text, pos, len,
+    WEBVTT_SIZE_BAD_VALUE, SIZE, tokens, &vc ) ) > 0 ) {
+    if( cue->flags & CUE_HAVE_SIZE ) {
+      ERROR_AT( WEBVTT_SIZE_ALREADY_SET, last_line, last_column );
+    }
+    cue->flags |= CUE_HAVE_SIZE;
+    if( tokens[ v - 1 ] ) {
+      int digits;
+      const webvtt_byte *t = self->token;
+      webvtt_int64 value;
+      self->token_pos = 0;
+      value = parse_int( &t, &digits );
+      if( value < 0 || value > 100 ) {
+        ERROR_AT_COLUMN( WEBVTT_SIZE_BAD_VALUE, vc );
+      } else {
+        cue->settings.size = ( int )value;
+      }
+    }
+  }
+  return v >= 0 ? WEBVTT_SUCCESS : v;
+}
+
+WEBVTT_INTERN webvtt_status
+webvtt_parse_vertical( webvtt_parser self, webvtt_cue *cue,
+                       const webvtt_byte *text, webvtt_uint *pos,
+                       webvtt_uint len )
+{
+  webvtt_uint last_line = self->line;
+  webvtt_uint last_column = self->column;
+  webvtt_status v;
+  webvtt_uint vc;
+  webvtt_token tokens[] = { RL, LR, 0 };
+  webvtt_vertical_type values[] = { WEBVTT_VERTICAL_RL, WEBVTT_VERTICAL_LR };
+  if( ( v = webvtt_parse_cuesetting( self, text, pos, len,
+        WEBVTT_VERTICAL_BAD_VALUE, VERTICAL, tokens, &vc ) ) > 0 ) {
+    if( cue->flags & CUE_HAVE_VERTICAL ) {
+      ERROR_AT( WEBVTT_VERTICAL_ALREADY_SET, last_line, last_column );
+    }
+    cue->flags |= CUE_HAVE_VERTICAL;
+    cue->settings.vertical = values[ v - 1 ];
+  }
+  return v >= 0 ? WEBVTT_SUCCESS : v;
+}
+/**
+ * Read a timestamp into 'result' field, following the rules of the cue-times
+ * section of the draft:
+ * http://dev.w3.org/html5/webvtt/#collect-webvtt-cue-timings-and-settings
+ *
+ * - Ignore whitespace
+ * - Return immediately after having parsed a timestamp
+ * - Return error if timestamp invalid.
+ */
+WEBVTT_INTERN webvtt_status
+webvtt_get_timestamp( webvtt_parser self, webvtt_timestamp *result,
+                      const webvtt_byte *text, webvtt_uint *pos,
+                      webvtt_uint len, const char *accepted )
+{
+  webvtt_uint last_column = self->column;
+  webvtt_uint last_line = self->line;
+  webvtt_token token;
+  while( *pos < len ) {
+    last_column = self->column;
+    last_line = self->line;
+    token = webvtt_lex( self, text, pos, len, 1 );
+    self->token_pos = 0;
+
+    if( token == TIMESTAMP ) {
+      if( *pos < len && text[ *pos ] != '\r' && text[ *pos ] != '\n' &&
+          text[ *pos ] != ' ' &&  text[ *pos ] != '\t' ) {
+        if( accepted == 0 || !strchr( accepted, text[ *pos ] ) ) {
+          ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column );
+          return WEBVTT_PARSE_ERROR;
+        }
+      }
+
+      if( !parse_timestamp( self->token, result ) ) {
+        /* Read a bad timestamp, throw away and abort cue */
+        ERROR_AT( WEBVTT_MALFORMED_TIMESTAMP, last_line, last_column );
+        if( BAD_TIMESTAMP( *result ) ) {
+          return WEBVTT_PARSE_ERROR;
+        }
+      }
+      return WEBVTT_SUCCESS;
+    }
+    else if( token == WHITESPACE ) {
+      /* Ignore whitespace */
+    } else {
+      /* Not a timestamp, this is an error. */
+      ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column );
+      return WEBVTT_PARSE_ERROR;
+    }
+  }
+
+  /* If we finish entire line without reading a timestamp,
+     this is not a proper cue header and should be discarded. */
+  ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column );
+  return WEBVTT_PARSE_ERROR;
+}
+
+WEBVTT_INTERN webvtt_status
+webvtt_proc_cueline( webvtt_parser self, webvtt_cue *cue,
+                     webvtt_string *line )
+{
+  const webvtt_byte *text;
+  webvtt_uint length;
+  DIE_IF( line == NULL );
+  length = webvtt_string_length( line );
+  text = webvtt_string_text( line );
+  /* backup the column */
+  self->column = 1;
+  if( find_bytes( text, length, separator, sizeof( separator ) )
+      == WEBVTT_SUCCESS) {
+    /* It's not a cue id, we found '-->'. It can't be a second
+       cueparams line, because if we had it, we would be in
+       a different state. */
+    int v;
+    self->cuetext_line = self->line + 1;
+    if( ( v = parse_cueparams( self, text, length, cue ) ) < 0 ) {
+        if( v == WEBVTT_PARSE_ERROR ) {
+          return WEBVTT_PARSE_ERROR;
+        }
+        self->mode = M_SKIP_CUE;
+      } else {
+        cue->flags |= CUE_HAVE_CUEPARAMS;
+        self->mode = M_CUETEXT;
+      }
+  } else {
+    /* It is a cue-id */
+    if( cue && cue->flags & CUE_HAVE_ID ) {
+      /**
+       * This isn't actually a cue-id, because we already
+       * have one. It seems to be cuetext, which is occurring
+       * before cue-params
+       */
+      webvtt_release_string( line );
+      ERROR( WEBVTT_CUE_INCOMPLETE );
+      self->mode = M_SKIP_CUE;
+      return WEBVTT_SUCCESS;
+    } else {
+      webvtt_uint last_column = self->column;
+      webvtt_uint last_line = self->line;
+      webvtt_token token = UNFINISHED;
+      self->column += length;
+      self->cuetext_line = self->line;
+      if( WEBVTT_FAILED( webvtt_string_append( &cue->id, text,
+                                               length ) ) ) {
+        webvtt_release_string( line );
+        ERROR( WEBVTT_ALLOCATION_FAILED );
+        return WEBVTT_OUT_OF_MEMORY;
+      }
+      cue->flags |= CUE_HAVE_ID;
+
+      /* Read cue-params line */
+      PUSH0( T_CUEREAD, 0, V_NONE );
+      webvtt_init_string( &SP->v.text );
+      SP->type = V_TEXT;
+    }
+  }
+
+  webvtt_release_string( line );
+  return WEBVTT_SUCCESS;
+}
+
 WEBVTT_INTERN int
 parse_cueparams( webvtt_parser self, const webvtt_byte *buffer,
                  webvtt_uint len, webvtt_cue *cue )
 {
-  int digits;
-  int have_ws = 0;
-  int unexpected_whitespace = 0;
-  webvtt_uint baddelim = 0;
   webvtt_uint pos = 0;
-  webvtt_token last_token = 0;
-  webvtt_uint last_line = self->line;
 
   enum cp_state {
-    CP_T1, CP_T2, CP_T3, CP_T4, CP_T5, /* 'start' cuetime, whitespace1,
-                   'separator', whitespace2, 'end' cuetime */
-    CP_CS0, /* pre-cuesetting */
-
-    CP_SD, /* cuesettings delimiter here */
+    CP_STARTTIME = 0, /* Start timestamp */
+    CP_SEPARATOR, /* --> */
+    CP_ENDTIME, /* End timestamp */
 
-    CP_V1, /* 'vertical' cuesetting */
-    CP_P1, /* 'position' cuesetting */
-    CP_A1, /* 'align' cuesetting */
-    CP_S1, /* 'size' cuesetting */
-    CP_L1, /* 'line' cuesetting */
-
-    CP_SV, /* cuesettings value here */
-
-    CP_V2,
-    CP_P2,
-    CP_A2,
-    CP_S2,
-    CP_L2,
+    CP_SETTING, /* Cuesetting */
   };
 
-  enum cp_state last_state = CP_T1;
-  enum cp_state state = CP_T1;
+  enum cp_state state = CP_STARTTIME;
 
-#define SETST(X) do { baddelim = 0; last_state = state; state = (X); } while( 0 )
+#define SETST(X) do { baddelim = 0; state = (X); } while( 0 )
 
   self->token_pos = 0;
   while( pos < len ) {
-    webvtt_uint last_column = self->column;
-    webvtt_token token = webvtt_lex( self, buffer, &pos, len, 1 );
-    webvtt_uint tlen = self->token_pos;
-    self->token_pos = 0;
-_recheck:
+    webvtt_uint last_column;
+    webvtt_uint last_line;
+    webvtt_token token;
+    webvtt_uint token_len;
+
     switch( state ) {
-        /* start timestamp */
-      case CP_T1:
-        if( token == WHITESPACE && !unexpected_whitespace ) {
-          ERROR_AT_COLUMN( WEBVTT_UNEXPECTED_WHITESPACE, self->column );
-          unexpected_whitespace = 1;
-        } else if( token == TIMESTAMP )
-          if( !parse_timestamp( self->token, &cue->from ) ) {
-            ERROR_AT_COLUMN(
-              ( BAD_TIMESTAMP( cue->from )
-                ? WEBVTT_EXPECTED_TIMESTAMP
-                : WEBVTT_MALFORMED_TIMESTAMP ), last_column  );
-            if( self->token_pos && !webvtt_isdigit( self->token[self->token_pos - 1] ) ) {
-              while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) { ++pos; }
-            }
-            if( BAD_TIMESTAMP( cue->from ) )
-            { return -1; }
-            SETST( CP_T2 );
-          } else {
-            SETST( CP_T2 );
-          }
-        else {
-          ERROR_AT_COLUMN( WEBVTT_EXPECTED_TIMESTAMP, last_column );
+      /* start timestamp */
+      case CP_STARTTIME:
+        if( WEBVTT_FAILED( webvtt_get_timestamp( self, &cue->from, buffer, &pos,
+                                                 len, "-" ) ) ) {
+          /* abort cue */
           return -1;
         }
+
+        state = CP_SEPARATOR;
         break;
-        /* end timestamp */
-      case CP_T5:
-        if( token == WHITESPACE ) {
-          /* no problem, just ignore it and continue */
-        } else if( token == TIMESTAMP )
-          if( !parse_timestamp( self->token, &cue->until ) ) {
-            ERROR_AT_COLUMN(
-              ( BAD_TIMESTAMP( cue->until )
-                ? WEBVTT_EXPECTED_TIMESTAMP
-                : WEBVTT_MALFORMED_TIMESTAMP ), last_column  );
-            if( !webvtt_isdigit( self->token[self->token_pos - 1] ) ) {
-              while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) { ++pos; }
-            }
-            if( BAD_TIMESTAMP( cue->until ) )
-            { return -1; }
-            SETST( CP_CS0 );
-          } else {
-            SETST( CP_CS0 );
-          }
-        else {
-          ERROR_AT_COLUMN( WEBVTT_EXPECTED_TIMESTAMP, last_column );
+
+      /* '-->' separator */
+      case CP_SEPARATOR:
+        last_column = self->column;
+        last_line = self->line;
+        token = webvtt_lex( self, buffer, &pos, len, 1 );
+        self->token_pos = 0;
+        if( token == SEPARATOR ) {
+          /* Expect end timestamp */
+          state = CP_ENDTIME;
+        } else if( token == WHITESPACE ) {
+          /* ignore whitespace */
+        } else {
+          /* Unexpected token. Abort cue. */
+          ERROR_AT( WEBVTT_EXPECTED_CUETIME_SEPARATOR, last_line, last_column );
           return -1;
         }
         break;
 
-        /* whitespace 1 */
-      case CP_T2:
-        switch( token ) {
-          case SEPARATOR:
-            ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column );
-            SETST( CP_T4 );
-            break;
-          case WHITESPACE:
-            SETST( CP_T3 );
-            break;
+      /* end timestamp */
+      case CP_ENDTIME:
+	if( WEBVTT_FAILED( webvtt_get_timestamp( self, &cue->until, buffer,
+                                                 &pos, len, 0 ) ) ) {
+          /* abort cue */
+          return -1;
         }
+
+        /* Expect cuesetting */
+        state = CP_SETTING;
         break;
-      case CP_T3:
-        switch( token ) {
-          case WHITESPACE: /* ignore this whitespace */
-            break;
+
 
-          case SEPARATOR:
-            SETST( CP_T4 );
-            break;
+      default:
+        /**
+         * Most of these branches need to read a token for the time
+         * being, so they're grouped together here.
+         *
+         * TODO: Remove need to call lexer at all here.
+         */
+        last_column = self->column;
+        last_line = self->line;
+        token = webvtt_lex( self, buffer, &pos, len, 1 );
+        token_len = self->token_pos;
+        self->token_pos = 0;
 
-          case TIMESTAMP:
-            ERROR( WEBVTT_MISSING_CUETIME_SEPARATOR );
-            SETST( CP_T5 );
-            goto _recheck;
-
-          default: /* some garbage */
-            ERROR_AT_COLUMN( WEBVTT_EXPECTED_CUETIME_SEPARATOR, last_column );
-            return -1;
-        }
-        break;
-      case CP_T4:
         switch( token ) {
+          case NEWLINE:
+            return 0;
           case WHITESPACE:
-            SETST( CP_T5 );
-            break;
-          case TIMESTAMP:
-            ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column );
-            goto _recheck;
-          default:
-            ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column );
-            goto _recheck;
-        }
-        break;
-#define CHKDELIM \
-if( baddelim ) \
-  ERROR_AT_COLUMN(WEBVTT_INVALID_CUESETTING_DELIMITER,baddelim); \
-else if( !have_ws ) \
-  ERROR_AT_COLUMN(WEBVTT_EXPECTED_WHITESPACE,last_column);
+            continue;
+
+          case VERTICAL:
+          {
+            webvtt_status status;
+            pos -= token_len; /* Required for parse_vertical() */
+            self->column = last_column; /* Reset for parse_vertical() */
+            status = webvtt_parse_vertical( self, cue, buffer, &pos, len );
+            if( status == WEBVTT_PARSE_ERROR ) {
+              return WEBVTT_PARSE_ERROR;
+            }
+          }
+          break;
 
-        /**
-         * This section is "pre-cuesetting". We are expecting whitespace,
-         * followed by a cuesetting keyword
-         *
-         * If we don't see a keyword, but have our whitespace, it is considered
-         * a bad keyword (invalid cuesetting)
-         *
-         * Otherwise, if we don't have whitespace and have a bad token, it's an
-         * invalid delimiter
-         */
-      case CP_CS0:
-        switch( token ) {
-          case WHITESPACE:
-            have_ws = last_column;
-            break;
-          case VERTICAL:
-            CHKDELIM have_ws = 0;
-            SETST( CP_V1 );
-            break;
+
           case POSITION:
-            CHKDELIM have_ws = 0;
-            SETST( CP_P1 );
-            break;
+          {
+            webvtt_status status;
+            pos -= token_len; /* Required for parse_position() */
+            self->column = last_column; /* Reset for parse_position() */
+            status = webvtt_parse_position( self, cue, buffer, &pos, len );
+            if( status == WEBVTT_PARSE_ERROR ) {
+              return WEBVTT_PARSE_ERROR;
+            }
+          }
+          break;
+
           case ALIGN:
           {
             webvtt_status status;
-            pos -= tlen; /* Required for parse_align() */
+            pos -= token_len; /* Required for parse_align() */
             self->column = last_column; /* Reset for parse_align() */
             status = webvtt_parse_align( self, cue, buffer, &pos, len );
             if( status == WEBVTT_PARSE_ERROR ) {
               return WEBVTT_PARSE_ERROR;
             }
           }
           break;
 
           case SIZE:
-            CHKDELIM have_ws = 0;
-            SETST( CP_S1 );
-            break;
+          {
+            webvtt_status status;
+            pos -= token_len; /* Required for parse_size() */
+            self->column = last_column; /* Reset for parse_size() */
+            status = webvtt_parse_size( self, cue, buffer, &pos, len );
+            if( status == WEBVTT_PARSE_ERROR ) {
+              return WEBVTT_PARSE_ERROR;
+            }
+          }
+          break;
+
           case LINE:
           {
             webvtt_status status;
-            pos -= tlen; /* Required for parse_align() */
-            self->column = last_column; /* Reset for parse_align() */
+            pos -= token_len; /* Required for parse_line() */
+            self->column = last_column; /* Reset for parse_line() */
             status = webvtt_parse_line( self, cue, buffer, &pos, len );
             if( status == WEBVTT_PARSE_ERROR ) {
               return WEBVTT_PARSE_ERROR;
             }
           }
           break;
           default:
-            if( have_ws ) {
-              ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column );
-            } else if( token == BADTOKEN ) {
-              /* it was a bad delimiter... */
-              if( !baddelim ) {
-                baddelim = last_column;
-              }
-              ++pos;
-            }
-            while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) {
+            ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column );
+            while( pos < len && buffer[pos] != '\t' && buffer[pos] != ' ' ) {
               ++pos;
             }
         }
         break;
-#define CS1(S) \
-  if( token == COLON ) \
-  { if(have_ws) { ERROR_AT_COLUMN(WEBVTT_UNEXPECTED_WHITESPACE,have_ws); } SETST((S)); have_ws = 0; } \
-  else if( token == WHITESPACE && !have_ws ) \
-  { \
-    have_ws = last_column; \
-  } \
-  else \
-  { \
-    switch(token) \
-    { \
-    case LR: case RL: case INTEGER: case PERCENTAGE: case START: case MIDDLE: case END: case LEFT: case RIGHT: \
-       ERROR_AT_COLUMN(WEBVTT_MISSING_CUESETTING_DELIMITER,have_ws ? have_ws : last_column); break; \
-    default: \
-      ERROR_AT_COLUMN(WEBVTT_INVALID_CUESETTING_DELIMITER,last_column); \
-      while( pos < len && buffer[pos] != 0x20 && buffer[pos] != 0x09 ) ++pos; \
-      break; \
-    } \
-    have_ws = 0; \
-  }
-
-        /**
-         * If we get a COLON, we advance to the next state.
-         * If we encounter whitespace first, fire an "unexpected whitespace"
-         * error and continue. If we encounter a cue-setting value, fire a
-         * "missing cuesetting delimiter" error otherwise (eg vertical;rl), fire
-         * "invalid cuesetting delimiter" error
-         *
-         * this logic is performed by the CS1 macro, defined above
-         */
-      case CP_V1:
-        CS1( CP_V2 );
-        break;
-      case CP_P1:
-        CS1( CP_P2 );
-        break;
-      case CP_A1:
-        CS1( CP_A2 );
-        break;
-      case CP_S1:
-        CS1( CP_S2 );
-        break;
-      case CP_L1:
-        CS1( CP_L2 );
-        break;
-#undef CS1
-
-/* BV: emit the BAD_VALUE error for the appropriate setting, when required */
-#define BV(T) \
-ERROR_AT_COLUMN(WEBVTT_##T##_BAD_VALUE,last_column); \
-while( pos < len && buffer[pos] != 0x20 && buffer[pos] != 0x09 ) ++pos; \
-SETST(CP_CS0);
-
-/* HV: emit the ALREADY_SET (have value) error for the appropriate setting, when required */
-#define HV(T) \
-if( cue->flags & CUE_HAVE_##T ) \
-{ \
-  ERROR_AT_COLUMN(WEBVTT_##T##_ALREADY_SET,last_column); \
-}
-/* WS: emit the WEBVTT_UNEXPECTED_WHITESPACE error when required. */
-#define WS \
-case WHITESPACE: \
-  if( !have_ws ) \
-  { \
-    ERROR_AT_COLUMN(WEBVTT_UNEXPECTED_WHITESPACE,last_column); \
-    have_ws = last_column; \
-  } \
-break
-
-/* set that the cue already has a value for this */
-#define SV(T) cue->flags |= CUE_HAVE_##T
-      case CP_V2:
-        HV( VERTICAL );
-        switch( token ) {
-            WS;
-          case LR:
-            cue->settings.vertical = WEBVTT_VERTICAL_LR;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( VERTICAL );
-            break;
-          case RL:
-            cue->settings.vertical = WEBVTT_VERTICAL_RL;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( VERTICAL );
-            break;
-          default:
-            BV( VERTICAL );
-        }
-        break;
-
-      case CP_P2:
-        HV( POSITION );
-        switch( token ) {
-            WS;
-          case PERCENTAGE: {
-            int digits;
-            const webvtt_byte *t = self->token;
-            webvtt_int64 v = parse_int( &t, &digits );
-            if( v < 0 ) {
-              BV( POSITION );
-            }
-            cue->settings.position = ( webvtt_uint )v;
-            SETST( CP_CS0 );
-            SV( POSITION );
-          }
-          break;
-          default:
-            BV( POSITION );
-            break;
-        }
-        break;
-
-      case CP_A2:
-        HV( ALIGN );
-        switch( token ) {
-            WS;
-          case START:
-            cue->settings.align = WEBVTT_ALIGN_START;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( ALIGN );
-            break;
-          case MIDDLE:
-            cue->settings.align = WEBVTT_ALIGN_MIDDLE;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( ALIGN );
-            break;
-          case END:
-            cue->settings.align = WEBVTT_ALIGN_END;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( ALIGN );
-            break;
-          case LEFT:
-            cue->settings.align = WEBVTT_ALIGN_LEFT;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( ALIGN );
-            break;
-          case RIGHT:
-            cue->settings.align = WEBVTT_ALIGN_RIGHT;
-            have_ws = 0;
-            SETST( CP_CS0 );
-            SV( ALIGN );
-            break;
-          default:
-            BV( ALIGN );
-            break;
-        }
-        break;
-
-      case CP_S2:
-        HV( SIZE );
-        switch( token ) {
-            WS;
-          case PERCENTAGE: {
-            int digits;
-            const webvtt_byte *t = self->token;
-            webvtt_int64 v = parse_int( &t, &digits );
-            if( v < 0 ) {
-              BV( SIZE );
-            }
-            cue->settings.size = ( webvtt_uint )v;
-            SETST( CP_CS0 );
-            SV( SIZE );
-          }
-          break;
-          default:
-            BV( SIZE );
-            break;
-        }
-        break;
-
-      case CP_L2:
-        HV( LINE );
-        switch( token ) {
-            WS;
-          case INTEGER: {
-            const webvtt_byte *t = self->token;
-            webvtt_int64 v = parse_int( &t, &digits );
-            cue->snap_to_lines = 1;
-            cue->settings.line = ( int )v;
-            SETST( CP_CS0 );
-            SV( LINE );
-          }
-          break;
-          case PERCENTAGE: {
-            const webvtt_byte *t = self->token;
-            webvtt_int64 v = parse_int( &t, &digits );
-            if( v < 0 ) {
-              BV( POSITION );
-            }
-            cue->snap_to_lines = 0;
-            cue->settings.line = ( int )v;
-            SETST( CP_CS0 );
-            SV( LINE );
-          }
-          break;
-          default:
-            BV( LINE );
-            break;
-        }
-#undef BV
-#undef HV
-#undef SV
-#undef WS
     }
     self->token_pos = 0;
-    last_token = token;
   }
   /**
    * If we didn't finish in a good state...
    */
-  if( state != CP_CS0 ) {
+  if( state != CP_SETTING ) {
     /* if we never made it to the cuesettings, we didn't finish the cuetimes */
-    if( state < CP_CS0 ) {
+    if( state < CP_SETTING ) {
       ERROR( WEBVTT_UNFINISHED_CUETIMES );
       return -1;
     } else {
       /* if we did, we should report an error but continue parsing. */
       webvtt_error e = WEBVTT_INVALID_CUESETTING;
-      switch( state ) {
-        case CP_V2:
-          e = WEBVTT_VERTICAL_BAD_VALUE;
-          break;
-        case CP_P2:
-          e = WEBVTT_POSITION_BAD_VALUE;
-          break;
-        case CP_A2:
-          e = WEBVTT_ALIGN_BAD_VALUE;
-          break;
-        case CP_S2:
-          e = WEBVTT_SIZE_BAD_VALUE;
-          break;
-        case CP_L2:
-          e = WEBVTT_LINE_BAD_VALUE;
-          break;
-      }
       ERROR( e );
     }
-  } else {
-    if( baddelim ) {
-      ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING_DELIMITER, baddelim );
-    }
   }
 #undef SETST
   return 0;
 }
 
 static webvtt_status
 parse_webvtt( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *ppos,
-              webvtt_uint len, webvtt_parse_mode *mode, int finish )
+              webvtt_uint len, int finish )
 {
   webvtt_status status = WEBVTT_SUCCESS;
-  webvtt_token token;
+  webvtt_token token = 0;
   webvtt_uint pos = *ppos;
-  int settings_delimiter = 0;
   int skip_error = 0;
-  int settings_whitespace = 0;
 
   while( pos < len ) {
-    webvtt_uint last_column, last_line, last_pos;
+    webvtt_uint last_column, last_line;
     skip_error = 0;
-_next:
     last_column = self->column;
     last_line = self->line;
-    last_pos = pos;
 
     /**
      * If we're in certain states, we don't want to get a token and just
      * want to read text instead.
      */
     if( SP->state == T_CUEREAD ) {
-      int v;
-      webvtt_uint old_pos = pos;
-      if( v = webvtt_string_getline( &SP->v.text, buffer, &pos,
-                                        len, 0, finish, 0 ) ) {
-        if( v < 0 ) {
-          webvtt_release_string( &SP->v.text );
-          SP->type = V_NONE;
+      DIE_IF( SP->type != V_TEXT );
+      if( SP->flags == 0 ) {
+        int v;
+        if( ( v = webvtt_string_getline( &SP->v.text, buffer, &pos, len, 0,
+                                         finish ) ) ) {
+          if( v < 0 ) {
+            webvtt_release_string( &SP->v.text );
+            SP->type = V_NONE;
+            POP();
+            ERROR( WEBVTT_ALLOCATION_FAILED );
+            status = WEBVTT_OUT_OF_MEMORY;
+            goto _finish;
+          }
+          SP->flags = 1;
+        }
+      }
+      if( SP->flags ) {
+        webvtt_token token = webvtt_lex_newline( self, buffer, &pos, len,
+                                                 self->finished );
+        if( token == NEWLINE ) {
           POP();
-          ERROR( WEBVTT_ALLOCATION_FAILED );
-          status = WEBVTT_OUT_OF_MEMORY;
-          goto _finish;
+          continue;
         }
-        /* POP the stack and let the previous frame deal with it */
-        POP();
       }
     }
 
     /**
      * Get the next token from the stream
      *
      * If the token is 'UNFINISHED', but we are at the end of our input
      * data, change it to BADTOKEN because it will never be finished.
@@ -1159,41 +1081,40 @@ parse_webvtt( webvtt_parser self, const 
           token = BADTOKEN;
         } else if( pos == len ) {
           goto _finish;
         }
       }
     }
 _recheck:
     switch( SP->state ) {
+      default:
+        /* Should never happen */
+        break;
+
       case T_INITIAL:
         /**
          * In the initial state:
          * We should have WEBVTT as the first token returned,
          * otherwise this isn't really a valid file.
          *
          * If we get 'WEBVTT', push us into the TAG state, where we
          * check for a tag comment (arbitrary text following a whitespace
          * after the WEBVTT token) until a newline
          *
          * If WEBVTT is not the first token, then report error and
          * abort parsing.
          */
         if( token == WEBVTT ) {
           PUSH0( T_TAG, 0, V_NONE );
           break;
-        } else {
-          if( pos != len ) {
-            if( !skip_error ) {
-              ERROR_AT_COLUMN( WEBVTT_MALFORMED_TAG, last_column );
-              skip_error = 1;
-            }
-            status = WEBVTT_PARSE_ERROR;
-            goto _finish;
-          }
+        } else if( token != UNFINISHED ) {
+          ERROR_AT( WEBVTT_MALFORMED_TAG, 1, 1 );
+          status = WEBVTT_PARSE_ERROR;
+          goto _finish;
         }
         break;
 
       case T_TAG:
         /**
          * If we have a WHITESPACE following the WEBVTT token,
          * switch to T_TAGCOMMENT state and skip the comment.
          * Otherwise, if it's a NEWLINE, we can just skip to
@@ -1251,16 +1172,19 @@ parse_webvtt( webvtt_parser self, const 
         switch( token ) {
             /**
              * We're only really expecting a newline here --
              * The cue id should have been read already
              */
           case NEWLINE:
             SP->state = T_FROM;
             break;
+
+          default:
+            break;
         }
 
         /**
          * Count EOLs, POP when finished
          */
       case T_EOL:
         switch( token ) {
           case NEWLINE:
@@ -1302,104 +1226,47 @@ parse_webvtt( webvtt_parser self, const 
           PUSH0( T_CUE, cue, V_CUE );
           PUSH0( T_CUEREAD, 0, V_TEXT );
           SP->v.text.d = tk.d;
         }
         break;
 
 
       case T_CUE:
-        if( self->popped && FRAMEUP( 1 )->state == T_CUEREAD ) {
-          /**
-           * We're expecting either cue-id (contains '-->') or cue
-           * params
-           */
-          webvtt_cue *cue = SP->v.cue;
-          webvtt_state *st = FRAMEUP( 1 );
-          webvtt_string text = st->v.text;
+      {
+        webvtt_cue *cue;
+        webvtt_state *st;
+        webvtt_string text;
+        SAFE_ASSERT( self->popped && FRAMEUP( 1 )->state == T_CUEREAD );
+        /**
+         * We're expecting either cue-id (contains '-->') or cue
+         * params
+         */
+        cue = SP->v.cue;
+        st = FRAMEUP( 1 );
+        text.d = st->v.text.d;
 
-          /* FIXME: guard inconsistent state */
-          if (!cue) {
-            ERROR( WEBVTT_PARSE_ERROR );
-            status = WEBVTT_PARSE_ERROR;
-            goto _finish;
-          }
-
-          st->type = V_NONE;
-          st->v.cue = NULL;
+        st->type = V_NONE;
+        st->v.cue = NULL;
 
-          /**
-           * The type should be V_TEXT. If it's not, somethings wrong.
-           *
-           * TODO: Add debug assertion
-           */
-          if( find_bytes( webvtt_string_text( &text ), webvtt_string_length( &text ), separator,
-                          sizeof( separator ) ) == WEBVTT_SUCCESS) {
-            /* It's not a cue id, we found '-->'. It can't be a second
-               cueparams line, because if we had it, we would be in
-               a different state. */
-            int v;
-            /* backup the column */
-            self->column = 1;
-            if( ( v = parse_cueparams( self, webvtt_string_text( &text ),
-                                       webvtt_string_length( &text ), cue ) ) < 0 ) {
-              if( v == WEBVTT_PARSE_ERROR ) {
-                status = WEBVTT_PARSE_ERROR;
-                goto _finish;
-              }
-              webvtt_release_string( &text );
-              *mode = M_SKIP_CUE;
-              goto _finish;
-            } else {
-              webvtt_release_string( &text );
-              cue->flags |= CUE_HAVE_CUEPARAMS;
-              *mode = M_CUETEXT;
-              goto _finish;
-            }
-          } else {
-            /* It is a cue-id */
-            if( cue && cue->flags & CUE_HAVE_ID ) {
-              /**
-               * This isn't actually a cue-id, because we already
-               * have one. It seems to be cuetext, which is occurring
-               * before cue-params
-               */
-              webvtt_release_string( &text );
-              ERROR( WEBVTT_CUE_INCOMPLETE );
-              *mode = M_SKIP_CUE;
-              goto _finish;
-            } else {
-              self->column += webvtt_string_length( &text );
-              if( WEBVTT_FAILED( status = webvtt_string_append(
-                                            &cue->id, webvtt_string_text( &text ), webvtt_string_length( &text ) ) ) ) {
-                webvtt_release_string( &text );
-                ERROR( WEBVTT_ALLOCATION_FAILED );
-              }
+        /* FIXME: guard inconsistent state */
+        if (!cue) {
+          ERROR( WEBVTT_PARSE_ERROR );
+          status = WEBVTT_PARSE_ERROR;
+          goto _finish;
+        }
 
-              cue->flags |= CUE_HAVE_ID;
-            }
-          }
-          webvtt_release_string( &text );
-          self->popped = 0;
-        } else {
-          webvtt_cue *cue = SP->v.cue;
-          /* If we have a newline, it might be the end of the cue. */
-          if( token == NEWLINE ) {
-            if( cue->flags & CUE_HAVE_CUEPARAMS ) {
-              *mode = M_CUETEXT;
-            } else if( cue->flags & CUE_HAVE_ID ) {
-              PUSH0( T_CUEREAD, 0, V_NONE );
-            } else {
-              /* I don't think this should ever happen? */
-              POPBACK();
-            }
-          }
+        status = webvtt_proc_cueline( self, cue, &text );
+        ++self->line;
+        if( self->mode != M_WEBVTT ) {
+          goto _finish;
         }
-        break;
-
+        self->popped = 0;
+      }
+      break;
     }
 
     /**
      * reset token pos
      */
     self->token_pos = 0;
   }
 
@@ -1407,133 +1274,227 @@ parse_webvtt( webvtt_parser self, const 
 _finish:
   if( status == WEBVTT_OUT_OF_MEMORY ) {
     cleanup_stack( self );
   }
   *ppos = pos;
   return status;
 }
 
-static webvtt_status
-read_cuetext( webvtt_parser self, const webvtt_byte *b, webvtt_uint
-*ppos, webvtt_uint len, webvtt_parse_mode *mode, webvtt_bool finish )
+WEBVTT_INTERN webvtt_status
+webvtt_read_cuetext( webvtt_parser self, const webvtt_byte *b,
+                     webvtt_uint *ppos, webvtt_uint len, webvtt_bool finish )
 {
   webvtt_status status = WEBVTT_SUCCESS;
   webvtt_uint pos = *ppos;
   int finished = 0;
+  int flags = 0;
+  webvtt_cue *cue;
+
+  /* Ensure that we have a cue to work with */
+  SAFE_ASSERT( self->top->type = V_CUE );
+  cue = self->top->v.cue;
+
+  /**
+   * Hack to support lines spanning multiple buffers.
+   *
+   * TODO: Do this some better way. This is not good!
+   */
+  if( self->line_buffer.d != 0 && self->line_buffer.d->text[
+      self->line_buffer.d->length - 1 ] == '\n' ) {
+    flags = 1;
+  }
+
   do {
-    int v;
-    if( ( v = webvtt_string_getline( &self->line_buffer, b, &pos, len, &self->truncate, finish, 1 ) ) ) {
-      if( v < 0 ) {
-        status = WEBVTT_OUT_OF_MEMORY;
-        goto _finish;
+    if( !flags ) {
+      int v;
+      if( ( v = webvtt_string_getline( &self->line_buffer, b, &pos, len,
+                                       &self->truncate, finish ) ) ) {
+        if( v < 0 || WEBVTT_FAILED( webvtt_string_putc( &self->line_buffer,
+                                                        '\n' ) ) ) {
+          status = WEBVTT_OUT_OF_MEMORY;
+          goto _finish;
+        }
+        flags = 1;
       }
+    }
+    if( flags ) {
+      webvtt_token token = webvtt_lex_newline( self, b, &pos, len, finish );
+      if( token == NEWLINE ) {
+        self->token_pos = 0;
+        self->line++;
 
-      /**
-       * We've encountered a line without any cuetext on it, i.e. there is no
-       * newline character and len is 0 or there is and len is 1, therefore,
-       * the cue text is finished.
-       */
-      if( self->line_buffer.d->length <= 1 ) {
+        /* Remove the '\n' that we appended to determine that we're in state 1
+         */
+        self->line_buffer.d->text[ --self->line_buffer.d->length ] = 0;
         /**
-         * finished
+         * We've encountered a line without any cuetext on it, i.e. there is no
+         * newline character and len is 0 or there is and len is 1, therefore,
+         * the cue text is finished.
          */
-        finished = 1;
+        if( self->line_buffer.d->length == 0 ) {
+          webvtt_release_string( &self->line_buffer );
+          finished = 1;
+        } else if( find_bytes( webvtt_string_text( &self->line_buffer ),
+                   webvtt_string_length( &self->line_buffer ), separator,
+                   sizeof( separator ) ) == WEBVTT_SUCCESS ) {
+          /**
+           * Line contains cue-times separator, and thus we treat it as a
+           * separate cue. Trick program into thinking that T_CUEREAD had read
+           * this line.
+           */
+          do_push( self, 0, 0, T_CUEREAD, 0, V_NONE, self->line, self->column );
+          webvtt_copy_string( &SP->v.text, &self->line_buffer );
+          webvtt_release_string( &self->line_buffer );
+          SP->type = V_TEXT;
+          POP();
+          finished = 1;
+        } else {
+          /**
+           * If it's not the end of a cue, simply append it to the cue's payload
+           * text.
+           */
+          if( webvtt_string_length( &cue->body ) &&
+              WEBVTT_FAILED( webvtt_string_putc( &cue->body, '\n' ) ) ) {
+            status = WEBVTT_OUT_OF_MEMORY;
+            goto _finish;
+          }
+          webvtt_string_append_string( &cue->body, &self->line_buffer );
+          webvtt_release_string( &self->line_buffer );
+          flags = 0;
+        }
       }
     }
   } while( pos < len && !finished );
 _finish:
   *ppos = pos;
+  if( finish ) {
+    finished = 1;
+  }
+
   /**
    * If we didn't encounter 2 successive EOLs, and it's not the final buffer in
    * the file, notify the caller.
    */
-  if( pos >= len && !WEBVTT_FAILED( status ) && !finished ) {
+  if( !finish && pos >= len && !WEBVTT_FAILED( status ) && !finished ) {
     status = WEBVTT_UNFINISHED;
   }
   return status;
 }
 
+WEBVTT_INTERN webvtt_status
+webvtt_proc_cuetext( webvtt_parser self, const webvtt_byte *b,
+                     webvtt_uint *ppos, webvtt_uint len, webvtt_bool finish )
+{
+  webvtt_status status;
+  webvtt_cue *cue;
+  SAFE_ASSERT( ( self->mode == M_CUETEXT || self->mode == M_SKIP_CUE )
+               && self->top->type == V_CUE );
+  cue = self->top->v.cue;
+  SAFE_ASSERT( cue != 0 );
+  status  = webvtt_read_cuetext( self, b, ppos, len, finish );
+
+  if( status == WEBVTT_SUCCESS ) {
+    if( self->mode != M_SKIP_CUE ) {
+      /**
+       * Once we've successfully read the cuetext into line_buffer, call the
+       * cuetext parser from cuetext.c
+       */
+      status = webvtt_parse_cuetext( self, cue, &cue->body,
+                                     self->finished );
+
+      /**
+       * return the cue to the user, if possible.
+       */
+      finish_cue( self, &cue );
+    } else {
+      webvtt_release_cue( &cue );
+    }
+
+    self->top->type = V_NONE;
+    self->top->state = 0;
+    self->top->v.cue = 0;
+
+    if( (self->top+1)->type == V_NONE ) {
+      (self->top+1)->state = 0;
+      /* Pop from T_CUE state */
+      POP();
+    } else {
+      /**
+       * If we found '-->', we need to create another cue and remain
+       * in T_CUE state
+       */
+      webvtt_create_cue( &self->top->v.cue );
+      self->top->type = V_CUE;
+      self->top->state = T_CUE;
+    }
+    self->mode = M_WEBVTT;
+  }
+  return status;
+}
+
 WEBVTT_EXPORT webvtt_status
 webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len )
 {
   webvtt_status status;
   webvtt_uint pos = 0;
   const webvtt_byte *b = ( const webvtt_byte * )buffer;
 
   while( pos < len ) {
     switch( self->mode ) {
       case M_WEBVTT:
-        if( WEBVTT_FAILED( status = parse_webvtt( self, b, &pos, len, &self->mode, self->finished ) ) ) {
+        if( WEBVTT_FAILED( status = parse_webvtt( self, b, &pos, len, self->finished ) ) ) {
           return status;
         }
         break;
 
       case M_CUETEXT:
         /**
          * read in cuetext
          */
-        if( WEBVTT_FAILED( status = read_cuetext( self, b, &pos, len, &self->mode, self->finished ) ) ) {
+        if( WEBVTT_FAILED( status = webvtt_proc_cuetext( self, b, &pos, len,
+                                                  self->finished ) ) ) {
           if( status == WEBVTT_UNFINISHED ) {
             /* Make an exception here, because this isn't really a failure. */
             return WEBVTT_SUCCESS;
           }
           return status;
         }
-        /**
-         * Once we've successfully read the cuetext into line_buffer, call the
-         * cuetext parser from cuetext.c
-         */
-        status = webvtt_parse_cuetext( self, SP->v.cue, &self->line_buffer, self->finished );
-
-        /**
-         * return the cue to the user, if possible.
-         */
-        finish_cue( self, &SP->v.cue );
-
-        /**
-         * return to our typical parsing mode now.
-         */
-        SP->type = V_NONE;
-        webvtt_release_string( &self->line_buffer );
-        self->mode = M_WEBVTT;
 
         /* If we failed to parse cuetext, return the error */
         if( WEBVTT_FAILED( status ) ) {
           return status;
         }
         break;
 
       case M_SKIP_CUE:
-        if( WEBVTT_FAILED( status = read_cuetext( self, b, &pos, len, &self->mode, self->finished ) ) ) {
+        if( WEBVTT_FAILED( status = webvtt_proc_cuetext( self, b, &pos, len,
+                                                  self->finished ) ) ) {
           return status;
         }
-        webvtt_release_string( &self->line_buffer );
-        self->mode = M_WEBVTT;
         break;
 
       case M_READ_LINE: {
         /**
          * Read in a line of text into the line-buffer,
          * we will and depending on our state, do something with it.
          */
         int ret;
-        if( ( ret = webvtt_string_getline( &self->line_buffer, b, &pos, len, &self->truncate, self->finished, 0 ) ) ) {
+        if( ( ret = webvtt_string_getline( &self->line_buffer, b, &pos, len,
+                                           &self->truncate,
+                                           self->finished ) ) ) {
           if( ret < 0 ) {
             ERROR( WEBVTT_ALLOCATION_FAILED );
             return WEBVTT_OUT_OF_MEMORY;
           }
           self->mode = M_WEBVTT;
         }
         break;
       }
     }
-    if( WEBVTT_FAILED( status = webvtt_skipwhite( b, &pos, len ) ) ) {
-      return status;
-    }
   }
 
   return WEBVTT_SUCCESS;
 }
 
 #undef SP
 #undef AT_BOTTOM
 #undef ON_HEAP
@@ -1553,19 +1514,19 @@ parse_int( const webvtt_byte **pb, int *
   webvtt_int64 mul = 1;
   const webvtt_byte *b = *pb;
   while( *b ) {
     webvtt_byte ch = *b;
     if( webvtt_isdigit( ch ) ) {
       /**
        * Digit character, carry on
        */
-      result = result * 10 + ( ch - UTF8_DIGIT_ZERO );
+      result = result * 10 + ( ch - '0' );
       ++digits;
-    } else if( mul == 1 && digits == 0 && ch == UTF8_HYPHEN_MINUS ) {
+    } else if( mul == 1 && digits == 0 && ch == '-' ) {
       mul = -1;
     } else {
       break;
     }
     ++b;
   }
   *pb = b;
   if( pdigits ) {
@@ -1597,35 +1558,35 @@ parse_timestamp( const webvtt_byte *b, w
    * assume v[0] contains hours if more or less than 2 digits, or value is
    * greater than 59
    */
   if ( digits != 2 || v[0] > 59 ) {
     have_hours = 1;
   }
 
   /* fail if missing colon ':' character */
-  if ( !*b || *b++ != UTF8_COLON ) {
+  if ( !*b || *b++ != ':' ) {
     malformed = 1;
   }
 
   /* fail if end of data reached, or byte is not an ASCII digit */
   if ( !*b || !webvtt_isdigit( *b ) ) {
     malformed = 1;
   }
 
   /* get another integer value, fail if digits is not equal to 2 */
   v[1] = parse_int( &b, &digits );
   if( digits != 2 ) {
     malformed = 1;
   }
 
-  /* if we already know there's an hour component, or if the next byte is a 
+  /* if we already know there's an hour component, or if the next byte is a
      colon ':', read the next value */
-  if ( have_hours || ( *b == UTF8_COLON ) ) {
-    if( *b++ != UTF8_COLON ) {
+  if ( have_hours || ( *b == ':' ) ) {
+    if( *b++ != ':' ) {
       goto _malformed;
     }
     if( !*b || !webvtt_isdigit( *b ) ) {
       malformed = 1;
     }
     v[2] = parse_int( &b, &digits );
     if( digits != 2 ) {
       malformed = 1;
@@ -1634,17 +1595,17 @@ parse_timestamp( const webvtt_byte *b, w
     /* Otherwise, if there is no hour component, shift everything over */
     v[2] = v[1];
     v[1] = v[0];
     v[0] = 0;
   }
 
   /* collect the manditory seconds-frac component. fail if there is no FULL_STOP
      '.' or if there is no ascii digit following it */
-  if( *b++ != UTF8_FULL_STOP || !webvtt_isdigit( *b ) ) {
+  if( *b++ != '.' || !webvtt_isdigit( *b ) ) {
     goto _malformed;
   }
   v[3] = parse_int( &b, &digits );
   if( digits != 3 ) {
     malformed = 1;
   }
 
   /* Ensure that minutes and seconds are acceptable values */
--- a/media/webvtt/parser_internal.h
+++ b/media/webvtt/parser_internal.h
@@ -139,28 +139,31 @@ webvtt_parse_state_t {
    */
 } webvtt_parse_state;
 
 /**
  * lexer state
  */
 typedef enum
 webvtt_lexer_state_t {
-  L_START = 0, L_BOM0, L_BOM1, L_WEBVTT0, L_WEBVTT1, L_WEBVTT2, L_WEBVTT3, L_WEBVTT4, L_WEBVTT5, L_DASH0, L_SEP1,
-  L_DIGIT0, L_NEWLINE0, L_WHITESPACE, L_POSITION0, L_POSITION1, L_POSITION2, L_POSITION3, L_POSITION4, L_POSITION5,
-  L_POSITION6, L_ALIGN0, L_ALIGN1, L_ALIGN2, L_ALIGN3, L_L0, L_LINE1, L_LINE2, L_LINE3,
-  L_VERTICAL0, L_VERTICAL1, L_VERTICAL2, L_VERTICAL3, L_VERTICAL4, L_VERTICAL5, L_VERTICAL6, L_RL0,
-  L_S0, L_SIZE1, L_SIZE2, L_START1, L_START2, L_START3, L_MIDDLE0, L_MIDDLE1, L_MIDDLE2, L_MIDDLE3,
-  L_MIDDLE4, L_END0, L_END1, L_TIMESTAMP1, L_TIMESTAMP2, L_TIMESTAMP3, L_RIGHT1, L_RIGHT2,
-  L_RIGHT3, L_NOTE1, L_NOTE2, L_NOTE3, L_LEFT1, L_LEFT2,
+  L_START = 0, L_BOM0, L_BOM1, L_WEBVTT0, L_WEBVTT1, L_WEBVTT2, L_WEBVTT3,
+  L_WEBVTT4, L_DASH0, L_SEP1, L_DIGIT0, L_NEWLINE0, L_WHITESPACE, L_POSITION0,
+  L_POSITION1, L_POSITION2, L_POSITION3, L_POSITION4, L_POSITION5, L_POSITION6,
+  L_ALIGN0, L_ALIGN1, L_ALIGN2, L_ALIGN3, L_L0, L_LINE1, L_LINE2, L_VERTICAL0,
+  L_VERTICAL1, L_VERTICAL2, L_VERTICAL3, L_VERTICAL4, L_VERTICAL5, L_VERTICAL6,
+  L_RL0, L_S0, L_SIZE1, L_SIZE2, L_START1, L_START2, L_START3, L_MIDDLE0,
+  L_MIDDLE1, L_MIDDLE2, L_MIDDLE3, L_MIDDLE4, L_END0, L_END1, L_TIMESTAMP1,
+  L_TIMESTAMP2, L_TIMESTAMP3, L_RIGHT1, L_RIGHT2, L_RIGHT3, L_NOTE1, L_NOTE2,
+  L_NOTE3, L_LEFT1, L_LEFT2,
 } webvtt_lexer_state;
 
 typedef struct
 webvtt_state {
   webvtt_parse_state state;
+  webvtt_uint flags; /* Defaults to 0 when pushed */
   webvtt_token token;
   webvtt_state_value_type type;
   webvtt_uint back;
   webvtt_uint line;
   webvtt_uint column;
   union {
     /**
      * cue value
@@ -194,16 +197,18 @@ webvtt_parser_t {
   webvtt_uint bytes; /* number of bytes read. */
   webvtt_uint line;
   webvtt_uint column;
   webvtt_cue_fn read;
   webvtt_error_fn error;
   void *userdata;
   webvtt_bool finished;
   
+  webvtt_uint cuetext_line; /* start line of cuetext */
+
   /**
    * 'mode' can have several states, it is not boolean.
    */
   webvtt_parse_mode mode;
 
   webvtt_state *top; /* Top parse state */
   webvtt_state astack[0x100];
   webvtt_state *stack; /* dynamically allocated stack, if 'astack' fills up */
@@ -222,18 +227,57 @@ webvtt_parser_t {
    */
   webvtt_lexer_state tstate;
   webvtt_uint token_pos;
   webvtt_byte token[0x100];
 };
 
 WEBVTT_INTERN webvtt_token webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish );
 WEBVTT_INTERN webvtt_status webvtt_lex_word( webvtt_parser self, webvtt_string *pba, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish );
+
+/* Tokenize newline sequence, without incrementing 'self->line'. Returns
+ * BAD_TOKEN when a newline sequence is not found. */
+WEBVTT_INTERN webvtt_token webvtt_lex_newline( webvtt_parser self, const
+  webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish );
+
+WEBVTT_INTERN webvtt_status webvtt_proc_cueline( webvtt_parser self,
+  webvtt_cue *cue, webvtt_string *line );
+
+WEBVTT_INTERN webvtt_status webvtt_parse_align( webvtt_parser self,
+  webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len );
+
+WEBVTT_INTERN webvtt_status webvtt_parse_line( webvtt_parser self,
+  webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len );
+
+WEBVTT_INTERN webvtt_status webvtt_parse_position( webvtt_parser self,
+  webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len );
+
+WEBVTT_INTERN webvtt_status webvtt_parse_size( webvtt_parser self,
+  webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len );
+
+WEBVTT_INTERN webvtt_status webvtt_parse_vertical( webvtt_parser self,
+  webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len );
+
 WEBVTT_INTERN int parse_timestamp( const webvtt_byte *b, webvtt_timestamp *result );
 
+WEBVTT_INTERN webvtt_status do_push( webvtt_parser self, webvtt_uint token,
+  webvtt_uint back, webvtt_uint state, void *data, webvtt_state_value_type type,
+  webvtt_uint line, webvtt_uint column );
+
+WEBVTT_INTERN webvtt_status webvtt_read_cuetext( webvtt_parser self,
+  const webvtt_byte *b, webvtt_uint *ppos, webvtt_uint len,
+  webvtt_bool finish );
+
+WEBVTT_INTERN webvtt_status webvtt_proc_cuetext( webvtt_parser self,
+  const webvtt_byte *b, webvtt_uint *ppos, webvtt_uint len,
+  webvtt_bool finish );
+
+WEBVTT_INTERN int parse_cueparams( webvtt_parser self, const webvtt_byte *text,
+  webvtt_uint len, webvtt_cue *cue );
+
 /** 
  * Flags which can apply additional meaning to a token. find_token() will
  * test for only the actual token and ignore the additional flags.
  */
 typedef
 enum webvtt_token_flags_t
 {
   /* Number can be positive */
--- a/media/webvtt/string.c
+++ b/media/webvtt/string.c
@@ -79,18 +79,16 @@ webvtt_create_string( webvtt_uint32 allo
   result->d = d;
 
   return WEBVTT_SUCCESS;
 }
 
 WEBVTT_EXPORT webvtt_status
 webvtt_create_string_with_text( webvtt_string *result, const webvtt_byte *init_text, int len )
 {
-  webvtt_uint pos = 0;
-
   if( !result ) {
     return WEBVTT_INVALID_PARAM;
   }
   
   if( !init_text ) {
     webvtt_init_string( result );
     return WEBVTT_SUCCESS;
   }
@@ -195,28 +193,28 @@ webvtt_string_text(const webvtt_string *
   if( !str || !str->d )
   {
     return 0;
   }
 
   return str->d->text;
 }
 
-WEBVTT_EXPORT const webvtt_uint32
+WEBVTT_EXPORT webvtt_uint32
 webvtt_string_length(const webvtt_string *str)
 {
   if( !str || !str->d )
   {
     return 0;
   }
 
   return str->d->length;
 }
 
-WEBVTT_EXPORT const webvtt_uint32
+WEBVTT_EXPORT webvtt_uint32
 webvtt_string_capacity(const webvtt_string *str)
 {
   if( !str || !str->d )
   {
     return 0;
   }
 
   return str->d->alloc;
@@ -282,24 +280,25 @@ grow( webvtt_string *str, webvtt_uint ne
     webvtt_free( d );
   }
 
   return WEBVTT_SUCCESS;
 }
 
 WEBVTT_EXPORT int
 webvtt_string_getline( webvtt_string *src, const webvtt_byte *buffer,
-    webvtt_uint *pos, webvtt_uint len, int *truncate, webvtt_bool finish, webvtt_bool retain_new_line )
+                       webvtt_uint *pos, int len, int *truncate,
+                       webvtt_bool finish )
 {
   int ret = 0;
   webvtt_string *str = src;
   webvtt_string_data *d = 0;
   const webvtt_byte *s = buffer + *pos;
   const webvtt_byte *p = s;
-  const webvtt_byte *n = buffer + len;
+  const webvtt_byte *n;
 
   /**
    *if this is public now, maybe we should return webvtt_status so we can
    * differentiate between WEBVTT_OUT_OF_MEMORY and WEBVTT_INVALID_PARAM
    */
   if( !str ) {
     return -1;
   }
@@ -307,23 +306,23 @@ webvtt_string_getline( webvtt_string *sr
   /* This had better be a valid string_data, or else NULL. */
   d = str->d;
   if( !str->d ) {
     if(WEBVTT_FAILED(webvtt_create_string( 0x100, str ))) {
       return -1;
     }
     d = str->d;
   }
-
-  while( p < n && *p != UTF8_CARRIAGE_RETURN && *p != UTF8_LINE_FEED ) {
-    ++p;
+  if( len < 0 ) {
+    len = strlen( (const char *)buffer );
   }
-  /* Retain the new line character. */
-  if( p < n && retain_new_line ) {
-    p++;
+  n = buffer + len;
+
+  while( p < n && *p != '\r' && *p != '\n' ) {
+    ++p;
   }
 
   if( p < n || finish ) {
     ret = 1; /* indicate that we found EOL */
   }
   len = (webvtt_uint)( p - s );
   *pos += len;
   if( d->length + len + 1 >= d->alloc ) {
@@ -366,21 +365,31 @@ webvtt_string_putc( webvtt_string *str, 
     str->d->text[ str->d->length++ ] = to_append;
     str->d->text[ str->d->length ] = 0;
   }
 
   return result;
 }
 
 WEBVTT_EXPORT webvtt_bool
-webvtt_string_is_equal( webvtt_string *str, webvtt_byte *to_compare, webvtt_uint len )
+webvtt_string_is_equal( const webvtt_string *str, const webvtt_byte *to_compare,
+                        int len )
 {
-  if( !str || !to_compare || webvtt_string_length( str ) != len ) {
+  if( !str || !to_compare ) {
     return 0;
   }
+
+  if( len < 0 ) {
+    len = strlen( (const char *)to_compare );
+  }
+
+  if( str->d->length != (unsigned)len ) {
+    return 0;
+  }
+
   return memcmp( webvtt_string_text( str ), to_compare, len ) == 0;
 }
 
 WEBVTT_EXPORT webvtt_status
 webvtt_string_append( webvtt_string *str, const webvtt_byte *buffer, int len )
 {
   webvtt_status result;
 
@@ -579,18 +588,18 @@ webvtt_skip_utf8( const webvtt_byte **be
 
   return n_chars == 0;
 }
 
 WEBVTT_EXPORT webvtt_uint16
 webvtt_utf8_to_utf16( const webvtt_byte *utf8, const webvtt_byte *end,
   webvtt_uint16 *high_surrogate )
 {
-  int need = 0, min = 0;
-  webvtt_uint32 uc = 0;
+  int need = 0;
+  webvtt_uint32 uc = 0, min = 0;
   
   /* We're missing our pointers */
   if( !utf8 ) {
     return 0;
   }
   if( !end ) {
     end = utf8 + strlen( ( const char * )utf8 );
   }
@@ -620,17 +629,18 @@ webvtt_utf8_to_utf16( const webvtt_byte 
         if (!--need) {
           int nc;
           if ( !( nc = UTF_IS_NONCHAR( uc ) ) && uc > 0xFFFF && uc < 0x110000) {
             /* Surrogate pair */
             if( high_surrogate ) {
               *high_surrogate = UTF_HIGH_SURROGATE( uc );
             }
             return UTF_LOW_SURROGATE( uc );
-          } else if ( ( uc < min ) || ( uc >= 0xD800 && uc <= 0xDFFF ) || nc || uc >= 0x110000) {
+          } else if ( ( uc < min ) || ( uc >= 0xD800 && uc <= 0xDFFF ) || nc
+                      || uc >= 0x110000) {
             /* Non-character, overlong sequence, or utf16 surrogate */
             return 0xFFFD;  
           } else {
             /* Non-surrogate */
             return uc;
           }
         }
       }
--- a/media/webvtt/string_internal.h
+++ b/media/webvtt/string_internal.h
@@ -24,70 +24,24 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef __INTERN_STRING_H__
 # define __INTERN_STRING_H__
 # include <webvtt/string.h>
 
-# define UTF8_AMPERSAND         (0x26)
-# define UTF8_LESS_THAN         (0x3C)
-# define UTF8_GREATER_THAN      (0x3E)
-# define UTF8_HYPHEN_MINUS      (0x2D)
 # define UTF8_LEFT_TO_RIGHT_1   (0xE2)
 # define UTF8_LEFT_TO_RIGHT_2   (0x80)
 # define UTF8_LEFT_TO_RIGHT_3   (0x8E)
 # define UTF8_RIGHT_TO_LEFT_1   (0xE2)
 # define UTF8_RIGHT_TO_LEFT_2   (0x80)
 # define UTF8_RIGHT_TO_LEFT_3   (0x8F)
 # define UTF8_NO_BREAK_SPACE_1  (0xC2)
 # define UTF8_NO_BREAK_SPACE_2  (0xA0)
-# define UTF8_NULL_BYTE         (0x00)
-# define UTF8_COLON             (0x3A)
-# define UTF8_SEMI_COLON        (0x3B)
-# define UTF8_TAB               (0x09)
-# define UTF8_FORM_FEED         (0x0C)
-# define UTF8_LINE_FEED         (0x0A)
-# define UTF8_CARRIAGE_RETURN   (0x0D)
-# define UTF8_FULL_STOP         (0x2E)
-# define UTF8_SOLIDUS           (0x2F)
-# define UTF8_SPACE             (0x20)
-# define UTF8_DIGIT_ZERO        (0x30)
-# define UTF8_DIGIT_NINE        (0x39)
-
-# define UTF8_CAPITAL_A         (0x41)
-# define UTF8_CAPITAL_Z         (0x5A)
-
-# define UTF8_A                 (0x61)
-# define UTF8_B                 (0x62)
-# define UTF8_C                 (0x63)
-# define UTF8_D                 (0x64)
-# define UTF8_E                 (0x65)
-# define UTF8_F                 (0x66)
-# define UTF8_G                 (0x67)
-# define UTF8_H                 (0x68)
-# define UTF8_I                 (0x69)
-# define UTF8_J                 (0x6A)
-# define UTF8_K                 (0x6B)
-# define UTF8_L                 (0x6C)
-# define UTF8_M                 (0x6D)
-# define UTF8_N                 (0x6E)
-# define UTF8_O                 (0x6F)
-# define UTF8_P                 (0x70)
-# define UTF8_Q                 (0x71)
-# define UTF8_R                 (0x72)
-# define UTF8_S                 (0x73)
-# define UTF8_T                 (0x74)
-# define UTF8_U                 (0x75)
-# define UTF8_V                 (0x76)
-# define UTF8_W                 (0x77)
-# define UTF8_X                 (0x78)
-# define UTF8_Y                 (0x79)
-# define UTF8_Z                 (0x7A)
 
 /**
  * Taken from ICU
  * http://source.icu-project.org/repos/icu/icu/trunk/source/common/unicode/utf.h
  */
 # define UTF_IS_NONCHAR( C ) \
   ( ( C )>=0xFDD0 && \
   ( ( webvtt_uint32 )( C ) <= 0xfdef || ( ( C ) & 0xFFFE)==0xFFFE) && \
@@ -113,31 +67,31 @@ webvtt_string_data_t {
   webvtt_uint32 length;
   webvtt_byte *text;
   webvtt_byte array[1];
 };
 
 static __WEBVTT_STRING_INLINE  int
 webvtt_isalpha( webvtt_byte ch )
 {
-  return ( ( ( ch >= UTF8_CAPITAL_A ) && ( ch <= UTF8_CAPITAL_Z ) ) || ( ( ch >= UTF8_A ) && ( ch <= UTF8_Z ) ) );
+  return ( ( ( ch >= 'A' ) && ( ch <= 'Z' ) ) || ( ( ch >= 'a' ) && ( ch <= 'z' ) ) );
 }
 static __WEBVTT_STRING_INLINE int
 webvtt_isdigit( webvtt_byte ch )
 {
-  return ( ( ch >= UTF8_DIGIT_ZERO ) && ( ch <= UTF8_DIGIT_NINE ) );
+  return ( ( ch >= '0' ) && ( ch <= '9' ) );
 }
 
 static __WEBVTT_STRING_INLINE int
 webvtt_isalphanum( webvtt_byte ch )
 {
   return ( webvtt_isalpha( ch ) || webvtt_isdigit( ch ) );
 }
 
 static __WEBVTT_STRING_INLINE int
 webvtt_iswhite( webvtt_byte ch )
 {
-  return ( ( ch == UTF8_CARRIAGE_RETURN ) || ( ch == UTF8_LINE_FEED ) || ( ch == UTF8_FORM_FEED )
-           || ( ch == UTF8_TAB ) || ( ch == UTF8_SPACE ) ) ;
+  return ( ( ch == '\r' ) || ( ch == '\n' ) || ( ch == '\f' )
+           || ( ch == '\t' ) || ( ch == ' ' ) ) ;
 }
 
 # undef __WEBVTT_STRING_INLINE
 #endif