@@ -33,15 +33,27 @@ using namespace std;
3333#include < srs_app_pithy_print.hpp>
3434#include < srs_app_source.hpp>
3535#include < srs_app_server.hpp>
36+ #include < srs_service_utility.hpp>
37+ #include < srs_app_http_hooks.hpp>
38+ #include < srs_app_statistic.hpp>
39+
40+ #define SRS_CONTEXT_IN_HLS " hls_ctx"
3641
3742SrsVodStream::SrsVodStream (string root_dir) : SrsHttpFileServer(root_dir)
3843{
44+ _srs_hybrid->timer5s ()->subscribe (this );
3945}
4046
4147SrsVodStream::~SrsVodStream ()
4248{
49+ _srs_hybrid->timer5s ()->unsubscribe (this );
50+ std::map<std::string, SrsM3u8CtxInfo>::iterator it;
51+ for (it = map_ctx_info_.begin (); it != map_ctx_info_.end (); ++it) {
52+ srs_freep (it->second .req );
53+ }
54+ map_ctx_info_.clear ();
4355}
44-
56+
4557srs_error_t SrsVodStream::serve_flv_stream (ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset)
4658{
4759 srs_error_t err = srs_success;
@@ -171,6 +183,173 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
171183 }
172184
173185 return err;
186+ }
187+
188+ srs_error_t SrsVodStream::serve_m3u8_ctx (ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
189+ {
190+ srs_error_t err = srs_success;
191+
192+ SrsHttpMessage* hr = dynamic_cast <SrsHttpMessage*>(r);
193+ srs_assert (hr);
194+
195+ SrsRequest* req = hr->to_request (hr->host ())->as_http ();
196+ SrsAutoFree (SrsRequest, req);
197+
198+ string ctx = hr->query_get (SRS_CONTEXT_IN_HLS);
199+ if (!ctx.empty () && ctx_is_exist (ctx)) {
200+ alive (ctx, NULL );
201+ return SrsHttpFileServer::serve_m3u8_ctx (w, r, fullpath);
202+ }
203+
204+ if ((err = http_hooks_on_play (req)) != srs_success) {
205+ return srs_error_wrap (err, " HLS: http_hooks_on_play" );
206+ }
207+
208+ if (ctx.empty ()) {
209+ // make sure unique
210+ do {
211+ ctx = srs_random_str (8 ); // the same as cid
212+ } while (ctx_is_exist (ctx));
213+ }
214+
215+ std::stringstream ss;
216+ ss << " #EXTM3U" << SRS_CONSTS_LF;
217+ ss << " #EXT-X-STREAM-INF:BANDWIDTH=1,AVERAGE-BANDWIDTH=1" << SRS_CONSTS_LF;
218+ ss << hr->path () << " ?" << SRS_CONTEXT_IN_HLS << " =" << ctx;
219+ if (!hr->query ().empty () && hr->query_get (SRS_CONTEXT_IN_HLS).empty ())
220+ {
221+ ss << " &" << hr->query ();
222+ }
223+
224+ std::string res = ss.str ();
225+ int length = res.length ();
226+
227+ w->header ()->set_content_length (length);
228+ w->header ()->set_content_type (" application/vnd.apple.mpegurl" );
229+ w->write_header (SRS_CONSTS_HTTP_OK);
230+
231+ if ((err = w->write ((char *)res.c_str (), length)) != srs_success) {
232+ return srs_error_wrap (err, " write bytes=%d" , length);
233+ }
234+
235+ if ((err = w->final_request ()) != srs_success) {
236+ return srs_error_wrap (err, " final request" );
237+ }
238+
239+ alive (ctx, req->copy ());
240+
241+ // update the statistic when source disconveried.
242+ SrsStatistic* stat = SrsStatistic::instance ();
243+ if ((err = stat->on_client (ctx, req, NULL , SrsRtmpConnPlay)) != srs_success) {
244+ return srs_error_wrap (err, " stat on client" );
245+ }
246+
247+ return err;
248+ }
249+
250+ bool SrsVodStream::ctx_is_exist (std::string ctx)
251+ {
252+ return (map_ctx_info_.find (ctx) != map_ctx_info_.end ());
253+ }
254+
255+ void SrsVodStream::alive (std::string ctx, SrsRequest* req)
256+ {
257+ std::map<std::string, SrsM3u8CtxInfo>::iterator it;
258+ if ((it = map_ctx_info_.find (ctx)) != map_ctx_info_.end ()) {
259+ it->second .request_time = srs_get_system_time ();
260+ } else {
261+ SrsM3u8CtxInfo info;
262+ info.req = req;
263+ info.request_time = srs_get_system_time ();
264+ map_ctx_info_.insert (make_pair (ctx, info));
265+ }
266+ }
267+
268+ srs_error_t SrsVodStream::http_hooks_on_play (SrsRequest* req)
269+ {
270+ srs_error_t err = srs_success;
271+
272+ if (!_srs_config->get_vhost_http_hooks_enabled (req->vhost )) {
273+ return err;
274+ }
275+
276+ // the http hooks will cause context switch,
277+ // so we must copy all hooks for the on_connect may freed.
278+ // @see https://github.com/ossrs/srs/issues/475
279+ vector<string> hooks;
280+
281+ if (true ) {
282+ SrsConfDirective* conf = _srs_config->get_vhost_on_play (req->vhost );
283+
284+ if (!conf) {
285+ return err;
286+ }
287+
288+ hooks = conf->args ;
289+ }
290+
291+ for (int i = 0 ; i < (int )hooks.size (); i++) {
292+ std::string url = hooks.at (i);
293+ if ((err = SrsHttpHooks::on_play (url, req)) != srs_success) {
294+ return srs_error_wrap (err, " http on_play %s" , url.c_str ());
295+ }
296+ }
297+
298+ return err;
299+ }
300+
301+ void SrsVodStream::http_hooks_on_stop (SrsRequest* req)
302+ {
303+ if (!_srs_config->get_vhost_http_hooks_enabled (req->vhost )) {
304+ return ;
305+ }
306+
307+ // the http hooks will cause context switch,
308+ // so we must copy all hooks for the on_connect may freed.
309+ // @see https://github.com/ossrs/srs/issues/475
310+ vector<string> hooks;
311+
312+ if (true ) {
313+ SrsConfDirective* conf = _srs_config->get_vhost_on_stop (req->vhost );
314+
315+ if (!conf) {
316+ srs_info (" ignore the empty http callback: on_stop" );
317+ return ;
318+ }
319+
320+ hooks = conf->args ;
321+ }
322+
323+ for (int i = 0 ; i < (int )hooks.size (); i++) {
324+ std::string url = hooks.at (i);
325+ SrsHttpHooks::on_stop (url, req);
326+ }
327+
328+ return ;
329+ }
330+
331+ srs_error_t SrsVodStream::on_timer (srs_utime_t interval)
332+ {
333+ srs_error_t err = srs_success;
334+
335+ std::map<std::string, SrsM3u8CtxInfo>::iterator it;
336+ for (it = map_ctx_info_.begin (); it != map_ctx_info_.end (); ++it) {
337+ string ctx = it->first ;
338+ SrsRequest* req = it->second .req ;
339+ srs_utime_t hls_window = _srs_config->get_hls_window (req->vhost );
340+ if (it->second .request_time + (2 * hls_window) < srs_get_system_time ()) {
341+ http_hooks_on_stop (req);
342+ srs_freep (req);
343+
344+ SrsStatistic* stat = SrsStatistic::instance ();
345+ stat->on_disconnect (ctx);
346+ map_ctx_info_.erase (it);
347+
348+ break ;
349+ }
350+ }
351+
352+ return err;
174353}
175354
176355SrsHttpStaticServer::SrsHttpStaticServer (SrsServer* svr)
0 commit comments