From 0cf74425f9ab503722ef70dcc6895585b48a6535 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 11 Jun 2014 15:47:20 +0200 Subject: [PATCH] Implemented various TS-related library functions. --- lib/bitstream.h | 4 + lib/nal.cpp | 561 +++++++++++++++++++++--- lib/nal.h | 54 ++- lib/ts_packet.cpp | 1034 ++++++++++++++++++++++++++++++--------------- lib/ts_packet.h | 98 ++++- 5 files changed, 1325 insertions(+), 426 deletions(-) mode change 100644 => 100755 lib/nal.cpp mode change 100644 => 100755 lib/nal.h mode change 100644 => 100755 lib/ts_packet.cpp mode change 100644 => 100755 lib/ts_packet.h diff --git a/lib/bitstream.h b/lib/bitstream.h index 37abffdb..8fb47103 100644 --- a/lib/bitstream.h +++ b/lib/bitstream.h @@ -8,6 +8,10 @@ namespace Utils{ append(input); return *this; }; + bitstream& operator<< (char input){ + append(std::string(input, 1)); + return *this; + }; void append (char* input, size_t bytes); void append (std::string input); long long unsigned int size(); diff --git a/lib/nal.cpp b/lib/nal.cpp old mode 100644 new mode 100755 index 975dd8fa..e276f026 --- a/lib/nal.cpp +++ b/lib/nal.cpp @@ -1,82 +1,507 @@ #include "nal.h" +#include "bitstream.h" +#include "defines.h" +#include +#include +#include //for log +namespace h264 { -NAL_Unit::NAL_Unit(){ + ///empty constructor of NAL + NAL::NAL() { -} - -NAL_Unit::NAL_Unit(std::string & InputData){ - ReadData(InputData); -} - -bool NAL_Unit::ReadData(std::string & InputData){ - std::string FullAnnexB; - FullAnnexB += (char)0x00; - FullAnnexB += (char)0x00; - FullAnnexB += (char)0x00; - FullAnnexB += (char)0x01; - std::string ShortAnnexB; - ShortAnnexB += (char)0x00; - ShortAnnexB += (char)0x00; - ShortAnnexB += (char)0x01; - if (InputData.size() < 3){ - return false; } - bool AnnexB = false; - if (InputData.substr(0, 3) == ShortAnnexB){ - AnnexB = true; + + ///Constructor capable of directly inputting NAL data from a string + ///\param InputData the nal input data, as a string + NAL::NAL(std::string & InputData) { + ReadData(InputData); } - if (InputData.substr(0, 4) == FullAnnexB){ - InputData.erase(0, 1); - AnnexB = true; + + ///Gets the raw NAL unit data, as a string + ///\return the raw NAL unit data + std::string NAL::getData() { + return MyData; } - if (AnnexB){ - MyData = ""; - InputData.erase(0, 3); //Intro Bytes - int Location = std::min(InputData.find(ShortAnnexB), InputData.find(FullAnnexB)); - MyData = InputData.substr(0, Location); - InputData.erase(0, Location); - }else{ - if (InputData.size() < 4){ + + ///Reads data from a string,either raw or annexed + ///\param InputData the h264 data, as string + ///\param raw a boolean that determines whether the string contains raw h264 data or not + bool NAL::ReadData(std::string & InputData, bool raw) { + if (raw) { + MyData = InputData; + InputData.clear(); + return true; + } + std::string FullAnnexB; + FullAnnexB += (char)0x00; + FullAnnexB += (char)0x00; + FullAnnexB += (char)0x00; + FullAnnexB += (char)0x01; + std::string ShortAnnexB; + ShortAnnexB += (char)0x00; + ShortAnnexB += (char)0x00; + ShortAnnexB += (char)0x01; + if (InputData.size() < 3) { + //DEBUG_MSG(DLVL_DEVEL, "fal1"); return false; } - unsigned int UnitLen = (InputData[0] << 24) + (InputData[1] << 16) + (InputData[2] << 8) + InputData[3]; - if (InputData.size() < 4 + UnitLen){ - return false; - } - InputData.erase(0, 4); //Remove Length - MyData = InputData.substr(0, UnitLen); - InputData.erase(0, UnitLen); //Remove this unit from the string - } - return true; -} + bool AnnexB = false; + if (InputData.substr(0, 3) == ShortAnnexB) { -std::string NAL_Unit::AnnexB(bool LongIntro){ - std::string Result; - if (MyData.size()){ - if (LongIntro){ + AnnexB = true; + } + if (InputData.substr(0, 4) == FullAnnexB) { + InputData.erase(0, 1); + AnnexB = true; + } + if (AnnexB) { + MyData = ""; + InputData.erase(0, 3); //Intro Bytes + int Location = std::min(InputData.find(ShortAnnexB), InputData.find(FullAnnexB)); + MyData = InputData.substr(0, Location); + InputData.erase(0, Location); + } else { + if (InputData.size() < 4) { + DEBUG_MSG(DLVL_DEVEL, "fal2"); + return false; + } + unsigned int UnitLen = (InputData[0] << 24) + (InputData[1] << 16) + (InputData[2] << 8) + InputData[3]; + if (InputData.size() < 4 + UnitLen) { + DEBUG_MSG(DLVL_DEVEL, "fal3"); + return false; + } + InputData.erase(0, 4); //Remove Length + MyData = InputData.substr(0, UnitLen); + InputData.erase(0, UnitLen); //Remove this unit from the string + + + } + //DEBUG_MSG(DLVL_DEVEL, "tru"); + return true; + } + + ///Returns an annex B prefix + ///\param LongIntro determines whether it is a short or long annex B + ///\return the desired annex B prefix + std::string NAL::AnnexB(bool LongIntro) { + std::string Result; + if (MyData.size()) { + if (LongIntro) { + Result += (char)0x00; + } Result += (char)0x00; + Result += (char)0x00; + Result += (char)0x01; //Annex B Lead-In + Result += MyData; } - Result += (char)0x00; - Result += (char)0x00; - Result += (char)0x01; //Annex B Lead-In - Result += MyData; + return Result; } - return Result; + + ///Returns raw h264 data as Size Prepended + ///\return the h264 data as Size prepended + std::string NAL::SizePrepended() { + std::string Result; + if (MyData.size()) { + int DataSize = MyData.size(); + Result += (char)((DataSize & 0xFF000000) >> 24); + Result += (char)((DataSize & 0x00FF0000) >> 16); + Result += (char)((DataSize & 0x0000FF00) >> 8); + Result += (char)(DataSize & 0x000000FF); //Size Lead-In + Result += MyData; + } + return Result; + } + + ///returns the nal unit type + ///\return the nal unit type + int NAL::Type() { + return (MyData[0] & 0x1F); + } + + SPS::SPS(std::string & input, bool raw) : NAL() { + ReadData(input, raw); + } + + ///computes SPS data from an SPS nal unit, and saves them in a useful + ///more human-readable format in the parameter spsmeta + ///The function is based on the analyzeSPS() function. If more data needs to be stored in sps meta, + ///refer to that function to determine which variable comes at which place (as all couts have been removed). + ///\param spsmeta the sps metadata, in which data from the sps is stored + ///\todo some h264 sps data types are not supported (due to them containing matrixes and have never been encountered in practice). If needed, these need to be implemented + SPSMeta SPS::getCharacteristics() { + SPSMeta result; + + //For calculating width + unsigned int widthInMbs = 0; + unsigned int cropHorizontal = 0; + + //For calculating height + bool mbsOnlyFlag = 0; + unsigned int heightInMapUnits = 0; + unsigned int cropVertical = 0; + + Utils::bitstream bs; + for (unsigned int i = 1; i < MyData.size(); i++) { + if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) { + bs << MyData.substr(i, 2); + i += 2; + } else { + bs << MyData.substr(i, 1); + } + } + + char profileIdc = bs.get(8); + //Start skipping unused data + bs.skip(16); + bs.getUExpGolomb(); + if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) { + //chroma format idc + if (bs.getUExpGolomb() == 3) { + bs.skip(1); + } + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.skip(1); + if (bs.get(1)) { + DEBUG_MSG(DLVL_DEVEL, "Scaling matrix not implemented yet"); + } + } + bs.getUExpGolomb(); + unsigned int pic_order_cnt_type = bs.getUExpGolomb(); + if (!pic_order_cnt_type) { + bs.getUExpGolomb(); + } else if (pic_order_cnt_type == 1) { + DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately."); + } + bs.getUExpGolomb(); + bs.skip(1); + //Stop skipping data and start doing usefull stuff + + + widthInMbs = bs.getUExpGolomb() + 1; + heightInMapUnits = bs.getUExpGolomb() + 1; + + mbsOnlyFlag = bs.get(1);//Gets used in height calculation + if (!mbsOnlyFlag) { + bs.skip(1); + } + bs.skip(1); + //cropping flag + if (bs.get(1)) { + cropHorizontal = bs.getUExpGolomb();//leftOffset + cropHorizontal += bs.getUExpGolomb();//rightOffset + cropVertical = bs.getUExpGolomb();//topOffset + cropVertical += bs.getUExpGolomb();//bottomOffset + } + + //vuiParameters + if (bs.get(1)) { + //Skipping all the paramters we dont use + if (bs.get(1)){ + if (bs.get(8) == 255) { + bs.skip(32); + } + } + if (bs.get(1)) { + bs.skip(1); + } + if (bs.get(1)) { + bs.skip(4); + if (bs.get(1)) { + bs.skip(24); + } + } + if (bs.get(1)) { + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + + //Decode timing info + if (bs.get(1)) { + unsigned int unitsInTick = bs.get(32); + unsigned int timeScale = bs.get(32); + result.fps = (double)timeScale / (2 * unitsInTick); + bs.skip(1); + } + } + + result.width = (widthInMbs * 16) - (cropHorizontal * 2); + result.height = ((mbsOnlyFlag ? 1 : 2) * heightInMapUnits * 16) - (cropVertical * 2); + return result; + } + + ///Analyzes an SPS nal unit, and prints the values of all existing fields + ///\todo some h264 sps data types are not supported (due to them containing matrixes and have not yet been encountered in practice). If needed, these need to be implemented + void SPS::analyzeSPS() { + if (Type() != 7) { + DEBUG_MSG(DLVL_DEVEL, "This is not an SPS, but type %d", Type()); + return; + } + Utils::bitstream bs; + //put rbsp bytes in mydata + for (unsigned int i = 1; i < MyData.size(); i++) { + //DEBUG_MSG(DLVL_DEVEL, "position %u out of %lu",i,MyData.size()); + if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) { + bs << MyData.substr(i, 2); + //DEBUG_MSG(DLVL_DEVEL, "0x000003 encountered at i = %u",i); + i += 2; + } else { + bs << MyData.substr(i, 1); + } + } + //bs contains all rbsp bytes, now we can analyze them + std::cout << "seq_parameter_set_data()" << std::endl; + std::cout << std::hex << std::setfill('0') << std::setw(2); + char profileIdc = bs.get(8); + std::cout << "profile idc: " << (unsigned int) profileIdc << std::endl; + std::cout << "constraint_set0_flag: " << bs.get(1) << std::endl; + std::cout << "constraint_set1_flag: " << bs.get(1) << std::endl; + std::cout << "constraint_set2_flag: " << bs.get(1) << std::endl; + std::cout << "constraint_set3_flag: " << bs.get(1) << std::endl; + std::cout << "constraint_set4_flag: " << bs.get(1) << std::endl; + std::cout << "constraint_set5_flag: " << bs.get(1) << std::endl; + std::cout << "reserved_zero_2bits: " << bs.get(2) << std::endl; + std::cout << "level idc: " << bs.get(8) << std::endl; + std::cout << "seq_parameter_set_id: " << bs.getUExpGolomb() << std::endl; + if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) { + chroma_format_idc = bs.getUExpGolomb(); + std::cout << "chroma_format_idc: " << chroma_format_idc << std::endl; + if (chroma_format_idc == 3) { + std::cout << "separate_colour_plane_flag" << bs.get(1) << std::endl; + } + std::cout << "bit_depth_luma_minus8: " << bs.getUExpGolomb() << std::endl; + std::cout << "bit_depth_chroma_minus8: " << bs.getUExpGolomb() << std::endl; + std::cout << "qpprime_y_zero_transform_bypass_flag: " << bs.get(1) << std::endl; + unsigned int seq_scaling_matrix_present_flag = bs.get(1); + std::cout << "seq_scaling_matrix_present_flag: " << seq_scaling_matrix_present_flag << std::endl; + if (seq_scaling_matrix_present_flag) { + for (unsigned int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12) ; i++) { + unsigned int seq_scaling_list_present_flag = bs.get(1); + std::cout << "seq_scaling_list_present_flag: " << seq_scaling_list_present_flag << std::endl; + DEBUG_MSG(DLVL_DEVEL, "not implemented, ending"); + return; + if (seq_scaling_list_present_flag) { + //LevelScale4x4( m, i, j ) = weightScale4x4( i, j ) * normAdjust4x4( m, i, j ) + // + if (i < 6) { + //scaling)list(ScalingList4x4[i],16,UseDefaultScalingMatrix4x4Flag[i] + } else { + //scaling)list(ScalingList4x4[i-6],64,UseDefaultScalingMatrix4x4Flag[i-6] + + } + } + } + } + } + std::cout << "log2_max_frame_num_minus4: " << bs.getUExpGolomb() << std::endl; + unsigned int pic_order_cnt_type = bs.getUExpGolomb(); + std::cout << "pic_order_cnt_type: " << pic_order_cnt_type << std::endl; + if (pic_order_cnt_type == 0) { + std::cout << "log2_max_pic_order_cnt_lsb_minus4: " << bs.getUExpGolomb() << std::endl; + } else if (pic_order_cnt_type == 1) { + DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately."); + return; + } + std::cout << "max_num_ref_frames: " << bs.getUExpGolomb() << std::endl; + std::cout << "gaps_in_frame_num_allowed_flag: " << bs.get(1) << std::endl; + std::cout << "pic_width_in_mbs_minus1: " << bs.getUExpGolomb() << std::endl; + std::cout << "pic_height_in_map_units_minus1: " << bs.getUExpGolomb() << std::endl; + unsigned int frame_mbs_only_flag = bs.get(1); + std::cout << "frame_mbs_only_flag: " << frame_mbs_only_flag << std::endl; + if (frame_mbs_only_flag == 0) { + std::cout << "mb_adaptive_frame_field_flag: " << bs.get(1) << std::endl; + } + std::cout << "direct_8x8_inference_flag: " << bs.get(1) << std::endl; + unsigned int frame_cropping_flag = bs.get(1); + std::cout << "frame_cropping_flag: " << frame_cropping_flag << std::endl; + if (frame_cropping_flag != 0) { + std::cout << "frame_crop_left_offset: " << bs.getUExpGolomb() << std::endl; + std::cout << "frame_crop_right_offset: " << bs.getUExpGolomb() << std::endl; + std::cout << "frame_crop_top_offset: " << bs.getUExpGolomb() << std::endl; + std::cout << "frame_crop_bottom_offset: " << bs.getUExpGolomb() << std::endl; + } + unsigned int vui_parameters_present_flag = bs.get(1); + std::cout << "vui_parameters_present_flag: " << vui_parameters_present_flag << std::endl; + if (vui_parameters_present_flag != 0) { + //vuiParameters + unsigned int aspect_ratio_info_present_flag = bs.get(1); + std::cout << "aspect_ratio_info_present_flag: " << aspect_ratio_info_present_flag << std::endl; + if (aspect_ratio_info_present_flag != 0) { + unsigned int aspect_ratio_idc = bs.get(8); + std::cout << "aspect_ratio_idc: " << aspect_ratio_idc << std::endl; + if (aspect_ratio_idc == 255) { + std::cout << "sar_width: " << bs.get(16) << std::endl; + std::cout << "sar_height: " << bs.get(16) << std::endl; + } + } + unsigned int overscan_info_present_flag = bs.get(1); + std::cout << "overscan_info_present_flag: " << overscan_info_present_flag << std::endl; + if (overscan_info_present_flag) { + std::cout << "overscan_appropriate_flag: " << bs.get(1) << std::endl; + } + + unsigned int video_signal_type_present_flag = bs.get(1); + std::cout << "video_signal_type_present_flag: " << video_signal_type_present_flag << std::endl; + if (video_signal_type_present_flag) { + std::cout << "video_format: " << bs.get(3) << std::endl; + std::cout << "video_full_range_flag: " << bs.get(1) << std::endl; + unsigned int colour_description_present_flag = bs.get(1); + std::cout << "colour_description_present_flag: " << colour_description_present_flag << std::endl; + if (colour_description_present_flag) { + std::cout << "colour_primaries: " << bs.get(8) << std::endl; + std::cout << "transfer_characteristics: " << bs.get(8) << std::endl; + std::cout << "matrix_coefficients: " << bs.get(8) << std::endl; + } + } + unsigned int chroma_loc_info_present_flag = bs.get(1); + std::cout << "chroma_loc_info_present_flag: " << chroma_loc_info_present_flag << std::endl; + if (chroma_loc_info_present_flag) { + std::cout << "chroma_sample_loc_type_top_field: " << bs.getUExpGolomb() << std::endl; + std::cout << "chroma_sample_loc_type_bottom_field: " << bs.getUExpGolomb() << std::endl; + } + unsigned int timing_info_present_flag = bs.get(1); + std::cout << "timing_info_present_flag: " << timing_info_present_flag << std::endl; + if (timing_info_present_flag) { + std::cout << "num_units_in_tick: " << bs.get(32) << std::endl; + std::cout << "time_scale: " << bs.get(32) << std::endl; + std::cout << "fixed_frame_rate_flag: " << bs.get(1) << std::endl; + } + unsigned int nal_hrd_parameters_present_flag = bs.get(1); + std::cout << "nal_hrd_parameters_present_flag: " << nal_hrd_parameters_present_flag << std::endl; + if (nal_hrd_parameters_present_flag) { + unsigned int cpb_cnt_minus1 = bs.getUExpGolomb(); + std::cout << "cpb_cnt_minus1: " << cpb_cnt_minus1 << std::endl; + std::cout << "bit_rate_scale: " << bs.get(4) << std::endl; + std::cout << "cpb_rate_scale: " << bs.get(4) << std::endl; + for (unsigned int ssi = 0; ssi <= cpb_cnt_minus1 ; ssi++) { + std::cout << "bit_rate_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl; + std::cout << "cpb_size_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl; + std::cout << "cbr_flag[" << ssi << "]: " << bs.get(1) << std::endl; + } + std::cout << "initial_cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "dpb_output_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "time_offset_length: " << bs.get(5) << std::endl; + } + unsigned int vcl_hrd_parameters_present_flag = bs.get(1); + std::cout << "vcl_hrd_parameters_present_flag: " << vcl_hrd_parameters_present_flag << std::endl; + if (vcl_hrd_parameters_present_flag) { + unsigned int cpb_cnt_minus1 = bs.getUExpGolomb(); + std::cout << "cpb_cnt_minus1: " << cpb_cnt_minus1 << std::endl; + std::cout << "bit_rate_scale: " << bs.get(4) << std::endl; + std::cout << "cpb_rate_scale: " << bs.get(4) << std::endl; + for (unsigned int ssi = 0; ssi <= cpb_cnt_minus1 ; ssi++) { + std::cout << "bit_rate_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl; + std::cout << "cpb_size_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl; + std::cout << "cbr_flag[" << ssi << "]: " << bs.get(1) << std::endl; + } + std::cout << "initial_cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "dpb_output_delay_length_minus1: " << bs.get(5) << std::endl; + std::cout << "time_offset_length: " << bs.get(5) << std::endl; + } + if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { + std::cout << "low_delay_hrd_flag: " << bs.get(1) << std::endl; + } + std::cout << "pic_struct_present_flag: " << bs.get(1) << std::endl; + unsigned int bitstream_restriction_flag = bs.get(1); + std::cout << "bitstream_restriction_flag: " << bitstream_restriction_flag << std::endl; + if (bitstream_restriction_flag) { + std::cout << "motion_vectors_over_pic_boundaries_flag: " << bs.get(1) << std::endl; + std::cout << "max_bytes_per_pic_denom: " << bs.getUExpGolomb() << std::endl; + std::cout << "max_bits_per_mb_denom: " << bs.getUExpGolomb() << std::endl; + std::cout << "log2_max_mv_length_horizontal: " << bs.getUExpGolomb() << std::endl; + std::cout << "log2_max_mv_length_vertical: " << bs.getUExpGolomb() << std::endl; + std::cout << "num_reorder_frames: " << bs.getUExpGolomb() << std::endl; + std::cout << "max_dec_frame_buffering: " << bs.getUExpGolomb() << std::endl; + } + } + std::cout << std::dec << std::endl; + //DEBUG_MSG(DLVL_DEVEL, "SPS analyser"); + } + + ///Prints the values of all the fields of a PPS nal unit in a human readable format. + ///\todo some features, including analyzable matrices, are not implemented. They were never encountered in practice, so far + void PPS::analyzePPS() { + if (Type() != 8) { + DEBUG_MSG(DLVL_DEVEL, "This is not a PPS, but type %d", Type()); + return; + } + Utils::bitstream bs; + //put rbsp bytes in mydata + for (unsigned int i = 1; i < MyData.size(); i++) { + if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) { + bs << MyData.substr(i, 2); + i += 2; + } else { + bs << MyData.substr(i, 1); + } + } + //bs contains all rbsp bytes, now we can analyze them + std::cout << "pic_parameter_set_id: " << bs.getUExpGolomb() << std::endl; + std::cout << "seq_parameter_set_id: " << bs.getUExpGolomb() << std::endl; + std::cout << "entropy_coding_mode_flag: " << bs.get(1) << std::endl; + std::cout << "bottom_field_pic_order_in_frame_present_flag: " << bs.get(1) << std::endl; + unsigned int num_slice_groups_minus1 = bs.getUExpGolomb(); + std::cout << "num_slice_groups_minus1: " << num_slice_groups_minus1 << std::endl; + if (num_slice_groups_minus1 > 0) { + unsigned int slice_group_map_type = bs.getUExpGolomb(); + std::cout << "slice_group_map_type: " << slice_group_map_type << std::endl; + if (slice_group_map_type == 0) { + for (unsigned int ig = 0; ig <= num_slice_groups_minus1; ig++) { + std::cout << "runlengthminus1[" << ig << "]: " << bs.getUExpGolomb() << std::endl; + } + } else if (slice_group_map_type == 2) { + for (unsigned int ig = 0; ig <= num_slice_groups_minus1; ig++) { + std::cout << "top_left[" << ig << "]: " << bs.getUExpGolomb() << std::endl; + std::cout << "bottom_right[" << ig << "]: " << bs.getUExpGolomb() << std::endl; + } + } else if (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5) { + std::cout << "slice_group_change_direction_flag: " << bs.get(1) << std::endl; + std::cout << "slice_group_change_rate_minus1: " << bs.getUExpGolomb() << std::endl; + } else if (slice_group_map_type == 6) { + unsigned int pic_size_in_map_units_minus1 = bs.getUExpGolomb(); + std::cout << "pic_size_in_map_units_minus1: " << pic_size_in_map_units_minus1 << std::endl; + for (unsigned int i = 0; i <= pic_size_in_map_units_minus1; i++) { + std::cout << "slice_group_id[" << i << "]: " << bs.get((unsigned int)(ceil(log(num_slice_groups_minus1 + 1) / log(2)))) << std::endl; + } + } + } + std::cout << "num_ref_idx_l0_default_active_minus1: " << bs.getUExpGolomb() << std::endl; + std::cout << "num_ref_idx_l1_default_active_minus1: " << bs.getUExpGolomb() << std::endl; + std::cout << "weighted_pred_flag: " << bs.get(1) << std::endl; + std::cout << "weighted_bipred_idc: " << bs.get(2) << std::endl; + std::cout << "pic_init_qp_minus26: " << bs.getExpGolomb() << std::endl; + std::cout << "pic_init_qs_minus26: " << bs.getExpGolomb() << std::endl; + std::cout << "chroma_qp_index_offset: " << bs.getExpGolomb() << std::endl; + std::cout << "deblocking_filter_control_present_flag: " << bs.get(1) << std::endl; + std::cout << "constrained_intra_pred_flag: " << bs.get(1) << std::endl; + std::cout << "redundant_pic_cnt_present_flag: " << bs.get(1) << std::endl; + //check for more data + if (bs.size() == 0) { + return; + } + unsigned int transform_8x8_mode_flag = bs.get(1); + std::cout << "transform_8x8_mode_flag: " << transform_8x8_mode_flag << std::endl; + unsigned int pic_scaling_matrix_present_flag = bs.get(1); + std::cout << "pic_scaling_matrix_present_flag: " << pic_scaling_matrix_present_flag << std::endl; + if (pic_scaling_matrix_present_flag) { + for (unsigned int i = 0; i < 6 + ((chroma_format_idc != 3) ? 2 : 6)*transform_8x8_mode_flag ; i++) { + unsigned int pic_scaling_list_present_flag = bs.get(1); + std::cout << "pic_scaling_list_present_flag[" << i << "]: " << pic_scaling_list_present_flag << std::endl; + if (pic_scaling_list_present_flag) { + std::cout << "under development, pslpf" << std::endl; + return; + if (i < 6) { + //scaling list(ScalingList4x4[i],16,UseDefaultScalingMatrix4x4Flag[ i ]) + } else { + //scaling_list(ScalingList4x4[i],64,UseDefaultScalingMatrix4x4Flag[ i-6 ]) + } + } + } + } + std::cout << "second_chroma_qp_index_offset: " << bs.getExpGolomb() << std::endl; + } + } -std::string NAL_Unit::SizePrepended(){ - std::string Result; - if (MyData.size()){ - int DataSize = MyData.size(); - Result += (char)((DataSize & 0xFF000000) >> 24); - Result += (char)((DataSize & 0x00FF0000) >> 16); - Result += (char)((DataSize & 0x0000FF00) >> 8); - Result += (char)(DataSize & 0x000000FF); //Size Lead-In - Result += MyData; - } - return Result; -} - -int NAL_Unit::Type(){ - return (MyData[0] & 0x1F); -} diff --git a/lib/nal.h b/lib/nal.h old mode 100644 new mode 100755 index c8307eaf..00a797af --- a/lib/nal.h +++ b/lib/nal.h @@ -1,15 +1,45 @@ #include #include -class NAL_Unit{ - public: - NAL_Unit(); - NAL_Unit(std::string & InputData); - bool ReadData(std::string & InputData); - std::string AnnexB(bool LongIntro = false); - std::string SizePrepended(); - int Type(); - private: - std::string MyData; -}; -//NAL_Unit class +namespace h264{ + + ///Struct containing pre-calculated metadata of an SPS nal unit. Width and height in pixels, fps in Hz + struct SPSMeta{ + unsigned int width; + unsigned int height; + double fps; + }; + + ///Class for analyzing generic nal units + class NAL{ + public: + NAL(); + NAL(std::string & InputData); + bool ReadData(std::string & InputData, bool raw = false); + std::string AnnexB(bool LongIntro = false); + std::string SizePrepended(); + int Type(); + std::string getData(); + protected: + unsigned int chroma_format_idc;/// +#include #include "ts_packet.h" #include "defines.h" @@ -9,434 +10,801 @@ #define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio." #endif -/// This constructor creates an empty TS::Packet, ready for use for either reading or writing. -/// All this constructor does is call TS::Packet::Clear(). -TS::Packet::Packet(){ - strBuf.reserve(188); - Clear(); -} +namespace TS { +/// This constructor creates an empty Packet, ready for use for either reading or writing. +/// All this constructor does is call Packet::Clear(). + Packet::Packet() { + strBuf.reserve(188); + Clear(); + } -/// This function fills a TS::Packet from provided Data. +/// This function fills a Packet from provided Data. /// It fills the content with the first 188 bytes of Data. /// \param Data The data to be read into the packet. /// \return true if it was possible to read in a full packet, false otherwise. -bool TS::Packet::FromString(std::string & Data){ - if (Data.size() < 188){ - return false; - }else{ - strBuf = Data.substr(0, 188); - Data.erase(0, 188); + bool Packet::FromString(std::string & Data) { + if (Data.size() < 188) { + return false; + } else { + strBuf = Data.substr(0, 188); + Data.erase(0, 188); + } + return true; } - return true; -} -/// The deconstructor deletes all space that may be occupied by a TS::Packet. -TS::Packet::~Packet(){ -} +/// This function fills a Packet from a file. +/// It fills the content with the next 188 bytes int he file. +/// \param Data The data to be read into the packet. +/// \return true if it was possible to read in a full packet, false otherwise. + bool Packet::FromFile(FILE * data){ + strBuf.resize(188); + if (!fread((void*)strBuf.data(), 188, 1, data)){ + return false; + } + return true; + } -/// Sets the PID of a single TS::Packet. +///This funtion fills a Packet from +///a char array. It fills the content with +///the first 188 characters of a char array +///\param Data The char array that contains the data to be read into the packet +///\return true if successful (which always happens, or else a segmentation fault should occur) + bool Packet::FromPointer(char * Data) { + strBuf = std::string(Data, 188); + return true; + } + + + +/// The deconstructor deletes all space that may be occupied by a Packet. + Packet::~Packet() { + } + +/// Sets the PID of a single Packet. /// \param NewPID The new PID of the packet. -void TS::Packet::PID(int NewPID){ - strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8); - strBuf[2] = (NewPID & 0x00FF); -} - -/// Gets the PID of a single TS::Packet. -/// \return The value of the PID. -int TS::Packet::PID(){ - return ((strBuf[1] & 0x1F) << 8) + strBuf[2]; -} - -/// Sets the Continuity Counter of a single TS::Packet. -/// \param NewContinuity The new Continuity Counter of the packet. -void TS::Packet::ContinuityCounter(int NewContinuity){ - if (strBuf.size() < 4){ - strBuf.resize(4); + void Packet::PID(int NewPID) { + strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8); + strBuf[2] = (NewPID & 0x00FF); } - strBuf[3] = (strBuf[3] & 0xF0) + (NewContinuity & 0x0F); -} -/// Gets the Continuity Counter of a single TS::Packet. +/// Gets the PID of a single Packet. +/// \return The value of the PID. + unsigned int Packet::PID() { + return (unsigned int)(((strBuf[1] & 0x1F) << 8) + strBuf[2]); + } + +/// Sets the Continuity Counter of a single Packet. +/// \param NewContinuity The new Continuity Counter of the packet. + void Packet::ContinuityCounter(int NewContinuity) { + if (strBuf.size() < 4) { + strBuf.resize(4); + } + strBuf[3] = (strBuf[3] & 0xF0) + (NewContinuity & 0x0F); + } + +/// Gets the Continuity Counter of a single Packet. /// \return The value of the Continuity Counter. -int TS::Packet::ContinuityCounter(){ - return (strBuf[3] & 0x0F); -} + int Packet::ContinuityCounter() { + return (strBuf[3] & 0x0F); + } -/// Gets the amount of bytes that are not written yet in a TS::Packet. +/// Gets the amount of bytes that are not written yet in a Packet. /// \return The amount of bytes that can still be written to this packet. -int TS::Packet::BytesFree(){ - return 188 - strBuf.size(); -} + int Packet::BytesFree() { + return 188 - strBuf.size(); + } -/// Clears a TS::Packet. -void TS::Packet::Clear(){ - strBuf.resize(4); - strBuf[0] = 0x47; - strBuf[1] = 0x00; - strBuf[2] = 0x00; - strBuf[3] = 0x10; -} +/// Clears a Packet. + void Packet::Clear() { + strBuf.resize(4); + strBuf[0] = 0x47; + strBuf[1] = 0x00; + strBuf[2] = 0x00; + strBuf[3] = 0x10; + } -/// Sets the selection value for an adaptationfield of a TS::Packet. +/// Sets the selection value for an adaptationfield of a Packet. /// \param NewSelector The new value of the selection bits. /// - 1: No AdaptationField. /// - 2: AdaptationField Only. /// - 3: AdaptationField followed by Data. -void TS::Packet::AdaptationField(int NewSelector){ - strBuf[3] = (strBuf[3] & 0xCF) + ((NewSelector & 0x03) << 4); - if (NewSelector & 0x02){ - strBuf[4] = 0x00; - }else{ - strBuf.resize(4); + void Packet::AdaptationField(int NewSelector) { + strBuf[3] = (strBuf[3] & 0xCF) + ((NewSelector & 0x03) << 4); + if (NewSelector & 0x02) { + strBuf[4] = 0x00; + } else { + strBuf.resize(4); + } } -} -/// Gets whether a TS::Packet contains an adaptationfield. +/// Gets whether a Packet contains an adaptationfield. /// \return The existence of an adaptationfield. /// - 0: No adaptationfield present. /// - 1: Adaptationfield is present. -int TS::Packet::AdaptationField(){ - return ((strBuf[3] & 0x30) >> 4); -} + int Packet::AdaptationField() { + return ((strBuf[3] & 0x30) >> 4); + } -/// Sets the PCR (Program Clock Reference) of a TS::Packet. +/// Sets the PCR (Program Clock Reference) of a Packet. /// \param NewVal The new PCR Value. -void TS::Packet::PCR(int64_t NewVal){ - if (strBuf.size() < 12){ - strBuf.resize(12); + void Packet::PCR(int64_t NewVal) { + if (strBuf.size() < 12) { + strBuf.resize(12); + } + AdaptationField(3); + strBuf[4] = 0x07; + strBuf[5] = (strBuf[5] | 0x10); + int64_t TmpVal = NewVal / 300; + strBuf[6] = (((TmpVal >> 1) >> 24) & 0xFF); + strBuf[7] = (((TmpVal >> 1) >> 16) & 0xFF); + strBuf[8] = (((TmpVal >> 1) >> 8) & 0xFF); + strBuf[9] = ((TmpVal >> 1) & 0xFF); + int Remainder = NewVal % 300; + strBuf[10] = 0x7E + ((TmpVal & 0x01) << 7) + ((Remainder & 0x0100) >> 8); + strBuf[11] = (Remainder & 0x00FF); } - AdaptationField(3); - strBuf[4] = 0x07; - strBuf[5] = (strBuf[5] | 0x10); - int64_t TmpVal = NewVal / 300; - strBuf[6] = (((TmpVal >> 1) >> 24) & 0xFF); - strBuf[7] = (((TmpVal >> 1) >> 16) & 0xFF); - strBuf[8] = (((TmpVal >> 1) >> 8) & 0xFF); - strBuf[9] = ((TmpVal >> 1) & 0xFF); - int Remainder = NewVal % 300; - strBuf[10] = 0x7E + ((TmpVal & 0x01) << 7) + ((Remainder & 0x0100) >> 8); - strBuf[11] = (Remainder & 0x00FF); -} -/// Gets the PCR (Program Clock Reference) of a TS::Packet. +/// Gets the PCR (Program Clock Reference) of a Packet. /// \return The value of the PCR. -int64_t TS::Packet::PCR(){ - if ( !AdaptationField()){ - return -1; + int64_t Packet::PCR() { + if (!AdaptationField()) { + return -1; + } + if (!(strBuf[5] & 0x10)) { + return -1; + } + int64_t Result = 0; + Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + (strBuf[10] & 0x80 >> 7); + Result = Result * 300; + Result += (((strBuf[10] & 0x01) << 8) + strBuf[11]); + return Result; } - if ( !(strBuf[5] & 0x10)){ - return -1; + +/// Gets the OPCR (Original Program Clock Reference) of a Packet. +/// \return The value of the OPCR. + int64_t Packet::OPCR() { + if (!AdaptationField()) { + return -1; + } + if (!(strBuf[5 + 6] & 0x10)) { + return -1; + } + int64_t Result = 0; + Result = (((strBuf[6 + 6] << 24) + (strBuf[7 + 6] << 16) + (strBuf[8 + 6] << 8) + strBuf[9 + 6]) << 1) + (strBuf[10 + 6] & 0x80 >> 7); + Result = Result * 300; + Result += (((strBuf[10 + 6] & 0x01) << 8) + strBuf[11 + 6]); + return Result; + } +/// Gets the sync byte of a Packet. +/// \return The packet's sync byte + unsigned int Packet::getSyncByte() { + return (unsigned int)strBuf[0]; + } + +/// Gets the transport error inficator of a Packet +/// \return The transport error inficator of a Packet + unsigned int Packet::getTransportErrorIndicator() { + return (unsigned int)((strBuf[1] >> 7) & (0x01)); + } + +/// Gets the payload unit start inficator of a Packet +/// \return The payload unit start inficator of a Packet + unsigned int Packet::getPayloadUnitStartIndicator() { + return (unsigned int)((strBuf[1] >> 6) & (0x01)); + } + +/// Gets the transport priority of a Packet +/// \return The transport priority of a Packet + unsigned int Packet::getTransportPriority() { + return (unsigned int)((strBuf[1] >> 5) & (0x01)); + } + +/// Gets the transport scrambling control of a Packet +/// \return The transport scrambling control of a Packet + unsigned int Packet::getTransportScramblingControl() { + return (unsigned int)((strBuf[3] >> 6) & (0x03)); } - int64_t Result = 0; - Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + (strBuf[10] & 0x80 >> 7); - Result = Result * 300; - Result += (((strBuf[10] & 0x01) << 8) + strBuf[11]); - return Result; -} /// Gets the current length of the adaptationfield. /// \return The length of the adaptationfield. -int TS::Packet::AdaptationFieldLen(){ - if ( !AdaptationField()){ - return -1; + int Packet::AdaptationFieldLen() { + if (!AdaptationField()) { + return -1; + } + return (int)strBuf[4]; } - return (int)strBuf[4]; -} /// Prints a packet to stdout, for analyser purposes. -std::string TS::Packet::toPrettyString(size_t indent){ - std::stringstream output; - output << std::string(indent,' ') << "TS Packet: " << (strBuf[0] == 0x47) << std::endl; - output << std::string(indent+2,' ') << "NewUnit: " << UnitStart() << std::endl; - output << std::string(indent+2,' ') << "PID: " << PID() << std::endl; - output << std::string(indent+2,' ') << "Continuity Counter: " << ContinuityCounter() << std::endl; - output << std::string(indent+2,' ') << "Adaption Field: " << AdaptationField() << std::endl; - if (AdaptationField()){ - output << std::string(indent+4,' ') << "Adaptation Length: " << AdaptationFieldLen() << std::endl;; - if (AdaptationFieldLen()){ - output << std::string(indent+4,' ') << "Random Access: " << RandomAccess() << std::endl; + std::string Packet::toPrettyString(size_t indent) { + std::stringstream output; + output << std::string(indent, ' ') << "TS Packet: " << (strBuf[0] == 0x47) << std::endl; + output << std::string(indent + 2, ' ') << "Transport Error Indicator: " << getTransportErrorIndicator() << std::endl; + output << std::string(indent + 2, ' ') << "Payload Unit Start Indicator: " << getPayloadUnitStartIndicator() << std::endl; + output << std::string(indent + 2, ' ') << "Transport Priority: " << getTransportPriority() << std::endl; + output << std::string(indent + 2, ' ') << "PID: " << PID() << std::endl; + output << std::string(indent + 2, ' ') << "Transport Scrambling Control: " << getTransportScramblingControl() << std::endl; + output << std::string(indent + 2, ' ') << "Adaptation Field: " << AdaptationField() << std::endl; + output << std::string(indent + 2, ' ') << "Continuity Counter: " << ContinuityCounter() << std::endl; + if (AdaptationField() > 1) { + output << std::string(indent + 4, ' ') << "Adaptation Length: " << AdaptationFieldLen() << std::endl; + if (AdaptationFieldLen()) { + output << std::string(indent + 4, ' ') << "Discontinuity Indicator: " << DiscontinuityIndicator() << std::endl; + output << std::string(indent + 4, ' ') << "Random Access: " << RandomAccess() << std::endl; + output << std::string(indent + 4, ' ') << "Elementary Stream Priority Indicator: " << elementaryStreamPriorityIndicator() << std::endl; + output << std::string(indent + 4, ' ') << "PCRFlag: " << PCRFlag() << std::endl; + output << std::string(indent + 4, ' ') << "OPCRFlag: " << OPCRFlag() << std::endl; + output << std::string(indent + 4, ' ') << "Splicing Point Flag: " << splicingPointFlag() << std::endl; + ///\todo Implement this + //output << std::string(indent + 4, ' ') << "Transport Private Data Flag: " << transportPrivateDataFlag() << std::endl; + //output << std::string(indent + 4, ' ') << "Adaptation Field Extension Flag: " << adaptationFieldExtensionFlag() << std::endl; + if (PCRFlag()) { + output << std::string(indent + 6, ' ') << "PCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )" << std::endl; + } + if (OPCRFlag()) { + output << std::string(indent + 6, ' ') << "OPCR: " << PCR() << "( " << (double)OPCR() / 27000000 << " s )" << std::endl; + } + } } - if (PCR() != -1){ - output << std::string(indent+4,' ') << "PCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )" << std::endl; + if (PID() == 0x00) { + //program association table + output << ((ProgramAssociationTable *)this)->toPrettyString(indent + 2); + } else { + output << std::string(indent + 2, ' ') << "Data: " << 184 - ((AdaptationField() > 1) ? AdaptationFieldLen() + 1 : 0) << " bytes" << std::endl; } + return output.str(); } - return output.str(); -} -/// Gets whether a new unit starts in this TS::Packet. +/// Gets whether a new unit starts in this Packet. /// \return The start of a new unit. -int TS::Packet::UnitStart(){ - return (strBuf[1] & 0x40) >> 6; -} + int Packet::UnitStart() { + return (strBuf[1] & 0x40) >> 6; + } -/// Sets the start of a new unit in this TS::Packet. +/// Sets the start of a new unit in this Packet. /// \param NewVal The new value for the start of a unit. -void TS::Packet::UnitStart(int NewVal){ - if (NewVal){ - strBuf[1] |= 0x40; - }else{ - strBuf[1] &= 0xBF; - } -} - -/// Gets whether this TS::Packet can be accessed at random (indicates keyframe). -/// \return Whether or not this TS::Packet contains a keyframe. -int TS::Packet::RandomAccess(){ - if (AdaptationField() < 2){ - return -1; - } - return (strBuf[5] & 0x40) >> 6; -} - -/// Sets whether this TS::Packet contains a keyframe -/// \param NewVal Whether or not this TS::Packet contains a keyframe. -void TS::Packet::RandomAccess(int NewVal){ - if (AdaptationField() == 3){ - if (strBuf.size() < 6){ - strBuf.resize(6); + void Packet::UnitStart(int NewVal) { + if (NewVal) { + strBuf[1] |= 0x40; + } else { + strBuf[1] &= 0xBF; } - if ( !strBuf[4]){ + } + +/// Gets the elementary stream priority indicator of a Packet +/// \return The elementary stream priority indicator of a Packet + int Packet::elementaryStreamPriorityIndicator() { + return (strBuf[5] >> 5) & 0x01; + } + + + +/// Gets whether this Packet can be accessed at random (indicates keyframe). +/// \return Whether or not this Packet contains a keyframe. + int Packet::DiscontinuityIndicator() { + return (strBuf[5] >> 7) & 0x01; + } + + int Packet::RandomAccess() { + if (AdaptationField() < 2) { + return -1; + } + return (strBuf[5] & 0x40) >> 6; + } + +///Gets the value of the PCR flag +///\return true if there is a PCR, false otherwise + int Packet::PCRFlag() { + return (strBuf[5] >> 4) & 0x01; + } + +///Gets the value of the OPCR flag +///\return true if there is an OPCR, false otherwise + int Packet::OPCRFlag() { + return (strBuf[5] >> 3) & 0x01; + } + +///Gets the value of the splicing point flag +///\return the value of the splicing point flag + int Packet::splicingPointFlag() { + return (strBuf[5] >> 2) & 0x01; + } + +///Gets the value of the transport private data point flag +///\return the value of the transport private data point flag + void Packet::RandomAccess(int NewVal) { + if (AdaptationField() == 3) { + if (strBuf.size() < 6) { + strBuf.resize(6); + } + if (!strBuf[4]) { + strBuf[4] = 1; + } + if (NewVal) { + strBuf[5] |= 0x40; + } else { + strBuf[5] &= 0xBF; + } + } else { + if (strBuf.size() < 6) { + strBuf.resize(6); + } + AdaptationField(3); strBuf[4] = 1; - } - if (NewVal){ - strBuf[5] |= 0x40; - }else{ - strBuf[5] &= 0xBF; - } - }else{ - if (strBuf.size() < 6){ - strBuf.resize(6); - } - AdaptationField(3); - strBuf[4] = 1; - if (NewVal){ - strBuf[5] = 0x40; - }else{ - strBuf[5] = 0x00; + if (NewVal) { + strBuf[5] = 0x40; + } else { + strBuf[5] = 0x00; + } } } -} -/// Transforms the TS::Packet into a standard Program Association Table -void TS::Packet::DefaultPAT(){ - static int MyCntr = 0; - strBuf = std::string(TS::PAT, 188); - ContinuityCounter(MyCntr++); - MyCntr %= 0x10; -} +/// Transforms the Packet into a standard Program Association Table + void Packet::DefaultPAT() { + static int MyCntr = 0; + strBuf = std::string(PAT, 188); + ContinuityCounter(MyCntr++); + MyCntr %= 0x10; + } -/// Transforms the TS::Packet into a standard Program Mapping Table -void TS::Packet::DefaultPMT(){ - static int MyCntr = 0; - strBuf = std::string(TS::PMT, 188); - ContinuityCounter(MyCntr++); - MyCntr %= 0x10; -} +/// Transforms the Packet into a standard Program Mapping Table + void Packet::DefaultPMT() { + static int MyCntr = 0; + strBuf = std::string(PMT, 188); + ContinuityCounter(MyCntr++); + MyCntr %= 0x10; + } -/// Generates a string from the contents of the TS::Packet +/// Generates a string from the contents of the Packet /// \return A string representation of the packet. -const char* TS::Packet::ToString(){ - if (strBuf.size() != 188){ - DEBUG_MSG(DLVL_ERROR, "Size invalid (%i) - invalid data from this point on", (int)strBuf.size()); + const char * Packet::ToString() { + if (strBuf.size() != 188) { + DEBUG_MSG(DLVL_ERROR, "Size invalid (%i) - invalid data from this point on", (int)strBuf.size()); + } + return strBuf.c_str(); } - return strBuf.c_str(); -} /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. /// \param NewLen The length of this frame. /// \param PTS The timestamp of the frame. -void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){ - //NewLen += 19; - NewLen = 0; - strBuf += (char)0x00; //PacketStartCodePrefix - strBuf += (char)0x00; //PacketStartCodePrefix (Cont) - strBuf += (char)0x01; //PacketStartCodePrefix (Cont) - strBuf += (char)0xe0; //StreamType Video - strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength - strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - strBuf += (char)0x84; //Reserved + Flags - strBuf += (char)0xC0; //PTSOnlyFlag + Flags - strBuf += (char)0x0A; //PESHeaderDataLength - strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS - strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS - strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) - strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) - strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) - //PesPacket-Wise Prepended Data - strBuf += (char)0x00; //NALU StartCode - strBuf += (char)0x00; //NALU StartCode (Cont) - strBuf += (char)0x00; //NALU StartCode (Cont) - strBuf += (char)0x01; //NALU StartCode (Cont) - strBuf += (char)0x09; //NALU EndOfPacket (Einde Vorige Packet) - strBuf += (char)0xF0; //NALU EndOfPacket (Cont) -} + void Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS) { + //NewLen += 19; + NewLen = 0; + strBuf += (char)0x00; //PacketStartCodePrefix + strBuf += (char)0x00; //PacketStartCodePrefix (Cont) + strBuf += (char)0x01; //PacketStartCodePrefix (Cont) + strBuf += (char)0xe0; //StreamType Video + strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + strBuf += (char)0x84; //Reserved + Flags + strBuf += (char)0xC0; //PTSOnlyFlag + Flags + strBuf += (char)0x0A; //PESHeaderDataLength + strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) + //PesPacket-Wise Prepended Data + strBuf += (char)0x00; //NALU StartCode + strBuf += (char)0x00; //NALU StartCode (Cont) + strBuf += (char)0x00; //NALU StartCode (Cont) + strBuf += (char)0x01; //NALU StartCode (Cont) + strBuf += (char)0x09; //NALU EndOfPacket (Einde Vorige Packet) + strBuf += (char)0xF0; //NALU EndOfPacket (Cont) + } /// Generates a PES Lead-in for an audio frame. /// Starts at the first Free byte. /// \param NewLen The length of this frame. /// \param PTS The timestamp of the frame. -void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){ - NewLen += 5; - strBuf += (char)0x00; //PacketStartCodePrefix - strBuf += (char)0x00; //PacketStartCodePrefix (Cont) - strBuf += (char)0x01; //PacketStartCodePrefix (Cont) - strBuf += (char)0xc0; //StreamType Audio - strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength - strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - strBuf += (char)0x84; //Reserved + Flags - strBuf += (char)0x80; //PTSOnlyFlag + Flags - strBuf += (char)0x05; //PESHeaderDataLength - strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS - strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) -} + void Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS) { + NewLen += 5; + strBuf += (char)0x00; //PacketStartCodePrefix + strBuf += (char)0x00; //PacketStartCodePrefix (Cont) + strBuf += (char)0x01; //PacketStartCodePrefix (Cont) + strBuf += (char)0xc0; //StreamType Audio + strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + strBuf += (char)0x84; //Reserved + Flags + strBuf += (char)0x80; //PTSOnlyFlag + Flags + strBuf += (char)0x05; //PESHeaderDataLength + strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + } /// Generates a PES Lead-in for a video frame. /// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. /// \param toSend Data that is to be send, will be modified. /// \param PTS The timestamp of the frame. -void TS::Packet::PESVideoLeadIn(std::string & toSend, long long unsigned int PTS){ - std::string tmpStr; - tmpStr.reserve(25); - tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); - tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) - tmpStr.append("\000\000\000\001\011\360", 6); - toSend.insert(0, tmpStr); -} + void Packet::PESVideoLeadIn(std::string & toSend, long long unsigned int PTS) { + std::string tmpStr; + tmpStr.reserve(25); + tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) + tmpStr.append("\000\000\000\001\011\360", 6); + toSend.insert(0, tmpStr); + } /// Generates a PES Lead-in for an audio frame. /// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. /// \param toSend Data that is to be send, will be modified. /// \param PTS The timestamp of the frame. -void TS::Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS){ - std::string tmpStr; - tmpStr.reserve(14); - unsigned int NewLen = toSend.size() + 5; - tmpStr.append("\000\000\001\300", 4); - tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength - tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - tmpStr.append("\204\200\005", 3); - tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - toSend.insert(0, tmpStr); -} + void Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS) { + std::string tmpStr; + tmpStr.reserve(14); + unsigned int NewLen = toSend.size() + 5; + tmpStr.append("\000\000\001\300", 4); + tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + tmpStr.append("\204\200\005", 3); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + toSend.insert(0, tmpStr); + } /// Generates a PES Lead-in for a video frame. /// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. /// \param NewLen The length of this frame. /// \param PTS The timestamp of the frame. -std::string & TS::Packet::getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){ - static std::string tmpStr; - tmpStr.clear(); - tmpStr.reserve(25); - tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); - tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) - tmpStr.append("\000\000\000\001\011\360", 6); - return tmpStr; -} + std::string & Packet::getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS) { + static std::string tmpStr; + tmpStr.clear(); + tmpStr.reserve(25); + tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) + tmpStr.append("\000\000\000\001\011\360", 6); + return tmpStr; + } /// Generates a PES Lead-in for an audio frame. /// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. /// \param NewLen The length of this frame. /// \param PTS The timestamp of the frame. -std::string & TS::Packet::getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS){ - static std::string tmpStr; - tmpStr.clear(); - tmpStr.reserve(14); - NewLen = NewLen + 8; - tmpStr.append("\000\000\001\300", 4); - tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength - tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - tmpStr.append("\204\200\005", 3); - tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS - tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - return tmpStr; -} + std::string & Packet::getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS) { + static std::string tmpStr; + tmpStr.clear(); + tmpStr.reserve(14); + NewLen = NewLen + 8; + tmpStr.append("\000\000\001\300", 4); + tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + tmpStr.append("\204\200\005", 3); + tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + return tmpStr; + } -/// Fills the free bytes of the TS::Packet. +/// Fills the free bytes of the Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. -void TS::Packet::FillFree(std::string & NewVal){ - unsigned int toWrite = BytesFree(); - if (toWrite == NewVal.size()){ - strBuf += NewVal; - NewVal.clear(); - }else{ - strBuf += NewVal.substr(0, toWrite); - NewVal.erase(0, toWrite); + void Packet::FillFree(std::string & NewVal) { + unsigned int toWrite = BytesFree(); + if (toWrite == NewVal.size()) { + strBuf += NewVal; + NewVal.clear(); + } else { + strBuf += NewVal.substr(0, toWrite); + NewVal.erase(0, toWrite); + } } -} -/// Fills the free bytes of the TS::Packet. +/// Fills the free bytes of the Packet. /// Stores as many bytes from NewVal as possible in the packet. -/// The minimum of TS::Packet::BytesFree and maxLen is used. +/// The minimum of Packet::BytesFree and maxLen is used. /// \param NewVal The data to store in the packet. /// \param maxLen The maximum amount of bytes to store. -int TS::Packet::FillFree(const char* NewVal, int maxLen){ - int toWrite = std::min((int)BytesFree(), maxLen); - strBuf += std::string(NewVal, toWrite); - return toWrite; -} + int Packet::FillFree(const char * NewVal, int maxLen) { + int toWrite = std::min((int)BytesFree(), maxLen); + strBuf += std::string(NewVal, toWrite); + return toWrite; + } -/// Adds stuffing to the TS::Packet depending on how much content you want to send. +/// Adds stuffing to the Packet depending on how much content you want to send. /// \param NumBytes the amount of non-stuffing content bytes you want to send. /// \return The amount of content bytes that can be send. -unsigned int TS::Packet::AddStuffing(int NumBytes){ - if (BytesFree() <= NumBytes){ + unsigned int Packet::AddStuffing(int NumBytes) { + if (BytesFree() <= NumBytes) { + return BytesFree(); + } + NumBytes = BytesFree() - NumBytes; + if (AdaptationField() == 3) { + strBuf.resize(5 + strBuf[4]); + strBuf[4] += NumBytes; + for (int i = 0; i < NumBytes; i++) { + strBuf.append(FILLER_DATA[i % sizeof(FILLER_DATA)], 0); + } + } else { + AdaptationField(3); + if (NumBytes > 1) { + strBuf.resize(6); + strBuf[4] = (char)(NumBytes - 1); + strBuf[5] = (char)0x00; + for (int i = 0; i < (NumBytes - 2); i++) { + strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)]; + } + } else { + strBuf.resize(5); + strBuf[4] = (char)(NumBytes - 1); + } + } return BytesFree(); } - NumBytes = BytesFree() - NumBytes; - if (AdaptationField() == 3){ - strBuf.resize(5 + strBuf[4]); - strBuf[4] += NumBytes; - for (int i = 0; i < NumBytes; i++){ - strBuf.append(FILLER_DATA[i % sizeof(FILLER_DATA)], 0); - } - }else{ - AdaptationField(3); - if (NumBytes > 1){ - strBuf.resize(6); - strBuf[4] = (char)(NumBytes - 1); - strBuf[5] = (char)0x00; - for (int i = 0; i < (NumBytes - 2); i++){ - strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)]; - } - }else{ - strBuf.resize(5); - strBuf[4] = (char)(NumBytes - 1); - } + +///Gets the string buffer, containing the raw packet data as a string +///\return The raw TS data as a string + std::string Packet::getStrBuf() { + return strBuf; + } + +///Gets the buffer, containing the raw packet data as a char arrya +///\return The raw TS data as char array. + const char * Packet::getBuffer() { + return strBuf.data(); + } + +///Gets the payload of this packet, as a raw char array +///\return The payload of this ts packet as a char pointer + const char * Packet::getPayload() { + return strBuf.data() + (4 + (AdaptationField( ) > 1 ? AdaptationFieldLen() + 1 : 0)); + } + + ///Gets the length of the payload for this apcket + ///\return The amount of bytes payload in this packet + int Packet::getPayloadLength(){ + return 184 - ((AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0)); + } + + + ///Retrieves the current offset value for a PAT + char ProgramAssociationTable::getOffset() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0); + return strBuf[loc]; + } + + ///Retrieves the ID of this table + char ProgramAssociationTable::getTableId() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 1; + return strBuf[loc]; + } + + ///Retrieves the current section length + short ProgramAssociationTable::getSectionLength() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 2; + return (short)(strBuf[loc] & 0x0F) << 8 | strBuf[loc + 1]; + } + + ///Retrieves the Transport Stream ID + short ProgramAssociationTable::getTransportStreamId() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 4; + return (short)(strBuf[loc] & 0x0F) << 8 | strBuf[loc + 1]; + } + + ///Retrieves the version number + char ProgramAssociationTable::getVersionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 6; + return (strBuf[loc] >> 1) & 0x1F; + } + + ///Retrieves the "current/next" indicator + bool ProgramAssociationTable::getCurrentNextIndicator() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 6; + return (strBuf[loc] >> 1) & 0x01; + } + + ///Retrieves the section number + char ProgramAssociationTable::getSectionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 7; + return strBuf[loc]; + } + + ///Retrieves the last section number + char ProgramAssociationTable::getLastSectionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 8; + return strBuf[loc]; + } + + ///Returns the amount of programs in this table + short ProgramAssociationTable::getProgramCount() { + //This is correct, not -12 since we already parsed 4 bytes here + return (getSectionLength() - 8) / 4; + } + + short ProgramAssociationTable::getProgramNumber(short index) { + if (index > getProgramCount()) { + return 0; + } + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 9; + return ((short)(strBuf[loc + (index * 4)]) << 8) | strBuf[loc + (index * 4) + 1]; + } + + short ProgramAssociationTable::getProgramPID(short index) { + if (index > getProgramCount()) { + return 0; + } + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 9; + return (((short)(strBuf[loc + (index * 4) + 2]) << 8) | strBuf[loc + (index * 4) + 3]) & 0x1FFF; + } + + int ProgramAssociationTable::getCRC() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 9 + (getProgramCount() * 4); + return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) | ((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3]; + } + +///This function prints a program association table, +///prints all values in a human readable format +///\param indent The indentation of the string printed as wanted by the user +///\return The string with human readable data from a PAT + std::string ProgramAssociationTable::toPrettyString(size_t indent) { + std::stringstream output; + output << std::string(indent, ' ') << "[Program Association Table]" << std::endl; + output << std::string(indent + 2, ' ') << "Pointer Field: " << (int)getOffset() << std::endl; + output << std::string(indent + 2, ' ') << "Table ID: " << (int)getTableId() << std::endl; + output << std::string(indent + 2, ' ') << "Sectionlen: " << getSectionLength() << std::endl; + output << std::string(indent + 2, ' ') << "Transport Stream ID: " << getTransportStreamId() << std::endl; + output << std::string(indent + 2, ' ') << "Version Number: " << (int)getVersionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Current/Next Indicator: " << (int)getCurrentNextIndicator() << std::endl; + output << std::string(indent + 2, ' ') << "Section number: " << (int)getSectionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Last Section number: " << (int)getLastSectionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Programs [" << (int)getProgramCount() << "]" << std::endl; + for (int i = 0; i < getProgramCount(); i++) { + output << std::string(indent + 4, ' ') << "[" << i + 1 << "] "; + output << "Program Number: " << getProgramNumber(i) << ", "; + output << (getProgramNumber(i) == 0 ? "Network" : "Program Map") << " PID: " << getProgramPID(i); + output << std::endl; + } + output << std::string(indent + 2, ' ') << "CRC32: " << std::hex << std::setw(8) << std::setfill('0') << std::uppercase << getCRC() << std::dec << std::endl; + return output.str(); + + } + + + char ProgramMappingTable::getOffset() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0); + return strBuf[loc]; + } + + char ProgramMappingTable::getTableId() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 1; + return strBuf[loc]; + } + + short ProgramMappingTable::getSectionLength() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 2; + return (((short)strBuf[loc] & 0x0F) << 8) | strBuf[loc + 1]; + } + + short ProgramMappingTable::getProgramNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 4; + return (((short)strBuf[loc]) << 8) | strBuf[loc + 1]; + } + + char ProgramMappingTable::getVersionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 6; + return (strBuf[loc] >> 1) & 0x1F; + } + + ///Retrieves the "current/next" indicator + bool ProgramMappingTable::getCurrentNextIndicator() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 6; + return (strBuf[loc] >> 1) & 0x01; + } + + ///Retrieves the section number + char ProgramMappingTable::getSectionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 7; + return strBuf[loc]; + } + + ///Retrieves the last section number + char ProgramMappingTable::getLastSectionNumber() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 8; + return strBuf[loc]; + } + + short ProgramMappingTable::getPCRPID() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 9; + return (((short)strBuf[loc] & 0x1F) << 8) | strBuf[loc + 1]; + } + + short ProgramMappingTable::getProgramInfoLength() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 11; + return (((short)strBuf[loc] & 0x0F) << 8) | strBuf[loc + 1]; + } + + short ProgramMappingTable::getProgramCount() { + return (getSectionLength() - 13) / 5; + } + + char ProgramMappingTable::getStreamType(short index) { + if (index > getProgramCount()) { + return 0; + } + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength(); + return strBuf[loc + (index * 5)]; + } + + short ProgramMappingTable::getElementaryPID(short index) { + ; + if (index > getProgramCount()) { + return 0; + } + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength(); + return (((short)strBuf[loc + (index * 5) + 1] & 0x1F) << 8) | strBuf[loc + (index * 5) + 2]; + } + + short ProgramMappingTable::getESInfoLength(short index) { + if (index > getProgramCount()) { + return 0; + } + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength(); + return (((short)strBuf[loc + (index * 5) + 3] & 0x0F) << 8) | strBuf[loc + (index * 5) + 4]; + } + + int ProgramMappingTable::getCRC() { + unsigned int loc = 4 + (AdaptationField() > 1 ? AdaptationFieldLen() + 1 : 0) + getOffset() + 13 + getProgramInfoLength() + (getProgramCount() * 5); + return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) | ((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3]; + } + +///Print all PMT values in a human readable format +///\param indent The indentation of the string printed as wanted by the user +///\return The string with human readable data from a PMT table + std::string ProgramMappingTable::toPrettyString(size_t indent) { + std::stringstream output; + output << std::string(indent, ' ') << "[Program Mapping Table]" << std::endl; + output << std::string(indent + 2, ' ') << "Pointer Field: " << (int)getOffset() << std::endl; + output << std::string(indent + 2, ' ') << "Table ID: " << (int)getTableId() << std::endl; + output << std::string(indent + 2, ' ') << "Section Length: " << getSectionLength() << std::endl; + output << std::string(indent + 2, ' ') << "Program number: " << getProgramNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Version number: " << (int)getVersionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Current next indicator: " << (int)getCurrentNextIndicator() << std::endl; + output << std::string(indent + 2, ' ') << "Section number: " << (int)getSectionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "Last Section number: " << (int)getLastSectionNumber() << std::endl; + output << std::string(indent + 2, ' ') << "PCR PID: " << getPCRPID() << std::endl; + output << std::string(indent + 2, ' ') << "Program Info Length: " << getProgramInfoLength() << std::endl; + output << std::string(indent + 2, ' ') << "Programs [" << getProgramCount() << "]" << std::endl; + for (int i = 0; i < getProgramCount(); i++) { + output << std::string(indent + 4, ' ') << "[" << i + 1 << "] "; + output << "StreamType: 0x" << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)getStreamType(i) << std::dec << ", "; + output << "Elementary PID: " << getElementaryPID(i) << ", "; + output << "ES Info Length: " << getESInfoLength(i); + output << std::endl; + } + output << std::string(indent + 2, ' ') << "CRC32: " << std::hex << std::setw(8) << std::setfill('0') << std::uppercase << getCRC() << std::dec << std::endl; + return output.str(); } - return BytesFree(); } + + + diff --git a/lib/ts_packet.h b/lib/ts_packet.h old mode 100644 new mode 100755 index eba10c6e..4baa0cb0 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -3,30 +3,46 @@ #pragma once #include +#include #include #include //for uint64_t #include #include #include #include - #include "dtsc.h" /// Holds all TS processing related code. namespace TS { - /// Class for reading and writing TS Streams - class Packet{ + + + ///stores all the data of a pmt table. It must be mapped to a PID, and this is done in the function TS::getPMTTable(TS::Packet& packet) + ///\todo Add more necessary variables, or find a more efficient way to store metadata + struct pmtinfo { + unsigned short streamtype;//the streamtype, 0x1b is h264, 0x0f is aac. These are used in aac, there may be (undiscovered) others + unsigned int trackid;//track id + std::string curPayload;//payload without PES/TS headers + long long int lastPEStime;//the pes time of the packet that was last seen + }; + + + ///Class for reading and writing TS Streams. The class is capable of analyzing a packet of 188 bytes + ///and calculating key values + class Packet { public: Packet(); ~Packet(); bool FromString(std::string & Data); + bool FromPointer(char * Data); + bool FromFile(FILE * data); void PID(int NewPID); - int PID(); + unsigned int PID(); void ContinuityCounter(int NewContinuity); int ContinuityCounter(); void Clear(); void PCR(int64_t NewVal); int64_t PCR(); + int64_t OPCR(); void AdaptationField(int NewVal); int AdaptationField(); int AdaptationFieldLen(); @@ -36,33 +52,87 @@ namespace TS { void UnitStart(int NewVal); int RandomAccess(); void RandomAccess(int NewVal); + + int DiscontinuityIndicator(); + int elementaryStreamPriorityIndicator(); + int PCRFlag(); + int OPCRFlag(); + int splicingPointFlag(); + //int transportPrivateDataFlag(); + //int adaptationFieldExtensionFlag(); + int BytesFree(); + unsigned int getSyncByte(); + unsigned int getTransportErrorIndicator(); + unsigned int getPayloadUnitStartIndicator(); + unsigned int getTransportPriority(); + unsigned int getTransportScramblingControl(); std::string toPrettyString(size_t indent = 0); - const char* ToString(); + std::string getStrBuf(); + const char * getBuffer(); + const char * getPayload(); + int getPayloadLength(); + + const char * ToString(); void PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS = 1); void PESAudioLeadIn(unsigned int NewLen, uint64_t PTS = 0); static void PESAudioLeadIn(std::string & toSend, long long unsigned int PTS); static void PESVideoLeadIn(std::string & toSend, long long unsigned int PTS); static std::string & getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS); static std::string & getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS); - + void FillFree(std::string & PackageData); - int FillFree(const char* PackageData, int maxLen); + int FillFree(const char * PackageData, int maxLen); unsigned int AddStuffing(int NumBytes); - private: - //int Free; - std::string strBuf; + protected: + std::string strBuf;///> 3) - 1) << 6) & 0xC0); //AAC Profile - 1 ( First two bits ) @@ -75,6 +145,7 @@ namespace TS { return std::string(StandardHeader, 7); } + /// A standard Program Association Table, as generated by FFMPEG. /// Seems to be independent of the stream. //0x47 = sync byte @@ -130,3 +201,4 @@ namespace TS { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; } //TS namespace +