1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00
srs/trunk/src/app/srs_app_hds.cpp

731 lines
20 KiB
C++
Raw Normal View History

2017-03-25 09:21:39 +00:00
/**
* The MIT License (MIT)
*
2019-01-01 13:37:28 +00:00
* Copyright (c) 2013-2019 Winlin
2017-03-25 09:21:39 +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.
*/
2015-07-08 09:44:25 +00:00
#include <srs_app_hds.hpp>
#ifdef SRS_AUTO_HDS
#include <unistd.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#include <srs_app_hds.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_rtmp_stack.hpp>
2015-09-22 00:48:55 +00:00
#include <srs_kernel_buffer.hpp>
2015-07-08 09:44:25 +00:00
#include <srs_core_autofree.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_config.hpp>
static void update_box(char *start, int size)
{
char *p_size = (char*)&size;
start[0] = p_size[3];
start[1] = p_size[2];
start[2] = p_size[1];
start[3] = p_size[0];
}
char flv_header[] = {'F', 'L', 'V',
2017-03-25 09:21:39 +00:00
0x01, 0x05, 0x00, 0x00, 0x00, 0x09,
0x00, 0x00, 0x00, 0x00};
2015-07-08 09:44:25 +00:00
string serialFlv(SrsSharedPtrMessage *msg)
{
2017-03-25 09:21:39 +00:00
int size = 15 + msg->size;
char *byte = new char[size];
2018-01-01 13:20:57 +00:00
SrsBuffer *stream = new SrsBuffer(byte, size);
SrsAutoFree(SrsBuffer, stream);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// tag header
long long dts = msg->timestamp;
char type = msg->is_video() ? 0x09 : 0x08;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
stream->write_1bytes(type);
stream->write_3bytes(msg->size);
2018-01-01 13:20:57 +00:00
stream->write_3bytes((int32_t)dts);
2015-07-08 09:44:25 +00:00
stream->write_1bytes(dts >> 24 & 0xFF);
stream->write_3bytes(0);
stream->write_bytes(msg->payload, msg->size);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// pre tag size
int preTagSize = msg->size + 11;
stream->write_4bytes(preTagSize);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
string ret(stream->data(), stream->size());
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
delete [] byte;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
return ret;
}
class SrsHdsFragment
{
public:
SrsHdsFragment(SrsRequest *r)
2017-03-25 09:21:39 +00:00
: req(r)
, index(-1)
, start_time(0)
, videoSh(NULL)
, audioSh(NULL)
2015-07-08 09:44:25 +00:00
{
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
~SrsHdsFragment()
{
srs_freep(videoSh);
srs_freep(audioSh);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// clean msgs
list<SrsSharedPtrMessage *>::iterator iter;
for (iter = msgs.begin(); iter != msgs.end(); ++iter) {
SrsSharedPtrMessage *msg = *iter;
srs_freep(msg);
}
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
void on_video(SrsSharedPtrMessage *msg)
{
SrsSharedPtrMessage *_msg = msg->copy();
msgs.push_back(_msg);
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
void on_audio(SrsSharedPtrMessage *msg)
{
SrsSharedPtrMessage *_msg = msg->copy();
msgs.push_back(_msg);
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
flush data to disk.
*/
2018-01-01 13:20:57 +00:00
srs_error_t flush()
2015-07-08 09:44:25 +00:00
{
string data;
if (videoSh) {
videoSh->timestamp = start_time;
data.append(serialFlv(videoSh));
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (audioSh) {
audioSh->timestamp = start_time;
data.append(serialFlv(audioSh));
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
list<SrsSharedPtrMessage *>::iterator iter;
for (iter = msgs.begin(); iter != msgs.end(); ++iter) {
SrsSharedPtrMessage *msg = *iter;
data.append(serialFlv(msg));
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
char box_header[8];
2018-01-01 13:20:57 +00:00
SrsBuffer ss(box_header, 8);
ss.write_4bytes((int32_t)(8 + data.size()));
2015-07-08 09:44:25 +00:00
ss.write_string("mdat");
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
data = string(ss.data(), ss.size()) + data;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
const char *file_path = path.c_str();
int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (fd < 0) {
2018-01-01 13:20:57 +00:00
return srs_error_new(-1, "open fragment file failed, path=%s", file_path);
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (write(fd, data.data(), data.size()) != (int)data.size()) {
close(fd);
2018-01-01 13:20:57 +00:00
return srs_error_new(-1, "write fragment file failed, path=", file_path);
2015-07-08 09:44:25 +00:00
}
close(fd);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("build fragment success=%s", file_path);
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return srs_success;
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
calc the segment duration in milliseconds.
@return 0 if no msgs
or the last msg dts minus the first msg dts.
*/
2015-07-08 09:44:25 +00:00
int duration()
{
int duration_ms = 0;
long long first_msg_ts = 0;
long long last_msg_ts = 0;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (msgs.size() >= 2) {
SrsSharedPtrMessage *first_msg = msgs.front();
first_msg_ts = first_msg->timestamp;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
SrsSharedPtrMessage *last_msg = msgs.back();
last_msg_ts = last_msg->timestamp;
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
duration_ms = (int)(last_msg_ts - first_msg_ts);
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
return duration_ms;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
set/get index
*/
2015-07-08 09:44:25 +00:00
inline void set_index(int idx)
{
char file_path[1024] = {0};
sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str()
, req->app.c_str(), req->stream.c_str(), idx);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
path = file_path;
index = idx;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
inline int get_index()
{
return index;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
set/get start time
*/
2015-07-08 09:44:25 +00:00
inline void set_start_time(long long st)
{
start_time = st;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
inline long long get_start_time()
{
return start_time;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
void set_video_sh(SrsSharedPtrMessage *msg)
{
srs_freep(videoSh);
videoSh = msg->copy();
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
void set_audio_sh(SrsSharedPtrMessage *msg)
{
srs_freep(audioSh);
audioSh = msg->copy();
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
string fragment_path()
{
return path;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
private:
SrsRequest *req;
list<SrsSharedPtrMessage *> msgs;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
the index of this fragment
*/
2015-07-08 09:44:25 +00:00
int index;
long long start_time;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
SrsSharedPtrMessage *videoSh;
SrsSharedPtrMessage *audioSh;
string path;
};
2017-02-11 13:14:28 +00:00
SrsHds::SrsHds()
2017-03-25 09:21:39 +00:00
: currentSegment(NULL)
, fragment_index(1)
, video_sh(NULL)
, audio_sh(NULL)
, hds_req(NULL)
, hds_enabled(false)
2015-07-08 09:44:25 +00:00
{
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
}
SrsHds::~SrsHds()
{
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::on_publish(SrsRequest *req)
2015-07-08 09:44:25 +00:00
{
2018-01-01 13:20:57 +00:00
srs_error_t err = srs_success;
2015-07-08 09:44:25 +00:00
if (hds_enabled) {
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
std::string vhost = req->vhost;
if (!_srs_config->get_hds_enabled(vhost)) {
hds_enabled = false;
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
hds_enabled = true;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
hds_req = req->copy();
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
return flush_mainfest();
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::on_unpublish()
2015-07-08 09:44:25 +00:00
{
2018-01-01 13:20:57 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (!hds_enabled) {
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
hds_enabled = false;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_freep(video_sh);
srs_freep(audio_sh);
srs_freep(hds_req);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// clean fragments
list<SrsHdsFragment *>::iterator iter;
for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
SrsHdsFragment *st = *iter;
srs_freep(st);
}
fragments.clear();
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_freep(currentSegment);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("HDS un-published");
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::on_video(SrsSharedPtrMessage* msg)
2015-07-08 09:44:25 +00:00
{
2018-01-01 13:20:57 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (!hds_enabled) {
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2017-02-12 12:38:39 +00:00
if (SrsFlvVideo::sh(msg->payload, msg->size)) {
2015-07-08 09:44:25 +00:00
srs_freep(video_sh);
video_sh = msg->copy();
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (!currentSegment) {
currentSegment = new SrsHdsFragment(hds_req);
currentSegment->set_index(fragment_index++);
currentSegment->set_start_time(msg->timestamp);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (video_sh)
currentSegment->set_video_sh(video_sh);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (audio_sh)
currentSegment->set_audio_sh(audio_sh);
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
currentSegment->on_video(msg);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000;
if (currentSegment->duration() >= fragment_duration) {
// flush segment
2018-01-01 13:20:57 +00:00
if ((err = currentSegment->flush()) != srs_success) {
return srs_error_wrap(err, "flush segment");
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("flush Segment success.");
fragments.push_back(currentSegment);
currentSegment = NULL;
adjust_windows();
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// flush bootstrap
2018-01-01 13:20:57 +00:00
if ((err = flush_bootstrap()) != srs_success) {
return srs_error_wrap(err, "flush bootstrap");
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("flush BootStrap success.");
}
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::on_audio(SrsSharedPtrMessage* msg)
2015-07-08 09:44:25 +00:00
{
2018-01-01 13:20:57 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (!hds_enabled) {
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2017-02-12 12:38:39 +00:00
if (SrsFlvAudio::sh(msg->payload, msg->size)) {
2015-07-08 09:44:25 +00:00
srs_freep(audio_sh);
audio_sh = msg->copy();
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (!currentSegment) {
currentSegment = new SrsHdsFragment(hds_req);
currentSegment->set_index(fragment_index++);
currentSegment->set_start_time(msg->timestamp);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (video_sh)
currentSegment->set_video_sh(video_sh);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (audio_sh)
currentSegment->set_audio_sh(audio_sh);
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
currentSegment->on_audio(msg);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000;
if (currentSegment->duration() >= fragment_duration) {
// flush segment
2018-01-01 13:20:57 +00:00
if ((err = currentSegment->flush()) != srs_success) {
return srs_error_wrap(err, "flush segment");
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_info("flush Segment success.");
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// reset the current segment
fragments.push_back(currentSegment);
currentSegment = NULL;
adjust_windows();
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// flush bootstrap
2018-01-01 13:20:57 +00:00
if ((err = flush_bootstrap()) != srs_success) {
return srs_error_wrap(err, "flush bootstrap");
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_info("flush BootStrap success.");
}
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::flush_mainfest()
2015-07-08 09:44:25 +00:00
{
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
char buf[1024] = {0};
sprintf(buf, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n\t"
"<id>%s.f4m</id>\n\t"
"<streamType>live</streamType>\n\t"
"<deliveryType>streaming</deliveryType>\n\t"
"<bootstrapInfo profile=\"named\" url=\"%s.abst\" id=\"bootstrap0\" />\n\t"
"<media bitrate=\"0\" url=\"%s\" bootstrapInfoId=\"bootstrap0\"></media>\n"
"</manifest>"
, hds_req->stream.c_str(), hds_req->stream.c_str(), hds_req->stream.c_str());
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
string dir = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app;
if ((err = srs_create_dir_recursively(dir)) != srs_success) {
2018-01-01 13:20:57 +00:00
return srs_error_wrap(err, "hds create dir failed");
2015-07-08 09:44:25 +00:00
}
string path = dir + "/" + hds_req->stream + ".f4m";
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (fd < 0) {
2018-01-01 13:20:57 +00:00
return srs_error_new(ERROR_HDS_OPEN_F4M_FAILED, "open manifest file failed, path=%s", path.c_str());
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
int f4m_size = (int)strlen(buf);
2015-07-08 09:44:25 +00:00
if (write(fd, buf, f4m_size) != f4m_size) {
close(fd);
2018-01-01 13:20:57 +00:00
return srs_error_new(ERROR_HDS_WRITE_F4M_FAILED, "write manifest file failed, path=", path.c_str());
2015-07-08 09:44:25 +00:00
}
close(fd);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("build manifest success=%s", path.c_str());
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
2018-01-01 13:20:57 +00:00
srs_error_t SrsHds::flush_bootstrap()
2015-07-08 09:44:25 +00:00
{
2018-01-01 13:20:57 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
int size = 1024*100;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
char *start_abst = new char[1024*100];
SrsAutoFreeA(char, start_abst);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
int size_abst = 0;
char *start_asrt = NULL;
int size_asrt = 0;
char *start_afrt = NULL;
int size_afrt = 0;
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
SrsBuffer abst(start_abst, size);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// @see video_file_format_spec_v10_1
// page: 46
abst.write_4bytes(0);
abst.write_string("abst");
abst.write_1bytes(0x00); // Either 0 or 1
abst.write_3bytes(0x00); // Flags always 0
size_abst += 12;
/*!
2017-03-25 09:21:39 +00:00
@BootstrapinfoVersion UI32
The version number of the bootstrap information.
When the Update field is set, BootstrapinfoVersion
indicates the version number that is being updated.
we assume this is the last.
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(fragment_index - 1); // BootstrapinfoVersion
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0x20); // profile, live, update
abst.write_4bytes(1000); // TimeScale Typically, the value is 1000, for a unit of milliseconds
size_abst += 9;
/*!
2017-03-25 09:21:39 +00:00
The timestamp in TimeScale units of the latest available Fragment in the media presentation.
This timestamp is used to request the right fragment number.
The CurrentMedia Time can be the total duration.
For media presentations that are not live, CurrentMediaTime can be 0.
*/
2015-07-08 09:44:25 +00:00
SrsHdsFragment *st = fragments.back();
abst.write_8bytes(st->get_start_time());
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// SmpteTimeCodeOffset
abst.write_8bytes(0);
size_abst += 16;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@MovieIdentifier STRING
The identifier of this presentation.
we write null string.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_abst += 1;
/*!
2017-03-25 09:21:39 +00:00
@ServerEntryCount UI8
The number of ServerEntryTable entries.
The minimum value is 0.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_abst += 1;
/*!
2017-03-25 09:21:39 +00:00
@ServerEntryTable
because we write 0 of ServerEntryCount, so this feild is ignored.
*/
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@QualityEntryCount UI8
The number of QualityEntryTable entries, which is
also the number of available quality levels. The
minimum value is 0. Available quality levels are for,
for example, multi bit rate files or trick files.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_abst += 1;
/*!
2017-03-25 09:21:39 +00:00
@QualityEntryTable
because we write 0 of QualityEntryCount, so this feild is ignored.
*/
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@DrmData STRING
Null or null-terminated UTF-8 string. This string
holds Digital Rights Management metadata.
Encrypted files use this metadata to get the
necessary keys and licenses for decryption and play back.
we write null string.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_abst += 1;
/*!
2017-03-25 09:21:39 +00:00
@MetaData STRING
Null or null-terminated UTF - 8 string that holds metadata.
we write null string.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_abst += 1;
/*!
2017-03-25 09:21:39 +00:00
@SegmentRunTableCount UI8
The number of entries in SegmentRunTableEntries.
The minimum value is 1. Typically, one table
contains all segment runs. However, this count
provides the flexibility to define the segment runs
individually for each quality level (or trick file).
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(1);
size_abst += 1;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
start_asrt = start_abst + size_abst;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// follows by asrt
abst.write_4bytes(0);
abst.write_string("asrt");
size_asrt += 8;
/*!
2017-03-25 09:21:39 +00:00
@Version UI8
@Flags UI24
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(0);
size_asrt += 4;
/*!
2017-03-25 09:21:39 +00:00
@QualityEntryCount UI8
The number of QualitySegmen tUrlModifiers
(quality level references) that follow. If 0, this
Segment Run Table applies to all quality levels,
and there shall be only one Segment Run Table
box in the Bootstrap Info box.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_asrt += 1;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@QualitySegmentUrlModifiers
ignored.
*/
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@SegmentRunEntryCount
The number of items in this
SegmentRunEn tryTable. The minimum value is 1.
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(1);
size_asrt += 4;
/*!
2017-03-25 09:21:39 +00:00
@SegmentRunEntryTable
*/
2015-07-08 09:44:25 +00:00
for (int i = 0; i < 1; ++i) {
/*!
2017-03-25 09:21:39 +00:00
@FirstSegment UI32
The identifying number of the first segment in the run of
segments containing the same number of fragments.
The segment corresponding to the FirstSegment in the next
SEGMENTRUNENTRY will terminate this run.
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(1);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@FragmentsPerSegment UI32
The number of fragments in each segment in this run.
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(fragment_index - 1);
size_asrt += 8;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
update_box(start_asrt, size_asrt);
size_abst += size_asrt;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@FragmentRunTableCount UI8
The number of entries in FragmentRunTable-Entries.
The min i mum value is 1.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(1);
size_abst += 1;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
// follows by afrt
start_afrt = start_abst + size_abst;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
abst.write_4bytes(0);
abst.write_string("afrt");
size_afrt += 8;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@Version UI8
@Flags UI24
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(0);
size_afrt += 4;
/*!
2017-03-25 09:21:39 +00:00
@TimeScale UI32
The number of time units per second, used in the FirstFragmentTime stamp and
Fragment Duration fields.
Typically, the value is 1000.
*/
2015-07-08 09:44:25 +00:00
abst.write_4bytes(1000);
size_afrt += 4;
/*!
2017-03-25 09:21:39 +00:00
@QualityEntryCount UI8
The number of QualitySegment Url Modifiers
(quality level references) that follow.
If 0, this Fragment Run Table applies to all quality levels,
and there shall be only one Fragment Run Table
box in the Bootstrap Info box.
*/
2015-07-08 09:44:25 +00:00
abst.write_1bytes(0);
size_afrt += 1;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
/*!
2017-03-25 09:21:39 +00:00
@FragmentRunEntryCount UI32
The number of items in this FragmentRunEntryTable.
The minimum value is 1.
*/
2018-01-01 13:20:57 +00:00
abst.write_4bytes((int32_t)fragments.size());
2015-07-08 09:44:25 +00:00
size_afrt += 4;
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
list<SrsHdsFragment *>::iterator iter;
for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
SrsHdsFragment *st = *iter;
abst.write_4bytes(st->get_index());
abst.write_8bytes(st->get_start_time());
abst.write_4bytes(st->duration());
size_afrt += 16;
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
update_box(start_afrt, size_afrt);
size_abst += size_afrt;
update_box(start_abst, size_abst);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
string path = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app + "/" + hds_req->stream +".abst";
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (fd < 0) {
2018-01-01 13:20:57 +00:00
return srs_error_new(ERROR_HDS_OPEN_BOOTSTRAP_FAILED, "open bootstrap file failed, path=%s", path.c_str());
2015-07-08 09:44:25 +00:00
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
if (write(fd, start_abst, size_abst) != size_abst) {
close(fd);
2018-01-01 13:20:57 +00:00
return srs_error_new(ERROR_HDS_WRITE_BOOTSTRAP_FAILED, "write bootstrap file failed, path=", path.c_str());
2015-07-08 09:44:25 +00:00
}
close(fd);
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
srs_trace("build bootstrap success=%s", path.c_str());
2017-03-25 09:21:39 +00:00
2018-01-01 13:20:57 +00:00
return err;
2015-07-08 09:44:25 +00:00
}
void SrsHds::adjust_windows()
{
int windows_size = 0;
list<SrsHdsFragment *>::iterator iter;
for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
SrsHdsFragment *fragment = *iter;
windows_size += fragment->duration();
}
2017-03-25 09:21:39 +00:00
2015-07-08 09:44:25 +00:00
double windows_size_limit = _srs_config->get_hds_window(hds_req->vhost) * 1000;
if (windows_size > windows_size_limit ) {
SrsHdsFragment *fragment = fragments.front();
unlink(fragment->fragment_path().c_str());
fragments.erase(fragments.begin());
srs_freep(fragment);
}
}
#endif