winlin

support HLS(m3u8)

... ... @@ -332,7 +332,7 @@ int SrsClient::publish(SrsSource* source, bool is_fmle)
SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);
// notify the hls to prepare when publish start.
if ((ret = source->on_publish(req->vhost)) != ERROR_SUCCESS) {
if ((ret = source->on_publish(req->vhost, req->stream)) != ERROR_SUCCESS) {
srs_error("hls on_publish failed. ret=%d", ret);
return ret;
}
... ...
... ... @@ -37,194 +37,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_source.hpp>
#include <srs_core_autofree.hpp>
SrsHLS::SrsHLS()
{
hls_enabled = false;
codec = new SrsCodec();
sample = new SrsCodecSample();
muxer = NULL;
jitter = new SrsRtmpJitter();
}
SrsHLS::~SrsHLS()
{
srs_freep(codec);
srs_freep(sample);
srs_freep(muxer);
srs_freep(jitter);
}
int SrsHLS::on_publish(std::string _vhost)
{
int ret = ERROR_SUCCESS;
// TODO: check config.
if (muxer) {
hls_enabled = true;
srs_trace("hls is reopen, continue streaming HLS, vhost=%s", _vhost.c_str());
return ret;
}
vhost = _vhost;
muxer = new SrsTSMuxer();
// try to open the HLS muxer
SrsConfDirective* conf = config->get_hls(vhost);
if (!conf && conf->arg0() == "off") {
return ret;
}
// TODO: check the audio and video, ensure both exsists.
// for use fixed mpegts header specifeid the audio and video pid.
hls_enabled = true;
std::string path = SRS_CONF_DEFAULT_HLS_PATH;
if ((conf = config->get_hls_path(vhost)) != NULL) {
path = conf->arg0();
}
// TODO: generate by m3u8 muxer.
path += "/1.ts";
if ((ret = muxer->open(path)) != ERROR_SUCCESS) {
srs_error("open hls muxer failed. ret=%d", ret);
return ret;
}
return ret;
}
void SrsHLS::on_unpublish()
{
hls_enabled = false;
//muxer->close();
//srs_freep(muxer);
}
int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata)
{
int ret = ERROR_SUCCESS;
if (!metadata || !metadata->metadata) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
}
SrsAmf0Object* obj = metadata->metadata;
if (obj->size() <= 0) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
}
// finger out the codec info from metadata if possible.
SrsAmf0Any* prop = NULL;
if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) {
codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("width")) != NULL && prop->is_number()) {
codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("height")) != NULL && prop->is_number()) {
codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) {
codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) {
codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) {
codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value);
}
if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) {
codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) {
codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value);
}
// ignore the following, for each flv/rtmp packet contains them:
// audiosamplerate, sample->sound_rate
// audiosamplesize, sample->sound_size
// stereo, sample->sound_type
return ret;
}
int SrsHLS::on_audio(SrsSharedPtrMessage* audio)
{
int ret = ERROR_SUCCESS;
SrsAutoFree(SrsSharedPtrMessage, audio, false);
sample->clear();
if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) {
return ret;
}
if (codec->audio_codec_id != SrsCodecAudioAAC) {
return ret;
}
// TODO: maybe donot need to demux the aac?
if (!hls_enabled) {
return ret;
}
// ignore sequence header
if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
return ret;
}
if ((ret = jitter->correct(audio, 0, 0)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = muxer->write_audio(audio->header.timestamp, codec, sample)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsHLS::on_video(SrsSharedPtrMessage* video)
{
int ret = ERROR_SUCCESS;
SrsAutoFree(SrsSharedPtrMessage, video, false);
sample->clear();
if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) {
return ret;
}
if (codec->video_codec_id != SrsCodecVideoAVC) {
return ret;
}
// TODO: maybe donot need to demux the avc?
if (!hls_enabled) {
return ret;
}
// ignore sequence header
if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
return ret;
}
if ((ret = jitter->correct(video, 0, 0)) != ERROR_SUCCESS) {
return ret;
}
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_HLS_DELAY 63000
if ((ret = muxer->write_video(video->header.timestamp, codec, sample)) != ERROR_SUCCESS) {
return ret;
}
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
return ret;
}
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
// @see: ngx_rtmp_mpegts_header
u_int8_t mpegts_header[] = {
... ... @@ -287,10 +111,6 @@ u_int8_t mpegts_header[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_HLS_DELAY 63000
// @see: ngx_rtmp_SrsMpegtsFrame_t
struct SrsMpegtsFrame
{
... ... @@ -527,18 +347,29 @@ private:
}
};
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
SrsM3u8Segment::SrsM3u8Segment()
{
duration = 0;
sequence_no = 0;
muxer = new SrsTSMuxer();
segment_start_dts = 0;
}
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
SrsM3u8Segment::~SrsM3u8Segment()
{
muxer->close();
srs_freep(muxer);
}
SrsTSMuxer::SrsTSMuxer()
SrsHLS::SrsHLS()
{
fd = -1;
hls_enabled = false;
codec = new SrsCodec();
sample = new SrsCodecSample();
current = NULL;
jitter = new SrsRtmpJitter();
file_index = 0;
m3u8_dts = stream_dts = 0;
audio_buffer = new SrsCodecBuffer();
video_buffer = new SrsCodecBuffer();
... ... @@ -547,9 +378,20 @@ SrsTSMuxer::SrsTSMuxer()
video_frame = new SrsMpegtsFrame();
}
SrsTSMuxer::~SrsTSMuxer()
SrsHLS::~SrsHLS()
{
close();
srs_freep(codec);
srs_freep(sample);
srs_freep(jitter);
std::vector<SrsM3u8Segment*>::iterator it;
for (it = segments.begin(); it != segments.end(); ++it) {
SrsM3u8Segment* segment = *it;
srs_freep(segment);
}
segments.clear();
srs_freep(current);
audio_buffer->free();
video_buffer->free();
... ... @@ -561,6 +403,346 @@ SrsTSMuxer::~SrsTSMuxer()
srs_freep(video_frame);
}
int SrsHLS::on_publish(std::string _vhost, std::string _stream)
{
int ret = ERROR_SUCCESS;
vhost = _vhost;
stream = _stream;
if ((ret = reopen()) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
void SrsHLS::on_unpublish()
{
hls_enabled = false;
}
int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata)
{
int ret = ERROR_SUCCESS;
if (!metadata || !metadata->metadata) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
}
SrsAmf0Object* obj = metadata->metadata;
if (obj->size() <= 0) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
}
// finger out the codec info from metadata if possible.
SrsAmf0Any* prop = NULL;
if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) {
codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("width")) != NULL && prop->is_number()) {
codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("height")) != NULL && prop->is_number()) {
codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) {
codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) {
codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) {
codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value);
}
if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) {
codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value;
}
if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) {
codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value);
}
// ignore the following, for each flv/rtmp packet contains them:
// audiosamplerate, sample->sound_rate
// audiosamplesize, sample->sound_size
// stereo, sample->sound_type
return ret;
}
int SrsHLS::on_audio(SrsSharedPtrMessage* audio)
{
int ret = ERROR_SUCCESS;
SrsAutoFree(SrsSharedPtrMessage, audio, false);
// TODO: maybe donot need to demux the aac?
if (!hls_enabled) {
return ret;
}
sample->clear();
if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) {
return ret;
}
if (codec->audio_codec_id != SrsCodecAudioAAC) {
return ret;
}
// ignore sequence header
if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
return ret;
}
if ((ret = jitter->correct(audio, 0, 0)) != ERROR_SUCCESS) {
return ret;
}
srs_assert(current);
stream_dts = audio_frame->dts = audio_frame->pts = audio->header.timestamp * 90;
audio_frame->pid = TS_AUDIO_PID;
audio_frame->sid = TS_AUDIO_AAC;
if ((ret = current->muxer->write_audio(audio_frame, audio_buffer, codec, sample)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsHLS::on_video(SrsSharedPtrMessage* video)
{
int ret = ERROR_SUCCESS;
SrsAutoFree(SrsSharedPtrMessage, video, false);
// TODO: maybe donot need to demux the avc?
if (!hls_enabled) {
return ret;
}
sample->clear();
if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) {
return ret;
}
if (codec->video_codec_id != SrsCodecVideoAVC) {
return ret;
}
// ignore sequence header
if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
return ret;
}
if ((ret = jitter->correct(video, 0, 0)) != ERROR_SUCCESS) {
return ret;
}
stream_dts = video_frame->dts = video->header.timestamp * 90;
video_frame->pts = video_frame->dts + sample->cts * 90;
video_frame->pid = TS_VIDEO_PID;
video_frame->sid = TS_VIDEO_AVC;
video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
// reopen the muxer for a gop
if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) {
int64_t diff = stream_dts - m3u8_dts;
// 10s.
if (diff / 90000 >= 10) {
if ((ret = reopen()) != ERROR_SUCCESS) {
return ret;
}
}
}
srs_assert(current);
if ((ret = current->muxer->write_video(video_frame, video_buffer, codec, sample)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsHLS::reopen()
{
int ret = ERROR_SUCCESS;
// try to open the HLS muxer
SrsConfDirective* conf = config->get_hls(vhost);
if (!conf && conf->arg0() == "off") {
return ret;
}
// TODO: check the audio and video, ensure both exsists.
// for use fixed mpegts header specifeid the audio and video pid.
hls_enabled = true;
hls_path = SRS_CONF_DEFAULT_HLS_PATH;
if ((conf = config->get_hls_path(vhost)) != NULL) {
hls_path = conf->arg0();
}
// start new segment.
if (current) {
current->duration = (stream_dts - current->segment_start_dts) / 90000.0;
segments.push_back(current);
current = NULL;
if ((ret = refresh_m3u8()) != ERROR_SUCCESS) {
return ret;
}
}
// new segment.
current = new SrsM3u8Segment();
m3u8_dts = current->segment_start_dts = stream_dts;
// generate filename.
char filename[128];
snprintf(filename, sizeof(filename), "%s-%d.ts", stream.c_str(), file_index++);
current->full_path = hls_path;
current->full_path += "/";
current->full_path += filename;
// TODO: support base url, and so on.
current->uri = filename;
if ((ret = current->muxer->open(current->full_path)) != ERROR_SUCCESS) {
srs_error("open hls muxer failed. ret=%d", ret);
return ret;
}
srs_trace("open HLS muxer success. vhost=%s, path=%s", vhost.c_str(), current->full_path.c_str());
return ret;
}
int SrsHLS::refresh_m3u8()
{
int ret = ERROR_SUCCESS;
int fd = -1;
ret = _refresh_m3u8(fd);
if (fd >= 0) {
close(fd);
}
return ret;
}
int SrsHLS::_refresh_m3u8(int& fd)
{
int ret = ERROR_SUCCESS;
// no segments, return.
if (segments.size() == 0) {
return ret;
}
m3u8 = hls_path;
m3u8 += "/";
m3u8 += stream;
m3u8 += ".m3u8";
int flags = O_CREAT|O_WRONLY|O_TRUNC;
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
if ((fd = ::open(m3u8.c_str(), flags, mode)) < 0) {
ret = ERROR_HLS_OPEN_FAILED;
srs_error("open m3u8 file %s failed. ret=%d", m3u8.c_str(), ret);
return ret;
}
srs_info("open m3u8 file %s success.", m3u8.c_str());
// #EXTM3U\n#EXT-X-VERSION:3\n
char header[] = {
// #EXTM3U\n
0x23, 0x45, 0x58, 0x54, 0x4d, 0x33, 0x55, 0xa,
// #EXT-X-VERSION:3\n
0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x56, 0x45, 0x52,
0x53, 0x49, 0x4f, 0x4e, 0x3a, 0x33, 0xa
};
if (::write(fd, header, sizeof(header)) != sizeof(header)) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("write m3u8 header failed. ret=%d", ret);
return ret;
}
srs_verbose("write m3u8 header success.");
// #EXT-X-MEDIA-SEQUENCE:4294967295\n
SrsM3u8Segment* first = *segments.begin();
char sequence[34] = {};
int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no);
if (::write(fd, sequence, len) != len) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("write m3u8 sequence failed. ret=%d", ret);
return ret;
}
srs_verbose("write m3u8 sequence success.");
// #EXT-X-TARGETDURATION:4294967295\n
int target_duration = 0;
std::vector<SrsM3u8Segment*>::iterator it;
for (it = segments.begin(); it != segments.end(); ++it) {
SrsM3u8Segment* segment = *it;
target_duration = srs_max(target_duration, (int)segment->duration);
}
// TODO: maybe need to take an around value
target_duration += 1;
char duration[34];
len = snprintf(duration, sizeof(duration), "#EXT-X-TARGETDURATION:%d\n", target_duration);
if (::write(fd, duration, len) != len) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("write m3u8 duration failed. ret=%d", ret);
return ret;
}
srs_verbose("write m3u8 duration success.");
// write all segments
for (it = segments.begin(); it != segments.end(); ++it) {
SrsM3u8Segment* segment = *it;
// "#EXTINF:4294967295.208,\n"
char ext_info[25];
len = snprintf(ext_info, sizeof(ext_info), "#EXTINF:%.3f\n", segment->duration);
if (::write(fd, ext_info, len) != len) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("write m3u8 segment failed. ret=%d", ret);
return ret;
}
srs_verbose("write m3u8 segment success.");
// file name
std::string filename = segment->uri;
filename += "\n";
if (::write(fd, filename.c_str(), filename.length()) != (int)filename.length()) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("write m3u8 segment uri failed. ret=%d", ret);
return ret;
}
srs_verbose("write m3u8 segment uri success.");
}
srs_info("write m3u8 %s success.", m3u8.c_str());
return ret;
}
SrsTSMuxer::SrsTSMuxer()
{
fd = -1;
}
SrsTSMuxer::~SrsTSMuxer()
{
close();
}
int SrsTSMuxer::open(std::string _path)
{
int ret = ERROR_SUCCESS;
... ... @@ -585,14 +767,10 @@ int SrsTSMuxer::open(std::string _path)
return ret;
}
int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample)
int SrsTSMuxer::write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer, SrsCodec* codec, SrsCodecSample* sample)
{
int ret = ERROR_SUCCESS;
audio_frame->dts = audio_frame->pts = time * 90;
audio_frame->pid = TS_AUDIO_PID;
audio_frame->sid = TS_AUDIO_AAC;
for (int i = 0; i < sample->nb_buffers; i++) {
SrsCodecBuffer* buf = &sample->buffers[i];
int32_t size = buf->size;
... ... @@ -660,16 +838,10 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam
return ret;
}
int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample)
int SrsTSMuxer::write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer, SrsCodec* codec, SrsCodecSample* sample)
{
int ret = ERROR_SUCCESS;
video_frame->dts = time * 90;
video_frame->pts = video_frame->dts + sample->cts * 90;
video_frame->pid = TS_VIDEO_PID;
video_frame->sid = TS_VIDEO_AVC;
video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 };
video_buffer->append(aud_nal, sizeof(aud_nal));
... ...
... ... @@ -30,34 +30,88 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <string>
#include <vector>
class SrsOnMetaDataPacket;
class SrsSharedPtrMessage;
class SrsCodecSample;
class SrsCodecBuffer;
class SrsMpegtsFrame;
class SrsRtmpJitter;
class SrsTSMuxer;
class SrsCodec;
class SrsRtmpJitter;
/**
* 3.3.2. EXTINF
* The EXTINF tag specifies the duration of a media segment.
*/
struct SrsM3u8Segment
{
// 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.
SrsTSMuxer* muxer;
// current segment start dts for m3u8
int64_t segment_start_dts;
SrsM3u8Segment();
virtual ~SrsM3u8Segment();
};
/**
* write m3u8 hls.
*/
class SrsHLS
{
private:
std::string vhost;
std::string stream;
std::string hls_path;
private:
int file_index;
std::string m3u8;
private:
/**
* m3u8 segments.
*/
std::vector<SrsM3u8Segment*> segments;
/**
* current writing segment.
*/
SrsM3u8Segment* current;
// current frame and buffer
SrsMpegtsFrame* audio_frame;
SrsCodecBuffer* audio_buffer;
SrsMpegtsFrame* video_frame;
SrsCodecBuffer* video_buffer;
// last known dts
int64_t stream_dts;
// last segment dts in m3u8
int64_t m3u8_dts;
private:
bool hls_enabled;
SrsCodec* codec;
SrsCodecSample* sample;
SrsTSMuxer* muxer;
SrsRtmpJitter* jitter;
public:
SrsHLS();
virtual ~SrsHLS();
public:
virtual int on_publish(std::string _vhost);
virtual int on_publish(std::string _vhost, std::string _stream);
virtual void on_unpublish();
virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
virtual int on_audio(SrsSharedPtrMessage* audio);
virtual int on_video(SrsSharedPtrMessage* video);
private:
virtual int reopen();
virtual int refresh_m3u8();
virtual int _refresh_m3u8(int& fd);
};
class SrsTSMuxer
... ... @@ -65,18 +119,13 @@ class SrsTSMuxer
private:
int fd;
std::string path;
private:
SrsMpegtsFrame* audio_frame;
SrsCodecBuffer* audio_buffer;
SrsMpegtsFrame* video_frame;
SrsCodecBuffer* video_buffer;
public:
SrsTSMuxer();
virtual ~SrsTSMuxer();
public:
virtual int open(std::string _path);
virtual int write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample);
virtual int write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample);
virtual int write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer, SrsCodec* codec, SrsCodecSample* sample);
virtual int write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer, SrsCodec* codec, SrsCodecSample* sample);
virtual void close();
};
... ...
... ... @@ -413,6 +413,7 @@ int SrsSource::on_video(SrsCommonMessage* video)
}
srs_verbose("initialize shared ptr video success.");
// TODO: when return error, crash.
if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) {
srs_error("hls process video message failed. ret=%d", ret);
return ret;
... ... @@ -451,9 +452,9 @@ int SrsSource::on_video(SrsCommonMessage* video)
return ret;
}
int SrsSource::on_publish(std::string vhost)
int SrsSource::on_publish(std::string vhost, std::string stream)
{
return hls->on_publish(vhost);
return hls->on_publish(vhost, stream);
}
void SrsSource::on_unpublish()
... ...
... ... @@ -166,7 +166,7 @@ public:
virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata);
virtual int on_audio(SrsCommonMessage* audio);
virtual int on_video(SrsCommonMessage* video);
virtual int on_publish(std::string vhost);
virtual int on_publish(std::string vhost, std::string stream);
virtual void on_unpublish();
public:
virtual int create_consumer(SrsConsumer*& consumer);
... ...