Skip to content

Commit bdf0c27

Browse files
authored
Merge pull request #771 from Dash-Industry-Forum/development
Release v3.1.0
2 parents 2d5b88d + 56970b6 commit bdf0c27

300 files changed

Lines changed: 1142 additions & 32934 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Dockerfile‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
FROM dunglas/frankenphp AS base
22

3-
RUN apt-get update && apt install -y openjdk-21-jre-headless supervisor
3+
RUN apt-get update && apt install -y openjdk-21-jre-headless supervisor nodejs npm
44

55

66
RUN install-php-extensions \
7-
pcntl
7+
pcntl zip
88

99
FROM base AS builder
1010

@@ -17,10 +17,19 @@ FROM base AS jccp
1717

1818
COPY --from=builder /gpac/bin/gcc/* /usr/bin/
1919

20+
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
2021

2122
ENV SERVER_NAME=:80
2223
COPY . /app
23-
COPY laravel-queue-worker.conf /etc/supervisor/conf.d/laravel-queue-worker.conf
2424

25-
CMD /bin/bash /app/queue_wrapper.sh
25+
WORKDIR /app
26+
27+
RUN composer install --no-dev --optimize-autoloader --no-interaction && \
28+
npm install && \
29+
npm run build
30+
31+
RUN php artisan migrate --force
32+
33+
COPY laravel-queue-worker.conf /etc/supervisor/conf.d/laravel-queue-worker.conf
2634

35+
CMD ["/bin/bash", "/app/queue_wrapper.sh"]

‎app/Livewire/SegmentQueue.php‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public function stateCount(string $state): int
4141
**/
4242
public function failedSegments(): array
4343
{
44-
return $this->segmentManager->failedSegments();
44+
$segments = $this->segmentManager->failedSegments();
45+
$filteredSegments = array_filter($segments, function ($k, $v) {
46+
return !str_contains($v, "unavailable://");
47+
}, ARRAY_FILTER_USE_BOTH);
48+
49+
if (count($filteredSegments) == 0 && count($segments) != 0) {
50+
return [ "All Segments Unavailable" => "Manifest Contents without BaseURL"];
51+
}
52+
53+
return $filteredSegments;
4554
}
4655
}

‎app/Modules/CMAF/Segments.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use App\Modules\CMAF\Segments\HEVCComparison;
3030
use App\Modules\CMAF\Segments\Durations;
3131
use App\Modules\CMAF\Segments\Initialization;
32+
use App\Modules\CMAF\Segments\LegacyInitialization;
3233

