1 /*****************************************************************************
2 * adaptive.cpp: Adaptive streaming module
3 *****************************************************************************
4 * Copyright © 2015 - VideoLAN and VLC Authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /*****************************************************************************
23 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_demux.h>
35 #include "http/AuthStorage.hpp"
36 #include "playlist/BasePeriod.h"
37 #include "xml/DOMParser.h"
39 #include "../dash/DASHManager.h"
40 #include "../dash/DASHStream.hpp"
41 #include "../dash/mpd/IsoffMainParser.h"
43 #include "../hls/HLSManager.hpp"
44 #include "../hls/HLSStreams.hpp"
45 #include "../hls/playlist/Parser.hpp"
46 #include "../hls/playlist/M3U8.hpp"
47 #include "../smooth/SmoothManager.hpp"
48 #include "../smooth/SmoothStream.hpp"
49 #include "../smooth/playlist/Parser.hpp"
51 using namespace adaptive::http
;
52 using namespace adaptive::logic
;
53 using namespace adaptive::playlist
;
54 using namespace adaptive::xml
;
55 using namespace dash::mpd
;
58 using namespace hls::playlist
;
59 using namespace smooth
;
60 using namespace smooth::playlist
;
62 /*****************************************************************************
64 *****************************************************************************/
65 static int Open (vlc_object_t
*);
66 static void Close (vlc_object_t
*);
68 #define ADAPT_WIDTH_TEXT N_("Maximum device width")
69 #define ADAPT_HEIGHT_TEXT N_("Maximum device height")
71 #define ADAPT_BW_TEXT N_("Fixed Bandwidth in KiB/s")
72 #define ADAPT_BW_LONGTEXT N_("Preferred bandwidth for non adaptive streams")
74 #define ADAPT_LOGIC_TEXT N_("Adaptive Logic")
76 #define ADAPT_ACCESS_TEXT N_("Use regular HTTP modules")
77 #define ADAPT_ACCESS_LONGTEXT N_("Connect using HTTP access instead of custom HTTP code")
79 static const AbstractAdaptationLogic::LogicType pi_logics
[] = {
80 AbstractAdaptationLogic::Default
,
81 AbstractAdaptationLogic::Predictive
,
82 AbstractAdaptationLogic::NearOptimal
,
83 AbstractAdaptationLogic::RateBased
,
84 AbstractAdaptationLogic::FixedRate
,
85 AbstractAdaptationLogic::AlwaysLowest
,
86 AbstractAdaptationLogic::AlwaysBest
};
88 static const char *const ppsz_logics_values
[] = {
97 static const char *const ppsz_logics
[] = { N_("Default"),
100 N_("Bandwidth Adaptive"),
101 N_("Fixed Bandwidth"),
102 N_("Lowest Bandwidth/Quality"),
103 N_("Highest Bandwidth/Quality")};
105 static_assert( ARRAY_SIZE( pi_logics
) == ARRAY_SIZE( ppsz_logics
),
106 "pi_logics and ppsz_logics shall have the same number of elements" );
108 static_assert( ARRAY_SIZE( pi_logics
) == ARRAY_SIZE( ppsz_logics_values
),
109 "pi_logics and ppsz_logics_values shall have the same number of elements" );
112 set_shortname( N_("Adaptive"))
113 set_description( N_("Unified adaptive streaming for DASH/HLS") )
114 set_capability( "demux", 12 )
115 set_category( CAT_INPUT
)
116 set_subcategory( SUBCAT_INPUT_DEMUX
)
117 add_string( "adaptive-logic", "", ADAPT_LOGIC_TEXT
, NULL
, false )
118 change_string_list( ppsz_logics_values
, ppsz_logics
)
119 add_integer( "adaptive-maxwidth", 0,
120 ADAPT_WIDTH_TEXT
, ADAPT_WIDTH_TEXT
, false )
121 add_integer( "adaptive-maxheight", 0,
122 ADAPT_HEIGHT_TEXT
, ADAPT_HEIGHT_TEXT
, false )
123 add_integer( "adaptive-bw", 250, ADAPT_BW_TEXT
, ADAPT_BW_LONGTEXT
, false )
124 add_bool ( "adaptive-use-access", false, ADAPT_ACCESS_TEXT
, ADAPT_ACCESS_LONGTEXT
, true );
125 set_callbacks( Open
, Close
)
128 /*****************************************************************************
130 *****************************************************************************/
131 static PlaylistManager
* HandleDash(demux_t
*, AuthStorage
*auth
, DOMParser
&,
132 const std::string
&, AbstractAdaptationLogic::LogicType
);
133 static PlaylistManager
* HandleSmooth(demux_t
*, AuthStorage
*auth
, DOMParser
&,
134 const std::string
&, AbstractAdaptationLogic::LogicType
);
136 /*****************************************************************************
138 *****************************************************************************/
139 static int Open(vlc_object_t
*p_obj
)
141 demux_t
*p_demux
= (demux_t
*) p_obj
;
143 if(!p_demux
->s
->psz_url
)
146 std::string mimeType
;
148 char *psz_mime
= stream_ContentType(p_demux
->s
);
151 mimeType
= std::string(psz_mime
);
155 PlaylistManager
*p_manager
= NULL
;
156 AuthStorage
*authStorage
= new AuthStorage(p_obj
);
158 char *psz_logic
= var_InheritString(p_obj
, "adaptive-logic");
159 AbstractAdaptationLogic::LogicType logic
= AbstractAdaptationLogic::Default
;
162 for(size_t i
=0;i
<ARRAY_SIZE(pi_logics
); i
++)
164 if(!strcmp(psz_logic
, ppsz_logics_values
[i
]))
166 logic
= pi_logics
[i
];
173 std::string
playlisturl(p_demux
->s
->psz_url
);
175 bool dashmime
= DASHManager::mimeMatched(mimeType
);
176 bool smoothmime
= SmoothManager::mimeMatched(mimeType
);
178 if(!dashmime
&& !smoothmime
&& HLSManager::isHTTPLiveStreaming(p_demux
->s
))
180 M3U8Parser
parser(authStorage
);
181 M3U8
*p_playlist
= parser
.parse(VLC_OBJECT(p_demux
),p_demux
->s
, playlisturl
);
184 msg_Err( p_demux
, "Could not parse playlist" );
189 p_manager
= new (std::nothrow
) HLSManager(p_demux
, authStorage
, p_playlist
,
190 new (std::nothrow
) HLSStreamFactory
, logic
);
194 /* Handle XML Based ones */
195 DOMParser xmlParser
; /* Share that xml reader */
198 p_manager
= HandleDash(p_demux
, authStorage
, xmlParser
, playlisturl
, logic
);
202 p_manager
= HandleSmooth(p_demux
, authStorage
, xmlParser
, playlisturl
, logic
);
206 /* We need to probe content */
207 const uint8_t *p_peek
;
208 const ssize_t i_peek
= vlc_stream_Peek(p_demux
->s
, &p_peek
, 2048);
211 stream_t
*peekstream
= vlc_stream_MemoryNew(p_demux
, const_cast<uint8_t *>(p_peek
), (size_t)i_peek
, true);
214 if(xmlParser
.reset(peekstream
) && xmlParser
.parse(false))
216 if(DASHManager::isDASH(xmlParser
.getRootNode()))
218 p_manager
= HandleDash(p_demux
, authStorage
, xmlParser
, playlisturl
, logic
);
220 else if(SmoothManager::isSmoothStreaming(xmlParser
.getRootNode()))
222 p_manager
= HandleSmooth(p_demux
, authStorage
, xmlParser
, playlisturl
, logic
);
225 vlc_stream_Delete(peekstream
);
236 else if(!p_manager
->start())
242 p_demux
->p_sys
= reinterpret_cast<demux_sys_t
*>(p_manager
);
243 p_demux
->pf_demux
= p_manager
->demux_callback
;
244 p_demux
->pf_control
= p_manager
->control_callback
;
246 msg_Dbg(p_obj
,"opening playlist file (%s)", p_demux
->psz_location
);
251 /*****************************************************************************
253 *****************************************************************************/
254 static void Close(vlc_object_t
*p_obj
)
256 demux_t
*p_demux
= (demux_t
*) p_obj
;
257 PlaylistManager
*p_manager
= reinterpret_cast<PlaylistManager
*>(p_demux
->p_sys
);
263 /*****************************************************************************
265 *****************************************************************************/
266 static PlaylistManager
* HandleDash(demux_t
*p_demux
,
267 AuthStorage
*auth
, DOMParser
&xmlParser
,
268 const std::string
& playlisturl
,
269 AbstractAdaptationLogic::LogicType logic
)
271 if(!xmlParser
.reset(p_demux
->s
) || !xmlParser
.parse(true))
273 msg_Err(p_demux
, "Cannot parse MPD");
276 IsoffMainParser
mpdparser(xmlParser
.getRootNode(), VLC_OBJECT(p_demux
),
277 p_demux
->s
, playlisturl
);
278 MPD
*p_playlist
= mpdparser
.parse();
279 if(p_playlist
== NULL
)
281 msg_Err( p_demux
, "Cannot create/unknown MPD for profile");
285 return new (std::nothrow
) DASHManager( p_demux
, auth
, p_playlist
,
286 new (std::nothrow
) DASHStreamFactory
,
290 static PlaylistManager
* HandleSmooth(demux_t
*p_demux
,
291 AuthStorage
*auth
, DOMParser
&xmlParser
,
292 const std::string
& playlisturl
,
293 AbstractAdaptationLogic::LogicType logic
)
295 if(!xmlParser
.reset(p_demux
->s
) || !xmlParser
.parse(true))
297 msg_Err(p_demux
, "Cannot parse Manifest");
300 ManifestParser
mparser(xmlParser
.getRootNode(), VLC_OBJECT(p_demux
),
301 p_demux
->s
, playlisturl
);
302 Manifest
*p_playlist
= mparser
.parse();
303 if(p_playlist
== NULL
)
305 msg_Err( p_demux
, "Cannot create Manifest");
309 return new (std::nothrow
) SmoothManager( p_demux
, auth
, p_playlist
,
310 new (std::nothrow
) SmoothStreamFactory
,