mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
merge from 2.0.157, reap ts on the IDR.
This commit is contained in:
commit
4f9df4f1a0
15 changed files with 634 additions and 119 deletions
|
@ -566,7 +566,9 @@ Supported operating systems and hardware:
|
||||||
|
|
||||||
### SRS 2.0 history
|
### SRS 2.0 history
|
||||||
|
|
||||||
* v2.0, 2015-03-30, for [#372](https://github.com/winlinvip/simple-rtmp-server/issues/372), support transform vhost of edge 2.0.155.
|
* v2.0, 2015-04-04, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite annexb mux for ts, refer to apple sample. 2.0.157.
|
||||||
|
* v2.0, 2015-04-03, enhanced avc decode, parse the sps get width+height. 2.0.156.
|
||||||
|
* v2.0, 2015-04-03, for [#372](https://github.com/winlinvip/simple-rtmp-server/issues/372), support transform vhost of edge 2.0.155.
|
||||||
* v2.0, 2015-03-30, for [#366](https://github.com/winlinvip/simple-rtmp-server/issues/366), config hls to disable cleanup of ts. 2.0.154.
|
* v2.0, 2015-03-30, for [#366](https://github.com/winlinvip/simple-rtmp-server/issues/366), config hls to disable cleanup of ts. 2.0.154.
|
||||||
* v2.0, 2015-03-31, support server cycle handler. 2.0.153.
|
* v2.0, 2015-03-31, support server cycle handler. 2.0.153.
|
||||||
* v2.0, 2015-03-31, support on_hls for http hooks. 2.0.152.
|
* v2.0, 2015-03-31, support on_hls for http hooks. 2.0.152.
|
||||||
|
|
|
@ -44,6 +44,11 @@ max_connections 1000;
|
||||||
# @remark: donot support reload.
|
# @remark: donot support reload.
|
||||||
# default: on
|
# default: on
|
||||||
daemon on;
|
daemon on;
|
||||||
|
# whether use utc_time to generate the time struct,
|
||||||
|
# if off, use localtime() to generate it,
|
||||||
|
# if on, use gmtime() instead, which use UTC time.
|
||||||
|
# default: off
|
||||||
|
utc_time off;
|
||||||
|
|
||||||
#############################################################################################
|
#############################################################################################
|
||||||
# heartbeat/stats sections
|
# heartbeat/stats sections
|
||||||
|
|
|
@ -1336,7 +1336,8 @@ int SrsConfig::check_config()
|
||||||
&& n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file"
|
&& n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file"
|
||||||
&& n != "max_connections" && n != "daemon" && n != "heartbeat"
|
&& n != "max_connections" && n != "daemon" && n != "heartbeat"
|
||||||
&& n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms"
|
&& n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms"
|
||||||
&& n != "http_stream" && n != "http_server" && n != "stream_caster")
|
&& n != "http_stream" && n != "http_server" && n != "stream_caster"
|
||||||
|
&& n != "utc_time")
|
||||||
{
|
{
|
||||||
ret = ERROR_SYSTEM_CONFIG_INVALID;
|
ret = ERROR_SYSTEM_CONFIG_INVALID;
|
||||||
srs_error("unsupported directive %s, ret=%d", n.c_str(), ret);
|
srs_error("unsupported directive %s, ret=%d", n.c_str(), ret);
|
||||||
|
@ -1896,6 +1897,16 @@ int SrsConfig::get_pithy_print_ms()
|
||||||
return ::atoi(pithy->arg0().c_str());
|
return ::atoi(pithy->arg0().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_utc_time()
|
||||||
|
{
|
||||||
|
SrsConfDirective* utc = root->get("utc_time");
|
||||||
|
if (!utc || utc->arg0().empty()) {
|
||||||
|
return SRS_CONF_DEFAULT_UTC_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utc->arg0() == "on";
|
||||||
|
}
|
||||||
|
|
||||||
vector<SrsConfDirective*> SrsConfig::get_stream_casters()
|
vector<SrsConfDirective*> SrsConfig::get_stream_casters()
|
||||||
{
|
{
|
||||||
srs_assert(root);
|
srs_assert(root);
|
||||||
|
|
|
@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#define SRS_CONF_DEFAULT_LOG_TANK_CONSOLE "console"
|
#define SRS_CONF_DEFAULT_LOG_TANK_CONSOLE "console"
|
||||||
#define SRS_CONF_DEFAULT_COFNIG_FILE "conf/srs.conf"
|
#define SRS_CONF_DEFAULT_COFNIG_FILE "conf/srs.conf"
|
||||||
#define SRS_CONF_DEFAULT_FF_LOG_DIR "./objs"
|
#define SRS_CONF_DEFAULT_FF_LOG_DIR "./objs"
|
||||||
|
#define SRS_CONF_DEFAULT_UTC_TIME false
|
||||||
|
|
||||||
#define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000
|
#define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000
|
||||||
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
|
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
|
||||||
|
@ -435,6 +436,10 @@ public:
|
||||||
* every this interval in ms.
|
* every this interval in ms.
|
||||||
*/
|
*/
|
||||||
virtual int get_pithy_print_ms();
|
virtual int get_pithy_print_ms();
|
||||||
|
/**
|
||||||
|
* whether use utc-time to format the time.
|
||||||
|
*/
|
||||||
|
virtual bool get_utc_time();
|
||||||
// stream_caster section
|
// stream_caster section
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -312,12 +312,6 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
||||||
m3u8 = path + "/" + m3u8_file;
|
m3u8 = path + "/" + m3u8_file;
|
||||||
m3u8 = srs_path_build_stream(m3u8, req->vhost, req->app, req->stream);
|
m3u8 = srs_path_build_stream(m3u8, req->vhost, req->app, req->stream);
|
||||||
|
|
||||||
m3u8_dir = m3u8;
|
|
||||||
size_t pos = string::npos;
|
|
||||||
if ((pos = m3u8_dir.rfind("/")) != string::npos) {
|
|
||||||
m3u8_dir = m3u8_dir.substr(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we always keep the target duration increasing.
|
// we always keep the target duration increasing.
|
||||||
int max_td = srs_max(target_duration, (int)(fragment * _srs_config->get_hls_td_ratio(r->vhost)));
|
int max_td = srs_max(target_duration, (int)(fragment * _srs_config->get_hls_td_ratio(r->vhost)));
|
||||||
srs_info("hls update target duration %d=>%d, aof=%.2f", target_duration, max_td, aof_ratio);
|
srs_info("hls update target duration %d=>%d, aof=%.2f", target_duration, max_td, aof_ratio);
|
||||||
|
@ -336,6 +330,14 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
||||||
should_write_file = true;
|
should_write_file = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create m3u8 dir once.
|
||||||
|
m3u8_dir = srs_path_dirname(m3u8);
|
||||||
|
if (should_write_file && (ret = srs_create_dir_recursively(m3u8_dir)) != ERROR_SUCCESS) {
|
||||||
|
srs_error("create app dir %s failed. ret=%d", m3u8_dir.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_info("create m3u8 dir %s ok", m3u8_dir.c_str());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,11 +436,12 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
||||||
current->uri += ts_url;
|
current->uri += ts_url;
|
||||||
|
|
||||||
// create dir recursively for hls.
|
// create dir recursively for hls.
|
||||||
if (should_write_file && (ret = srs_create_dir_recursively(m3u8_dir)) != ERROR_SUCCESS) {
|
std::string ts_dir = srs_path_dirname(current->full_path);
|
||||||
srs_error("create app dir %s failed. ret=%d", m3u8_dir.c_str(), ret);
|
if (should_write_file && (ret = srs_create_dir_recursively(ts_dir)) != ERROR_SUCCESS) {
|
||||||
|
srs_error("create app dir %s failed. ret=%d", ts_dir.c_str(), ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
srs_info("create app dir %s ok", m3u8_dir.c_str());
|
srs_info("create ts dir %s ok", ts_dir.c_str());
|
||||||
|
|
||||||
// open temp ts file.
|
// open temp ts file.
|
||||||
std::string tmp_file = current->full_path + ".tmp";
|
std::string tmp_file = current->full_path + ".tmp";
|
||||||
|
@ -751,7 +754,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
||||||
// "#EXTINF:4294967295.208,\n"
|
// "#EXTINF:4294967295.208,\n"
|
||||||
ss.precision(3);
|
ss.precision(3);
|
||||||
ss.setf(std::ios::fixed, std::ios::floatfield);
|
ss.setf(std::ios::fixed, std::ios::floatfield);
|
||||||
ss << "#EXTINF:" << segment->duration << "," << SRS_CONSTS_LF;
|
ss << "#EXTINF:" << segment->duration << ", no desc" << SRS_CONSTS_LF;
|
||||||
srs_verbose("write m3u8 segment info success.");
|
srs_verbose("write m3u8 segment info success.");
|
||||||
|
|
||||||
// {file name}\n
|
// {file name}\n
|
||||||
|
@ -896,6 +899,7 @@ int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t
|
||||||
// we use absolutely overflow of segment to make jwplayer/ffplay happy
|
// we use absolutely overflow of segment to make jwplayer/ffplay happy
|
||||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184
|
// @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184
|
||||||
if (cache->audio && muxer->is_segment_absolutely_overflow()) {
|
if (cache->audio && muxer->is_segment_absolutely_overflow()) {
|
||||||
|
srs_warn("hls: absolute audio reap segment.");
|
||||||
if ((ret = reap_segment("audio", muxer, cache->audio->pts)) != ERROR_SUCCESS) {
|
if ((ret = reap_segment("audio", muxer, cache->audio->pts)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -914,9 +918,12 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t
|
||||||
}
|
}
|
||||||
|
|
||||||
// new segment when:
|
// new segment when:
|
||||||
// 1. base on gop.
|
// 1. base on gop(IDR).
|
||||||
// 2. some gops duration overflow.
|
// 2. some gops duration overflow.
|
||||||
if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && muxer->is_segment_overflow()) {
|
if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && muxer->is_segment_overflow()) {
|
||||||
|
if (!sample->has_idr) {
|
||||||
|
srs_warn("hls: ts starts without IDR, first nalu=%d", sample->first_nalu_type);
|
||||||
|
}
|
||||||
if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) {
|
if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -936,19 +943,21 @@ int SrsHlsCache::reap_segment(string log_desc, SrsHlsMuxer* muxer, int64_t segme
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// TODO: flush audio before or after segment?
|
||||||
|
// TODO: fresh segment begin with audio or video?
|
||||||
|
|
||||||
|
// close current ts.
|
||||||
if ((ret = muxer->segment_close(log_desc)) != ERROR_SUCCESS) {
|
if ((ret = muxer->segment_close(log_desc)) != ERROR_SUCCESS) {
|
||||||
srs_error("m3u8 muxer close segment failed. ret=%d", ret);
|
srs_error("m3u8 muxer close segment failed. ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// open new ts.
|
||||||
if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) {
|
if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) {
|
||||||
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: flush audio before or after segment?
|
|
||||||
// TODO: fresh segment begin with audio or video?
|
|
||||||
|
|
||||||
// segment open, flush video first.
|
// segment open, flush video first.
|
||||||
if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) {
|
if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) {
|
||||||
srs_error("m3u8 muxer flush video failed. ret=%d", ret);
|
srs_error("m3u8 muxer flush video failed. ret=%d", ret);
|
||||||
|
|
|
@ -274,8 +274,14 @@ bool SrsFastLog::generate_header(bool error, const char* tag, int context_id, co
|
||||||
|
|
||||||
// to calendar time
|
// to calendar time
|
||||||
struct tm* tm;
|
struct tm* tm;
|
||||||
if ((tm = localtime(&tv.tv_sec)) == NULL) {
|
if (_srs_config->get_utc_time()) {
|
||||||
return false;
|
if ((tm = gmtime(&tv.tv_sec)) == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((tm = localtime(&tv.tv_sec)) == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write log header
|
// write log header
|
||||||
|
|
|
@ -140,8 +140,14 @@ string srs_path_build_timestamp(string template_path)
|
||||||
|
|
||||||
// to calendar time
|
// to calendar time
|
||||||
struct tm* tm;
|
struct tm* tm;
|
||||||
if ((tm = localtime(&tv.tv_sec)) == NULL) {
|
if (_srs_config->get_utc_time()) {
|
||||||
return path;
|
if ((tm = gmtime(&tv.tv_sec)) == NULL) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((tm = localtime(&tv.tv_sec)) == NULL) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the buffer to format the date and time.
|
// the buffer to format the date and time.
|
||||||
|
@ -154,32 +160,32 @@ string srs_path_build_timestamp(string template_path)
|
||||||
}
|
}
|
||||||
// [2006], replace with current year.
|
// [2006], replace with current year.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year);
|
snprintf(buf, sizeof(buf), "%04d", 1900 + tm->tm_year);
|
||||||
path = srs_string_replace(path, "[2006]", buf);
|
path = srs_string_replace(path, "[2006]", buf);
|
||||||
}
|
}
|
||||||
// [01], replace this const to current month.
|
// [01], replace this const to current month.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon);
|
snprintf(buf, sizeof(buf), "%02d", 1 + tm->tm_mon);
|
||||||
path = srs_string_replace(path, "[01]", buf);
|
path = srs_string_replace(path, "[01]", buf);
|
||||||
}
|
}
|
||||||
// [02], replace this const to current date.
|
// [02], replace this const to current date.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", tm->tm_mday);
|
snprintf(buf, sizeof(buf), "%02d", tm->tm_mday);
|
||||||
path = srs_string_replace(path, "[02]", buf);
|
path = srs_string_replace(path, "[02]", buf);
|
||||||
}
|
}
|
||||||
// [15], replace this const to current hour.
|
// [15], replace this const to current hour.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", tm->tm_hour);
|
snprintf(buf, sizeof(buf), "%02d", tm->tm_hour);
|
||||||
path = srs_string_replace(path, "[15]", buf);
|
path = srs_string_replace(path, "[15]", buf);
|
||||||
}
|
}
|
||||||
// [04], repleace this const to current minute.
|
// [04], repleace this const to current minute.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", tm->tm_min);
|
snprintf(buf, sizeof(buf), "%02d", tm->tm_min);
|
||||||
path = srs_string_replace(path, "[04]", buf);
|
path = srs_string_replace(path, "[04]", buf);
|
||||||
}
|
}
|
||||||
// [05], repleace this const to current second.
|
// [05], repleace this const to current second.
|
||||||
if (true) {
|
if (true) {
|
||||||
snprintf(buf, sizeof(buf), "%d", tm->tm_sec);
|
snprintf(buf, sizeof(buf), "%02d", tm->tm_sec);
|
||||||
path = srs_string_replace(path, "[05]", buf);
|
path = srs_string_replace(path, "[05]", buf);
|
||||||
}
|
}
|
||||||
// [999], repleace this const to current millisecond.
|
// [999], repleace this const to current millisecond.
|
||||||
|
|
|
@ -31,6 +31,7 @@ using namespace std;
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_kernel_stream.hpp>
|
#include <srs_kernel_stream.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_core_autofree.hpp>
|
||||||
|
|
||||||
string srs_codec_video2str(SrsCodecVideo codec)
|
string srs_codec_video2str(SrsCodecVideo codec)
|
||||||
{
|
{
|
||||||
|
@ -265,6 +266,30 @@ bool SrsFlvCodec::audio_is_aac(char* data, int size)
|
||||||
return sound_format == SrsCodecAudioAAC;
|
return sound_format == SrsCodecAudioAAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string srs_codec_avc_nalu2str(SrsAvcNaluType nalu_type)
|
||||||
|
{
|
||||||
|
switch (nalu_type) {
|
||||||
|
case SrsAvcNaluTypeNonIDR: return "NonIDR";
|
||||||
|
case SrsAvcNaluTypeDataPartitionA: return "DataPartitionA";
|
||||||
|
case SrsAvcNaluTypeDataPartitionB: return "DataPartitionB";
|
||||||
|
case SrsAvcNaluTypeDataPartitionC: return "DataPartitionC";
|
||||||
|
case SrsAvcNaluTypeIDR: return "IDR";
|
||||||
|
case SrsAvcNaluTypeSEI: return "SEI";
|
||||||
|
case SrsAvcNaluTypeSPS: return "SPS";
|
||||||
|
case SrsAvcNaluTypePPS: return "PPS";
|
||||||
|
case SrsAvcNaluTypeAccessUnitDelimiter: return "AccessUnitDelimiter";
|
||||||
|
case SrsAvcNaluTypeEOSequence: return "EOSequence";
|
||||||
|
case SrsAvcNaluTypeEOStream: return "EOStream";
|
||||||
|
case SrsAvcNaluTypeFilterData: return "FilterData";
|
||||||
|
case SrsAvcNaluTypeSPSExt: return "SPSExt";
|
||||||
|
case SrsAvcNaluTypePrefixNALU: return "PrefixNALU";
|
||||||
|
case SrsAvcNaluTypeSubsetSPS: return "SubsetSPS";
|
||||||
|
case SrsAvcNaluTypeLayerWithoutPartition: return "LayerWithoutPartition";
|
||||||
|
case SrsAvcNaluTypeCodedSliceExt: return "CodedSliceExt";
|
||||||
|
case SrsAvcNaluTypeReserved: default: return "Other";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SrsCodecSampleUnit::SrsCodecSampleUnit()
|
SrsCodecSampleUnit::SrsCodecSampleUnit()
|
||||||
{
|
{
|
||||||
size = 0;
|
size = 0;
|
||||||
|
@ -292,6 +317,8 @@ void SrsCodecSample::clear()
|
||||||
cts = 0;
|
cts = 0;
|
||||||
frame_type = SrsCodecVideoAVCFrameReserved;
|
frame_type = SrsCodecVideoAVCFrameReserved;
|
||||||
avc_packet_type = SrsCodecVideoAVCTypeReserved;
|
avc_packet_type = SrsCodecVideoAVCTypeReserved;
|
||||||
|
has_idr = false;
|
||||||
|
first_nalu_type = SrsAvcNaluTypeReserved;
|
||||||
|
|
||||||
acodec = SrsCodecAudioReserved1;
|
acodec = SrsCodecAudioReserved1;
|
||||||
sound_rate = SrsCodecAudioSampleRateReserved;
|
sound_rate = SrsCodecAudioSampleRateReserved;
|
||||||
|
@ -315,6 +342,19 @@ int SrsCodecSample::add_sample_unit(char* bytes, int size)
|
||||||
sample_unit->bytes = bytes;
|
sample_unit->bytes = bytes;
|
||||||
sample_unit->size = size;
|
sample_unit->size = size;
|
||||||
|
|
||||||
|
// for video, parse the nalu type, set the IDR flag.
|
||||||
|
if (is_video) {
|
||||||
|
SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(bytes[0] & 0x1f);
|
||||||
|
|
||||||
|
if (nal_unit_type == SrsAvcNaluTypeIDR) {
|
||||||
|
has_idr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_nalu_type == SrsAvcNaluTypeReserved) {
|
||||||
|
first_nalu_type = nal_unit_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,7 +753,8 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 sps
|
// 1 sps, 7.3.2.1 Sequence parameter set RBSP syntax
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
|
||||||
if (!stream->require(1)) {
|
if (!stream->require(1)) {
|
||||||
ret = ERROR_HLS_DECODE_ERROR;
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
srs_error("avc decode sequenc header sps failed. ret=%d", ret);
|
srs_error("avc decode sequenc header sps failed. ret=%d", ret);
|
||||||
|
@ -740,8 +781,7 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream)
|
||||||
if (sequenceParameterSetLength > 0) {
|
if (sequenceParameterSetLength > 0) {
|
||||||
srs_freep(sequenceParameterSetNALUnit);
|
srs_freep(sequenceParameterSetNALUnit);
|
||||||
sequenceParameterSetNALUnit = new char[sequenceParameterSetLength];
|
sequenceParameterSetNALUnit = new char[sequenceParameterSetLength];
|
||||||
memcpy(sequenceParameterSetNALUnit, stream->data() + stream->pos(), sequenceParameterSetLength);
|
stream->read_bytes(sequenceParameterSetNALUnit, sequenceParameterSetLength);
|
||||||
stream->skip(sequenceParameterSetLength);
|
|
||||||
}
|
}
|
||||||
// 1 pps
|
// 1 pps
|
||||||
if (!stream->require(1)) {
|
if (!stream->require(1)) {
|
||||||
|
@ -770,10 +810,242 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream)
|
||||||
if (pictureParameterSetLength > 0) {
|
if (pictureParameterSetLength > 0) {
|
||||||
srs_freep(pictureParameterSetNALUnit);
|
srs_freep(pictureParameterSetNALUnit);
|
||||||
pictureParameterSetNALUnit = new char[pictureParameterSetLength];
|
pictureParameterSetNALUnit = new char[pictureParameterSetLength];
|
||||||
memcpy(pictureParameterSetNALUnit, stream->data() + stream->pos(), pictureParameterSetLength);
|
stream->read_bytes(pictureParameterSetNALUnit, pictureParameterSetLength);
|
||||||
stream->skip(pictureParameterSetLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return avc_demux_sps();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsAvcAacCodec::avc_demux_sps()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!sequenceParameterSetLength) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsStream stream;
|
||||||
|
if ((ret = stream.initialize(sequenceParameterSetNALUnit, sequenceParameterSetLength)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for NALU, 7.3.1 NAL unit syntax
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 61.
|
||||||
|
if (!stream.require(1)) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("avc decode sps failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int8_t nutv = stream.read_1bytes();
|
||||||
|
|
||||||
|
// forbidden_zero_bit shall be equal to 0.
|
||||||
|
int8_t forbidden_zero_bit = (nutv >> 7) & 0x01;
|
||||||
|
if (forbidden_zero_bit) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("forbidden_zero_bit shall be equal to 0. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nal_ref_idc not equal to 0 specifies that the content of the NAL unit contains a sequence parameter set or a picture
|
||||||
|
// parameter set or a slice of a reference picture or a slice data partition of a reference picture.
|
||||||
|
int8_t nal_ref_idc = (nutv >> 5) & 0x03;
|
||||||
|
if (!nal_ref_idc) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("for sps, nal_ref_idc shall be not be equal to 0. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.4.1 NAL unit semantics
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 61.
|
||||||
|
// nal_unit_type specifies the type of RBSP data structure contained in the NAL unit as specified in Table 7-1.
|
||||||
|
SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(nutv & 0x1f);
|
||||||
|
if (nal_unit_type != 7) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("for sps, nal_unit_type shall be equal to 7. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the rbsp from sps.
|
||||||
|
// rbsp[ i ] a raw byte sequence payload is specified as an ordered sequence of bytes.
|
||||||
|
int8_t* rbsp = new int8_t[sequenceParameterSetLength];
|
||||||
|
SrsAutoFree(int8_t, rbsp);
|
||||||
|
|
||||||
|
int nb_rbsp = 0;
|
||||||
|
while (!stream.empty()) {
|
||||||
|
rbsp[nb_rbsp] = stream.read_1bytes();
|
||||||
|
|
||||||
|
// XX 00 00 03 XX, the 03 byte should be drop.
|
||||||
|
if (nb_rbsp > 2 && rbsp[nb_rbsp - 2] == 0 && rbsp[nb_rbsp - 1] == 0 && rbsp[nb_rbsp] == 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb_rbsp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return avc_demux_sps_rbsp((char*)rbsp, nb_rbsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int SrsAvcAacCodec::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// reparse the rbsp.
|
||||||
|
SrsStream stream;
|
||||||
|
if ((ret = stream.initialize(rbsp, nb_rbsp)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for SPS, 7.3.2.1.1 Sequence parameter set data syntax
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62.
|
||||||
|
if (!stream.require(3)) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps shall atleast 3bytes. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
u_int8_t profile_idc = stream.read_1bytes();
|
||||||
|
if (!profile_idc) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the profile_idc invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t flags = stream.read_1bytes();
|
||||||
|
if (flags & 0x03) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the flags invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int8_t level_idc = stream.read_1bytes();
|
||||||
|
if (!level_idc) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the level_idc invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsBitStream bs;
|
||||||
|
if ((ret = bs.initialize(&stream)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t seq_parameter_set_id = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, seq_parameter_set_id)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (seq_parameter_set_id < 0) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the seq_parameter_set_id invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_info("sps parse profile=%d, level=%d, sps_id=%d", profile_idc, level_idc, seq_parameter_set_id);
|
||||||
|
|
||||||
|
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244
|
||||||
|
|| profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118
|
||||||
|
|| profile_idc == 128
|
||||||
|
) {
|
||||||
|
int64_t chroma_format_idc = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, chroma_format_idc)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (chroma_format_idc == 3) {
|
||||||
|
int8_t separate_colour_plane_flag = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_bit(&bs, separate_colour_plane_flag)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t bit_depth_luma_minus8 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, bit_depth_luma_minus8)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t bit_depth_chroma_minus8 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, bit_depth_chroma_minus8)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t qpprime_y_zero_transform_bypass_flag = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_bit(&bs, qpprime_y_zero_transform_bypass_flag)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t seq_scaling_matrix_present_flag = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_bit(&bs, seq_scaling_matrix_present_flag)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (seq_scaling_matrix_present_flag) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the seq_scaling_matrix_present_flag invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t log2_max_frame_num_minus4 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, log2_max_frame_num_minus4)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pic_order_cnt_type = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, pic_order_cnt_type)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pic_order_cnt_type == 0) {
|
||||||
|
int64_t log2_max_pic_order_cnt_lsb_minus4 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, log2_max_pic_order_cnt_lsb_minus4)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (pic_order_cnt_type == 1) {
|
||||||
|
int8_t delta_pic_order_always_zero_flag = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_bit(&bs, delta_pic_order_always_zero_flag)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t offset_for_non_ref_pic = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, offset_for_non_ref_pic)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t offset_for_top_to_bottom_field = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, offset_for_top_to_bottom_field)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t num_ref_frames_in_pic_order_cnt_cycle = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, num_ref_frames_in_pic_order_cnt_cycle)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (num_ref_frames_in_pic_order_cnt_cycle) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("sps the num_ref_frames_in_pic_order_cnt_cycle invalid. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t max_num_ref_frames = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, max_num_ref_frames)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t gaps_in_frame_num_value_allowed_flag = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_bit(&bs, gaps_in_frame_num_value_allowed_flag)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pic_width_in_mbs_minus1 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, pic_width_in_mbs_minus1)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pic_height_in_map_units_minus1 = -1;
|
||||||
|
if ((ret = srs_avc_nalu_read_uev(&bs, pic_height_in_map_units_minus1)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = (int)(pic_width_in_mbs_minus1 + 1) * 16;
|
||||||
|
height = (int)(pic_height_in_map_units_minus1 + 1) * 16;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,6 +275,52 @@ enum SrsCodecAudioSoundType
|
||||||
SrsCodecAudioSoundTypeStereo = 1,
|
SrsCodecAudioSoundTypeStereo = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
|
||||||
|
* H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
|
||||||
|
*/
|
||||||
|
enum SrsAvcNaluType
|
||||||
|
{
|
||||||
|
// Unspecified
|
||||||
|
SrsAvcNaluTypeReserved = 0,
|
||||||
|
|
||||||
|
// Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
|
||||||
|
SrsAvcNaluTypeNonIDR = 1,
|
||||||
|
// Coded slice data partition A slice_data_partition_a_layer_rbsp( )
|
||||||
|
SrsAvcNaluTypeDataPartitionA = 2,
|
||||||
|
// Coded slice data partition B slice_data_partition_b_layer_rbsp( )
|
||||||
|
SrsAvcNaluTypeDataPartitionB = 3,
|
||||||
|
// Coded slice data partition C slice_data_partition_c_layer_rbsp( )
|
||||||
|
SrsAvcNaluTypeDataPartitionC = 4,
|
||||||
|
// Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
|
||||||
|
SrsAvcNaluTypeIDR = 5,
|
||||||
|
// Supplemental enhancement information (SEI) sei_rbsp( )
|
||||||
|
SrsAvcNaluTypeSEI = 6,
|
||||||
|
// Sequence parameter set seq_parameter_set_rbsp( )
|
||||||
|
SrsAvcNaluTypeSPS = 7,
|
||||||
|
// Picture parameter set pic_parameter_set_rbsp( )
|
||||||
|
SrsAvcNaluTypePPS = 8,
|
||||||
|
// Access unit delimiter access_unit_delimiter_rbsp( )
|
||||||
|
SrsAvcNaluTypeAccessUnitDelimiter = 9,
|
||||||
|
// End of sequence end_of_seq_rbsp( )
|
||||||
|
SrsAvcNaluTypeEOSequence = 10,
|
||||||
|
// End of stream end_of_stream_rbsp( )
|
||||||
|
SrsAvcNaluTypeEOStream = 11,
|
||||||
|
// Filler data filler_data_rbsp( )
|
||||||
|
SrsAvcNaluTypeFilterData = 12,
|
||||||
|
// Sequence parameter set extension seq_parameter_set_extension_rbsp( )
|
||||||
|
SrsAvcNaluTypeSPSExt = 13,
|
||||||
|
// Prefix NAL unit prefix_nal_unit_rbsp( )
|
||||||
|
SrsAvcNaluTypePrefixNALU = 14,
|
||||||
|
// Subset sequence parameter set subset_seq_parameter_set_rbsp( )
|
||||||
|
SrsAvcNaluTypeSubsetSPS = 15,
|
||||||
|
// Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
|
||||||
|
SrsAvcNaluTypeLayerWithoutPartition = 19,
|
||||||
|
// Coded slice extension slice_layer_extension_rbsp( )
|
||||||
|
SrsAvcNaluTypeCodedSliceExt = 20,
|
||||||
|
};
|
||||||
|
std::string srs_codec_avc_nalu2str(SrsAvcNaluType nalu_type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the codec sample unit.
|
* the codec sample unit.
|
||||||
* for h.264 video packet, a NALU is a sample unit.
|
* for h.264 video packet, a NALU is a sample unit.
|
||||||
|
@ -334,6 +380,9 @@ public:
|
||||||
// video specified
|
// video specified
|
||||||
SrsCodecVideoAVCFrame frame_type;
|
SrsCodecVideoAVCFrame frame_type;
|
||||||
SrsCodecVideoAVCType avc_packet_type;
|
SrsCodecVideoAVCType avc_packet_type;
|
||||||
|
// whether sample_units contains IDR frame.
|
||||||
|
bool has_idr;
|
||||||
|
SrsAvcNaluType first_nalu_type;
|
||||||
public:
|
public:
|
||||||
// audio specified
|
// audio specified
|
||||||
SrsCodecAudio acodec;
|
SrsCodecAudio acodec;
|
||||||
|
@ -580,6 +629,11 @@ private:
|
||||||
* decode the sps and pps.
|
* decode the sps and pps.
|
||||||
*/
|
*/
|
||||||
virtual int avc_demux_sps_pps(SrsStream* stream);
|
virtual int avc_demux_sps_pps(SrsStream* stream);
|
||||||
|
/**
|
||||||
|
* decode the sps rbsp stream.
|
||||||
|
*/
|
||||||
|
virtual int avc_demux_sps();
|
||||||
|
virtual int avc_demux_sps_rbsp(char* rbsp, int nb_rbsp);
|
||||||
/**
|
/**
|
||||||
* demux the avc NALU in "AnnexB"
|
* demux the avc NALU in "AnnexB"
|
||||||
* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
||||||
|
|
|
@ -215,7 +215,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#define ERROR_HTTP_DVR_CREATE_REQUEST 3053
|
#define ERROR_HTTP_DVR_CREATE_REQUEST 3053
|
||||||
#define ERROR_HTTP_DVR_NO_TAEGET 3054
|
#define ERROR_HTTP_DVR_NO_TAEGET 3054
|
||||||
#define ERROR_ADTS_ID_NOT_AAC 3055
|
#define ERROR_ADTS_ID_NOT_AAC 3055
|
||||||
|
|
||||||
// HDS error code
|
// HDS error code
|
||||||
#define ERROR_HDS_OPEN_F4M_FAILED 3056
|
#define ERROR_HDS_OPEN_F4M_FAILED 3056
|
||||||
#define ERROR_HDS_WRITE_F4M_FAILED 3057
|
#define ERROR_HDS_WRITE_F4M_FAILED 3057
|
||||||
|
@ -254,6 +253,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#define ERROR_STREAM_CASTER_FLV_TAG 4024
|
#define ERROR_STREAM_CASTER_FLV_TAG 4024
|
||||||
#define ERROR_HTTP_RESPONSE_EOF 4025
|
#define ERROR_HTTP_RESPONSE_EOF 4025
|
||||||
#define ERROR_HTTP_INVALID_CHUNK_HEADER 4026
|
#define ERROR_HTTP_INVALID_CHUNK_HEADER 4026
|
||||||
|
#define ERROR_AVC_NALU_UEV 4027
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// user-define error.
|
// user-define error.
|
||||||
|
|
|
@ -252,4 +252,38 @@ void SrsStream::write_bytes(char* data, int size)
|
||||||
p += size;
|
p += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsBitStream::SrsBitStream()
|
||||||
|
{
|
||||||
|
cb = 0;
|
||||||
|
cb_left = 0;
|
||||||
|
stream = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsBitStream::~SrsBitStream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsBitStream::initialize(SrsStream* s) {
|
||||||
|
stream = s;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsBitStream::empty() {
|
||||||
|
if (cb_left) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stream->empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t SrsBitStream::read_bit() {
|
||||||
|
if (!cb_left) {
|
||||||
|
srs_assert(!stream->empty());
|
||||||
|
cb = stream->read_1bytes();
|
||||||
|
cb_left = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t v = (cb >> (cb_left - 1)) & 0x01;
|
||||||
|
cb_left--;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,4 +154,22 @@ public:
|
||||||
virtual void write_bytes(char* data, int size);
|
virtual void write_bytes(char* data, int size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the bit stream.
|
||||||
|
*/
|
||||||
|
class SrsBitStream
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int8_t cb;
|
||||||
|
u_int8_t cb_left;
|
||||||
|
SrsStream* stream;
|
||||||
|
public:
|
||||||
|
SrsBitStream();
|
||||||
|
virtual ~SrsBitStream();
|
||||||
|
public:
|
||||||
|
virtual int initialize(SrsStream* s);
|
||||||
|
virtual bool empty();
|
||||||
|
virtual int8_t read_bit();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2835,19 +2835,104 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
// for type1/5/6, insert aud packet.
|
// mux the samples in annexb format,
|
||||||
u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 };
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324.
|
||||||
|
|
||||||
bool sps_pps_sent = false;
|
|
||||||
bool aud_sent = false;
|
|
||||||
/**
|
/**
|
||||||
* a ts sample is format as:
|
* 00 00 00 01 // header
|
||||||
* 00 00 00 01 // header
|
* xxxxxxx // data bytes
|
||||||
* xxxxxxx // data bytes
|
* 00 00 01 // continue header
|
||||||
* 00 00 01 // continue header
|
* xxxxxxx // data bytes.
|
||||||
* xxxxxxx // data bytes.
|
*
|
||||||
* so, for each sample, we append header in aud_nal, then appends the bytes in sample.
|
* nal_unit_type specifies the type of RBSP data structure contained in the NAL unit as specified in Table 7-1.
|
||||||
*/
|
* Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
|
||||||
|
* H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
|
||||||
|
* 1, Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
|
||||||
|
* 2, Coded slice data partition A slice_data_partition_a_layer_rbsp( )
|
||||||
|
* 3, Coded slice data partition B slice_data_partition_b_layer_rbsp( )
|
||||||
|
* 4, Coded slice data partition C slice_data_partition_c_layer_rbsp( )
|
||||||
|
* 5, Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
|
||||||
|
* 6, Supplemental enhancement information (SEI) sei_rbsp( )
|
||||||
|
* 7, Sequence parameter set seq_parameter_set_rbsp( )
|
||||||
|
* 8, Picture parameter set pic_parameter_set_rbsp( )
|
||||||
|
* 9, Access unit delimiter access_unit_delimiter_rbsp( )
|
||||||
|
* 10, End of sequence end_of_seq_rbsp( )
|
||||||
|
* 11, End of stream end_of_stream_rbsp( )
|
||||||
|
* 12, Filler data filler_data_rbsp( )
|
||||||
|
* 13, Sequence parameter set extension seq_parameter_set_extension_rbsp( )
|
||||||
|
* 14, Prefix NAL unit prefix_nal_unit_rbsp( )
|
||||||
|
* 15, Subset sequence parameter set subset_seq_parameter_set_rbsp( )
|
||||||
|
* 19, Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
|
||||||
|
* 20, Coded slice extension slice_layer_extension_rbsp( )
|
||||||
|
* the first ts message of apple sample:
|
||||||
|
* annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
|
||||||
|
* annexb 4B header, 19B sps(nal_unit_type:7)
|
||||||
|
* annexb 3B header, 4B pps(nal_unit_type:8)
|
||||||
|
* annexb 3B header, 12B nalu(nal_unit_type:6)
|
||||||
|
* annexb 3B header, 21B nalu(nal_unit_type:6)
|
||||||
|
* annexb 3B header, 2762B nalu(nal_unit_type:5)
|
||||||
|
* annexb 3B header, 3535B nalu(nal_unit_type:5)
|
||||||
|
* the second ts message of apple ts sample:
|
||||||
|
* annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
|
||||||
|
* annexb 3B header, 21B nalu(nal_unit_type:6)
|
||||||
|
* annexb 3B header, 379B nalu(nal_unit_type:1)
|
||||||
|
* annexb 3B header, 406B nalu(nal_unit_type:1)
|
||||||
|
*/
|
||||||
|
static u_int8_t fresh_nalu_header[] = { 0x00, 0x00, 0x00, 0x01 };
|
||||||
|
static u_int8_t cont_nalu_header[] = { 0x00, 0x00, 0x01 };
|
||||||
|
|
||||||
|
// the aud(access unit delimiter) before each frame.
|
||||||
|
// 7.3.2.4 Access unit delimiter RBSP syntax
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 66.
|
||||||
|
//
|
||||||
|
// primary_pic_type u(3), the first 3bits, primary_pic_type indicates that the slice_type values
|
||||||
|
// for all slices of the primary coded picture are members of the set listed in Table 7-5 for
|
||||||
|
// the given value of primary_pic_type.
|
||||||
|
// 0, slice_type 2, 7
|
||||||
|
// 1, slice_type 0, 2, 5, 7
|
||||||
|
// 2, slice_type 0, 1, 2, 5, 6, 7
|
||||||
|
// 3, slice_type 4, 9
|
||||||
|
// 4, slice_type 3, 4, 8, 9
|
||||||
|
// 5, slice_type 2, 4, 7, 9
|
||||||
|
// 6, slice_type 0, 2, 3, 4, 5, 7, 8, 9
|
||||||
|
// 7, slice_type 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
// 7.4.2.4 Access unit delimiter RBSP semantics
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 102.
|
||||||
|
//
|
||||||
|
// slice_type specifies the coding type of the slice according to Table 7-6.
|
||||||
|
// 0, P (P slice)
|
||||||
|
// 1, B (B slice)
|
||||||
|
// 2, I (I slice)
|
||||||
|
// 3, SP (SP slice)
|
||||||
|
// 4, SI (SI slice)
|
||||||
|
// 5, P (P slice)
|
||||||
|
// 6, B (B slice)
|
||||||
|
// 7, I (I slice)
|
||||||
|
// 8, SP (SP slice)
|
||||||
|
// 9, SI (SI slice)
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 105.
|
||||||
|
static u_int8_t aud_nalu_7[] = { 0x09, 0xf0};
|
||||||
|
video->payload->append((const char*)fresh_nalu_header, 4);
|
||||||
|
video->payload->append((const char*)aud_nalu_7, 2);
|
||||||
|
|
||||||
|
// when ts message(samples) contains IDR, insert sps+pps.
|
||||||
|
if (sample->has_idr) {
|
||||||
|
// fresh nalu header before sps.
|
||||||
|
if (codec->sequenceParameterSetLength > 0) {
|
||||||
|
// AnnexB prefix, for sps always 4 bytes header
|
||||||
|
video->payload->append((const char*)fresh_nalu_header, 4);
|
||||||
|
// sps
|
||||||
|
video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength);
|
||||||
|
}
|
||||||
|
// cont nalu header before pps.
|
||||||
|
if (codec->pictureParameterSetLength > 0) {
|
||||||
|
// AnnexB prefix, for pps always 3 bytes header
|
||||||
|
video->payload->append((const char*)cont_nalu_header, 3);
|
||||||
|
// pps
|
||||||
|
video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all sample use cont nalu header, except the sps-pps before IDR frame.
|
||||||
for (int i = 0; i < sample->nb_sample_units; i++) {
|
for (int i = 0; i < sample->nb_sample_units; i++) {
|
||||||
SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
|
SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
|
||||||
int32_t size = sample_unit->size;
|
int32_t size = sample_unit->size;
|
||||||
|
@ -2858,83 +2943,22 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* step 1:
|
|
||||||
* first, before each "real" sample,
|
|
||||||
* we add some packets according to the nal_unit_type,
|
|
||||||
* for example, when got nal_unit_type=5, insert SPS/PPS before sample.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 5bits, 7.3.1 NAL unit syntax,
|
// 5bits, 7.3.1 NAL unit syntax,
|
||||||
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
|
||||||
u_int8_t nal_unit_type;
|
SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(sample_unit->bytes[0] & 0x1f);
|
||||||
nal_unit_type = *sample_unit->bytes;
|
|
||||||
nal_unit_type &= 0x1f;
|
|
||||||
|
|
||||||
// @see: ngx_rtmp_hls_video
|
// ignore SPS/PPS/AUD
|
||||||
// Table 7-1 NAL unit type codes, page 61
|
switch (nal_unit_type) {
|
||||||
// 1: Coded slice
|
case SrsAvcNaluTypeSPS:
|
||||||
if (nal_unit_type == 1) {
|
case SrsAvcNaluTypePPS:
|
||||||
sps_pps_sent = false;
|
case SrsAvcNaluTypeAccessUnitDelimiter:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61
|
// insert cont nalu header before frame.
|
||||||
// @see: ngx_rtmp_hls_append_aud
|
video->payload->append((const char*)cont_nalu_header, 3);
|
||||||
if (!aud_sent) {
|
|
||||||
// @remark, when got type 9, we donot send aud_nal, but it will make
|
|
||||||
// ios unhappy, so we remove it.
|
|
||||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/281
|
|
||||||
/*if (nal_unit_type == 9) {
|
|
||||||
aud_sent = true;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (nal_unit_type == 1 || nal_unit_type == 5 || nal_unit_type == 6) {
|
|
||||||
// for type 6, append a aud with type 9.
|
|
||||||
video->payload->append((const char*)aud_nal, sizeof(aud_nal));
|
|
||||||
aud_sent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5: Coded slice of an IDR picture.
|
|
||||||
// insert sps/pps before IDR or key frame is ok.
|
|
||||||
if (nal_unit_type == 5 && !sps_pps_sent) {
|
|
||||||
sps_pps_sent = true;
|
|
||||||
|
|
||||||
// @see: ngx_rtmp_hls_append_sps_pps
|
|
||||||
if (codec->sequenceParameterSetLength > 0) {
|
|
||||||
// AnnexB prefix, for sps always 4 bytes header
|
|
||||||
video->payload->append((const char*)aud_nal, 4);
|
|
||||||
// sps
|
|
||||||
video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength);
|
|
||||||
}
|
|
||||||
if (codec->pictureParameterSetLength > 0) {
|
|
||||||
// AnnexB prefix, for pps always 4 bytes header
|
|
||||||
video->payload->append((const char*)aud_nal, 4);
|
|
||||||
// pps
|
|
||||||
video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7-9, ignore, @see: ngx_rtmp_hls_video
|
|
||||||
if (nal_unit_type >= 7 && nal_unit_type <= 9) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* step 2:
|
|
||||||
* output the "real" sample, in buf.
|
|
||||||
* when we output some special assist packets according to nal_unit_type
|
|
||||||
*/
|
|
||||||
|
|
||||||
// sample start prefix, '00 00 00 01' or '00 00 01'
|
|
||||||
u_int8_t* p = aud_nal + 1;
|
|
||||||
u_int8_t* end = p + 3;
|
|
||||||
|
|
||||||
// first AnnexB prefix is long (4 bytes)
|
|
||||||
if (video->payload->length() == 0) {
|
|
||||||
p = aud_nal;
|
|
||||||
}
|
|
||||||
video->payload->append((const char*)p, end - p);
|
|
||||||
|
|
||||||
// sample data
|
// sample data
|
||||||
video->payload->append(sample_unit->bytes, sample_unit->size);
|
video->payload->append(sample_unit->bytes, sample_unit->size);
|
||||||
|
|
|
@ -46,6 +46,53 @@ using namespace std;
|
||||||
// @see SRS_SYS_TIME_RESOLUTION_MS_TIMES
|
// @see SRS_SYS_TIME_RESOLUTION_MS_TIMES
|
||||||
#define SYS_TIME_RESOLUTION_US 300*1000
|
#define SYS_TIME_RESOLUTION_US 300*1000
|
||||||
|
|
||||||
|
int srs_avc_nalu_read_uev(SrsBitStream* stream, int64_t& v)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (stream->empty()) {
|
||||||
|
return ERROR_AVC_NALU_UEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ue(v) in 9.1 Parsing process for Exp-Golomb codes
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 227.
|
||||||
|
// Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded.
|
||||||
|
// leadingZeroBits = -1;
|
||||||
|
// for( b = 0; !b; leadingZeroBits++ )
|
||||||
|
// b = read_bits( 1 )
|
||||||
|
// The variable codeNum is then assigned as follows:
|
||||||
|
// codeNum = (2<<leadingZeroBits) – 1 + read_bits( leadingZeroBits )
|
||||||
|
int leadingZeroBits = -1;
|
||||||
|
for (int8_t b = 0; !b && !stream->empty(); leadingZeroBits++) {
|
||||||
|
b = stream->read_bit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leadingZeroBits >= 64) {
|
||||||
|
return ERROR_AVC_NALU_UEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = (1 << leadingZeroBits) - 1;
|
||||||
|
for (int i = 0; i < leadingZeroBits; i++) {
|
||||||
|
int64_t b = stream->read_bit();
|
||||||
|
v += b << (leadingZeroBits - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_avc_nalu_read_bit(SrsBitStream* stream, int8_t& v)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (stream->empty()) {
|
||||||
|
return ERROR_AVC_NALU_UEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = stream->read_bit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t _srs_system_time_us_cache = 0;
|
static int64_t _srs_system_time_us_cache = 0;
|
||||||
static int64_t _srs_system_time_startup_time = 0;
|
static int64_t _srs_system_time_startup_time = 0;
|
||||||
|
|
||||||
|
@ -294,6 +341,21 @@ bool srs_path_exists(std::string path)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string srs_path_dirname(string path)
|
||||||
|
{
|
||||||
|
std::string dirname = path;
|
||||||
|
size_t pos = string::npos;
|
||||||
|
|
||||||
|
if ((pos = dirname.rfind("/")) != string::npos) {
|
||||||
|
if (pos == 0) {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
dirname = dirname.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirname;
|
||||||
|
}
|
||||||
|
|
||||||
bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
|
bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
|
||||||
{
|
{
|
||||||
char* bytes = stream->data() + stream->pos();
|
char* bytes = stream->data() + stream->pos();
|
||||||
|
|
|
@ -33,11 +33,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class SrsStream;
|
class SrsStream;
|
||||||
|
class SrsBitStream;
|
||||||
|
|
||||||
// compare
|
// compare
|
||||||
#define srs_min(a, b) (((a) < (b))? (a) : (b))
|
#define srs_min(a, b) (((a) < (b))? (a) : (b))
|
||||||
#define srs_max(a, b) (((a) < (b))? (b) : (a))
|
#define srs_max(a, b) (((a) < (b))? (b) : (a))
|
||||||
|
|
||||||
|
// read nalu uev.
|
||||||
|
extern int srs_avc_nalu_read_uev(SrsBitStream* stream, int64_t& v);
|
||||||
|
extern int srs_avc_nalu_read_bit(SrsBitStream* stream, int8_t& v);
|
||||||
|
|
||||||
// get current system time in ms, use cache to avoid performance problem
|
// get current system time in ms, use cache to avoid performance problem
|
||||||
extern int64_t srs_get_system_time_ms();
|
extern int64_t srs_get_system_time_ms();
|
||||||
extern int64_t srs_get_system_startup_time_ms();
|
extern int64_t srs_get_system_startup_time_ms();
|
||||||
|
@ -68,6 +73,8 @@ extern int srs_create_dir_recursively(std::string dir);
|
||||||
|
|
||||||
// whether path exists.
|
// whether path exists.
|
||||||
extern bool srs_path_exists(std::string path);
|
extern bool srs_path_exists(std::string path);
|
||||||
|
// get the dirname of path
|
||||||
|
extern std::string srs_path_dirname(std::string path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* whether stream starts with the avc NALU in "AnnexB"
|
* whether stream starts with the avc NALU in "AnnexB"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue