@@ -33,15 +33,28 @@ 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_SECRET_IN_HLS " srs_secret"
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, SrsRequest*>::iterator it;
51+ for (it = map_secret_req_.begin (); it != map_secret_req_.end (); ++it) {
52+ srs_freep (it->second );
53+ }
54+ map_secret_req_.clear ();
55+ map_secret_validity_.clear ();
4356}
44-
57+
4558srs_error_t SrsVodStream::serve_flv_stream (ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset)
4659{
4760 srs_error_t err = srs_success;
@@ -171,6 +184,162 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
171184 }
172185
173186 return err;
187+ }
188+
189+ srs_error_t SrsVodStream::serve_m3u8_secret (ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
190+ {
191+ srs_error_t err = srs_success;
192+
193+ SrsHttpMessage* hr = dynamic_cast <SrsHttpMessage*>(r);
194+ srs_assert (hr);
195+
196+ SrsRequest* req = hr->to_request (hr->host ())->as_http ();
197+ SrsAutoFree (SrsRequest, req);
198+
199+ string secret = r->query_get (SRS_SECRET_IN_HLS);
200+ if (!secret.empty () && secret_is_exist (secret)) {
201+ alive (secret);
202+ return SrsHttpFileServer::serve_m3u8_secret (w, r, fullpath);
203+ }
204+
205+ if ((err = http_hooks_on_play (req)) != srs_success) {
206+ return srs_error_wrap (err, " HLS: http_hooks_on_play" );
207+ }
208+
209+ if (secret.empty ()) {
210+ // make sure unique
211+ do {
212+ secret = srs_random_str (8 );
213+ } while (secret_is_exist (secret));
214+ }
215+
216+ std::string res = " #EXTM3U\r " ;
217+ res += " #EXT-X-STREAM-INF:BANDWIDTH=1,AVERAGE-BANDWIDTH=1\r " ;
218+ res += hr->path () + " ?" + SRS_SECRET_IN_HLS + " =" + secret;
219+
220+ int length = res.length ();
221+
222+ w->header ()->set_content_length (length);
223+ w->header ()->set_content_type (" application/vnd.apple.mpegurl" );
224+ w->write_header (SRS_CONSTS_HTTP_OK);
225+
226+ if ((err = w->write ((char *)res.c_str (), length)) != srs_success) {
227+ return srs_error_wrap (err, " write bytes=%d" , length);
228+ }
229+
230+ if ((err = w->final_request ()) != srs_success) {
231+ return srs_error_wrap (err, " final request" );
232+ }
233+
234+ // update the statistic when source disconveried.
235+ SrsStatistic* stat = SrsStatistic::instance ();
236+ if ((err = stat->on_client (secret, req, NULL , SrsRtmpConnPlay)) != srs_success) {
237+ return srs_error_wrap (err, " stat on client" );
238+ }
239+
240+ // save req for on_disconnect when timeout
241+ map_secret_req_.insert (make_pair (secret, req->copy ()));
242+ alive (secret);
243+
244+ return err;
245+ }
246+
247+ bool SrsVodStream::secret_is_exist (std::string secret)
248+ {
249+ return (map_secret_validity_.find (secret) != map_secret_validity_.end ());
250+ }
251+
252+ void SrsVodStream::alive (std::string secret)
253+ {
254+ map_secret_validity_[secret] = srs_get_system_time ();
255+ }
256+
257+ srs_error_t SrsVodStream::http_hooks_on_play (SrsRequest* req)
258+ {
259+ srs_error_t err = srs_success;
260+
261+ if (!_srs_config->get_vhost_http_hooks_enabled (req->vhost )) {
262+ return err;
263+ }
264+
265+ // the http hooks will cause context switch,
266+ // so we must copy all hooks for the on_connect may freed.
267+ // @see https://github.com/ossrs/srs/issues/475
268+ vector<string> hooks;
269+
270+ if (true ) {
271+ SrsConfDirective* conf = _srs_config->get_vhost_on_play (req->vhost );
272+
273+ if (!conf) {
274+ return err;
275+ }
276+
277+ hooks = conf->args ;
278+ }
279+
280+ for (int i = 0 ; i < (int )hooks.size (); i++) {
281+ std::string url = hooks.at (i);
282+ if ((err = SrsHttpHooks::on_play (url, req)) != srs_success) {
283+ return srs_error_wrap (err, " http on_play %s" , url.c_str ());
284+ }
285+ }
286+
287+ return err;
288+ }
289+
290+ void SrsVodStream::http_hooks_on_stop (SrsRequest* req)
291+ {
292+ if (!_srs_config->get_vhost_http_hooks_enabled (req->vhost )) {
293+ return ;
294+ }
295+
296+ // the http hooks will cause context switch,
297+ // so we must copy all hooks for the on_connect may freed.
298+ // @see https://github.com/ossrs/srs/issues/475
299+ vector<string> hooks;
300+
301+ if (true ) {
302+ SrsConfDirective* conf = _srs_config->get_vhost_on_stop (req->vhost );
303+
304+ if (!conf) {
305+ srs_info (" ignore the empty http callback: on_stop" );
306+ return ;
307+ }
308+
309+ hooks = conf->args ;
310+ }
311+
312+ for (int i = 0 ; i < (int )hooks.size (); i++) {
313+ std::string url = hooks.at (i);
314+ SrsHttpHooks::on_stop (url, req);
315+ }
316+
317+ return ;
318+ }
319+
320+ srs_error_t SrsVodStream::on_timer (srs_utime_t interval)
321+ {
322+ srs_error_t err = srs_success;
323+
324+ std::map<std::string, srs_utime_t >::iterator it;
325+ for (it = map_secret_validity_.begin (); it != map_secret_validity_.end (); ++it) {
326+ string secret = it->first ;
327+ SrsRequest* req = map_secret_req_[secret];
328+ srs_utime_t hls_window = _srs_config->get_hls_window (req->vhost );
329+ if (it->second + (2 * hls_window) < srs_get_system_time ()) {
330+ http_hooks_on_stop (req);
331+ srs_freep (req);
332+ map_secret_req_.erase (secret);
333+
334+ SrsStatistic* stat = SrsStatistic::instance ();
335+ stat->on_disconnect (secret);
336+ map_secret_validity_.erase (it);
337+
338+ break ;
339+ }
340+ }
341+
342+ return err;
174343}
175344
176345SrsHttpStaticServer::SrsHttpStaticServer (SrsServer* svr)
0 commit comments