diff --git a/NEWS b/NEWS
index 1ccdec538ac39226cb60dfacb049588b2c53c6c1..6d93061618d9a0a1b08f11c607e28c4760d85cbe 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,7 @@ Decoder:
  * Rewrite WPL playlists and add ZPL playlists support (Zune)
  * Support TDSC, Canopus HQX
  * Support HEVC hardware decoding on Windows, using DxVA2
+ * Basic TTML subtitles support
 
 Demuxers:
  * Support HD-DVD .evo (H.264, VC-1, MPEG-2, PCM, AC-3, E-AC3, MLP, DTS)
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 10400ef1dec21d997f41cd11bf3eae5b1289c94c..0d794e0d21a39496b91f088b241129e8c953ab0b 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -368,6 +368,7 @@ $Id$
  * stream_out_transcode: audio & video transcoder
  * subsdec: a codec to output textual subtitles
  * subsdelay: subtitles delay filter
+ * substtml: TTML subtitles decoder
  * substx3g: tx3g styled subtitles decoder
  * subsusf: a demuxer for USF subtitles
  * subtitle: a demuxer for subtitle files
@@ -388,6 +389,7 @@ $Id$
  * trivial_channel_mixer: Simple channel mixer plugin
  * ts: MPEG-TS demuxer
  * tta: Lossless True Audio parser
+ * ttml: a TTML subtitles demuxer
  * twolame: a mp1 mp2 audio encoder based on twolame
  * ty: TY demuxer
  * udev: udev probing module
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index bd893261f39ce5f2d664a12fab847ea5bd831087..f173c0e29b23568dfbf9e3dd1b30039a6439d366 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -198,6 +198,9 @@ codec_LTLIBRARIES += libsubsdec_plugin.la
 libsubsusf_plugin_la_SOURCES = codec/subsusf.c
 codec_LTLIBRARIES += libsubsusf_plugin.la
 
+libsubsttml_plugin_la_SOURCES = codec/substtml.c
+codec_LTLIBRARIES += libsubsttml_plugin.la
+
 libsvcdsub_plugin_la_SOURCES = codec/svcdsub.c
 codec_LTLIBRARIES += libsvcdsub_plugin.la
 
