diff --git a/README.md b/README.md index 76aed8b2f..5065d2be8 100755 --- a/README.md +++ b/README.md @@ -566,7 +566,9 @@ Supported operating systems and hardware: ### 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-31, support server cycle handler. 2.0.153. * v2.0, 2015-03-31, support on_hls for http hooks. 2.0.152. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 9d106768a..491250669 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -44,6 +44,11 @@ max_connections 1000; # @remark: donot support reload. # default: 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 diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 96c84fdcf..8193a3be2 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1336,7 +1336,8 @@ int SrsConfig::check_config() && n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file" && n != "max_connections" && n != "daemon" && n != "heartbeat" && 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; 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()); } +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 SrsConfig::get_stream_casters() { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 1df56679d..025e6e005 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -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_COFNIG_FILE "conf/srs.conf" #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_HLS_PATH "./objs/nginx/html" @@ -435,6 +436,10 @@ public: * every this interval in ms. */ virtual int get_pithy_print_ms(); + /** + * whether use utc-time to format the time. + */ + virtual bool get_utc_time(); // stream_caster section public: /** diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 8a8b1a237..57e77153a 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -311,12 +311,6 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, // generate the m3u8 dir and path. m3u8 = path + "/" + m3u8_file; 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. int max_td = srs_max(target_duration, (int)(fragment * _srs_config->get_hls_td_ratio(r->vhost))); @@ -336,6 +330,14 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, 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; } @@ -434,11 +436,12 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) current->uri += ts_url; // create dir recursively for hls. - 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); + std::string ts_dir = srs_path_dirname(current->full_path); + 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; } - 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. std::string tmp_file = current->full_path + ".tmp"; @@ -751,7 +754,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) // "#EXTINF:4294967295.208,\n" ss.precision(3); 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."); // {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 // @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184 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) { return ret; } @@ -914,9 +918,12 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t } // new segment when: - // 1. base on gop. + // 1. base on gop(IDR). // 2. some gops duration 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) { return ret; } @@ -935,26 +942,28 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t int SrsHlsCache::reap_segment(string log_desc, SrsHlsMuxer* muxer, int64_t segment_start_dts) { 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) { srs_error("m3u8 muxer close segment failed. ret=%d", ret); return ret; } + // open new ts. if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) { srs_error("m3u8 muxer open segment failed. ret=%d", ret); return ret; } - - // TODO: flush audio before or after segment? - // TODO: fresh segment begin with audio or video? // segment open, flush video first. if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) { srs_error("m3u8 muxer flush video failed. ret=%d", ret); return ret; } - + // segment open, flush the audio. // @see: ngx_rtmp_hls_open_fragment /* start fragment with audio to make iPhone happy */ diff --git a/trunk/src/app/srs_app_log.cpp b/trunk/src/app/srs_app_log.cpp index cbd4a3368..798450213 100644 --- a/trunk/src/app/srs_app_log.cpp +++ b/trunk/src/app/srs_app_log.cpp @@ -274,8 +274,14 @@ bool SrsFastLog::generate_header(bool error, const char* tag, int context_id, co // to calendar time struct tm* tm; - if ((tm = localtime(&tv.tv_sec)) == NULL) { - return false; + if (_srs_config->get_utc_time()) { + if ((tm = gmtime(&tv.tv_sec)) == NULL) { + return false; + } + } else { + if ((tm = localtime(&tv.tv_sec)) == NULL) { + return false; + } } // write log header diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 9e93a4601..854656891 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -140,8 +140,14 @@ string srs_path_build_timestamp(string template_path) // to calendar time struct tm* tm; - if ((tm = localtime(&tv.tv_sec)) == NULL) { - return path; + if (_srs_config->get_utc_time()) { + 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. @@ -154,32 +160,32 @@ string srs_path_build_timestamp(string template_path) } // [2006], replace with current year. 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); } // [01], replace this const to current month. 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); } // [02], replace this const to current date. 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); } // [15], replace this const to current hour. 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); } // [04], repleace this const to current minute. 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); } // [05], repleace this const to current second. 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); } // [999], repleace this const to current millisecond. diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index f695b4f49..640b4db1a 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -31,6 +31,7 @@ using namespace std; #include #include #include +#include string srs_codec_video2str(SrsCodecVideo codec) { @@ -265,6 +266,30 @@ bool SrsFlvCodec::audio_is_aac(char* data, int size) 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() { size = 0; @@ -292,6 +317,8 @@ void SrsCodecSample::clear() cts = 0; frame_type = SrsCodecVideoAVCFrameReserved; avc_packet_type = SrsCodecVideoAVCTypeReserved; + has_idr = false; + first_nalu_type = SrsAvcNaluTypeReserved; acodec = SrsCodecAudioReserved1; sound_rate = SrsCodecAudioSampleRateReserved; @@ -315,6 +342,19 @@ int SrsCodecSample::add_sample_unit(char* bytes, int size) sample_unit->bytes = bytes; 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; } @@ -713,7 +753,8 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) 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)) { ret = ERROR_HLS_DECODE_ERROR; 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) { srs_freep(sequenceParameterSetNALUnit); sequenceParameterSetNALUnit = new char[sequenceParameterSetLength]; - memcpy(sequenceParameterSetNALUnit, stream->data() + stream->pos(), sequenceParameterSetLength); - stream->skip(sequenceParameterSetLength); + stream->read_bytes(sequenceParameterSetNALUnit, sequenceParameterSetLength); } // 1 pps if (!stream->require(1)) { @@ -770,10 +810,242 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) if (pictureParameterSetLength > 0) { srs_freep(pictureParameterSetNALUnit); pictureParameterSetNALUnit = new char[pictureParameterSetLength]; - memcpy(pictureParameterSetNALUnit, stream->data() + stream->pos(), pictureParameterSetLength); - stream->skip(pictureParameterSetLength); + stream->read_bytes(pictureParameterSetNALUnit, 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; } diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index b619bb9e1..4df7596bd 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -275,6 +275,52 @@ enum SrsCodecAudioSoundType 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. * for h.264 video packet, a NALU is a sample unit. @@ -334,6 +380,9 @@ public: // video specified SrsCodecVideoAVCFrame frame_type; SrsCodecVideoAVCType avc_packet_type; + // whether sample_units contains IDR frame. + bool has_idr; + SrsAvcNaluType first_nalu_type; public: // audio specified SrsCodecAudio acodec; @@ -580,6 +629,11 @@ private: * decode the sps and pps. */ 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" * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 8d87eae1f..1b142dae7 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -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_NO_TAEGET 3054 #define ERROR_ADTS_ID_NOT_AAC 3055 - // HDS error code #define ERROR_HDS_OPEN_F4M_FAILED 3056 #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_HTTP_RESPONSE_EOF 4025 #define ERROR_HTTP_INVALID_CHUNK_HEADER 4026 +#define ERROR_AVC_NALU_UEV 4027 /////////////////////////////////////////////////////// // user-define error. diff --git a/trunk/src/kernel/srs_kernel_stream.cpp b/trunk/src/kernel/srs_kernel_stream.cpp index cacfafd11..eaf70a2f2 100644 --- a/trunk/src/kernel/srs_kernel_stream.cpp +++ b/trunk/src/kernel/srs_kernel_stream.cpp @@ -252,4 +252,38 @@ void SrsStream::write_bytes(char* data, int 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; +} diff --git a/trunk/src/kernel/srs_kernel_stream.hpp b/trunk/src/kernel/srs_kernel_stream.hpp index 937e28f97..a708bb35e 100644 --- a/trunk/src/kernel/srs_kernel_stream.hpp +++ b/trunk/src/kernel/srs_kernel_stream.hpp @@ -154,4 +154,22 @@ public: 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 diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 856482a24..b813710ad 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -2835,19 +2835,104 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) { int ret = ERROR_SUCCESS; - // for type1/5/6, insert aud packet. - u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; - - bool sps_pps_sent = false; - bool aud_sent = false; + // mux the samples in annexb format, + // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324. /** - * a ts sample is format as: - * 00 00 00 01 // header - * xxxxxxx // data bytes - * 00 00 01 // continue header - * xxxxxxx // data bytes. - * so, for each sample, we append header in aud_nal, then appends the bytes in sample. - */ + * 00 00 00 01 // header + * xxxxxxx // data bytes + * 00 00 01 // continue header + * xxxxxxx // data bytes. + * + * 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++) { SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; int32_t size = sample_unit->size; @@ -2858,83 +2943,22 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) 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, + // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83. + SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(sample_unit->bytes[0] & 0x1f); - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - u_int8_t nal_unit_type; - nal_unit_type = *sample_unit->bytes; - nal_unit_type &= 0x1f; - - // @see: ngx_rtmp_hls_video - // Table 7-1 NAL unit type codes, page 61 - // 1: Coded slice - if (nal_unit_type == 1) { - sps_pps_sent = false; + // ignore SPS/PPS/AUD + switch (nal_unit_type) { + case SrsAvcNaluTypeSPS: + case SrsAvcNaluTypePPS: + case SrsAvcNaluTypeAccessUnitDelimiter: + continue; + default: + break; } - // 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61 - // @see: ngx_rtmp_hls_append_aud - 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); + // insert cont nalu header before frame. + video->payload->append((const char*)cont_nalu_header, 3); // sample data video->payload->append(sample_unit->bytes, sample_unit->size); diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 82061b60c..6e1a50b90 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -46,6 +46,53 @@ using namespace std; // @see SRS_SYS_TIME_RESOLUTION_MS_TIMES #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<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_startup_time = 0; @@ -294,6 +341,21 @@ bool srs_path_exists(std::string path) 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) { char* bytes = stream->data() + stream->pos(); diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 5269703fe..a23474afa 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -33,11 +33,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class SrsStream; +class SrsBitStream; // compare #define srs_min(a, b) (((a) < (b))? (a) : (b)) #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 extern int64_t srs_get_system_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. 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"