From 74c03744b880f5dc5bb7798d43fa517dba39526d Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 12:11:47 +0200 Subject: [PATCH 01/18] Init --- src/Descriptors/Identifier.php | 1 + src/Descriptors/ServiceDescriptor.php | 68 ++++++++++++++++++++++++ src/TableParsers/TableParserAbstract.php | 3 ++ 3 files changed, 72 insertions(+) create mode 100644 src/Descriptors/ServiceDescriptor.php 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..c2a5cd2 --- /dev/null +++ b/src/Descriptors/ServiceDescriptor.php @@ -0,0 +1,68 @@ + => + */ + public $services; + + /** + * ServiceList constructor. + * @param $data + * @throws \PhpBg\DvbPsi\Exception + */ + public function __construct($data) + { + $pointer = 0; + $len = strlen($data); + while ($pointer < $len) { + $serviceId = unpack('n', substr($data, $pointer, 2))[1]; + $pointer += 2; + $serviceType = unpack('C', $data[$pointer])[1]; + $pointer += 1; + $this->services[$serviceId] = new ServiceType($serviceType); + } + } + + public function __toString() + { + $msg = "Services:\n"; + foreach ($this->services as $serviceId => $serviceType) { + $msg .= sprintf("Service ID: %d (0x%x) => %s\n", $serviceId, $serviceId, $serviceType->getKey()); + } + return $msg; + } +} \ 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: From 9a7abd5ab8c39eb5ffcba6a354ddc3c077085b98 Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 12:50:09 +0200 Subject: [PATCH 02/18] SDT parser --- src/TableParsers/Sdt.php | 123 ++++++++++++++++++++++++++++++++++++++ src/Tables/Sdt.php | 111 ++++++++++++++++++++++++++++++++++ src/Tables/SdtService.php | 109 +++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 src/TableParsers/Sdt.php create mode 100644 src/Tables/Sdt.php create mode 100644 src/Tables/SdtService.php diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php new file mode 100644 index 0000000..9139a54 --- /dev/null +++ b/src/TableParsers/Sdt.php @@ -0,0 +1,123 @@ +tableId = $tableId; + + //section_length + $headersBin = substr($data, $currentPointer + 1, 2); + $headers = unpack('n', $headersBin)[1]; + + //The section_length is a 12-bit field. + // For some tables, it may use less bits (eg. NIT uses 10bits). In that case MSB are set to 0 + $sectionLength = $headers & 0xfff; + var_dump($sectionLength); + + //Move pointer after 3 bytes of header + $currentPointer += 3; + $crcOffset = $currentPointer + $sectionLength - 4; + $sdt->transportStreamId = unpack('n', substr($data, $currentPointer, 2))[1]; + var_dump($sdt->transportStreamId); + + $currentPointer += 2; + $tmp = unpack('C', $data[$currentPointer])[1]; + $sdt->versionNumber = ($tmp >> 1) & 0x1f; + $sdt->currentNextIndicator = $tmp & 0x01; + + $currentPointer += 1; + $sectionNumber = unpack('C', $data[$currentPointer])[1]; + + $currentPointer += 1; + $lastSectionNumber = unpack('C', $data[$currentPointer])[1]; + + $currentPointer += 1; + $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]; + var_dump($sdtService->serviceId); + + $currentPointer += 2; + $tmp = unpack('C', $data[$currentPointer])[1]; + $sdtService->eitScheduleFlag = ($tmp >> 1) & 0x01; + $sdtService->eitPresentFollowingFlag = $tmp & 0x01; + + $currentPointer += 1; + $tmp = unpack('n', substr($data, $currentPointer, 2))[1]; + $runningStatus = ($tmp >> 13) & 0x7; + $freeCaMode = ($tmp >> 12) & 0x1; + $loopLength = $tmp & 0xfff; + var_dump($loopLength); + if ($currentPointer + $loopLength > $crcOffset) { + throw new Exception("Unexpected descriptors loop length"); + } + $currentPointer += 2; + $descriptors = $this->parseDescriptorsLoop($data, $currentPointer, $loopLength); + var_dump($descriptors); + $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/Tables/Sdt.php b/src/Tables/Sdt.php new file mode 100644 index 0000000..0ae54de --- /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..f8a5fcb --- /dev/null +++ b/src/Tables/SdtService.php @@ -0,0 +1,109 @@ +runningStatus)) { + return EitRunningStatus::UNDEFINED(); + } + return new EitRunningStatus($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); +// $str .= sprintf("Start %d (%s)\n", $this->startTimestamp, date('Y-m-d H:i:s', $this->startTimestamp)); +// $str .= sprintf("Duration %d (until %s)\n", $this->duration, date('Y-m-d H:i:s', $this->startTimestamp + $this->duration)); + $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 From 1df31177c79eab0ee5587a52f0cee0b8ce530d41 Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 13:10:40 +0200 Subject: [PATCH 03/18] SDT identifier --- src/TableParsers/Sdt.php | 5 ++++- src/Tables/Identifier.php | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php index 9139a54..0ac8973 100644 --- a/src/TableParsers/Sdt.php +++ b/src/TableParsers/Sdt.php @@ -44,7 +44,10 @@ public function getPids(): array public function getTableIds(): array { - return []; + return [ + Identifier::SERVICE_DESCRIPTION_SECTION_ACTUAL_TRANSPORT_STREAM, + Identifier::SERVICE_DESCRIPTION_SECTION_OTHER_TRANSPORT_STREAM + ]; } public function getEventName(): string 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; From 2537c6d3bc7bd523e5907b8375e6a62062134e26 Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 13:20:18 +0200 Subject: [PATCH 04/18] SDT parser improve --- src/TableParsers/Sdt.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php index 0ac8973..7f4636f 100644 --- a/src/TableParsers/Sdt.php +++ b/src/TableParsers/Sdt.php @@ -60,18 +60,8 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $sdt = new \PhpBg\DvbPsi\Tables\Sdt(); $sdt->tableId = $tableId; - //section_length - $headersBin = substr($data, $currentPointer + 1, 2); - $headers = unpack('n', $headersBin)[1]; - - //The section_length is a 12-bit field. - // For some tables, it may use less bits (eg. NIT uses 10bits). In that case MSB are set to 0 - $sectionLength = $headers & 0xfff; - var_dump($sectionLength); - - //Move pointer after 3 bytes of header - $currentPointer += 3; $crcOffset = $currentPointer + $sectionLength - 4; + $sdt->transportStreamId = unpack('n', substr($data, $currentPointer, 2))[1]; var_dump($sdt->transportStreamId); From 53a7ef7642f3a725f42c4b50a17734cc6d64c967 Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 14:30:14 +0200 Subject: [PATCH 05/18] SDT parser improve --- src/Descriptors/ServiceDescriptor.php | 42 +++++++++++++++---------- src/TableParsers/Sdt.php | 10 ++---- src/Tables/SdtService.php | 10 +++--- src/Tables/Values/SdtRunningStatus.php | 43 ++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 src/Tables/Values/SdtRunningStatus.php diff --git a/src/Descriptors/ServiceDescriptor.php b/src/Descriptors/ServiceDescriptor.php index c2a5cd2..dc67342 100644 --- a/src/Descriptors/ServiceDescriptor.php +++ b/src/Descriptors/ServiceDescriptor.php @@ -30,39 +30,49 @@ /** * Class ServiceList - * @see Final draft ETSI EN 300 468 V1.15.1 (2016-03), 6.2.34 Service availability descriptor + * @see Final draft ETSI EN 300 468 V1.15.1 (2016-03), 6.2.33 Service descriptor */ class ServiceDescriptor { + /** - * @var array => + * This is an 8-bit field specifying the type of the service. The assignment of service_type value for a + * service is described in annex I. */ - public $services; + public $serviceType; + public $serviceProviderName; + public $serviceName; /** - * ServiceList constructor. + * ServiceDescriptor constructor. * @param $data * @throws \PhpBg\DvbPsi\Exception */ public function __construct($data) { $pointer = 0; - $len = strlen($data); - while ($pointer < $len) { - $serviceId = unpack('n', substr($data, $pointer, 2))[1]; - $pointer += 2; - $serviceType = unpack('C', $data[$pointer])[1]; - $pointer += 1; - $this->services[$serviceId] = new ServiceType($serviceType); - } + $serviceType = unpack('C', $data[ $pointer])[1]; + $this->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 = "Services:\n"; - foreach ($this->services as $serviceId => $serviceType) { - $msg .= sprintf("Service ID: %d (0x%x) => %s\n", $serviceId, $serviceId, $serviceType->getKey()); - } + $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/TableParsers/Sdt.php b/src/TableParsers/Sdt.php index 7f4636f..03309a2 100644 --- a/src/TableParsers/Sdt.php +++ b/src/TableParsers/Sdt.php @@ -63,7 +63,6 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $crcOffset = $currentPointer + $sectionLength - 4; $sdt->transportStreamId = unpack('n', substr($data, $currentPointer, 2))[1]; - var_dump($sdt->transportStreamId); $currentPointer += 2; $tmp = unpack('C', $data[$currentPointer])[1]; @@ -84,7 +83,6 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $sdtService = new SdtService(); $sdtService->serviceId = unpack('n', substr($data, $currentPointer, 2))[1]; - var_dump($sdtService->serviceId); $currentPointer += 2; $tmp = unpack('C', $data[$currentPointer])[1]; @@ -93,16 +91,14 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $currentPointer += 1; $tmp = unpack('n', substr($data, $currentPointer, 2))[1]; - $runningStatus = ($tmp >> 13) & 0x7; - $freeCaMode = ($tmp >> 12) & 0x1; + $sdtService->runningStatus = ($tmp >> 13) & 0x7; + $sdtService->freeCaMode = ($tmp >> 12) & 0x1; $loopLength = $tmp & 0xfff; - var_dump($loopLength); if ($currentPointer + $loopLength > $crcOffset) { throw new Exception("Unexpected descriptors loop length"); } $currentPointer += 2; - $descriptors = $this->parseDescriptorsLoop($data, $currentPointer, $loopLength); - var_dump($descriptors); + $sdtService->descriptors = $this->parseDescriptorsLoop($data, $currentPointer, $loopLength); $currentPointer += $loopLength; $sdt->services[] = $sdtService; diff --git a/src/Tables/SdtService.php b/src/Tables/SdtService.php index f8a5fcb..a53cfce 100644 --- a/src/Tables/SdtService.php +++ b/src/Tables/SdtService.php @@ -27,7 +27,7 @@ namespace PhpBg\DvbPsi\Tables; use PhpBg\DvbPsi\Descriptors\ShortEvent; -use PhpBg\DvbPsi\Tables\Values\EitRunningStatus; +use PhpBg\DvbPsi\Tables\Values\SdtRunningStatus; class SdtService { @@ -72,11 +72,11 @@ class SdtService public $descriptors = []; - public function getRunningStatus(): EitRunningStatus { + public function getRunningStatus(): SdtRunningStatus { if (! isset($this->runningStatus)) { - return EitRunningStatus::UNDEFINED(); + return SdtRunningStatus::UNDEFINED(); } - return new EitRunningStatus($this->runningStatus); + return new SdtRunningStatus($this->runningStatus); } /** @@ -96,8 +96,6 @@ public function getShortEventText() { public function __toString() { $str = sprintf("Service id: %d (0x%x)\n", $this->serviceId, $this->serviceId); -// $str .= sprintf("Start %d (%s)\n", $this->startTimestamp, date('Y-m-d H:i:s', $this->startTimestamp)); -// $str .= sprintf("Duration %d (until %s)\n", $this->duration, date('Y-m-d H:i:s', $this->startTimestamp + $this->duration)); $runningStatus = $this->getRunningStatus(); $str .= "Running status: " . $runningStatus->getKey() . "\n"; $str .= sprintf("Scrambled: %s\n", $this->freeCaMode === 1 ? 'yes' : 'no'); 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 @@ + Date: Tue, 18 Mar 2025 14:54:08 +0200 Subject: [PATCH 06/18] SDT parser improve --- src/Descriptors/Values/ServiceType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From ebd7d9f4283a4ee743ef542090d6c2b9a26a9c2f Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 16:51:19 +0200 Subject: [PATCH 07/18] SDT parser improve --- src/Context/StreamContext.php | 17 +++++++++++++++++ src/Parser.php | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index 663217f..2e330ef 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 $sdt; + + public function addPat(Pat $pat) { if (!isset($this->pat) || $this->pat->version < $pat->version || ($this->pat->version !== 0 && $pat->version === 0)) { @@ -59,6 +66,16 @@ public function addPat(Pat $pat) } } + public function addSdt(Sdt $sdt) + { + if (!isset($this->sdt) || $this->sdt->versionNumber < $sdt->versionNumber || ($this->sdt->versionNumber !== 0 && $sdt->versionNumber === 0)) { + $oldSdt = $this->sdt; + $this->sdt = $sdt; + $this->pmts = []; + $this->emit('sdt-update', [$this->sdt, $oldSdt]); + } + } + 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)) { 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 * From c380d85a054d2ae126a1771c0c56dc8808d3478f Mon Sep 17 00:00:00 2001 From: tolich Date: Tue, 18 Mar 2025 19:02:33 +0200 Subject: [PATCH 08/18] SDT parser improve --- src/Context/StreamContext.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index 2e330ef..cd5f3bf 100644 --- a/src/Context/StreamContext.php +++ b/src/Context/StreamContext.php @@ -51,9 +51,9 @@ class StreamContext extends EventEmitter public $pmts; /** - * @var Sdt + * @var Sdt[] */ - public $sdt; + public $sdts; public function addPat(Pat $pat) @@ -68,11 +68,12 @@ public function addPat(Pat $pat) public function addSdt(Sdt $sdt) { - if (!isset($this->sdt) || $this->sdt->versionNumber < $sdt->versionNumber || ($this->sdt->versionNumber !== 0 && $sdt->versionNumber === 0)) { - $oldSdt = $this->sdt; - $this->sdt = $sdt; - $this->pmts = []; - $this->emit('sdt-update', [$this->sdt, $oldSdt]); + 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('update'); } } From 54793667e29f06b6c10e1a95ac70373f5816e946 Mon Sep 17 00:00:00 2001 From: tolich Date: Thu, 20 Mar 2025 10:45:40 +0200 Subject: [PATCH 09/18] SDT parser improve --- src/Context/StreamContext.php | 1 + src/Descriptors/Descriptor.php | 58 ++++++++++++++++++++++++++++++++++ src/TableParsers/Pmt.php | 17 ++++++++-- src/TableParsers/Sdt.php | 15 ++++----- src/TableParsers/Text.php | 2 +- src/Tables/Pmt.php | 2 +- src/Tables/PtmEsDescriptor.php | 46 +++++++++++++++++++++++++++ src/Tables/PtmEsStream.php | 55 ++++++++++++++++++++++++++++++++ 8 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 src/Descriptors/Descriptor.php create mode 100644 src/Tables/PtmEsDescriptor.php create mode 100644 src/Tables/PtmEsStream.php diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index cd5f3bf..ced67c6 100644 --- a/src/Context/StreamContext.php +++ b/src/Context/StreamContext.php @@ -75,6 +75,7 @@ public function addSdt(Sdt $sdt) $this->sdts[$sdt->transportStreamId] = $sdt; $this->emit('update'); } + } public function addPmt(Pmt $pmt) diff --git a/src/Descriptors/Descriptor.php b/src/Descriptors/Descriptor.php new file mode 100644 index 0000000..230c3c8 --- /dev/null +++ b/src/Descriptors/Descriptor.php @@ -0,0 +1,58 @@ +descriptorTag = $tag; + switch ($tag) { + // 0x0a ISO 639 language and audio type + case 10: + $esDescriptor->properties = $this->languageDescriptor($data); + break; + } + return $esDescriptor; + } + + protected function languageDescriptor($data) { + $tmp = unpack('N', $data)[1]; + $result = [ + 'language_code' => hex2bin(dechex(($tmp >> 8) & 0xffffff)), + 'audio_type' => $tmp & 0xff + ]; + return $result; + } +} \ No newline at end of file diff --git a/src/TableParsers/Pmt.php b/src/TableParsers/Pmt.php index 7ae8fb7..f0604e6 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,27 @@ 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 + $descriptorBin = substr($data, $currentPointer, $descriptorsLen); $currentPointer += $descriptorsLen; - $pmt->streams[$pid] = null; + $es->descriptorTag = unpack('C', $descriptorBin[0])[1]; + $descriptorLength = unpack('C', $descriptorBin[1])[1]; + + $tmp = substr($descriptorBin, 2, $descriptorLength); + $es->descriptors[] = $this->parseDescriptor($es->descriptorTag, $tmp); + $pmt->streams[$pid] = $es; } } diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php index 03309a2..081f1cb 100644 --- a/src/TableParsers/Sdt.php +++ b/src/TableParsers/Sdt.php @@ -63,41 +63,40 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $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; - $currentPointer += 1; $sectionNumber = unpack('C', $data[$currentPointer])[1]; - $currentPointer += 1; - $lastSectionNumber = unpack('C', $data[$currentPointer])[1]; + $lastSectionNumber = unpack('C', $data[$currentPointer])[1]; $currentPointer += 1; - $originalNetworkId = unpack('n', substr($data, $currentPointer, 2))[1]; + $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; - $currentPointer += 1; $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"); } - $currentPointer += 2; $sdtService->descriptors = $this->parseDescriptorsLoop($data, $currentPointer, $loopLength); $currentPointer += $loopLength; 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/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..15adfc6 --- /dev/null +++ b/src/Tables/PtmEsStream.php @@ -0,0 +1,55 @@ +streamType, EsType::desc($this->streamType)); + $str .= sprintf("%d %s\n", $this->descriptorTag, ProgramEsDescriptorTag::desc($this->descriptorTag)); + foreach ($this->descriptors as $descriptor) { + $str .= "{$descriptor}\n"; + } + return $str; + } +} \ No newline at end of file From 555dbe1aeb606266cd65bda1e9897bfd51dae513 Mon Sep 17 00:00:00 2001 From: tolich Date: Thu, 20 Mar 2025 10:47:08 +0200 Subject: [PATCH 10/18] SDT parser improve --- src/{Descriptors => TableParsers}/Descriptor.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{Descriptors => TableParsers}/Descriptor.php (100%) diff --git a/src/Descriptors/Descriptor.php b/src/TableParsers/Descriptor.php similarity index 100% rename from src/Descriptors/Descriptor.php rename to src/TableParsers/Descriptor.php From 5e620ae50260363ef3b4283213a6587250a05c6d Mon Sep 17 00:00:00 2001 From: tolich Date: Thu, 20 Mar 2025 10:48:40 +0200 Subject: [PATCH 11/18] SDT parser improve --- src/Descriptors/Values/EsType.php | 100 ++++++++++++++++++ .../Values/ProgramEsDescriptorTag.php | 98 +++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 src/Descriptors/Values/EsType.php create mode 100644 src/Descriptors/Values/ProgramEsDescriptorTag.php 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 From 2a1de7466b7b4d324a94d9648d91849344b41ca6 Mon Sep 17 00:00:00 2001 From: tolich Date: Thu, 20 Mar 2025 15:14:34 +0200 Subject: [PATCH 12/18] PMT parser improve --- src/TableParsers/Pmt.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/TableParsers/Pmt.php b/src/TableParsers/Pmt.php index f0604e6..ee6fa15 100644 --- a/src/TableParsers/Pmt.php +++ b/src/TableParsers/Pmt.php @@ -112,17 +112,17 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $es->streamType = $elementaryStreamHeaderArray['st']; $pid = $elementaryStreamHeaderArray['headers1'] & 0x1FFF; - $descriptorsLen = $elementaryStreamHeaderArray['headers2'] & 0x3FF; + if ($descriptorsLen = $elementaryStreamHeaderArray['headers2'] & 0x3FF) { + // Descriptor info data + $descriptorBin = substr($data, $currentPointer, $descriptorsLen); + $currentPointer += $descriptorsLen; - // Descriptor info data - $descriptorBin = substr($data, $currentPointer, $descriptorsLen); - $currentPointer += $descriptorsLen; + $es->descriptorTag = unpack('C', $descriptorBin[0])[1]; + $descriptorLength = unpack('C', $descriptorBin[1])[1]; - $es->descriptorTag = unpack('C', $descriptorBin[0])[1]; - $descriptorLength = unpack('C', $descriptorBin[1])[1]; - - $tmp = substr($descriptorBin, 2, $descriptorLength); - $es->descriptors[] = $this->parseDescriptor($es->descriptorTag, $tmp); + $tmp = substr($descriptorBin, 2, $descriptorLength); + $es->descriptors[] = $this->parseDescriptor($es->descriptorTag, $tmp); + } $pmt->streams[$pid] = $es; } From c317f9a6f04115f28be511a201235c94d5faa14d Mon Sep 17 00:00:00 2001 From: tolich Date: Sun, 23 Mar 2025 11:00:50 +0200 Subject: [PATCH 13/18] Parse descriptors improve --- src/TableParsers/Descriptor.php | 51 ++++++++++++++++++++++++++------- src/TableParsers/Pmt.php | 14 ++++----- src/Tables/PtmEsStream.php | 6 ---- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/TableParsers/Descriptor.php b/src/TableParsers/Descriptor.php index 230c3c8..a045a53 100644 --- a/src/TableParsers/Descriptor.php +++ b/src/TableParsers/Descriptor.php @@ -33,18 +33,38 @@ trait Descriptor /** * @param $tag * @param $data - * @return PtmEsDescriptor + * @return PtmEsDescriptor[] */ - protected function parseDescriptor($tag, $data) { - $esDescriptor = new PtmEsDescriptor(); - $esDescriptor->descriptorTag = $tag; - switch ($tag) { - // 0x0a ISO 639 language and audio type - case 10: - $esDescriptor->properties = $this->languageDescriptor($data); - break; + protected function parseDescriptors($data) { + $result = []; + $pointer = 0; + $dataLen = strlen($data); + + while ($pointer < $dataLen) { + $esDescriptor = new PtmEsDescriptor(); + $esDescriptor->descriptorTag = unpack('C', substr($data, $pointer, 1))[1]; + $pointer += 1; + + $descriptorLen = unpack('C', substr($data, $pointer, 1))[1]; + $pointer += 1; + + var_dump(sprintf("0x%x %d", $esDescriptor->descriptorTag, $descriptorLen)); + + $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 $esDescriptor; + return $result; } protected function languageDescriptor($data) { @@ -55,4 +75,15 @@ protected function languageDescriptor($data) { ]; return $result; } + + protected function bitrateDescriptor($data) { + var_dump(strlen($data)); + $tmp = unpack('C1a/n1b', $data); + var_dump($tmp); + $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 ee6fa15..b165bb8 100644 --- a/src/TableParsers/Pmt.php +++ b/src/TableParsers/Pmt.php @@ -112,17 +112,13 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $es->streamType = $elementaryStreamHeaderArray['st']; $pid = $elementaryStreamHeaderArray['headers1'] & 0x1FFF; - if ($descriptorsLen = $elementaryStreamHeaderArray['headers2'] & 0x3FF) { - // Descriptor info data - $descriptorBin = substr($data, $currentPointer, $descriptorsLen); - $currentPointer += $descriptorsLen; + $descriptorsLen = $elementaryStreamHeaderArray['headers2'] & 0x3FF; - $es->descriptorTag = unpack('C', $descriptorBin[0])[1]; - $descriptorLength = unpack('C', $descriptorBin[1])[1]; + // Descriptor info data + $descriptorsBin = substr($data, $currentPointer, $descriptorsLen); + $currentPointer += $descriptorsLen; - $tmp = substr($descriptorBin, 2, $descriptorLength); - $es->descriptors[] = $this->parseDescriptor($es->descriptorTag, $tmp); - } + $es->descriptors = $this->parseDescriptors($descriptorsBin); $pmt->streams[$pid] = $es; } diff --git a/src/Tables/PtmEsStream.php b/src/Tables/PtmEsStream.php index 15adfc6..6bbff8b 100644 --- a/src/Tables/PtmEsStream.php +++ b/src/Tables/PtmEsStream.php @@ -36,17 +36,11 @@ class PtmEsStream */ public $streamType; - /** - * @var int - */ - public $descriptorTag; - public $descriptors = []; public function __toString() { $str = sprintf("%d %s\n", $this->streamType, EsType::desc($this->streamType)); - $str .= sprintf("%d %s\n", $this->descriptorTag, ProgramEsDescriptorTag::desc($this->descriptorTag)); foreach ($this->descriptors as $descriptor) { $str .= "{$descriptor}\n"; } From bc3799854383279c46162e8ee9b1a12fc2fe296a Mon Sep 17 00:00:00 2001 From: tolich Date: Sun, 23 Mar 2025 11:05:28 +0200 Subject: [PATCH 14/18] Parse descriptors improve --- src/TableParsers/Descriptor.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/TableParsers/Descriptor.php b/src/TableParsers/Descriptor.php index a045a53..8df416b 100644 --- a/src/TableParsers/Descriptor.php +++ b/src/TableParsers/Descriptor.php @@ -48,8 +48,6 @@ protected function parseDescriptors($data) { $descriptorLen = unpack('C', substr($data, $pointer, 1))[1]; $pointer += 1; - var_dump(sprintf("0x%x %d", $esDescriptor->descriptorTag, $descriptorLen)); - $tmp = substr($data, $pointer, $descriptorLen); $pointer += $descriptorLen; switch ($esDescriptor->descriptorTag) { @@ -77,9 +75,7 @@ protected function languageDescriptor($data) { } protected function bitrateDescriptor($data) { - var_dump(strlen($data)); $tmp = unpack('C1a/n1b', $data); - var_dump($tmp); $result = [ 'maximum_bitrate' => ($tmp['a'] << 16 | $tmp['b']) & 0x3fffff, ]; From 35029fb36479bffe1963ca964e4734e99f16bd3a Mon Sep 17 00:00:00 2001 From: tolich Date: Mon, 24 Mar 2025 21:44:19 +0200 Subject: [PATCH 15/18] SDT parser improve. Add test for SDT --- src/TableParsers/Sdt.php | 6 ++-- src/Tables/Sdt.php | 4 +-- tests/ParserTest.php | 50 ++++++++++++++++++++++++++++- tests/data/2_mpegts_sdt_packets.ts | Bin 0 -> 376 bytes 4 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 tests/data/2_mpegts_sdt_packets.ts diff --git a/src/TableParsers/Sdt.php b/src/TableParsers/Sdt.php index 081f1cb..cc8b880 100644 --- a/src/TableParsers/Sdt.php +++ b/src/TableParsers/Sdt.php @@ -70,13 +70,13 @@ public function parse(int $tableId, string $data, int $currentPointer, int $sect $sdt->versionNumber = ($tmp >> 1) & 0x1f; $sdt->currentNextIndicator = $tmp & 0x01; - $sectionNumber = unpack('C', $data[$currentPointer])[1]; + $sdt->sectionNumber = unpack('C', $data[$currentPointer])[1]; $currentPointer += 1; - $lastSectionNumber = unpack('C', $data[$currentPointer])[1]; + $sdt->lastSectionNumber = unpack('C', $data[$currentPointer])[1]; $currentPointer += 1; - $originalNetworkId = unpack('n', substr($data, $currentPointer, 2))[1]; + $sdt->originalNetworkId = unpack('n', substr($data, $currentPointer, 2))[1]; $currentPointer += 3; while ($currentPointer < $crcOffset) { $sdtService = new SdtService(); diff --git a/src/Tables/Sdt.php b/src/Tables/Sdt.php index 0ae54de..c5bd975 100644 --- a/src/Tables/Sdt.php +++ b/src/Tables/Sdt.php @@ -34,14 +34,14 @@ class Sdt { /** - * The table id the eit belongs to + * The table id the sdt belongs to * @see \PhpBg\DvbPsi\Tables\Identifier * @var int */ public $tableId; /** - * This is a 16-bit field which serves as a label for identification of the TS, about which the EIT + * This is a 16-bit field which serves as a label for identification of the TS, about which the SDT * informs, from any other multiplex within the delivery system. * @var int */ diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 23982da..3f600db 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -32,12 +32,13 @@ use PhpBg\DvbPsi\Parser; use PhpBg\DvbPsi\ParserFactory; use PhpBg\DvbPsi\TableParsers\Pmt; +use PhpBg\DvbPsi\TableParsers; use PhpBg\DvbPsi\TableParsers\TableParserInterface; use PhpBg\DvbPsi\Tables\Eit; use PhpBg\DvbPsi\Tables\Nit; use PhpBg\DvbPsi\Tables\NitTs; use PhpBg\DvbPsi\Tables\Pat; - +use PhpBg\DvbPsi\Tables\Sdt; /** * For all tests use wireshark to inspect data samples and check expected values */ @@ -293,4 +294,51 @@ public function testParseNit() $this->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 0000000000000000000000000000000000000000..5a4cc31601d38ec0ea9ee9a946ad59bdd299f0cd GIT binary patch literal 376 zcmZ>F5R_za`k=s|bd-VNKf`~P=syko9=wt)t}eboY+ Date: Sat, 5 Apr 2025 15:29:53 +0300 Subject: [PATCH 16/18] register SDT parser --- src/ParserFactory.php | 2 ++ 1 file changed, 2 insertions(+) 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 From 54744c345ae21a7f0e7f718c234823be65d12777 Mon Sep 17 00:00:00 2001 From: tolich Date: Mon, 7 Apr 2025 17:36:41 +0300 Subject: [PATCH 17/18] fixed event name sdt-update, added sdt output to StreamContext toString --- src/Context/StreamContext.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index ced67c6..fb66db1 100644 --- a/src/Context/StreamContext.php +++ b/src/Context/StreamContext.php @@ -73,7 +73,7 @@ public function addSdt(Sdt $sdt) || ($this->sdts[$sdt->transportStreamId]->versionNumber !== 0 && $sdt->versionNumber === 0) ) { $this->sdts[$sdt->transportStreamId] = $sdt; - $this->emit('update'); + $this->emit('sdt-update'); } } @@ -114,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)) { From b0f92cca1d3704db0f2c26b299830feebec8ee2e Mon Sep 17 00:00:00 2001 From: tolich Date: Thu, 10 Apr 2025 11:41:40 +0300 Subject: [PATCH 18/18] Passing an object in an update event --- src/Context/StreamContext.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Context/StreamContext.php b/src/Context/StreamContext.php index fb66db1..d73ee77 100644 --- a/src/Context/StreamContext.php +++ b/src/Context/StreamContext.php @@ -73,7 +73,7 @@ public function addSdt(Sdt $sdt) || ($this->sdts[$sdt->transportStreamId]->versionNumber !== 0 && $sdt->versionNumber === 0) ) { $this->sdts[$sdt->transportStreamId] = $sdt; - $this->emit('sdt-update'); + $this->emit('sdt-update', [$sdt]); } } @@ -82,7 +82,7 @@ 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])) {