dash.cpp 10.9 KB
Newer Older
Christopher Mueller's avatar
Christopher Mueller committed
1
2
3
/*****************************************************************************
 * dash.cpp: DASH module
 *****************************************************************************
4
 * Copyright © 2010 - 2011 Klagenfurt University
Christopher Mueller's avatar
Christopher Mueller committed
5
6
7
8
9
 *
 * Created on: Aug 10, 2010
 * Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
 *          Christian Timmerer  <christian.timmerer@itec.uni-klu.ac.at>
 *
10
11
 * 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
Christopher Mueller's avatar
Christopher Mueller committed
12
13
14
15
16
 * 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
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Lesser General Public License for more details.
Christopher Mueller's avatar
Christopher Mueller committed
19
20
 *
 * You should have received a copy of the GNU Lesser General Public License
21
22
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Christopher Mueller's avatar
Christopher Mueller committed
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28

Christopher Mueller's avatar
Christopher Mueller committed
29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
34
#include <stdint.h>

Christopher Mueller's avatar
Christopher Mueller committed
35
36
#include <vlc_common.h>
#include <vlc_plugin.h>
37
#include <vlc_demux.h>
38
#include <vlc_meta.h>
Christopher Mueller's avatar
Christopher Mueller committed
39
40
41

#include <errno.h>

42
#include "dash.hpp"
Christopher Mueller's avatar
Christopher Mueller committed
43
#include "xml/DOMParser.h"
44
#include "mpd/MPDFactory.h"
45
46
47
48
49
50
51
52
#include "mpd/Period.h"
#include "mpd/ProgramInformation.h"

using namespace adaptative::logic;
using namespace adaptative::playlist;
using namespace dash::mpd;
using namespace dash::xml;
using namespace dash;
Christopher Mueller's avatar
Christopher Mueller committed
53
54
55
56
57
58
59

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open    (vlc_object_t *);
static void Close   (vlc_object_t *);

60
61
62
63
64
65
#define DASH_WIDTH_TEXT N_("Preferred Width")
#define DASH_WIDTH_LONGTEXT N_("Preferred Width")

#define DASH_HEIGHT_TEXT N_("Preferred Height")
#define DASH_HEIGHT_LONGTEXT N_("Preferred Height")

66
67
68
#define DASH_BW_TEXT N_("Fixed Bandwidth in KiB/s")
#define DASH_BW_LONGTEXT N_("Preferred bandwidth for non adaptative streams")

69
70
#define DASH_LOGIC_TEXT N_("Adaptation Logic")

71
72
73
74
static const int pi_logics[] = {AbstractAdaptationLogic::RateBased,
                                AbstractAdaptationLogic::FixedRate,
                                AbstractAdaptationLogic::AlwaysLowest,
                                AbstractAdaptationLogic::AlwaysBest};
75
76
77
78
79
80

static const char *const ppsz_logics[] = { N_("Bandwidth Adaptive"),
                                           N_("Fixed Bandwidth"),
                                           N_("Lowest Bandwidth/Quality"),
                                           N_("Highest Bandwith/Quality")};

Christopher Mueller's avatar
Christopher Mueller committed
81
82
83
vlc_module_begin ()
        set_shortname( N_("DASH"))
        set_description( N_("Dynamic Adaptive Streaming over HTTP") )
84
        set_capability( "demux", 10 )
Christopher Mueller's avatar
Christopher Mueller committed
85
        set_category( CAT_INPUT )
86
        set_subcategory( SUBCAT_INPUT_DEMUX )
87
        add_integer( "dash-logic",      dash::logic::AbstractAdaptationLogic::Default,
88
89
                                             DASH_LOGIC_TEXT, NULL, false )
            change_integer_list( pi_logics, ppsz_logics )
90
91
        add_integer( "dash-prefwidth",  480, DASH_WIDTH_TEXT,  DASH_WIDTH_LONGTEXT,  true )
        add_integer( "dash-prefheight", 360, DASH_HEIGHT_TEXT, DASH_HEIGHT_LONGTEXT, true )
92
        add_integer( "dash-prefbw",     250, DASH_BW_TEXT,     DASH_BW_LONGTEXT,     false )
Christopher Mueller's avatar
Christopher Mueller committed
93
94
        set_callbacks( Open, Close )
vlc_module_end ()
95

Christopher Mueller's avatar
Christopher Mueller committed
96
97
98
99
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

100
101
static int  Demux( demux_t * );
static int  Control         (demux_t *p_demux, int i_query, va_list args);
102

Christopher Mueller's avatar
Christopher Mueller committed
103
104
105
/*****************************************************************************
 * Open:
 *****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
106
static int Open(vlc_object_t *p_obj)
Christopher Mueller's avatar
Christopher Mueller committed
107
{
108
    demux_t *p_demux = (demux_t*) p_obj;
Christopher Mueller's avatar
Christopher Mueller committed
109

110
111
112
113
114
115
116
117
    bool b_mimematched = false;
    char *psz_mime = stream_ContentType(p_demux->s);
    if(psz_mime)
    {
        b_mimematched = !strcmp(psz_mime, "application/dash+xml");
        free(psz_mime);
    }

118
    if(!b_mimematched && !DOMParser::isDash(p_demux->s))
119
        return VLC_EGENERIC;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
120

121
    //Build a XML tree
122
    DOMParser        parser(p_demux->s);
123
    if( !parser.parse() )
Christopher Mueller's avatar
Christopher Mueller committed
124
    {
125
        msg_Err( p_demux, "Could not parse MPD" );
126
127
        return VLC_EGENERIC;
    }
128

129
    //Begin the actual MPD parsing:
130
    MPD *mpd = MPDFactory::create(parser.getRootNode(), p_demux->s, parser.getProfile());
131
    if(mpd == NULL)
132
133
    {
        msg_Err( p_demux, "Cannot create/unknown MPD for profile");
Christopher Mueller's avatar
Christopher Mueller committed
134
        return VLC_EGENERIC;
135
    }
Christopher Mueller's avatar
Christopher Mueller committed
136

137
    demux_sys_t        *p_sys = (demux_sys_t *) malloc(sizeof(demux_sys_t));
Christopher Mueller's avatar
Christopher Mueller committed
138
139
140
    if (unlikely(p_sys == NULL))
        return VLC_ENOMEM;

141
    p_sys->p_mpd = mpd;
142
    int logic = var_InheritInteger( p_obj, "dash-logic" );
143
    DASHManager*p_dashManager = new DASHManager(p_sys->p_mpd,
144
            new (std::nothrow) DASHStreamOutputFactory,
145
            static_cast<AbstractAdaptationLogic::LogicType>(logic),
146
            p_demux->s);
Christopher Mueller's avatar
Christopher Mueller committed
147

148
    BasePeriod *period = mpd->getFirstPeriod();
149
    if(period && !p_dashManager->start(p_demux))
150
151
152
153
154
    {
        delete p_dashManager;
        free( p_sys );
        return VLC_EGENERIC;
    }
Christopher Mueller's avatar
Christopher Mueller committed
155
    p_sys->p_dashManager    = p_dashManager;
156
157
158
    p_demux->p_sys         = p_sys;
    p_demux->pf_demux      = Demux;
    p_demux->pf_control    = Control;
Christopher Mueller's avatar
Christopher Mueller committed
159

160
161
    p_sys->i_nzpcr = 0;

162
    msg_Dbg(p_obj,"opening mpd file (%s)", p_demux->s->psz_path);
Christopher Mueller's avatar
Christopher Mueller committed
163
164
165
166
167
168

    return VLC_SUCCESS;
}
/*****************************************************************************
 * Close:
 *****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
169
static void Close(vlc_object_t *p_obj)
Christopher Mueller's avatar
Christopher Mueller committed
170
{
171
172
    demux_t                            *p_demux       = (demux_t*) p_obj;
    demux_sys_t                        *p_sys          = (demux_sys_t *) p_demux->p_sys;
173
    DASHManager                        *p_dashManager  = p_sys->p_dashManager;
Christopher Mueller's avatar
Christopher Mueller committed
174

175
    delete p_dashManager;
Christopher Mueller's avatar
Christopher Mueller committed
176
177
178
179
180
    free(p_sys);
}
/*****************************************************************************
 * Callbacks:
 *****************************************************************************/
