diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index 663217f..d73ee77 100644 --- a/src/Context/StreamContext.php +++ b/src/Context/StreamContext.php @@ -30,6 +30,7 @@ use Evenement\EventEmitter; use PhpBg\DvbPsi\Tables\Pat; use PhpBg\DvbPsi\Tables\Pmt; +use PhpBg\DvbPsi\Tables\Sdt; class StreamContext extends EventEmitter { @@ -49,6 +50,12 @@ class StreamContext extends EventEmitter */ public $pmts; + /** + * @var Sdt[] + */ + public $sdts; + + public function addPat(Pat $pat) { if (!isset($this->pat) || $this->pat->version < $pat->version || ($this->pat->version !== 0 && $pat->version === 0)) { @@ -59,11 +66,23 @@ public function addPat(Pat $pat) } } + public function addSdt(Sdt $sdt) + { + if (!isset($this->sdts[$sdt->transportStreamId]) + || $this->sdts[$sdt->transportStreamId]->versionNumber < $sdt->versionNumber + || ($this->sdts[$sdt->transportStreamId]->versionNumber !== 0 && $sdt->versionNumber === 0) + ) { + $this->sdts[$sdt->transportStreamId] = $sdt; + $this->emit('sdt-update', [$sdt]); + } + + } + public function addPmt(Pmt $pmt) { if (!isset($this->pmts[$pmt->programNumber]) || $this->pmts[$pmt->programNumber]->version < $pmt->version || ($this->pmts[$pmt->programNumber]->version !== 0 && $pmt->version === 0)) { $this->pmts[$pmt->programNumber] = $pmt; - $this->emit('pmt-update'); + $this->emit('pmt-update', [$pmt]); $programsCount = count($this->pat->programs); if (isset($this->pat->programs[0])) { @@ -95,6 +114,13 @@ public function __toString() $str .= "$pmt\n"; } } + + if (!empty($this->sdts)) { + $str .= "SDTs\n"; + foreach ($this->sdts as $sdt) { + $str .= "$sdt\n"; + } + } } if (isset($this->tdtTimestamp)) { diff --git a/src/Descriptors/Identifier.php b/src/Descriptors/Identifier.php index df05e4b..d86c33f 100644 --- a/src/Descriptors/Identifier.php +++ b/src/Descriptors/Identifier.php @@ -37,6 +37,7 @@ class Identifier extends Enum const NETWORK_NAME_DESCRIPTOR = 0x40; const SERVICE_LIST_DESCRIPTOR = 0x41; const STUFFING_DESCRIPTOR = 0x42; + const SERVICE_DESCRIPTOR = 0x48; const LINKAGE_DESCRIPTOR = 0x4a; const SHORT_EVENT_DESCRIPTOR = 0x4d; const EXTENDED_EVENT_DESCRIPTOR = 0x4e; diff --git a/src/Descriptors/ServiceDescriptor.php b/src/Descriptors/ServiceDescriptor.php new file mode 100644 index 0000000..dc67342 --- /dev/null +++ b/src/Descriptors/ServiceDescriptor.php @@ -0,0 +1,78 @@ +serviceType = new ServiceType($serviceType); + + $pointer += 1; + $serviceProviderNameLength = unpack('C', $data[$pointer])[1]; + + $pointer += 1; + $this->serviceProviderName = substr($data, $pointer, $serviceProviderNameLength); + + $pointer += $serviceProviderNameLength; + $serviceNameLength = unpack('C', $data[$pointer])[1]; + + $pointer += 1; + $this->serviceName = substr($data, $pointer, $serviceNameLength); + } + + public function __toString() + { + $msg = sprintf("Service type: %s (0x%x)\n", $this->serviceType->getKey(), $this->serviceType->getValue() ); + $msg .= "Service provider name: $this->serviceProviderName\n"; + $msg .= "Service name: $this->serviceName\n"; + + return $msg; + } +} \ No newline at end of file diff --git a/src/Descriptors/Values/EsType.php b/src/Descriptors/Values/EsType.php new file mode 100644 index 0000000..3121038 --- /dev/null +++ b/src/Descriptors/Values/EsType.php @@ -0,0 +1,100 @@ +'ISO/IEC 11172-2 (MPEG-1 video)in a packetized stream', + 2 =>'ITU-T Rec. H.262 and ISO/IEC 13818-2 (MPEG-2 higher rate interlaced video) in a packetized stream', + 3 =>'ISO/IEC 11172-3 (MPEG-1 audio) in a packetized stream', + 4 =>'ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) in a packetized stream', + 5 =>'ITU-T Rec. H.222 and ISO/IEC 13818-1 (MPEG-2 tabled data) privately defined', + 6 =>'ITU-T Rec. H.222 and ISO/IEC 13818-1 (MPEG-2 packetized data) privately defined (i.e., DVB subtitles/VBI and AC-3)', + 7 =>'ISO/IEC 13522 (MHEG) in a packetized stream', + 8 =>'ITU-T Rec. H.222 and ISO/IEC 13818-1 DSM CC in a packetized stream', + 9 =>'ITU-T Rec. H.222 and ISO/IEC 13818-1/11172-1 auxiliary data in a packetized stream', + 10 =>'ISO/IEC 13818-6 DSM CC multiprotocol encapsulation ', + 11 =>'ISO/IEC 13818-6 DSM CC U-N messages', + 12 =>'ISO/IEC 13818-6 DSM CC stream descriptors', + 13 =>'ISO/IEC 13818-6 DSM CC tabled data', + 14 =>'ISO/IEC 13818-1 auxiliary data in a packetized stream', + 15 =>'ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) in a packetized stream', + 16 =>'ISO/IEC 14496-2 (MPEG-4 H.263 based video) in a packetized stream', + 17 =>'ISO/IEC 14496-3 (MPEG-4 LOAS multi-format framed audio) in a packetized stream', + 18 =>'ISO/IEC 14496-1 (MPEG-4 FlexMux) in a packetized stream', + 19 =>'ISO/IEC 14496-1 (MPEG-4 FlexMux) in ISO/IEC 14496 tables', + 20 =>'ISO/IEC 13818-6 DSM CC synchronized download protocol', + 21 =>'Packetized metadata', + 22 =>'Sectioned metadata', + 23 =>'ISO/IEC 13818-6 DSM CC Data Carousel metadata', + 24 =>'ISO/IEC 13818-6 DSM CC Object Carousel metadata', + 25 =>'ISO/IEC 13818-6 Synchronized Download Protocol metadata', + 26 =>'ISO/IEC 13818-11 IPMP', + 27 =>'ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) in a packetized stream', + 28 =>'ISO/IEC 14496-3 (MPEG-4 raw audio) in a packetized stream', + 29 =>'ISO/IEC 14496-17 (MPEG-4 text) in a packetized stream', + 30 =>'ISO/IEC 23002-3 (MPEG-4 auxiliary video) in a packetized stream', + 31 =>'ISO/IEC 14496-10 SVC (MPEG-4 AVC sub-bitstream) in a packetized stream', + 32 =>'ISO/IEC 14496-10 MVC (MPEG-4 AVC sub-bitstream) in a packetized stream', + 33 =>'ITU-T Rec. T.800 and ISO/IEC 15444 (JPEG 2000 video) in a packetized stream', + 36 =>'ITU-T Rec. H.265 and ISO/IEC 23008-2 (Ultra HD video) in a packetized stream', + 66 =>'Chinese Video Standard in a packetized stream', + 127 =>'ISO/IEC 13818-11 IPMP (DRM) in a packetized stream', + 128 =>'ITU-T Rec. H.262 and ISO/IEC 13818-2 with DES-64-CBC encryption for DigiCipher II or PCM audio for Blu-ray in a packetized stream', + 129 =>'Dolby Digital (AC-3) up to six channel audio for ATSC and Blu-ray in a packetized stream', + 130 =>'SCTE subtitle or DTS 6 channel audio for Blu-ray in a packetized stream', + 131 =>'Dolby TrueHD lossless audio for Blu-ray in a packetized stream', + 132 =>'Dolby Digital Plus (enhanced AC-3) up to 16 channel audio for Blu-ray in a packetized stream', + 133 =>'DTS 8 channel audio for Blu-ray in a packetized stream', + 134 =>'SCTE-35[5] digital program insertion cue message or DTS 8 channel lossless audio for Blu-ray in a packetized stream', + 135 =>'Dolby Digital Plus (enhanced AC-3) up to 16 channel audio for ATSC in a packetized stream', + 144 =>'Blu-ray Presentation Graphic Stream (subtitling) in a packetized stream', + 145 =>'ATSC DSM CC Network Resources table', + 192 =>'DigiCipher II text in a packetized stream', + 193 =>'Dolby Digital (AC-3) up to six channel audio with AES-128-CBC data encryption in a packetized stream', + 194 =>'ATSC DSM CC synchronous data or Dolby Digital Plus up to 16 channel audio with AES-128-CBC data encryption in a packetized stream', + 207 =>'ISO/IEC 13818-7 ADTS AAC with AES-128-CBC frame encryption in a packetized stream', + 209 =>'BBC Dirac (Ultra HD video) in a packetized stream', + 210 =>'Audio Video Standard AVS2 (Ultra HD video) in a packetized stream', + 211 =>'Audio Video Standard AVS3 Audio in a packetized stream', + 212 =>'Audio Video Standard AVS3 Video (Ultra HD video) in a packetized stream', + 219 =>'ITU-T Rec. H.264 and ISO/IEC 14496-10 with AES-128-CBC slice encryption in a packetized stream', + 234 =>'Microsoft Windows Media Video 9 (lower bit-rate video) in a packetized stream', + ]; + + public static function desc ($type) { + return self::ES_TYPES[$type]??'Unknown'; + } +} \ No newline at end of file diff --git a/src/Descriptors/Values/ProgramEsDescriptorTag.php b/src/Descriptors/Values/ProgramEsDescriptorTag.php new file mode 100644 index 0000000..abf256e --- /dev/null +++ b/src/Descriptors/Values/ProgramEsDescriptorTag.php @@ -0,0 +1,98 @@ +'Video stream header parameters for ITU-T Rec. H.262, ISO/IEC 13818-2 and ISO/IEC 11172-2', + 3 =>'Audio stream header parameters for ISO/IEC 13818-3 and ISO/IEC 11172-3', + 4 =>'Hierarchy for stream selection', + 5 =>'Registration of private formats', + 6 =>'Data stream alignment for packetized video and audio sync point', + 7 =>'Target background grid defines total display area size', + 8 =>'Video Window defines position in display area', + 9 =>'Conditional access system and EMM/ECM PID', + 10 =>'ISO 639 language and audio type', + 11 =>'System clock external reference', + 12 =>'Multiplex buffer utilization bounds', + 13 =>'Copyright identification system and reference', + 14 =>'Maximum bit rate', + 15 =>'Private data indicator', + 16 =>'Smoothing buffer', + 17 =>'STD video buffer leak control', + 18 =>'IBP video I-frame indicator', + 19 =>'ISO/IEC13818-6 DSM CC carousel identifier', + 20 =>'ISO/IEC13818-6 DSM CC association tag', + 21 =>'ISO/IEC13818-6 DSM CC deferred association tag', + 22 =>'ISO/IEC13818-6 DSM CC Reserved.', + 23 =>'DSM CC NPT reference', + 24 =>'DSM CC NPT endpoint', + 25 =>'DSM CC stream mode', + 26 =>'DSM CC stream event', + 27 =>'Video stream header parameters for ISO/IEC 14496-2 (MPEG-4 H.263 based)', + 28 =>'Audio stream header parameters for ISO/IEC 14496-3 (MPEG-4 LOAS multi-format framed)', + 29 =>'IOD parameters for ISO/IEC 14496-1', + 30 =>'SL parameters for ISO/IEC 14496-1', + 31 =>'FMC parameters for ISO/IEC 14496-1', + 32 =>'External ES identifier for ISO/IEC 14496-1', + 33 =>'MuxCode for ISO/IEC 14496-1', + 34 =>'FMX Buffer Size for ISO/IEC 14496-1', + 35 =>'Multiplex Buffer for ISO/IEC 14496-1', + 36 =>'Content labeling for ISO/IEC 14496-1', + 37 =>'Metadata pointer', + 38 =>'Metadata', + 39 =>'Metadata STD', + 40 =>'Video stream header parameters for ITU-T Rec. H.264 and ISO/IEC 14496-10', + 41 =>'ISO/IEC 13818-11 IPMP (DRM)', + 42 =>'Timing and HRD for ITU-T Rec. H.264 and ISO/IEC 14496-10', + 43 =>'Audio stream header parameters for ISO/IEC 13818-7 ADTS AAC', + 44 =>'FlexMux Timing for ISO/IEC 14496-1', + 45 =>'Text stream header parameters for ISO/IEC 14496', + 46 =>'Audio extension stream header parameters for ISO/IEC 14496-3 (MPEG-4 LOAS multi-format framed)', + 47 =>'Video auxiliary stream header parameters', + 48 =>'Video scalable stream header parameters', + 49 =>'Video multi stream header parameters', + 50 =>'Video stream header parameters for ITU-T Rec. T.800 and ISO/IEC 15444 (JPEG 2000)', + 51 =>'Video multi operation point stream header parameters', + 52 =>'Video stereoscopic (3D) stream header parameters for ITU-T Rec. H.262, ISO/IEC 13818-2 and ISO/IEC 11172-2', + 53 =>'Program stereoscopic (3D) information', + 54 =>'Video stereoscopic (3D) information', + 160 =>'VideoLAN FourCC, video size and codec initialization data', + ]; + + public static function desc ($tag) { + return self::TAG[$tag]??'Unknown'; + } +} \ No newline at end of file diff --git a/src/Descriptors/Values/ServiceType.php b/src/Descriptors/Values/ServiceType.php index a89312d..be392d7 100644 --- a/src/Descriptors/Values/ServiceType.php +++ b/src/Descriptors/Values/ServiceType.php @@ -72,6 +72,6 @@ public function __construct(int $value) if ($value < 0 || $value > 0xff) { throw new Exception("Invalid service type value: $value"); } - $this->value = $value; + parent::__construct($value); } } \ No newline at end of file diff --git a/src/Parser.php b/src/Parser.php index 72302a2..32e46d9 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -56,6 +56,10 @@ * The `eit` event will be emitted when an EIT table is decoded * The event will receive a single argument: PhpBg\DvbPsi\Tables\Eit instance * + * sdt event: + * The `sdt` event will be emitted when an SDT table is decoded + * The event will receive a single argument: PhpBg\DvbPsi\Tables\Sdt instance + * * parserAdd event: * The `parserAdd` will be emitted when a parser is added * diff --git a/src/ParserFactory.php b/src/ParserFactory.php index ad8cfcb..9d59fa2 100644 --- a/src/ParserFactory.php +++ b/src/ParserFactory.php @@ -29,6 +29,7 @@ use PhpBg\DvbPsi\TableParsers\Eit; use PhpBg\DvbPsi\TableParsers\Nit; use PhpBg\DvbPsi\TableParsers\Pat; +use PhpBg\DvbPsi\TableParsers\Sdt; use PhpBg\DvbPsi\TableParsers\Tdt; class ParserFactory @@ -44,6 +45,7 @@ public static function create(): Parser $parser->registerTableParser(new Nit()); $parser->registerTableParser(new Tdt()); $parser->registerTableParser(new Eit()); + $parser->registerTableParser(new Sdt()); return $parser; } } \ No newline at end of file diff --git a/src/TableParsers/Descriptor.php b/src/TableParsers/Descriptor.php new file mode 100644 index 0000000..8df416b --- /dev/null +++ b/src/TableParsers/Descriptor.php @@ -0,0 +1,85 @@ +descriptorTag = unpack('C', substr($data, $pointer, 1))[1]; + $pointer += 1; + + $descriptorLen = unpack('C', substr($data, $pointer, 1))[1]; + $pointer += 1; + + $tmp = substr($data, $pointer, $descriptorLen); + $pointer += $descriptorLen; + switch ($esDescriptor->descriptorTag) { + // 0x0a ISO 639 language and audio type + case 10: + $esDescriptor->properties = $this->languageDescriptor($tmp); + break; + // 0x0E Maximum bit rate + case 14: + $esDescriptor->properties = $this->bitrateDescriptor($tmp); + break; + } + $result[] = $esDescriptor; + } + return $result; + } + + protected function languageDescriptor($data) { + $tmp = unpack('N', $data)[1]; + $result = [ + 'language_code' => hex2bin(dechex(($tmp >> 8) & 0xffffff)), + 'audio_type' => $tmp & 0xff + ]; + return $result; + } + + protected function bitrateDescriptor($data) { + $tmp = unpack('C1a/n1b', $data); + $result = [ + 'maximum_bitrate' => ($tmp['a'] << 16 | $tmp['b']) & 0x3fffff, + ]; + return $result; + } + +} \ No newline at end of file diff --git a/src/TableParsers/Pmt.php b/src/TableParsers/Pmt.php index 7ae8fb7..b165bb8 100644 --- a/src/TableParsers/Pmt.php +++ b/src/TableParsers/Pmt.php @@ -27,6 +27,7 @@ namespace PhpBg\DvbPsi\TableParsers; use PhpBg\DvbPsi\Tables\Identifier; +use PhpBg\DvbPsi\Tables\PtmEsStream; use PhpBg\MpegTs\Pid; /** @@ -36,6 +37,8 @@ class Pmt implements TableParserInterface { + use Descriptor; + protected $pids = []; /** @@ -101,19 +104,23 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect // Elementary stream info data while ($currentPointer < $crcOffset) { + $es = new PtmEsStream(); $elementaryStreamHeaderBin = substr($data, $currentPointer, 5); $currentPointer += 5; $elementaryStreamHeaderArray = unpack('C1st/n2headers', $elementaryStreamHeaderBin); - $streamType = $elementaryStreamHeaderArray['st']; + $es->streamType = $elementaryStreamHeaderArray['st']; + $pid = $elementaryStreamHeaderArray['headers1'] & 0x1FFF; $descriptorsLen = $elementaryStreamHeaderArray['headers2'] & 0x3FF; - //TODO handle $streamType and descriptors + // Descriptor info data + $descriptorsBin = substr($data, $currentPointer, $descriptorsLen); $currentPointer += $descriptorsLen; - $pmt->streams[$pid] = null; + $es->descriptors = $this->parseDescriptors($descriptorsBin); + $pmt->streams[$pid] = $es; } } diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php new file mode 100644 index 0000000..cc8b880 --- /dev/null +++ b/src/TableParsers/Sdt.php @@ -0,0 +1,111 @@ +tableId = $tableId; + + $crcOffset = $currentPointer + $sectionLength - 4; + + $sdt->transportStreamId = unpack('n', substr($data, $currentPointer, 2))[1]; + $currentPointer += 2; + + $tmp = unpack('C', $data[$currentPointer])[1]; + $currentPointer += 1; + $sdt->versionNumber = ($tmp >> 1) & 0x1f; + $sdt->currentNextIndicator = $tmp & 0x01; + + $sdt->sectionNumber = unpack('C', $data[$currentPointer])[1]; + $currentPointer += 1; + + $sdt->lastSectionNumber = unpack('C', $data[$currentPointer])[1]; + $currentPointer += 1; + + $sdt->originalNetworkId = unpack('n', substr($data, $currentPointer, 2))[1]; + $currentPointer += 3; + while ($currentPointer < $crcOffset) { + $sdtService = new SdtService(); + + $sdtService->serviceId = unpack('n', substr($data, $currentPointer, 2))[1]; + $currentPointer += 2; + + $tmp = unpack('C', $data[$currentPointer])[1]; + $currentPointer += 1; + $sdtService->eitScheduleFlag = ($tmp >> 1) & 0x01; + $sdtService->eitPresentFollowingFlag = $tmp & 0x01; + + $tmp = unpack('n', substr($data, $currentPointer, 2))[1]; + $currentPointer += 2; + $sdtService->runningStatus = ($tmp >> 13) & 0x7; + $sdtService->freeCaMode = ($tmp >> 12) & 0x1; + $loopLength = $tmp & 0xfff; + if ($currentPointer + $loopLength > $crcOffset) { + throw new Exception("Unexpected descriptors loop length"); + } + $sdtService->descriptors = $this->parseDescriptorsLoop($data, $currentPointer, $loopLength); + $currentPointer += $loopLength; + + $sdt->services[] = $sdtService; + } + + //TODO check CRC + $crc = unpack('N', substr($data, $currentPointer, 4))[1]; + + return $sdt; + } +} \ No newline at end of file diff --git a/src/TableParsers/TableParserAbstract.php b/src/TableParsers/TableParserAbstract.php index af5538f..52a0149 100644 --- a/src/TableParsers/TableParserAbstract.php +++ b/src/TableParsers/TableParserAbstract.php @@ -34,6 +34,7 @@ use PhpBg\DvbPsi\Descriptors\ParentalRating; use PhpBg\DvbPsi\Descriptors\PrivateDataSpecifier; use PhpBg\DvbPsi\Descriptors\PrivateDescriptors\EACEM\LogicalChannel; +use PhpBg\DvbPsi\Descriptors\ServiceDescriptor; use PhpBg\DvbPsi\Descriptors\ServiceList; use PhpBg\DvbPsi\Descriptors\ShortEvent; use PhpBg\DvbPsi\Descriptors\TerrestrialDeliverySystem; @@ -142,6 +143,8 @@ protected function parseDescriptor(int $descriptorId, string $data, int $current case Identifier::CONTENT_DESCRIPTOR: return new Content($descriptorData); + case Identifier::SERVICE_DESCRIPTOR: + return new ServiceDescriptor($descriptorData); // Private descriptors case 0x83: diff --git a/src/TableParsers/Text.php b/src/TableParsers/Text.php index aadac09..3189c89 100644 --- a/src/TableParsers/Text.php +++ b/src/TableParsers/Text.php @@ -131,6 +131,6 @@ protected function toUtf8String($string): string throw new Exception("Encoding invalid"); } - return iconv($encoding, 'UTF-8', substr($string, $stringPointer)); + return @iconv($encoding, 'UTF-8', substr($string, $stringPointer))?:$encoding; } } \ No newline at end of file diff --git a/src/Tables/Identifier.php b/src/Tables/Identifier.php index de4307f..10bc980 100644 --- a/src/Tables/Identifier.php +++ b/src/Tables/Identifier.php @@ -45,7 +45,8 @@ class Identifier extends Enum const NETWORK_INFORMATION_SECTION_ACTUAL_NETWORK = 0x40; const NETWORK_INFORMATION_SECTION_OTHER_NETWORK = 0x41; - + const SERVICE_DESCRIPTION_SECTION_ACTUAL_TRANSPORT_STREAM = 0x42; + const SERVICE_DESCRIPTION_SECTION_OTHER_TRANSPORT_STREAM = 0x46; const EVENT_INFORMATION_SECTION_ACTUAL_TS_PRESENT_FOLLOWING = 0x4E; const EVENT_INFORMATION_SECTION_OTHER_TS_PRESENT_FOLLOWING = 0x4F; diff --git a/src/Tables/Pmt.php b/src/Tables/Pmt.php index 37949d6..0487f76 100644 --- a/src/Tables/Pmt.php +++ b/src/Tables/Pmt.php @@ -84,7 +84,7 @@ public function __toString() $buf .= "Streams:\n"; $buf .= "\tPID > Elementary stream descriptor\n"; foreach ($this->streams as $pid => $esDescriptor) { - $buf .= sprintf("\t%d (0x%x) > ??? TODO ???\n", $pid, $pid); + $buf .= sprintf("\t%d (0x%x) > %s\n", $pid, $pid, $esDescriptor); } return $buf; diff --git a/src/Tables/PtmEsDescriptor.php b/src/Tables/PtmEsDescriptor.php new file mode 100644 index 0000000..62f0b3f --- /dev/null +++ b/src/Tables/PtmEsDescriptor.php @@ -0,0 +1,46 @@ +properties as $key => $value) { + $str .= sprintf("%s => %s\n", $key, $value); + } + return $str; + } +} \ No newline at end of file diff --git a/src/Tables/PtmEsStream.php b/src/Tables/PtmEsStream.php new file mode 100644 index 0000000..6bbff8b --- /dev/null +++ b/src/Tables/PtmEsStream.php @@ -0,0 +1,49 @@ +streamType, EsType::desc($this->streamType)); + foreach ($this->descriptors as $descriptor) { + $str .= "{$descriptor}\n"; + } + return $str; + } +} \ No newline at end of file diff --git a/src/Tables/Sdt.php b/src/Tables/Sdt.php new file mode 100644 index 0000000..c5bd975 --- /dev/null +++ b/src/Tables/Sdt.php @@ -0,0 +1,111 @@ +transportStreamId, $this->transportStreamId); + $msg .= sprintf("Network ID: %d (0x%x)\n", $this->originalNetworkId, $this->originalNetworkId); + $msg .= sprintf("Transport stream id: %d (0x%x)\n", $this->transportStreamId, $this->transportStreamId); + $msg .= sprintf("Version: %d (0x%x)\n", $this->versionNumber, $this->versionNumber); + $msg .= sprintf("Current next indicator: %d\n", $this->currentNextIndicator); + $msg .= sprintf("Section: %d/%d\n", $this->sectionNumber, $this->lastSectionNumber); + + foreach ($this->services as $service) { + $msg .= "Service:\n{$service}\n"; + } + + return $msg; + } +} \ No newline at end of file diff --git a/src/Tables/SdtService.php b/src/Tables/SdtService.php new file mode 100644 index 0000000..a53cfce --- /dev/null +++ b/src/Tables/SdtService.php @@ -0,0 +1,107 @@ +runningStatus)) { + return SdtRunningStatus::UNDEFINED(); + } + return new SdtRunningStatus($this->runningStatus); + } + + /** + * This is a convenient method to get text from short event descriptor (if any) + * @return null|string + */ + public function getShortEventText() { + foreach ($this->descriptors as $descriptor) { + if (! $descriptor instanceof ShortEvent) { + continue; + } + return $descriptor->eventName . ' ' . $descriptor->text; + } + return null; + } + + public function __toString() + { + $str = sprintf("Service id: %d (0x%x)\n", $this->serviceId, $this->serviceId); + $runningStatus = $this->getRunningStatus(); + $str .= "Running status: " . $runningStatus->getKey() . "\n"; + $str .= sprintf("Scrambled: %s\n", $this->freeCaMode === 1 ? 'yes' : 'no'); + foreach ($this->descriptors as $descriptor) { + $str .= "{$descriptor}\n"; + } + return $str; + } +} \ No newline at end of file diff --git a/src/Tables/Values/SdtRunningStatus.php b/src/Tables/Values/SdtRunningStatus.php new file mode 100644 index 0000000..3c8082d --- /dev/null +++ b/src/Tables/Values/SdtRunningStatus.php @@ -0,0 +1,43 @@ +assertInstanceOf(LogicalChannel::class, $lcn); $this->assertSame(5, count($lcn->services)); } + + public function testParseSdt() + { + $data = $this->getTestFileContent('2_mpegts_sdt_packets.ts'); + + $mpegTsParser = new \PhpBg\MpegTs\Parser(); + $mpegTsParser->filterAllPids = true; + + $dvbPsiParser = ParserFactory::create(); + $sdtParser = new TableParsers\Sdt(); + $dvbPsiParser->registerTableParser($sdtParser); + + $dvbPsiParser->on('error', function ($e) { + $this->assertTrue(false); + }); + $incomingSdt = null; + $dvbPsiParser->on('sdt', function ($sdt) use (&$incomingSdt) { + if ($incomingSdt !== null) { + throw new \Exception('Only one sdt is expected'); + } + $incomingSdt = $sdt; + }); + $mpegTsParser->on('error', function ($e) { + $this->assertTrue(false); + }); + $mpegTsParser->on('pes', function ($pid, $data) use ($dvbPsiParser) { + $dvbPsiParser->write($pid, $data); + }); + + $mpegTsParser->write($data); + + $this->assertInstanceOf(Sdt::class, $incomingSdt); + $this->assertSame(34, $incomingSdt->transportStreamId); + $this->assertSame(65280, $incomingSdt->originalNetworkId); + $this->assertSame(2, $incomingSdt->versionNumber); + $this->assertSame(1, $incomingSdt->currentNextIndicator); + $this->assertSame(0, $incomingSdt->sectionNumber); + $this->assertSame(0, $incomingSdt->lastSectionNumber); + $this->assertSame(1115, $incomingSdt->services[0]->serviceId); + $this->assertSame(0, $incomingSdt->services[0]->eitScheduleFlag); + $this->assertSame(0, $incomingSdt->services[0]->eitPresentFollowingFlag); + $this->assertSame(4, $incomingSdt->services[0]->runningStatus); + $this->assertSame(0, $incomingSdt->services[0]->freeCaMode); + $this->assertSame(0x19, $incomingSdt->services[0]->descriptors[0]->serviceType->getValue()); + $this->assertSame('EDMR', $incomingSdt->services[0]->descriptors[0]->serviceProviderName); + $this->assertSame('VEXEHD', $incomingSdt->services[0]->descriptors[0]->serviceName); + } } \ No newline at end of file diff --git a/tests/data/2_mpegts_sdt_packets.ts b/tests/data/2_mpegts_sdt_packets.ts new file mode 100644 index 0000000..5a4cc31 Binary files /dev/null and b/tests/data/2_mpegts_sdt_packets.ts differ