diff --git a/modules/codec/substtml.c b/modules/codec/substtml.c
new file mode 100644
index 0000000000000000000000000000000000000000..e4c80a6614be7720a0478b8fd247def51f8e6b5d
--- /dev/null
+++ b/modules/codec/substtml.c
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ * substtml.c : TTML subtitles decoder
+ *****************************************************************************
+ * Copyright (C) 2015 VLC authors and VideoLAN
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
+ *          Sushma Reddy <sushma.reddy@research.iiit.ac.in>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_modules.h>
+#include <vlc_codec.h>
+
+#include "substext.h"
+
+#define ALIGN_TEXT N_("Subtitle justification")
+#define ALIGN_LONGTEXT N_("Set the justification of subtitles")
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+static int  OpenDecoder   ( vlc_object_t * );
+static void CloseDecoder  ( vlc_object_t * );
+
+vlc_module_begin ()
+    set_capability( "decoder", 10 )
+    set_shortname( N_("TTML decoder"))
+    set_description( N_("TTML subtitles decoder") )
+    set_callbacks( OpenDecoder, CloseDecoder )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_SCODEC )
+    add_integer( "ttml-align", 0, ALIGN_TEXT, ALIGN_LONGTEXT, false )
+vlc_module_end ();
+
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+struct decoder_sys_t
+{
+    int i_align;
+};
+
+static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    subpicture_t *p_spu = NULL;
+    char *psz_subtitle = NULL;
+
+    /* We cannot display a subpicture with no date */
+    if( p_block->i_pts <= VLC_TS_INVALID )
+    {
+        msg_Warn( p_dec, "subtitle without a date" );
+        return NULL;
+    }
+
+    /* Check validity of packet data */
+    /* An "empty" line containing only \0 can be used to force
+       and ephemer picture from the screen */
+
+    if( p_block->i_buffer < 1 )
+    {
+        msg_Warn( p_dec, "no subtitle data" );
+        return NULL;
+    }
+
+    psz_subtitle = malloc( p_block->i_buffer );
+    if ( unlikely( psz_subtitle == NULL ) )
+        return NULL;
+    memcpy( psz_subtitle, p_block->p_buffer, p_block->i_buffer );
+
+    /* Create the subpicture unit */
+    p_spu = decoder_NewSubpictureText( p_dec );
+    if( !p_spu )
+    {
+        free( psz_subtitle );
+        return NULL;
+    }
+    p_spu->i_start    = p_block->i_pts;
+    p_spu->i_stop     = p_block->i_pts + p_block->i_length;
+    p_spu->b_ephemer  = (p_block->i_length == 0);
+    p_spu->b_absolute = false;
+    subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
+
+    p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
+    p_spu_sys->text  = psz_subtitle;
+
+    return p_spu;
+}
+
+/****************************************************************************
+ * DecodeBlock: the whole thing
+ ****************************************************************************/
+static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
+{
+    if( !pp_block || *pp_block == NULL )
+        return NULL;
+
+    block_t* p_block = *pp_block;
+    subpicture_t *p_spu = ParseText( p_dec, p_block );
+
+    block_Release( p_block );
+    *pp_block = NULL;
+
+    return p_spu;
+}
+
+/*****************************************************************************
+ * OpenDecoder: probe the decoder and return score
+ *****************************************************************************/
+static int OpenDecoder( vlc_object_t *p_this )
+{
+    decoder_t *p_dec = (decoder_t*)p_this;
+    decoder_sys_t *p_sys;
+
+    if ( p_dec->fmt_in.i_codec != VLC_CODEC_TTML )
+    {
+        return VLC_EGENERIC;
+    }
+
+    /* Allocate the memory needed to store the decoder's structure */
+    p_dec->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
+    if( unlikely( p_sys == NULL ) )
+    {
+        return VLC_ENOMEM;
+    }
+
+    p_dec->pf_decode_sub = DecodeBlock;
+    p_dec->fmt_out.i_cat = SPU_ES;
+    p_sys->i_align = var_InheritInteger( p_dec, "ttml-align" );
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * CloseDecoder: clean up the decoder
+ *****************************************************************************/
+static void CloseDecoder( vlc_object_t *p_this )
+{
+    /* Cleanup here */
+    decoder_t *p_dec = (decoder_t *)p_this;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    free( p_sys );
+}
+
diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index f3eb976b8eb30262a8d151b7b8b3c495d6d0bac0..ec0b03d60165433b2477795160a9382efbff732e 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -340,4 +340,6 @@ libdash_plugin_la_LIBADD += -lz
 endif
 demux_LTLIBRARIES += libdash_plugin.la
 
+libttml_plugin_la_SOURCES = demux/ttml.c
+demux_LTLIBRARIES += libttml_plugin.la
 
diff --git a/modules/demux/ttml.c b/modules/demux/ttml.c
new file mode 100644
index 0000000000000000000000000000000000000000..139854e31477b937f7f643da6f8d0aa7673b419f
--- /dev/null
+++ b/modules/demux/ttml.c
@@ -0,0 +1,337 @@
+/*****************************************************************************
+ * ttml.c : TTML subtitles demux
+ *****************************************************************************
+ * Copyright (C) 2015 VLC authors and VideoLAN
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
+ *          Sushma Reddy <sushma.reddy@research.iiit.ac.in>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_demux.h>
+#include <vlc_xml.h>
+#include <vlc_strings.h>
+#include <vlc_memory.h>
+
+static int Open( vlc_object_t* p_this );
+static void Close( demux_t* p_demux );
+
+vlc_module_begin ()
+    set_shortname( N_("TTML") )
+    set_description( N_("TTML demuxer") )
+    set_capability( "demux", 10 )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_DEMUX )
+    set_callbacks( Open, Close )
+    add_shortcut( "ttml", "subtitle" )
+vlc_module_end ();
+
+
+typedef struct
+{
+   int64_t i_start;
+   int64_t i_stop;
+   char *psz_text;
+} subtitle_t;
+
+struct demux_sys_t
+{
+    xml_t* p_xml;
+    xml_reader_t* p_reader;
+    subtitle_t* subtitle;
+    es_out_id_t* p_es;
+    int64_t i_length;
+    int64_t i_next_demux_time;
+    int i_subtitle;
+    int i_subtitles;
+};
+
+static int Control( demux_t* p_demux, int i_query, va_list args )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int64_t *pi64, i64;
+    double *pf, f;
+
+    switch( i_query )
+    {
+        case DEMUX_GET_TIME:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            if( p_sys->i_subtitle < p_sys->i_subtitles )
+            {
+                *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
+                return VLC_SUCCESS;
+            }
+        case DEMUX_SET_TIME:
+            i64 = (int64_t)va_arg( args, int64_t );
+            p_sys->i_subtitle = 0;
+            while( p_sys->i_subtitle < p_sys->i_subtitles )
+            {
+                const subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];
+
+                if( p_subtitle->i_start > i64 )
+                    break;
+                if( p_subtitle->i_stop > p_subtitle->i_start && p_subtitle->i_stop > i64 )
+                    break;
+
+                p_sys->i_subtitle++;
+            }
+
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+                return VLC_EGENERIC;
+            return VLC_SUCCESS;
+        case DEMUX_SET_NEXT_DEMUX_TIME:
+            i64 = (int64_t)va_arg( args, int64_t );
+            p_sys->i_next_demux_time = i64;
+            return VLC_SUCCESS;
+        case DEMUX_GET_LENGTH:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = p_sys->i_length;
+            return VLC_SUCCESS;
+        case DEMUX_GET_POSITION:
+            pf = (double*)va_arg( args, double * );
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+            {
+                *pf = 1.0;
+            }
+            else if( p_sys->i_subtitles > 0 )
+            {
+                *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
+                      (double)p_sys->i_length;
+            }
+            else
+            {
+                *pf = 0.0;
+            }
+            return VLC_SUCCESS;
+        case DEMUX_SET_POSITION:
+            f = (double)va_arg( args, double );
+            i64 = f * p_sys->i_length;
+
+            p_sys->i_subtitle = 0;
+            while( p_sys->i_subtitle < p_sys->i_subtitles &&
+                   p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
+            {
+                p_sys->i_subtitle++;
+            }
+            if( p_sys->i_subtitle >= p_sys->i_subtitles )
+                return VLC_EGENERIC;
+            return VLC_SUCCESS;
+        case DEMUX_GET_PTS_DELAY:
+        case DEMUX_GET_FPS:
+        case DEMUX_GET_META:
+        case DEMUX_GET_ATTACHMENTS:
+        case DEMUX_GET_TITLE_INFO:
+        case DEMUX_HAS_UNSUPPORTED_META:
+        case DEMUX_CAN_RECORD:
+            return VLC_EGENERIC;
+        default:
+            msg_Err( p_demux, "unknown query %d in subtitle control", i_query );
+            return VLC_EGENERIC;
+    }
+    return VLC_EGENERIC;
+}
+
+static int Convert_time( int64_t *timing_value, const char *s )
+{
+    int h1, m1, s1, d1 = 0;
+
+    if ( sscanf( s, "%d:%d:%d,%d",
+                 &h1, &m1, &s1, &d1 ) == 4 ||
+         sscanf( s, "%d:%d:%d.%d",
+                 &h1, &m1, &s1, &d1 ) == 4 ||
+         sscanf( s, "%d:%d:%d",
+                 &h1, &m1, &s1) == 3 )
+    {
+        (*timing_value) = ( (int64_t)h1 * 3600 * 1000 +
+                            (int64_t)m1 * 60 * 1000 +
+                            (int64_t)s1 * 1000 +
+                            (int64_t)d1 ) * 1000;
+
+        return VLC_SUCCESS;
+    }
+
+    return VLC_EGENERIC;
+}
+
+
+
+static int ReadSubtitles( demux_sys_t* p_sys )
+{
+    const char* psz_name;
+    int i_max_sub = 0;
+    int i_type;
+    do
+    {
+        i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_name );
+
+        if ( i_type == XML_READER_STARTELEM && !strcasecmp( psz_name, "p" ) )
+        {
+            char* psz_begin = NULL;
+            char* psz_end = NULL;
+
+            while ( !psz_begin || !psz_end )
+            {
+                const char* psz_attr_value = NULL;
+                const char* psz_attr_name = xml_ReaderNextAttr( p_sys->p_reader, &psz_attr_value );
+                if ( !psz_attr_name || !psz_attr_value )
+                    break;
+                if ( !strcasecmp( psz_attr_name, "begin" ) )
+                    psz_begin = strdup( psz_attr_value );
+                else if ( !strcasecmp( psz_attr_name, "end" ) )
+                    psz_end = strdup( psz_attr_value );
+            }
+
+            if ( psz_begin && psz_end )
+            {
+                if ( p_sys->i_subtitles >= i_max_sub )
+                {
+                    i_max_sub += 500;
+                    p_sys->subtitle = realloc_or_free( p_sys->subtitle,
+                            sizeof( *p_sys->subtitle ) * i_max_sub );
+                    if ( unlikely( p_sys->subtitle == NULL ) )
+                    {
+                        free( psz_begin );
+                        free( psz_end );
+                        return VLC_ENOMEM;
+                    }
+                }
+                const char* psz_text = NULL;
+                subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitles];
+
+                Convert_time( &p_subtitle->i_start, psz_begin );
+                Convert_time( &p_subtitle->i_stop, psz_end );
+
+                i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_text );
+                if ( i_type == XML_READER_TEXT && psz_text != NULL )
+                {
+                    if( *psz_text != 0 )
+                    {
+                        p_subtitle->psz_text = strdup( psz_text );
+                        if ( likely( p_subtitle->psz_text != NULL ) )
+                        {
+                            resolve_xml_special_chars( p_subtitle->psz_text );
+                            p_sys->i_subtitles++;
+                        }
+                    }
+                }
+            }
+            free( psz_begin );
+            free( psz_end );
+        }
+    } while ( i_type != XML_READER_ENDELEM || strcasecmp( psz_name, "tt" ) );
+
+    return VLC_SUCCESS;
+}
+
+static int Demux( demux_t* p_demux )
+{
+    demux_sys_t* p_sys = p_demux->p_sys;
+
+    if( p_sys->i_subtitle >= p_sys->i_subtitles )
+        return 0;
+
+    while ( p_sys->i_subtitle < p_sys->i_subtitles &&
+            p_sys->subtitle[p_sys->i_subtitle].i_start < p_sys->i_next_demux_time )
+    {
+        const subtitle_t* p_subtitle = &p_sys->subtitle[p_sys->i_subtitle];
+
+        block_t* p_block = block_Alloc( strlen( p_subtitle->psz_text ) + 1 );
+        if ( unlikely( p_block == NULL ) )
+            return -1;
+
+        p_block->i_dts =
+        p_block->i_pts = VLC_TS_0 + p_subtitle->i_start;
+
+        if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
+            p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
+
+        memcpy( p_block->p_buffer, p_subtitle->psz_text, p_block->i_buffer );
+
+        es_out_Send( p_demux->out, p_sys->p_es, p_block );
+
+        p_sys->i_subtitle++;
+    }
+    p_sys->i_next_demux_time = 0;
+    return 1;
+}
+
+static int Open( vlc_object_t* p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys;
+
+    p_demux->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
+    if ( unlikely( p_sys == NULL ) )
+        return VLC_ENOMEM;
+
+    p_sys->p_xml = xml_Create( p_demux );
+    if ( !p_sys->p_xml )
+    {
+        Close( p_demux );
+        return VLC_EGENERIC;
+    }
+
+    p_sys->p_reader = xml_ReaderCreate( p_sys->p_xml, p_demux->s );
+    if ( !p_sys->p_reader )
+    {
+        Close( p_demux );
+        return VLC_EGENERIC;
+    }
+
+    const char* psz_name;
+    int i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_name );
+    if ( i_type != XML_READER_STARTELEM || strcmp( psz_name, "tt" ) )
+    {
+        Close( p_demux );
+        return VLC_EGENERIC;
+    }
+    p_demux->pf_demux = Demux;
+    p_demux->pf_control = Control;
+
+    es_format_t fmt;
+    es_format_Init( &fmt, SPU_ES, VLC_CODEC_TTML );
+    p_sys->p_es = es_out_Add( p_demux->out, &fmt );
+    es_format_Clean( &fmt );
+
+    if ( ReadSubtitles( p_sys ) != VLC_SUCCESS )
+        return VLC_EGENERIC;
+    p_sys->i_length = p_sys->subtitle[ p_sys->i_subtitles - 1 ].i_stop;
+
+    return VLC_SUCCESS;
+}
+
+static void Close( demux_t* p_demux )
+{
+    demux_sys_t* p_sys = p_demux->p_sys;
+    if ( p_sys->p_es )
+        es_out_Del( p_demux->out, p_sys->p_es );
+    if ( p_sys->p_reader )
+        xml_ReaderDelete( p_sys->p_reader );
+    if ( p_sys->p_xml )
+        xml_Delete( p_sys->p_xml );
+    for ( int i = 0; i < p_sys->i_subtitles; ++i )
+    {
+        free( p_sys->subtitle[i].psz_text );
+    }
+    free( p_sys->subtitle );
+    free( p_sys );
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 04338208292815c991cfd62ec0bdfafc0f626c7a..773a7281a706469402f184812f2b51c12275d3bc 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -411,6 +411,7 @@ modules/codec/svcdsub.c
 modules/codec/t140.c
 modules/codec/telx.c
 modules/codec/theora.c
+modules/codec/substtml.c
 modules/codec/twolame.c
 modules/codec/uleaddvaudio.c
 modules/codec/vorbis.c
@@ -524,6 +525,7 @@ modules/demux/smf.c
 modules/demux/stl.c
 modules/demux/subtitle.c
 modules/demux/tta.c
+modules/demux/ttml.c
 modules/demux/ty.c
 modules/demux/vc1.c
 modules/demux/vobsub.c