forked from MPEGGroup/isobmff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_sample_groups.cpp
465 lines (426 loc) · 17 KB
/
test_sample_groups.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/**
* @file test_sample_groups.cpp
* @author Dimitri Podborski
* @brief Perform checks on sample groups, in movie and movie fragments, defragmentation, changning
* the sample group mapping types etc.
* @version 0.1
* @date 2020-11-20
*
* @copyright This software module was originally developed by Apple Computer, Inc. in the course of
* development of MPEG-4. This software module is an implementation of a part of one or more MPEG-4
* tools as specified by MPEG-4. ISO/IEC gives users of MPEG-4 free license to this software module
* or modifications thereof for use in hardware or software products claiming conformance to MPEG-4.
* Those intending to use this software module in hardware or software products are advised that its
* use may infringe existing patents. The original developer of this software module and his/her
* company, the subsequent editors and their companies, and ISO/IEC have no liability for use of
* this software module or modifications thereof in an implementation. Copyright is not released for
* non MPEG-4 conforming products. Apple Computer, Inc. retains full right to use the code for its
* own purpose, assign or donate the code to a third party and to inhibit third parties from using
* the code for non MPEG-4 conforming products. This copyright notice must be included in all copies
* or derivative works. Copyright (c) 1999.
*
*/
#include <catch.hpp>
#include "test_helpers.h"
extern "C"
{
MP4_EXTERN(MP4Err)
ISONewHEVCSampleDescription(MP4Track theTrack, MP4Handle sampleDescriptionH,
u32 dataReferenceIndex, u32 length_size, MP4Handle first_sps,
MP4Handle first_pps, MP4Handle first_spsext);
}
const u32 FOURCC_COLOR = MP4_FOUR_CHAR_CODE('c', 'o', 'l', 'r');
const u32 FOURCC_TEST = MP4_FOUR_CHAR_CODE('t', 'e', 's', 't');
const u32 FOURCC_BLACK = MP4_FOUR_CHAR_CODE('b', 'l', 'c', 'k');
/**
* @brief Helper function to add group description with the payload of a given string
*
* @param media media to add the description to
* @param fcc grouping_type
* @param strDescription Playload string
* @param [out] idx Index of the group entry
* @return ISOErr error code
*/
ISOErr addGroupDescription(MP4Media media, u32 fcc, std::string strDescription, u32 &idx)
{
ISOErr err;
ISOHandle descrH;
ISONewHandle(strDescription.length() * sizeof(char), &descrH);
std::memcpy(*descrH, strDescription.data(), strDescription.length() * sizeof(char));
err = ISOAddGroupDescription(media, fcc, descrH, &idx);
ISODisposeHandle(descrH);
return err;
}
/**
* @brief Helper function to map samples to groups based on pattern
*
* @param media media to map sample in
* @param strPattern color pattern same as used in addHEVCSamples
* @param idRed The index of the Red entry in the COLOR group
* @param idBlue The index of the Blue entry in the COLOR group
* @param idGreen The index of the Green entry in the COLOR group
* @param idYellow The index of the Yellow entry in the COLOR group
* @param repeatPattern number of times to repeat the pattern. No samples are mapped if this is 0
* @return ISOErr error code
*/
ISOErr mapSamplesToGroups(MP4Media media, std::string strPattern, u32 idRed, u32 idBlue,
u32 idGreen, u32 idYellow, u32 repeatPattern = 1)
{
ISOErr err = ISONoErr;
std::string fullPattern = strPattern;
for(u32 n = 1; n < repeatPattern; ++n)
{
fullPattern += strPattern;
}
// map samples to groups
if(repeatPattern > 0)
{
for(u32 n = 0; n < fullPattern.size(); ++n)
{
switch(fullPattern[n])
{
case 'r':
err = ISOMapSamplestoGroup(media, FOURCC_COLOR, idRed, n, 1);
CHECK(err == ISONoErr);
break;
case 'b':
err = ISOMapSamplestoGroup(media, FOURCC_COLOR, idBlue, n, 1);
CHECK(err == ISONoErr);
break;
case 'g':
err = ISOMapSamplestoGroup(media, FOURCC_COLOR, idGreen, n, 1);
CHECK(err == ISONoErr);
break;
case 'y':
err = ISOMapSamplestoGroup(media, FOURCC_COLOR, idYellow, n, 1);
CHECK(err == ISONoErr);
break;
default:
break;
}
}
}
return err;
}
/**
* @brief Helper function to check all samples in the given file
*
* @param strFile input file to check
* @return ISOErr error code
*/
ISOErr checkSamples(std::string strFile)
{
ISOErr err;
ISOMovie moov;
ISOTrack trak;
ISOMedia media;
err = ISOOpenMovieFile(&moov, strFile.c_str(), MP4OpenMovieNormal);
err = ISOGetMovieIndTrack(moov, 1, &trak);
err = ISOGetTrackMedia(trak, &media);
u32 sampleCnt = 0;
u32 colorEntryCount = 0;
u32 blackEntryCount = 0;
u32 *sampleNumbers;
u32 groupIdRed = 0;
u32 groupIdBlue = 0;
u32 groupIdGreen = 0;
u32 groupIdYellow = 0;
u32 groupIdBlack = 0;
// parse the entries of the COLOR type and get the indexes
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_COLOR, &colorEntryCount);
CHECK(err == ISONoErr);
CHECK(4 == colorEntryCount);
for(u32 n = 0; n < colorEntryCount; ++n)
{
MP4Handle entryH;
u32 size = 0;
MP4NewHandle(0, &entryH);
err = ISOGetGroupDescription(media, FOURCC_COLOR, n + 1, entryH);
CHECK(err == ISONoErr);
MP4GetHandleSize(entryH, &size);
if(size == 0) return -1;
std::string entryString(*entryH, size);
if(entryString == "Red frames") groupIdRed = n + 1;
if(entryString == "Blue frames") groupIdBlue = n + 1;
if(entryString == "Green frames") groupIdGreen = n + 1;
if(entryString == "Yellow frames") groupIdYellow = n + 1;
MP4DisposeHandle(entryH);
}
CHECK(groupIdRed != 0);
CHECK(groupIdBlue != 0);
CHECK(groupIdGreen != 0);
CHECK(groupIdYellow != 0);
// parse the entries of the BLACK type and get the indexes
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_BLACK, &blackEntryCount);
CHECK(err == ISONoErr);
CHECK(1 == blackEntryCount);
for(u32 n = 0; n < blackEntryCount; ++n)
{
MP4Handle entryH;
u32 size = 0;
MP4NewHandle(0, &entryH);
err = ISOGetGroupDescription(media, FOURCC_BLACK, n + 1, entryH);
CHECK(err == ISONoErr);
MP4GetHandleSize(entryH, &size);
if(size == 0) return -1;
std::string entryString(*entryH, size);
if(entryString == "Single black frame") groupIdBlack = n + 1;
MP4DisposeHandle(entryH);
}
CHECK(groupIdBlack != 0);
err = ISOGetSampleGroupSampleNumbers(media, FOURCC_COLOR, groupIdRed, &sampleNumbers, &sampleCnt);
CHECK(err == ISONoErr);
CHECK(sampleCnt == 5);
for(u32 i = 0; i < sampleCnt; ++i)
{
err = checkSample(media, sampleNumbers[i], HEVC::auRed, sizeof(HEVC::auRed));
CHECK(err == ISONoErr);
}
err =
ISOGetSampleGroupSampleNumbers(media, FOURCC_COLOR, groupIdBlue, &sampleNumbers, &sampleCnt);
CHECK(sampleCnt == 5);
for(u32 i = 0; i < sampleCnt; ++i)
{
err = checkSample(media, sampleNumbers[i], HEVC::auBlue, sizeof(HEVC::auBlue));
CHECK(err == ISONoErr);
}
err =
ISOGetSampleGroupSampleNumbers(media, FOURCC_COLOR, groupIdGreen, &sampleNumbers, &sampleCnt);
CHECK(sampleCnt == 4);
for(u32 i = 0; i < sampleCnt; ++i)
{
err = checkSample(media, sampleNumbers[i], HEVC::auGreen, sizeof(HEVC::auGreen));
CHECK(err == ISONoErr);
}
err =
ISOGetSampleGroupSampleNumbers(media, FOURCC_COLOR, groupIdYellow, &sampleNumbers, &sampleCnt);
CHECK(sampleCnt == 4);
for(u32 i = 0; i < sampleCnt; ++i)
{
err = checkSample(media, sampleNumbers[i], HEVC::auYellow, sizeof(HEVC::auYellow));
CHECK(err == ISONoErr);
}
err =
ISOGetSampleGroupSampleNumbers(media, FOURCC_BLACK, groupIdBlack, &sampleNumbers, &sampleCnt);
CHECK(sampleCnt == 1);
for(u32 i = 0; i < sampleCnt; ++i)
{
err = checkSample(media, sampleNumbers[i], HEVC::auBlack, sizeof(HEVC::auBlack));
CHECK(err == ISONoErr);
}
return err;
}
/**
* @brief Starting point for this testing case
*
*/
TEST_CASE("sample_groups")
{
std::string strFrag = "test_samplegroups_fragmeted.mp4";
std::string strDefragNormal = "test_samplegroups_defrag_normal.mp4";
std::string strDefragCompact = "test_samplegroups_defrag_compact.mp4";
std::string strDefragAuto = "test_samplegroups_defrag_auto.mp4";
/**
* @brief Create a Fragmented File with different sample groups for testing
*
* COLOR gropuing with: RED (r), BLUE (b), GREEN (g) and YELLOW (y)
* Additional frames without groups: WHITE (w) and BLACK (k)
*
* Moovie: Global group description for COLOR RED and BLUE in stbl
*
* Fragment 1: local group description COLOR RED (not used)
* local group description TEST with 2 entries
* Fragment 2: local group description COLOR GREEN and YELLOW (both used in COMPACT
* sampleToGroupBox)
* local group description TEST with 2 entries (one of these entries is the same as
* in Fragment 1)
* Fragment 3: no group description
* Fragment 4: local group descrition COLOR GREEN & YELLOW (both used in NORMAL sampleToGroupBox)
* Fragment 5: local group description BLACK + mapping a single sample to it (SampleIdx=3)
*
* Samples: red (r), blue(b), green (g), yellow (y), white (w), black (k)
* Groups: global (*), local (.), none ( )
* | frag. 1 | frag. 2 | frag. 3 | frag. 4 | frag. 5 |
* |r|b|r|b|r|b|g|r|y|g|r|y|w|k|w|k|w|k|b|g|y|b|g|y|w|k|w|k|w|k|
* | | | | | |
* |*|*|*|*|*|*|.|*|.|.|*|.| | | | | | |*|.|.|*|.|.| | | |.| | |
*
*/
SECTION("Check Fragmented file creation with sample groups")
{
ISOErr err;
ISOMovie moov;
ISOMedia media;
ISOTrack trak;
u32 colorEntryCount = 0;
u32 groupIdRed = 0;
u32 groupIdBlue = 0;
u32 groupIdGreen = 0;
u32 groupIdYellow = 0;
ISOHandle spsHandle, ppsHandle, vpsHandle, sampleEntryH;
err = MP4NewHandle(sizeof(HEVC::SPS), &spsHandle);
std::memcpy((*spsHandle), HEVC::SPS, sizeof(HEVC::SPS));
err = MP4NewHandle(sizeof(HEVC::PPS), &ppsHandle);
std::memcpy((*ppsHandle), HEVC::PPS, sizeof(HEVC::PPS));
err = MP4NewHandle(sizeof(HEVC::VPS), &vpsHandle);
std::memcpy((*vpsHandle), HEVC::VPS, sizeof(HEVC::VPS));
err = MP4NewHandle(0, &sampleEntryH);
err = MP4NewMovie(&moov, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
REQUIRE(err == ISONoErr);
err = ISONewMovieTrack(moov, MP4NewTrackIsVisual, &trak);
REQUIRE(err == ISONoErr);
err = MP4AddTrackToMovieIOD(trak);
CHECK(err == ISONoErr);
err = ISONewTrackMedia(trak, &media, ISOVisualHandlerType, TIMESCALE, NULL);
REQUIRE(err == ISONoErr);
err = ISOBeginMediaEdits(media);
err = ISONewHEVCSampleDescription(trak, sampleEntryH, 1, 1, spsHandle, ppsHandle, vpsHandle);
REQUIRE(err == ISONoErr);
err = ISOSetTrackFragmentDefaults(trak, TIMESCALE / FPS, sizeof(HEVC::auBlue), 1, 0);
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_COLOR, &colorEntryCount);
CHECK(err != ISONoErr);
CHECK(colorEntryCount == 0);
// red and blue are in stbl
err = addGroupDescription(media, FOURCC_COLOR, "Red frames", groupIdRed);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_COLOR, "Blue frames", groupIdBlue);
CHECK(err == ISONoErr);
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_COLOR, &colorEntryCount);
CHECK(err == ISONoErr);
CHECK(colorEntryCount == 2);
u32 temp = 0;
err = addGroupDescription(media, FOURCC_COLOR, "Red frames", temp);
// this must fail because "Red frames" payload is already added with the same type
CHECK(err != ISONoErr);
// just add sample entry, call addHEVCSamples with sample count = 0
err = addHEVCSamples(media, "r", 0, sampleEntryH);
CHECK(err == ISONoErr);
err = MP4EndMediaEdits(media);
CHECK(err == ISONoErr);
// Fragment 1
err = ISOStartMovieFragment(moov);
CHECK(err == ISONoErr);
err = ISOSetSamplestoGroupType(media, SAMPLE_GROUP_NORMAL);
CHECK(err == ISONoErr);
err = addHEVCSamples(media, "rb", 3);
CHECK(err == ISONoErr);
colorEntryCount = 0;
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_COLOR, &colorEntryCount);
CHECK(err != ISONoErr); // there sould be no entries in this fragment yet
CHECK(colorEntryCount == 0);
err = addGroupDescription(media, FOURCC_TEST, "Duplicate test", temp);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_COLOR, "Red frames", temp);
CHECK(err == ISONoErr); // this must pass even if the same type and payload is in stbl
// (but it shall not be in defragmented file)
err = addGroupDescription(media, FOURCC_TEST, "Test", temp);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_TEST, "Test", temp);
CHECK(err != ISONoErr); // this must fail because same type and payload already added
err = mapSamplesToGroups(media, "rb", groupIdRed, groupIdBlue, groupIdGreen, groupIdYellow, 3);
CHECK(err == ISONoErr);
err = ISOGetGroupDescriptionEntryCount(media, FOURCC_COLOR, &colorEntryCount);
CHECK(err == ISONoErr); // there should be now exactly one entry
CHECK(colorEntryCount == 1);
// Fragment 2
err = ISOStartMovieFragment(moov);
ISOSetSamplestoGroupType(media, SAMPLE_GROUP_COMPACT);
// local green and yellow groups
err = addGroupDescription(media, FOURCC_COLOR, "Green frames", groupIdGreen);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_COLOR, "Yellow frames", groupIdYellow);
CHECK(err == ISONoErr);
err = addHEVCSamples(media, "gry", 2);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_TEST, "Unique entry", temp);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_TEST, "Duplicate test", temp);
CHECK(err == ISONoErr);
err = mapSamplesToGroups(media, "gry", groupIdRed, groupIdBlue, groupIdGreen, groupIdYellow, 2);
CHECK(err == ISONoErr);
// Fragment 3
err = ISOStartMovieFragment(moov);
CHECK(err == ISONoErr);
ISOSetSamplestoGroupType(media, SAMPLE_GROUP_NORMAL);
err = addHEVCSamples(media, "wk", 3);
CHECK(err == ISONoErr);
// Fragment 4
err = ISOStartMovieFragment(moov);
ISOSetSamplestoGroupType(media, SAMPLE_GROUP_NORMAL);
// local green and yellow groups
err = addGroupDescription(media, FOURCC_COLOR, "Green frames", groupIdGreen);
CHECK(err == ISONoErr);
err = addGroupDescription(media, FOURCC_COLOR, "Yellow frames", groupIdYellow);
CHECK(err == ISONoErr);
err = addHEVCSamples(media, "bgy", 2);
CHECK(err == ISONoErr);
err = mapSamplesToGroups(media, "bgy", groupIdRed, groupIdBlue, groupIdGreen, groupIdYellow, 2);
CHECK(err == ISONoErr);
// Fragment 5
err = ISOStartMovieFragment(moov);
CHECK(err == ISONoErr);
ISOSetSamplestoGroupType(media, SAMPLE_GROUP_NORMAL);
err = addHEVCSamples(media, "wk", 3);
CHECK(err == ISONoErr);
u32 groupIDBlack = 0;
err = addGroupDescription(media, FOURCC_BLACK, "Single black frame", groupIDBlack);
CHECK(err == ISONoErr);
err = ISOMapSamplestoGroup(media, FOURCC_BLACK, groupIDBlack, 3, 1);
CHECK(err == ISONoErr);
err = MP4WriteMovieToFile(moov, strFrag.c_str());
CHECK(err == ISONoErr);
}
/**
* @brief Create different flavors of defragmented files from the fragmented file
*
* create Defragmented file with NORMAL SampleToGroupBox
* create Defragmented file with COMPACT SampleToGroupBox
* create Defragmented file with AUTO SampleToGroupBox (smaller size is used automatically)
*
*/
SECTION("Check defragmentation of sample groups")
{
ISOErr err;
ISOMovie moov;
ISOTrack trak;
ISOMedia media;
err = ISOOpenMovieFile(&moov, strFrag.c_str(), MP4OpenMovieNormal);
REQUIRE(err == ISONoErr);
err = ISOGetMovieIndTrack(moov, 1, &trak);
REQUIRE(err == ISONoErr);
err = ISOGetTrackMedia(trak, &media);
REQUIRE(err == ISONoErr);
// write file with compact sample groups
err = ISOSetSamplestoGroupType(media, SAMPLE_GROUP_COMPACT);
CHECK(err == ISONoErr);
err = MP4WriteMovieToFile(moov, strDefragCompact.c_str());
CHECK(err == ISONoErr);
// write file with normal sample groups
err = ISOSetSamplestoGroupType(media, SAMPLE_GROUP_NORMAL);
CHECK(err == ISONoErr);
err = MP4WriteMovieToFile(moov, strDefragNormal.c_str());
CHECK(err == ISONoErr);
// write file with auto sample groups
err = ISOSetSamplestoGroupType(media, SAMPLE_GROUP_AUTO);
CHECK(err == ISONoErr);
err = MP4WriteMovieToFile(moov, strDefragAuto.c_str());
CHECK(err == ISONoErr);
}
/**
* @brief Check parse all files, find the COLOR group ids, extract all samples and check if all
* samples are correclty extracted (check the payload of every extracted sample as well)
*
*/
SECTION("Check all sample groups in fragmented and defragmented files")
{
ISOErr err;
err = checkSamples(strFrag);
CHECK(err == ISONoErr);
err = checkSamples(strDefragNormal);
CHECK(err == ISONoErr);
err = checkSamples(strDefragCompact);
CHECK(err == ISONoErr);
err = checkSamples(strDefragAuto);
CHECK(err == ISONoErr);
}
}