3334
class Segments extends Module
3435
{
@@ -78,6 +79,7 @@ public function validateSegments(Representation $representation, array $segments
7879
private function validateInitialization(Representation $representation, Segment $segment): void
7980
{
8081
new Initialization()->withInitSegment($representation, $segment);
82+
new LegacyInitialization()->withInitSegment($representation, $segment);
8183
}
8284

8385
private function validateSegment(Representation $representation, Segment $segment, int $segmentIndex): void

‎app/Modules/CMAF/Segments/AudioMediaProfile.php‎

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
class AudioMediaProfile extends AdaptationComponent
1818
{
19-
private TestCase $profileCase;
2019
private TestCase $brandCase;
2120
private TestCase $caacCase;
2221

@@ -26,87 +25,73 @@ public function __construct()
2625
self::class,
2726
new ReporterContext(
2827
"Segments",
29-
"LEGACY",
28+
"Edition 3",
3029
"CMAF",
3130
[]
3231
)
3332
);
3433

35-
$this->profileCase = $this->reporter->add(
36-
section: 'Section 7.3.4.1',
37-
test: "All CMAF audio tracks in a CMAF Switching Set SHALL conform to one CMAF Media Profile",
38-
skipReason: 'No audio switching set found'
39-
);
4034
$this->brandCase = $this->reporter->add(
4135
section: 'Section A.3',
42-
test: "If a CMAF brand is signalled, it SHALL correspond with the table",
36+
test: "The maximum encoding parameters [..as per the table..] SHALL not be exceeded",
4337
skipReason: 'No cmaf brands signalled'
4438
);
4539
$this->caacCase = $this->reporter->add(
46-
section: 'Section A.1.2/A.1.3/A.1.4',
40+
section: 'Section A.1.2',
4741
test: "Audio adaptation sets SHALL include at least one 'caac' representation",
48-
skipReason: 'No audio track found with CMAF profile found'
42+
skipReason: 'No audio track found, or no CMFHD profile signalled'
4943
);
5044
}
5145

5246
//Public validation functions
5347
public function validateAdaptationSet(AdaptationSet $adaptationSet): void
5448
{
55-
//TODO: Only if audio
56-
$signalledBrands = [];
57-
49+
if (!str_starts_with($adaptationSet->getTransientAttribute('mimeType'), 'audio/')) {
50+
return;
51+
}
5852
$segmentManager = app(SegmentManager::class);
5953

6054
$hasCAAC = false;
6155
foreach ($adaptationSet->allRepresentations() as $representation) {
6256
$segmentList = $segmentManager->representationSegments($representation);
6357

64-
$highestBrand = '____'; // Unknown;
6558
if (count($segmentList)) {
6659
if (in_array('caac', $segmentList[0]->getBrands())) {
6760
$hasCAAC = true;
6861
}
69-
$highestBrand = $this->validateAndDetermineBrand($representation, $segmentList[0]);
62+
$this->validateAndDetermineBrand($representation, $segmentList[0]);
7063
}
71-
$signalledBrands[] = $highestBrand; // Unknown
7264
}
7365

74-
$this->profileCase->pathAdd(
75-
result: count(array_unique($signalledBrands)) == 1,
76-
severity: "FAIL",
77-
path: $adaptationSet->path(),
78-
pass_message: "All representations signal the same highest brand",
79-
fail_message: "Not all representations signal the same highest brand"
80-
);
66+
$mpdCache = app(MPDCache::class);
8167

82-
$this->caacCase->pathAdd(
83-
result: $hasCAAC,
84-
severity: "FAIL",
85-
path: $adaptationSet->path(),
86-
pass_message: "At least one 'caac' track found",
87-
fail_message: "No 'caac' tracks found"
88-
);
68+
if ($mpdCache->hasProfile("urn:mpeg:cmaf:presentation_profile:cmfhd:2016")) {
69+
$this->caacCase->pathAdd(
70+
result: $hasCAAC,
71+
severity: "FAIL",
72+
path: $adaptationSet->path(),
73+
pass_message: "At least one 'caac' track found",
74+
fail_message: "No 'caac' tracks found"
75+
);
76+
}
8977
}
9078

9179
//Private helper functions
92-
private function validateAndDetermineBrand(Representation $representation, Segment $segment): string
80+
private function validateAndDetermineBrand(Representation $representation, Segment $segment): void
9381
{
9482
$sdType = $segment->getSampleDescriptor();
9583

96-
if ($sdType != 'mp4a') {
97-
return '____';
84+
if ($sdType == 'mp4a') {
85+
$this->validateAndDetermineBrandAAC($representation, $segment);
9886
}
99-
return $this->validateAndDetermineBrandAAC($representation, $segment);
10087
}
10188

102-
private function validateAndDetermineBrandAAC(Representation $representation, Segment $segment): string
89+
private function validateAndDetermineBrandAAC(Representation $representation, Segment $segment): void
10390
{
10491
$brands = $segment->getBrands();
10592

106-
$highestBrand = '____'; //Unknown
10793

10894
if (in_array('caac', $brands)) {
109-
$highestBrand = 'caac';
11095
$this->validateAACParameters(
11196
representation: $representation,
11297
segment: $segment,
@@ -116,10 +101,6 @@ private function validateAndDetermineBrandAAC(Representation $representation, Se
116101
allowedObjectTypes: ["2","5","29"]
117102
);
118103
}
119-
if (in_array('caaa', $brands)) {
120-
$highestBrand = 'caaa';
121-
}
122-
return $highestBrand;
123104
}
124105

125106
/**
@@ -154,7 +135,7 @@ private function validateAACParameters(
154135

155136
$this->brandCase->pathAdd(
156137
path: $representation->path() . "-init",
157-
result: in_array($aacConfiguration['objectTypeIndication'], $allowedObjectTypes),
138+
result: in_array($aacConfiguration['streamType'], $allowedObjectTypes),
158139
severity: "FAIL",
159140
pass_message: "Signalled brand $brand conforms to allowed object types",
160141
fail_message: "Signalled brand $brand does not conform to allowed object types",

‎app/Modules/CMAF/Segments/CompositionTimes.php‎

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ public function __construct()
2424
self::class,
2525
new ReporterContext(
2626
"Segments",
27-
"LEGACY",
27+
"Edition 3",
2828
"CMAF",
2929
[]
3030
)
3131
);
3232

3333
$this->offsetCase = $this->reporter->add(
3434
section: 'Section 9.2.1',
35-
test: "Video tracks SHALL contain either 'trun' v1 or 'elst', but no both",
35+
test: "Video tracks SHALL contain either 'trun(v1)' OR 'elst' combined with 'trun(v0)', but not both",
3636
skipReason: 'No video track found'
3737
);
3838
}
@@ -51,23 +51,33 @@ public function validateSegment(Representation $representation, Segment $segment
5151
$trunVersions[] = $trunBox->version;
5252
}
5353

54-
$trunV1 = count(array_unique($trunVersions)) == 1 && $trunVersions[0] == "1";
5554

56-
$this->offsetCase->pathAdd(
57-
result: $trunV1 xor count($elstBoxes),
58-
severity: "FAIL",
59-
path: $representation->path() . "-$segmentIndex",
60-
pass_message: "'trun' v1 OR 'elst' found",
61-
fail_message: "None or mixed elements found"
62-
);
63-
if (count($elstBoxes) && !$trunV1) {
64-
//TODO: Validate earliestCompositionTime
55+
if (count(array_unique($trunVersions)) != 1) {
56+
$this->offsetCase->pathAdd(
57+
result: false,
58+
severity: "FAIL",
59+
path: $representation->path() . "-$segmentIndex",
60+
pass_message: "",
61+
fail_message: "Mixed 'trun' versions found",
62+
);
63+
return;
64+
}
65+
66+
if ($trunVersions[0] == "0") {
67+
$this->offsetCase->pathAdd(
68+
result: count($elstBoxes) > 0,
69+
severity: "FAIL",
70+
path: $representation->path() . "-$segmentIndex",
71+
pass_message: "'trun(v0) with 'elst' box found",
72+
fail_message: "Missing 'elst' box with 'trun(v0)'"
73+
);
74+
} else {
6575
$this->offsetCase->pathAdd(
66-
result: true,
67-
severity: "INFO",
76+
result: count($elstBoxes) == 0,
77+
severity: "FAIL",
6878
path: $representation->path() . "-$segmentIndex",
69-
pass_message: "'elst' is not validated yet",
70-
fail_message: ""
79+
pass_message: "'trun(v1) without 'elst' box found",
80+
fail_message: "Disallowed 'elst' box with 'trun(v1)' found"
7181
);
7282
}
7383
}

‎app/Modules/CMAF/Segments/DataOffsets.php‎

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,64 +26,51 @@ public function __construct()
2626
self::class,
2727
new ReporterContext(
2828
"Segments",
29-
"LEGACY",
29+
"Edition 3",
3030
"CMAF",
3131
[]
3232
)
3333
);
3434

3535
$this->offsetCase = $this->reporter->add(
36-
section: 'Section 7.3.2.3',
37-
test: "All media samples in a CMAF chunk shall be addressed by byte offsets in a 'trun' box",
36+
section: 'Section 7.3.5',
37+
test: "All media samples in a CMAF Fragment SHALL be addressed by byte offsets in the 'trun' box",
3838
skipReason: "No valid track found"
3939
);
4040
}
4141

4242
//Public validation functions
4343
public function validateSegment(Representation $representation, Segment $segment, int $segmentIndex): void
4444
{
45-
$sidxBoxes = $segment->boxAccess()->sidx();
46-
$moofBoxes = $segment->boxAccess()->moof();
47-
$trunBoxes = $segment->boxAccess()->trun();
45+
$trunBox = $segment->boxAccess()->trun()[0];
46+
$samples = $segment->getNalSamples();
4847

49-
$allReferences = [];
50-
foreach ($sidxBoxes as $sidxBox) {
51-
foreach ($sidxBox->references as $reference) {
52-
$allReferences[] = $reference;
53-
}
54-
}
55-
56-
if (count($allReferences) != count($moofBoxes) || count($allReferences) != count($trunBoxes)) {
48+
if (count($samples) != $trunBox->sampleCount) {
5749
$this->offsetCase->pathAdd(
5850
path: $representation->path() . "-$segmentIndex",
5951
result: false,
6052
severity: "FAIL",
6153
pass_message: "",
62-
fail_message: "Unable to check due to inconsistent box counts"
54+
fail_message: "Trun signals " . $trunBox->sampleCount . " but " . count($samples) . " samples found",
6355
);
6456
return;
6557
}
6658

6759
$index = 0;
6860
$allValid = true;
69-
while ($index < count($trunBoxes)) {
70-
if ($trunBoxes[$index]->dataOffset < ($moofBoxes[$index]->boxSize + 8)) {
61+
while ($index < $trunBox->sampleCount) {
62+
if ($trunBox->sizes[$index] != $samples[$index]->size) {
7163
$allValid = false;
7264
break;
7365
}
74-
if ($trunBoxes[$index]->dataOffset > (($moofBoxes[$index]->boxSize + 8) + $allReferences[$index]->size)) {
75-
$allValid = false;
76-
break;
77-
}
78-
7966
$index++;
8067
}
8168
$this->offsetCase->pathAdd(
8269
path: $representation->path() . "-$segmentIndex",
8370
result: $allValid,
8471
severity: "FAIL",
85-
pass_message: "All trun offsets between begin and end of referenced boxes",
86-
fail_message: "At least one offset out of bounds"
72+
pass_message: "All sample sizes correspond with their 'trun' counterpart",
73+
fail_message: "Not all sizes corrsespond to their trun counterpart",
8774
);
8875
}
8976

0 commit comments

Comments
 (0)