2013-11-23 11:15:11 +00:00
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
|
2014-12-31 12:32:09 +00:00
|
|
|
Copyright (c) 2013-2015 winlin
|
2013-11-23 11:15:11 +00:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
|
|
the Software without restriction, including without limitation the rights to
|
|
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2014-03-02 13:49:09 +00:00
|
|
|
#ifndef SRS_APP_HLS_HPP
|
|
|
|
#define SRS_APP_HLS_HPP
|
2013-11-23 11:15:11 +00:00
|
|
|
|
|
|
|
/*
|
2014-03-02 13:49:09 +00:00
|
|
|
#include <srs_app_hls.hpp>
|
2013-11-23 11:15:11 +00:00
|
|
|
*/
|
|
|
|
#include <srs_core.hpp>
|
|
|
|
|
2014-07-13 05:42:08 +00:00
|
|
|
/**
|
|
|
|
* the HLS section, only available when HLS enabled.
|
|
|
|
*/
|
2014-04-15 06:01:57 +00:00
|
|
|
#ifdef SRS_AUTO_HLS
|
2013-11-27 14:41:58 +00:00
|
|
|
|
2013-11-24 09:15:37 +00:00
|
|
|
#include <string>
|
2013-11-26 08:06:58 +00:00
|
|
|
#include <vector>
|
2013-11-24 09:15:37 +00:00
|
|
|
|
2015-01-25 08:42:22 +00:00
|
|
|
#include <srs_kernel_codec.hpp>
|
2015-02-03 08:01:07 +00:00
|
|
|
#include <srs_kernel_file.hpp>
|
2015-01-25 08:42:22 +00:00
|
|
|
|
2014-04-29 06:44:07 +00:00
|
|
|
class SrsSharedPtrMessage;
|
2013-11-24 08:00:45 +00:00
|
|
|
class SrsCodecSample;
|
2013-12-15 12:29:18 +00:00
|
|
|
class SrsAmf0Object;
|
2013-11-26 08:06:58 +00:00
|
|
|
class SrsRtmpJitter;
|
2013-11-24 09:15:37 +00:00
|
|
|
class SrsTSMuxer;
|
2014-06-08 14:36:17 +00:00
|
|
|
class SrsAvcAacCodec;
|
2013-12-01 09:32:06 +00:00
|
|
|
class SrsRequest;
|
2013-12-05 15:34:26 +00:00
|
|
|
class SrsPithyPrint;
|
2013-12-15 12:29:18 +00:00
|
|
|
class SrsSource;
|
2014-07-04 23:33:18 +00:00
|
|
|
class SrsFileWriter;
|
2014-12-04 03:29:47 +00:00
|
|
|
class SrsSimpleBuffer;
|
2015-01-22 10:13:33 +00:00
|
|
|
class SrsTsAacJitter;
|
|
|
|
class SrsTsCache;
|
2015-02-03 08:01:07 +00:00
|
|
|
class SrsHlsSegment;
|
2015-02-15 08:37:28 +00:00
|
|
|
class SrsTsCache;
|
2015-02-03 08:01:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the handler for hls event.
|
|
|
|
* for example, we use memory only hls for
|
|
|
|
*/
|
|
|
|
class ISrsHlsHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ISrsHlsHandler();
|
|
|
|
virtual ~ISrsHlsHandler();
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* when publish stream
|
|
|
|
*/
|
|
|
|
virtual int on_hls_publish(SrsRequest* req) = 0;
|
|
|
|
/**
|
|
|
|
* when update the m3u8 file.
|
|
|
|
*/
|
|
|
|
virtual int on_update_m3u8(SrsRequest* r, std::string m3u8) = 0;
|
|
|
|
/**
|
|
|
|
* when reap new ts file.
|
|
|
|
*/
|
|
|
|
virtual int on_update_ts(SrsRequest* r, std::string uri, std::string ts) = 0;
|
|
|
|
/**
|
|
|
|
* when unpublish stream
|
|
|
|
*/
|
|
|
|
virtual int on_hls_unpublish(SrsRequest* req) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* write to file and cache.
|
|
|
|
*/
|
|
|
|
class SrsHlsCacheWriter : public SrsFileWriter
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
SrsFileWriter impl;
|
|
|
|
std::string data;
|
|
|
|
bool should_write_cache;
|
|
|
|
bool should_write_file;
|
|
|
|
public:
|
|
|
|
SrsHlsCacheWriter(bool write_cache, bool write_file);
|
|
|
|
virtual ~SrsHlsCacheWriter();
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* open file writer, can open then close then open...
|
|
|
|
*/
|
|
|
|
virtual int open(std::string file);
|
|
|
|
virtual void close();
|
|
|
|
public:
|
|
|
|
virtual bool is_open();
|
|
|
|
virtual int64_t tellg();
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* write to file.
|
|
|
|
* @param pnwrite the output nb_write, NULL to ignore.
|
|
|
|
*/
|
|
|
|
virtual int write(void* buf, size_t count, ssize_t* pnwrite);
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* get the string cache.
|
|
|
|
*/
|
|
|
|
virtual std::string cache();
|
|
|
|
};
|
2013-11-27 09:30:16 +00:00
|
|
|
|
2013-12-02 06:24:09 +00:00
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* the wrapper of m3u8 segment from specification:
|
|
|
|
*
|
2013-12-02 06:24:09 +00:00
|
|
|
* 3.3.2. EXTINF
|
|
|
|
* The EXTINF tag specifies the duration of a media segment.
|
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
class SrsHlsSegment
|
2013-12-02 06:24:09 +00:00
|
|
|
{
|
2014-03-20 10:19:08 +00:00
|
|
|
public:
|
2014-03-18 03:32:58 +00:00
|
|
|
// duration in seconds in m3u8.
|
|
|
|
double duration;
|
|
|
|
// sequence number in m3u8.
|
|
|
|
int sequence_no;
|
|
|
|
// ts uri in m3u8.
|
|
|
|
std::string uri;
|
|
|
|
// ts full file to write.
|
|
|
|
std::string full_path;
|
|
|
|
// the muxer to write ts.
|
2015-02-03 08:01:07 +00:00
|
|
|
SrsHlsCacheWriter* writer;
|
2014-03-18 03:32:58 +00:00
|
|
|
SrsTSMuxer* muxer;
|
|
|
|
// current segment start dts for m3u8
|
|
|
|
int64_t segment_start_dts;
|
2014-03-21 09:10:24 +00:00
|
|
|
// whether current segement is sequence header.
|
|
|
|
bool is_sequence_header;
|
2015-02-03 08:01:07 +00:00
|
|
|
public:
|
2015-02-15 09:43:35 +00:00
|
|
|
SrsHlsSegment(bool write_cache, bool write_file, SrsCodecAudio ac);
|
2014-03-20 10:19:08 +00:00
|
|
|
virtual ~SrsHlsSegment();
|
2015-02-03 08:01:07 +00:00
|
|
|
public:
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
|
|
|
* update the segment duration.
|
2014-03-20 10:19:08 +00:00
|
|
|
* @current_frame_dts the dts of frame, in tbn of ts.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-03-20 10:55:45 +00:00
|
|
|
virtual void update_duration(int64_t current_frame_dts);
|
2013-12-02 06:24:09 +00:00
|
|
|
};
|
|
|
|
|
2013-11-26 08:06:58 +00:00
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* muxer the HLS stream(m3u8 and ts files).
|
|
|
|
* generally, the m3u8 muxer only provides methods to open/close segments,
|
|
|
|
* to flush video/audio, without any mechenisms.
|
|
|
|
*
|
|
|
|
* that is, user must use HlsCache, which will control the methods of muxer,
|
|
|
|
* and provides HLS mechenisms.
|
2013-11-26 08:06:58 +00:00
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
class SrsHlsMuxer
|
2013-11-23 12:16:47 +00:00
|
|
|
{
|
2013-11-24 04:39:47 +00:00
|
|
|
private:
|
2015-02-03 08:01:07 +00:00
|
|
|
SrsRequest* req;
|
2013-12-02 07:55:10 +00:00
|
|
|
private:
|
2014-03-18 03:32:58 +00:00
|
|
|
std::string hls_path;
|
|
|
|
int hls_fragment;
|
|
|
|
int hls_window;
|
2013-11-26 08:06:58 +00:00
|
|
|
private:
|
2014-05-04 02:45:13 +00:00
|
|
|
int _sequence_no;
|
2015-02-12 05:34:59 +00:00
|
|
|
int target_duration;
|
2014-03-18 03:32:58 +00:00
|
|
|
std::string m3u8;
|
2015-02-03 08:01:07 +00:00
|
|
|
private:
|
|
|
|
ISrsHlsHandler* handler;
|
2015-02-03 08:29:59 +00:00
|
|
|
// TODO: FIXME: supports reload.
|
2015-02-03 08:01:07 +00:00
|
|
|
bool should_write_cache;
|
|
|
|
bool should_write_file;
|
2013-11-26 08:06:58 +00:00
|
|
|
private:
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
|
|
|
* m3u8 segments.
|
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
std::vector<SrsHlsSegment*> segments;
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
|
|
|
* current writing segment.
|
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
SrsHlsSegment* current;
|
2015-01-25 09:06:49 +00:00
|
|
|
/**
|
|
|
|
* the current audio codec, when open new muxer,
|
|
|
|
* set the muxer audio codec.
|
|
|
|
* @see https://github.com/winlinvip/simple-rtmp-server/issues/301
|
|
|
|
*/
|
|
|
|
SrsCodecAudio acodec;
|
2013-12-02 07:55:10 +00:00
|
|
|
public:
|
2015-02-03 08:01:07 +00:00
|
|
|
SrsHlsMuxer(ISrsHlsHandler* h);
|
2014-03-20 10:19:08 +00:00
|
|
|
virtual ~SrsHlsMuxer();
|
2014-05-04 02:45:13 +00:00
|
|
|
public:
|
|
|
|
virtual int sequence_no();
|
2013-12-02 07:55:10 +00:00
|
|
|
public:
|
2015-02-03 08:01:07 +00:00
|
|
|
/**
|
|
|
|
* when publish, update the config for muxer.
|
|
|
|
*/
|
|
|
|
virtual int update_config(SrsRequest* r, std::string path, int fragment, int window);
|
2014-03-20 10:19:08 +00:00
|
|
|
/**
|
|
|
|
* open a new segment(a new ts file),
|
|
|
|
* @param segment_start_dts use to calc the segment duration,
|
|
|
|
* use 0 for the first segment of HLS.
|
|
|
|
*/
|
|
|
|
virtual int segment_open(int64_t segment_start_dts);
|
2014-03-21 09:10:24 +00:00
|
|
|
virtual int on_sequence_header();
|
2014-03-20 10:19:08 +00:00
|
|
|
/**
|
2015-01-23 07:29:14 +00:00
|
|
|
* whether segment overflow,
|
|
|
|
* that is whether the current segment duration>=(the segment in config)
|
2014-03-20 10:19:08 +00:00
|
|
|
*/
|
|
|
|
virtual bool is_segment_overflow();
|
2015-01-23 07:29:14 +00:00
|
|
|
/**
|
|
|
|
* whether segment absolutely overflow, for pure audio to reap segment,
|
|
|
|
* that is whether the current segment duration>=2*(the segment in config)
|
|
|
|
* @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184
|
|
|
|
*/
|
|
|
|
virtual bool is_segment_absolutely_overflow();
|
2015-01-25 08:42:22 +00:00
|
|
|
public:
|
2015-01-25 09:06:49 +00:00
|
|
|
virtual int update_acodec(SrsCodecAudio ac);
|
2015-02-15 08:37:28 +00:00
|
|
|
virtual int flush_audio(SrsTsCache* cache);
|
|
|
|
virtual int flush_video(SrsTsCache* cache);
|
2014-03-21 05:10:47 +00:00
|
|
|
/**
|
|
|
|
* close segment(ts).
|
|
|
|
* @param log_desc the description for log.
|
|
|
|
*/
|
|
|
|
virtual int segment_close(std::string log_desc);
|
2013-12-02 07:55:10 +00:00
|
|
|
private:
|
2014-03-18 03:32:58 +00:00
|
|
|
virtual int refresh_m3u8();
|
2015-02-03 08:01:07 +00:00
|
|
|
virtual int _refresh_m3u8(std::string m3u8_file);
|
2014-03-18 03:32:58 +00:00
|
|
|
virtual int create_dir();
|
2013-12-02 07:55:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* hls stream cache,
|
|
|
|
* use to cache hls stream and flush to hls muxer.
|
|
|
|
*
|
|
|
|
* when write stream to ts file:
|
|
|
|
* video frame will directly flush to M3u8Muxer,
|
|
|
|
* audio frame need to cache, because it's small and flv tbn problem.
|
|
|
|
*
|
|
|
|
* whatever, the Hls cache used to cache video/audio,
|
|
|
|
* and flush video/audio to m3u8 muxer if needed.
|
|
|
|
*
|
|
|
|
* about the flv tbn problem:
|
|
|
|
* flv tbn is 1/1000, ts tbn is 1/90000,
|
|
|
|
* when timestamp convert to flv tbn, it will loose precise,
|
2015-01-22 10:13:33 +00:00
|
|
|
* so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter,
|
2014-03-20 10:19:08 +00:00
|
|
|
* we use a aac jitter to correct the audio pts.
|
2013-12-02 07:55:10 +00:00
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
class SrsHlsCache
|
2013-12-02 07:55:10 +00:00
|
|
|
{
|
2013-12-02 14:09:10 +00:00
|
|
|
private:
|
2015-01-22 10:13:33 +00:00
|
|
|
SrsTsCache* cache;
|
2013-12-02 14:09:10 +00:00
|
|
|
public:
|
2014-03-20 10:19:08 +00:00
|
|
|
SrsHlsCache();
|
|
|
|
virtual ~SrsHlsCache();
|
2013-12-02 14:09:10 +00:00
|
|
|
public:
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* when publish or unpublish stream.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-03-20 10:19:08 +00:00
|
|
|
virtual int on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment_start_dts);
|
|
|
|
virtual int on_unpublish(SrsHlsMuxer* muxer);
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
2014-03-21 09:10:24 +00:00
|
|
|
* when get sequence header,
|
|
|
|
* must write a #EXT-X-DISCONTINUITY to m3u8.
|
|
|
|
* @see: hls-m3u8-draft-pantos-http-live-streaming-12.txt
|
|
|
|
* @see: 3.4.11. EXT-X-DISCONTINUITY
|
|
|
|
*/
|
|
|
|
virtual int on_sequence_header(SrsHlsMuxer* muxer);
|
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* write audio to cache, if need to flush, flush to muxer.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-06-08 14:36:17 +00:00
|
|
|
virtual int write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t pts, SrsCodecSample* sample);
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* write video to muxer.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-06-08 14:36:17 +00:00
|
|
|
virtual int write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample);
|
2013-12-02 14:09:10 +00:00
|
|
|
private:
|
2014-03-20 10:55:45 +00:00
|
|
|
/**
|
|
|
|
* reopen the muxer for a new hls segment,
|
|
|
|
* close current segment, open a new segment,
|
|
|
|
* then write the key frame to the new segment.
|
|
|
|
* so, user must reap_segment then flush_video to hls muxer.
|
|
|
|
*/
|
2014-03-21 05:10:47 +00:00
|
|
|
virtual int reap_segment(std::string log_desc, SrsHlsMuxer* muxer, int64_t segment_start_dts);
|
2013-12-02 14:09:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-03-20 10:19:08 +00:00
|
|
|
* delivery RTMP stream to HLS(m3u8 and ts),
|
|
|
|
* SrsHls provides interface with SrsSource.
|
2014-03-21 06:02:14 +00:00
|
|
|
* TODO: FIXME: add utest for hls.
|
2013-12-02 14:09:10 +00:00
|
|
|
*/
|
|
|
|
class SrsHls
|
|
|
|
{
|
|
|
|
private:
|
2014-03-20 10:19:08 +00:00
|
|
|
SrsHlsMuxer* muxer;
|
|
|
|
SrsHlsCache* hls_cache;
|
2015-02-03 08:01:07 +00:00
|
|
|
ISrsHlsHandler* handler;
|
2013-11-26 08:06:58 +00:00
|
|
|
private:
|
2014-03-18 03:32:58 +00:00
|
|
|
bool hls_enabled;
|
|
|
|
SrsSource* source;
|
2014-06-08 14:36:17 +00:00
|
|
|
SrsAvcAacCodec* codec;
|
2014-03-18 03:32:58 +00:00
|
|
|
SrsCodecSample* sample;
|
|
|
|
SrsRtmpJitter* jitter;
|
|
|
|
SrsPithyPrint* pithy_print;
|
2014-03-20 10:19:08 +00:00
|
|
|
/**
|
|
|
|
* we store the stream dts,
|
|
|
|
* for when we notice the hls cache to publish,
|
|
|
|
* it need to know the segment start dts.
|
|
|
|
*
|
|
|
|
* for example. when republish, the stream dts will
|
|
|
|
* monotonically increase, and the ts dts should start
|
|
|
|
* from current dts.
|
|
|
|
*
|
|
|
|
* or, simply because the HlsCache never free when unpublish,
|
|
|
|
* so when publish or republish it must start at stream dts,
|
|
|
|
* not zero dts.
|
|
|
|
*/
|
|
|
|
int64_t stream_dts;
|
2013-11-23 12:16:47 +00:00
|
|
|
public:
|
2015-02-03 08:01:07 +00:00
|
|
|
SrsHls(SrsSource* s, ISrsHlsHandler* h);
|
2014-03-18 03:32:58 +00:00
|
|
|
virtual ~SrsHls();
|
2013-11-24 04:39:47 +00:00
|
|
|
public:
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
|
|
|
* publish stream event, continue to write the m3u8,
|
|
|
|
* for the muxer object not destroyed.
|
|
|
|
*/
|
|
|
|
virtual int on_publish(SrsRequest* req);
|
|
|
|
/**
|
|
|
|
* the unpublish event, only close the muxer, donot destroy the
|
|
|
|
* muxer, for when we continue to publish, the m3u8 will continue.
|
|
|
|
*/
|
|
|
|
virtual void on_unpublish();
|
|
|
|
/**
|
|
|
|
* get some information from metadata, it's optinal.
|
|
|
|
*/
|
|
|
|
virtual int on_meta_data(SrsAmf0Object* metadata);
|
|
|
|
/**
|
|
|
|
* mux the audio packets to ts.
|
2014-12-05 15:49:53 +00:00
|
|
|
* @param __audio, directly ptr, copy it if need to save it.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-12-05 15:49:53 +00:00
|
|
|
virtual int on_audio(SrsSharedPtrMessage* __audio);
|
2014-03-18 03:32:58 +00:00
|
|
|
/**
|
|
|
|
* mux the video packets to ts.
|
2014-12-05 15:49:53 +00:00
|
|
|
* @param __video, directly ptr, copy it if need to save it.
|
2014-03-18 03:32:58 +00:00
|
|
|
*/
|
2014-12-05 15:49:53 +00:00
|
|
|
virtual int on_video(SrsSharedPtrMessage* __video);
|
2013-12-05 15:34:26 +00:00
|
|
|
private:
|
2014-03-18 03:32:58 +00:00
|
|
|
virtual void hls_mux();
|
2013-11-23 12:16:47 +00:00
|
|
|
};
|
|
|
|
|
2013-11-27 14:41:58 +00:00
|
|
|
#endif
|
|
|
|
|
2014-08-02 14:18:39 +00:00
|
|
|
#endif
|