181
#define DEMUX_INCREMENT (CLOCK_FREQ / 20)
182
static int Demux(demux_t *p_demux)
Frédéric Yhuel's avatar
Frédéric Yhuel committed
183
{
184
    demux_sys_t *p_sys = p_demux->p_sys;
François Cartegnie's avatar
François Cartegnie committed
185

186
187
188
189
190
191
192
    if(p_sys->i_nzpcr == VLC_TS_INVALID)
    {
        if( Stream::status_eof ==
            p_sys->p_dashManager->demux(p_sys->i_nzpcr + DEMUX_INCREMENT, false) )
        {
            return VLC_DEMUXER_EOF;
        }
193
194
195
        p_sys->i_nzpcr = p_sys->p_dashManager->getFirstDTS();
        if(p_sys->i_nzpcr == VLC_TS_INVALID)
            p_sys->i_nzpcr = p_sys->p_dashManager->getPCR();
196
197
    }

198
    Stream::status status =
199
            p_sys->p_dashManager->demux(p_sys->i_nzpcr + DEMUX_INCREMENT, true);
200

201
202
    switch(status)
    {
203
    case Stream::status_eof:
204
        return VLC_DEMUXER_EOF;
205
    case Stream::status_buffering:
206
        break;
207
208
209
210
    case Stream::status_eop:
        p_sys->i_nzpcr = VLC_TS_INVALID;
        es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
        break;
211
    case Stream::status_demuxed:
212
213
        if( p_sys->i_nzpcr != VLC_TS_INVALID )
        {
214
            p_sys->i_nzpcr += DEMUX_INCREMENT;
215
216
217
            int group = p_sys->p_dashManager->getGroup();
            es_out_Control(p_demux->out, ES_OUT_SET_GROUP_PCR, group, VLC_TS_0 + p_sys->i_nzpcr);
        }
218
        break;
Frédéric Yhuel's avatar
Frédéric Yhuel committed
219
    }
220
221

    if( !p_sys->p_dashManager->updatePlaylist() )
222
        msg_Warn(p_demux, "Can't update MPD");
223
224

    return VLC_DEMUXER_SUCCESS;
Frédéric Yhuel's avatar
Frédéric Yhuel committed
225
226
}

227
static int  Control         (demux_t *p_demux, int i_query, va_list args)
Christopher Mueller's avatar
Christopher Mueller committed
228
{
229
    demux_sys_t *p_sys = p_demux->p_sys;
Christopher Mueller's avatar
Christopher Mueller committed
230
231
232

    switch (i_query)
    {
233
        case DEMUX_CAN_SEEK:
234
            *(va_arg (args, bool *)) = p_sys->p_dashManager->seekAble();
Christopher Mueller's avatar
Christopher Mueller committed
235
            break;
236
237

        case DEMUX_CAN_CONTROL_PACE:
238
239
            *(va_arg (args, bool *)) = true;
            break;
240
241
242

        case DEMUX_CAN_PAUSE:
            *(va_arg (args, bool *)) = p_sys->p_mpd->isLive();
243
244
            break;

245
        case DEMUX_GET_TIME:
246
            *(va_arg (args, int64_t *)) = p_sys->i_nzpcr;
Christopher Mueller's avatar
Christopher Mueller committed
247
            break;
248
249
250
251
252
253
254

        case DEMUX_GET_LENGTH:
            *(va_arg (args, int64_t *)) = p_sys->p_dashManager->getDuration();
            break;

        case DEMUX_GET_POSITION:
            if(!p_sys->p_dashManager->getDuration())
Frédéric Yhuel's avatar
Frédéric Yhuel committed
255
                return VLC_EGENERIC;
256

257
            *(va_arg (args, double *)) = (double) p_sys->i_nzpcr
258
                                         / p_sys->p_dashManager->getDuration();
Christopher Mueller's avatar
Christopher Mueller committed
259
            break;
260

261
        case DEMUX_SET_POSITION:
262
263
        {
            int64_t time = p_sys->p_dashManager->getDuration() * va_arg(args, double);
264
265
            if(p_sys->p_mpd->isLive() ||
               !p_sys->p_dashManager->getDuration() ||
266
               !p_sys->p_dashManager->setPosition(time))
267
                return VLC_EGENERIC;
268
            p_sys->i_nzpcr = VLC_TS_INVALID;
269
            break;
270
        }
271
272

        case DEMUX_SET_TIME:
273
274
        {
            int64_t time = va_arg(args, int64_t);
275
            if(p_sys->p_mpd->isLive() ||
276
               !p_sys->p_dashManager->setPosition(time))
277
                return VLC_EGENERIC;
278
            p_sys->i_nzpcr = VLC_TS_INVALID;
279
            break;
280
        }
281

282
        case DEMUX_GET_PTS_DELAY:
283
            *va_arg (args, int64_t *) = INT64_C(1000) *
284
                var_InheritInteger(p_demux, "network-caching");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
285
286
             break;

287
288
        case DEMUX_GET_META:
        {
289
            if(!p_sys->p_mpd->programInfo.Get())
290
291
292
293
294
295
296
                break;

            vlc_meta_t *p_meta = (vlc_meta_t *) va_arg (args, vlc_meta_t*);
            vlc_meta_t *meta = vlc_meta_New();
            if (meta == NULL)
                return VLC_EGENERIC;

297
298
            if(!p_sys->p_mpd->programInfo.Get()->getTitle().empty())
                vlc_meta_SetTitle(meta, p_sys->p_mpd->programInfo.Get()->getTitle().c_str());
299

300
301
            if(!p_sys->p_mpd->programInfo.Get()->getSource().empty())
                vlc_meta_SetPublisher(meta, p_sys->p_mpd->programInfo.Get()->getSource().c_str());
302

303
304
            if(!p_sys->p_mpd->programInfo.Get()->getCopyright().empty())
                vlc_meta_SetCopyright(meta, p_sys->p_mpd->programInfo.Get()->getCopyright().c_str());
305

306
307
            if(!p_sys->p_mpd->programInfo.Get()->getMoreInformationUrl().empty())
                vlc_meta_SetURL(meta, p_sys->p_mpd->programInfo.Get()->getMoreInformationUrl().c_str());
308
309
310
311
312
313

            vlc_meta_Merge(p_meta, meta);
            vlc_meta_Delete(meta);
            break;
        }

Christopher Mueller's avatar
Christopher Mueller committed
314
315
316
317
318
        default:
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}