61
|
1 /*
|
|
2 * The contents of this file are subject to the Mozilla Public
|
|
3 * License Version 1.1 (the "License"); you may not use this file
|
|
4 * except in compliance with the License. You may obtain a copy of
|
|
5 * the License at http://www.mozilla.org/MPL/
|
|
6 *
|
|
7 * Software distributed under the License is distributed on an "AS
|
|
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
9 * implied. See the License for the specific language governing
|
|
10 * rights and limitations under the License.
|
|
11 *
|
|
12 * The Original Code is MPEG4IP.
|
|
13 *
|
|
14 * The Initial Developer of the Original Code is Cisco Systems Inc.
|
|
15 * Portions created by Cisco Systems Inc. are
|
|
16 * Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved.
|
|
17 *
|
|
18 * 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
|
|
19 * and was contributed by Ximpo Group Ltd.
|
|
20 *
|
|
21 * Portions created by Ximpo Group Ltd. are
|
|
22 * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
|
|
23 *
|
|
24 * Contributor(s):
|
|
25 * Dave Mackie dmackie@cisco.com
|
|
26 * Alix Marchandise-Franquet alix@cisco.com
|
|
27 * Ximpo Group Ltd. mp4v2@ximpo.com
|
|
28 */
|
|
29
|
|
30 #include "mp4common.h"
|
|
31
|
|
32 static const u_int8_t BifsV2Config[3] = {
|
|
33 0x00, 0x00, 0x60 // IsCommandStream = 1, PixelMetric = 1
|
|
34 };
|
|
35
|
|
36 void MP4File::MakeIsmaCompliant(bool addIsmaComplianceSdp)
|
|
37 {
|
|
38 ProtectWriteOperation("MP4MakeIsmaCompliant");
|
|
39
|
|
40 if (m_useIsma) {
|
|
41 // already done
|
|
42 return;
|
|
43 }
|
|
44
|
|
45 // find first audio and/or video tracks
|
|
46
|
|
47 MP4TrackId audioTrackId = MP4_INVALID_TRACK_ID;
|
|
48 try {
|
|
49 audioTrackId = FindTrackId(0, MP4_AUDIO_TRACK_TYPE);
|
|
50 }
|
|
51 catch (MP4Error* e) {
|
|
52 delete e;
|
|
53 }
|
|
54
|
|
55 MP4TrackId videoTrackId = MP4_INVALID_TRACK_ID;
|
|
56 try {
|
|
57 videoTrackId = FindTrackId(0, MP4_VIDEO_TRACK_TYPE);
|
|
58 }
|
|
59 catch (MP4Error* e) {
|
|
60 delete e;
|
|
61 }
|
201
|
62 if (audioTrackId == MP4_INVALID_TRACK_ID &&
|
|
63 videoTrackId == MP4_INVALID_TRACK_ID) return;
|
61
|
64
|
201
|
65 const char *audio_media_data_name, *video_media_data_name;
|
|
66 if (audioTrackId != MP4_INVALID_TRACK_ID) {
|
|
67 audio_media_data_name = MP4GetTrackMediaDataName(this, audioTrackId);
|
|
68 if (!(ATOMID(audio_media_data_name) == ATOMID("mp4a") ||
|
|
69 ATOMID(audio_media_data_name) == ATOMID("enca"))) {
|
|
70 VERBOSE_ERROR(m_verbosity,
|
|
71 printf("MakeIsmaCompliant:can't make ISMA compliant when file contains an %s track\n", audio_media_data_name);
|
|
72 );
|
|
73 return;
|
|
74 }
|
61
|
75 }
|
|
76 //
|
|
77 // Note - might have to check for avc1 here...
|
201
|
78 if (videoTrackId != MP4_INVALID_TRACK_ID) {
|
|
79 video_media_data_name = MP4GetTrackMediaDataName(this, videoTrackId);
|
|
80 if (!(ATOMID(video_media_data_name) == ATOMID("mp4v") ||
|
|
81 ATOMID(video_media_data_name) == ATOMID("encv"))) {
|
|
82 VERBOSE_ERROR(m_verbosity,
|
|
83 printf("MakeIsmaCompliant:can't make ISMA compliant when file contains an %s track\n", video_media_data_name);
|
|
84 );
|
|
85 return;
|
|
86 }
|
61
|
87 }
|
|
88
|
|
89 m_useIsma = true;
|
|
90
|
|
91 u_int64_t fileMsDuration =
|
|
92 ConvertFromMovieDuration(GetDuration(), MP4_MSECS_TIME_SCALE);
|
|
93
|
|
94 // delete any existing OD track
|
|
95 if (m_odTrackId != MP4_INVALID_TRACK_ID) {
|
|
96 DeleteTrack(m_odTrackId);
|
|
97 }
|
|
98
|
|
99 AddODTrack();
|
|
100 SetODProfileLevel(0xFF);
|
|
101
|
|
102 if (audioTrackId != MP4_INVALID_TRACK_ID) {
|
|
103 AddTrackToOd(audioTrackId);
|
|
104 }
|
|
105
|
|
106 if (videoTrackId != MP4_INVALID_TRACK_ID) {
|
|
107 AddTrackToOd(videoTrackId);
|
|
108 }
|
|
109
|
|
110 // delete any existing scene track
|
|
111 MP4TrackId sceneTrackId = MP4_INVALID_TRACK_ID;
|
|
112 try {
|
|
113 sceneTrackId = FindTrackId(0, MP4_SCENE_TRACK_TYPE);
|
|
114 }
|
|
115 catch (MP4Error *e) {
|
|
116 delete e;
|
|
117 }
|
|
118 if (sceneTrackId != MP4_INVALID_TRACK_ID) {
|
|
119 DeleteTrack(sceneTrackId);
|
|
120 }
|
|
121
|
|
122 // add scene track
|
|
123 sceneTrackId = AddSceneTrack();
|
|
124 SetSceneProfileLevel(0xFF);
|
|
125 SetGraphicsProfileLevel(0xFF);
|
|
126 SetTrackIntegerProperty(sceneTrackId,
|
|
127 "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
|
|
128 MP4SystemsV2ObjectType);
|
|
129
|
|
130 SetTrackESConfiguration(sceneTrackId,
|
|
131 BifsV2Config, sizeof(BifsV2Config));
|
|
132
|
|
133 u_int8_t* pBytes = NULL;
|
|
134 u_int64_t numBytes = 0;
|
|
135
|
|
136 // write OD Update Command
|
|
137 CreateIsmaODUpdateCommandFromFileForFile(
|
|
138 m_odTrackId,
|
|
139 audioTrackId,
|
|
140 videoTrackId,
|
|
141 &pBytes,
|
|
142 &numBytes);
|
|
143
|
|
144 WriteSample(m_odTrackId, pBytes, numBytes, fileMsDuration);
|
|
145
|
|
146 MP4Free(pBytes);
|
|
147 pBytes = NULL;
|
|
148
|
|
149 // write BIFS Scene Replace Command
|
|
150 CreateIsmaSceneCommand(
|
|
151 MP4_IS_VALID_TRACK_ID(audioTrackId),
|
|
152 MP4_IS_VALID_TRACK_ID(videoTrackId),
|
|
153 &pBytes,
|
|
154 &numBytes);
|
|
155
|
|
156 WriteSample(sceneTrackId, pBytes, numBytes, fileMsDuration);
|
|
157
|
|
158 MP4Free(pBytes);
|
|
159 pBytes = NULL;
|
|
160
|
|
161 // add session level sdp
|
|
162 CreateIsmaIodFromFile(
|
|
163 m_odTrackId,
|
|
164 sceneTrackId,
|
|
165 audioTrackId,
|
|
166 videoTrackId,
|
|
167 &pBytes,
|
|
168 &numBytes);
|
|
169
|
|
170 char* iodBase64 = MP4ToBase64(pBytes, numBytes);
|
|
171
|
|
172 char* sdpBuf = (char*)MP4Calloc(strlen(iodBase64) + 256);
|
|
173
|
|
174 if (addIsmaComplianceSdp) {
|
|
175 strcpy(sdpBuf, "a=isma-compliance:1,1.0,1\015\012");
|
|
176 }
|
|
177
|
|
178 sprintf(&sdpBuf[strlen(sdpBuf)],
|
|
179 "a=mpeg4-iod: \042data:application/mpeg4-iod;base64,%s\042\015\012",
|
|
180 iodBase64);
|
|
181
|
|
182 SetSessionSdp(sdpBuf);
|
|
183
|
|
184 VERBOSE_ISMA(GetVerbosity(),
|
|
185 printf("IOD SDP = %s\n", sdpBuf));
|
|
186
|
|
187 MP4Free(iodBase64);
|
|
188 iodBase64 = NULL;
|
|
189 MP4Free(pBytes);
|
|
190 pBytes = NULL;
|
|
191 MP4Free(sdpBuf);
|
|
192 sdpBuf = NULL;
|
|
193 }
|
|
194
|
|
195 static void CloneIntegerProperty(
|
|
196 MP4Descriptor* pDest,
|
|
197 MP4DescriptorProperty* pSrc,
|
|
198 const char* name)
|
|
199 {
|
|
200 MP4IntegerProperty* pGetProperty;
|
|
201 MP4IntegerProperty* pSetProperty;
|
|
202
|
|
203 pSrc->FindProperty(name, (MP4Property**)&pGetProperty);
|
|
204 pDest->FindProperty(name, (MP4Property**)&pSetProperty);
|
|
205
|
|
206 pSetProperty->SetValue(pGetProperty->GetValue());
|
|
207 }
|
|
208
|
|
209 void MP4File::CreateIsmaIodFromFile(
|
|
210 MP4TrackId odTrackId,
|
|
211 MP4TrackId sceneTrackId,
|
|
212 MP4TrackId audioTrackId,
|
|
213 MP4TrackId videoTrackId,
|
|
214 u_int8_t** ppBytes,
|
|
215 u_int64_t* pNumBytes)
|
|
216 {
|
|
217 MP4Descriptor* pIod = new MP4IODescriptor();
|
|
218 pIod->SetTag(MP4IODescrTag);
|
|
219 pIod->Generate();
|
|
220
|
|
221 MP4Atom* pIodsAtom = FindAtom("moov.iods");
|
|
222 ASSERT(pIodsAtom);
|
|
223 MP4DescriptorProperty* pSrcIod =
|
|
224 (MP4DescriptorProperty*)pIodsAtom->GetProperty(2);
|
|
225
|
|
226 CloneIntegerProperty(pIod, pSrcIod, "objectDescriptorId");
|
|
227 CloneIntegerProperty(pIod, pSrcIod, "ODProfileLevelId");
|
|
228 CloneIntegerProperty(pIod, pSrcIod, "sceneProfileLevelId");
|
|
229 CloneIntegerProperty(pIod, pSrcIod, "audioProfileLevelId");
|
|
230 CloneIntegerProperty(pIod, pSrcIod, "visualProfileLevelId");
|
|
231 CloneIntegerProperty(pIod, pSrcIod, "graphicsProfileLevelId");
|
|
232
|
|
233 // mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag
|
|
234 MP4DescriptorProperty* pEsProperty;
|
|
235 pIod->FindProperty("esIds", (MP4Property**)&pEsProperty);
|
|
236 pEsProperty->SetTags(MP4ESDescrTag);
|
|
237
|
|
238 MP4IntegerProperty* pSetProperty;
|
|
239 MP4IntegerProperty* pSceneESID;
|
|
240 MP4IntegerProperty* pOdESID;
|
|
241
|
|
242 // OD
|
|
243 MP4Descriptor* pOdEsd =
|
|
244 pEsProperty->AddDescriptor(MP4ESDescrTag);
|
|
245 pOdEsd->Generate();
|
|
246
|
|
247 pOdEsd->FindProperty("ESID",
|
|
248 (MP4Property**)&pOdESID);
|
|
249
|
|
250 // we set the OD ESID to a non-zero unique value
|
|
251 pOdESID->SetValue(m_odTrackId);
|
|
252
|
|
253 pOdEsd->FindProperty("URLFlag",
|
|
254 (MP4Property**)&pSetProperty);
|
|
255 pSetProperty->SetValue(1);
|
|
256
|
|
257 u_int8_t* pBytes;
|
|
258 u_int64_t numBytes;
|
|
259
|
|
260 CreateIsmaODUpdateCommandFromFileForStream(
|
|
261 audioTrackId,
|
|
262 videoTrackId,
|
|
263 &pBytes,
|
|
264 &numBytes);
|
|
265
|
|
266 VERBOSE_ISMA(GetVerbosity(),
|
|
267 printf("OD data =\n"); MP4HexDump(pBytes, numBytes));
|
|
268
|
|
269 char* odCmdBase64 = MP4ToBase64(pBytes, numBytes);
|
|
270
|
|
271 char* urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64);
|
|
272
|
|
273 sprintf(urlBuf,
|
|
274 "data:application/mpeg4-od-au;base64,%s",
|
|
275 odCmdBase64);
|
|
276
|
|
277 MP4StringProperty* pUrlProperty;
|
|
278 pOdEsd->FindProperty("URL",
|
|
279 (MP4Property**)&pUrlProperty);
|
|
280 pUrlProperty->SetValue(urlBuf);
|
|
281
|
|
282 VERBOSE_ISMA(GetVerbosity(),
|
|
283 printf("OD data URL = \042%s\042\n", urlBuf));
|
|
284
|
|
285 MP4Free(odCmdBase64);
|
|
286 odCmdBase64 = NULL;
|
|
287 MP4Free(pBytes);
|
|
288 pBytes = NULL;
|
|
289 MP4Free(urlBuf);
|
|
290 urlBuf = NULL;
|
|
291
|
|
292 MP4DescriptorProperty* pSrcDcd = NULL;
|
|
293
|
|
294 // HACK temporarily point to scene decoder config
|
|
295 FindProperty(MakeTrackName(odTrackId,
|
|
296 "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"),
|
|
297 (MP4Property**)&pSrcDcd);
|
|
298 ASSERT(pSrcDcd);
|
|
299 MP4Property* pOrgOdEsdProperty =
|
|
300 pOdEsd->GetProperty(8);
|
|
301 pOdEsd->SetProperty(8, pSrcDcd);
|
|
302
|
|
303 // bufferSizeDB needs to be set appropriately
|
|
304 MP4BitfieldProperty* pBufferSizeProperty = NULL;
|
|
305 pOdEsd->FindProperty("decConfigDescr.bufferSizeDB",
|
|
306 (MP4Property**)&pBufferSizeProperty);
|
|
307 ASSERT(pBufferSizeProperty);
|
|
308 pBufferSizeProperty->SetValue(numBytes);
|
|
309
|
|
310 // SL config needs to change from 2 (file) to 1 (null)
|
|
311 pOdEsd->FindProperty("slConfigDescr.predefined",
|
|
312 (MP4Property**)&pSetProperty);
|
|
313 pSetProperty->SetValue(1);
|
|
314
|
|
315
|
|
316 // Scene
|
|
317 MP4Descriptor* pSceneEsd =
|
|
318 pEsProperty->AddDescriptor(MP4ESDescrTag);
|
|
319 pSceneEsd->Generate();
|
|
320
|
|
321 pSceneEsd->FindProperty("ESID",
|
|
322 (MP4Property**)&pSceneESID);
|
|
323 // we set the Scene ESID to a non-zero unique value
|
|
324 pSceneESID->SetValue(sceneTrackId);
|
|
325
|
|
326 pSceneEsd->FindProperty("URLFlag",
|
|
327 (MP4Property**)&pSetProperty);
|
|
328 pSetProperty->SetValue(1);
|
|
329
|
|
330 CreateIsmaSceneCommand(
|
|
331 MP4_IS_VALID_TRACK_ID(audioTrackId),
|
|
332 MP4_IS_VALID_TRACK_ID(videoTrackId),
|
|
333 &pBytes,
|
|
334 &numBytes);
|
|
335
|
|
336 VERBOSE_ISMA(GetVerbosity(),
|
|
337 printf("Scene data =\n"); MP4HexDump(pBytes, numBytes));
|
|
338
|
|
339 char *sceneCmdBase64 = MP4ToBase64(pBytes, numBytes);
|
|
340
|
|
341 urlBuf = (char*)MP4Malloc(strlen(sceneCmdBase64) + 64);
|
|
342 sprintf(urlBuf,
|
|
343 "data:application/mpeg4-bifs-au;base64,%s",
|
|
344 sceneCmdBase64);
|
|
345
|
|
346 pSceneEsd->FindProperty("URL",
|
|
347 (MP4Property**)&pUrlProperty);
|
|
348 pUrlProperty->SetValue(urlBuf);
|
|
349
|
|
350 VERBOSE_ISMA(GetVerbosity(),
|
|
351 printf("Scene data URL = \042%s\042\n", urlBuf));
|
|
352
|
|
353 MP4Free(sceneCmdBase64);
|
|
354 sceneCmdBase64 = NULL;
|
|
355 MP4Free(urlBuf);
|
|
356 urlBuf = NULL;
|
|
357 MP4Free(pBytes);
|
|
358 pBytes = NULL;
|
|
359
|
|
360 // HACK temporarily point to scene decoder config
|
|
361 FindProperty(MakeTrackName(sceneTrackId,
|
|
362 "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"),
|
|
363 (MP4Property**)&pSrcDcd);
|
|
364 ASSERT(pSrcDcd);
|
|
365 MP4Property* pOrgSceneEsdProperty =
|
|
366 pSceneEsd->GetProperty(8);
|
|
367 pSceneEsd->SetProperty(8, pSrcDcd);
|
|
368
|
|
369 // bufferSizeDB needs to be set
|
|
370 pBufferSizeProperty = NULL;
|
|
371 pSceneEsd->FindProperty("decConfigDescr.bufferSizeDB",
|
|
372 (MP4Property**)&pBufferSizeProperty);
|
|
373 ASSERT(pBufferSizeProperty);
|
|
374 pBufferSizeProperty->SetValue(numBytes);
|
|
375
|
|
376 // SL config needs to change from 2 (file) to 1 (null)
|
|
377 pSceneEsd->FindProperty("slConfigDescr.predefined",
|
|
378 (MP4Property**)&pSetProperty);
|
|
379 pSetProperty->SetValue(1);
|
|
380
|
|
381
|
|
382 // finally get the whole thing written to a memory
|
|
383 pIod->WriteToMemory(this, ppBytes, pNumBytes);
|
|
384
|
|
385
|
|
386 // now carefully replace esd properties before destroying
|
|
387 pOdEsd->SetProperty(8, pOrgOdEsdProperty);
|
|
388 pSceneEsd->SetProperty(8, pOrgSceneEsdProperty);
|
|
389 pSceneESID->SetValue(0); // restore 0 value
|
|
390 pOdESID->SetValue(0);
|
|
391
|
|
392 delete pIod;
|
|
393
|
|
394 VERBOSE_ISMA(GetVerbosity(),
|
|
395 printf("IOD data =\n"); MP4HexDump(*ppBytes, *pNumBytes));
|
|
396 }
|
|
397
|
|
398 void MP4File::CreateIsmaIodFromParams(
|
|
399 u_int8_t videoProfile,
|
|
400 u_int32_t videoBitrate,
|
|
401 u_int8_t* videoConfig,
|
|
402 u_int32_t videoConfigLength,
|
|
403 u_int8_t audioProfile,
|
|
404 u_int32_t audioBitrate,
|
|
405 u_int8_t* audioConfig,
|
|
406 u_int32_t audioConfigLength,
|
|
407 u_int8_t** ppIodBytes,
|
|
408 u_int64_t* pIodNumBytes)
|
|
409 {
|
|
410 MP4IntegerProperty* pInt;
|
|
411 u_int8_t* pBytes = NULL;
|
|
412 u_int64_t numBytes;
|
|
413
|
|
414 // Create the IOD
|
|
415 MP4Descriptor* pIod = new MP4IODescriptor();
|
|
416 pIod->SetTag(MP4IODescrTag);
|
|
417 pIod->Generate();
|
|
418
|
|
419 // Set audio and video profileLevels
|
|
420 pIod->FindProperty("audioProfileLevelId",
|
|
421 (MP4Property**)&pInt);
|
|
422 pInt->SetValue(audioProfile);
|
|
423
|
|
424 pIod->FindProperty("visualProfileLevelId",
|
|
425 (MP4Property**)&pInt);
|
|
426 pInt->SetValue(videoProfile);
|
|
427
|
|
428 // Mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag
|
|
429 MP4DescriptorProperty* pEsProperty;
|
|
430 pIod->FindProperty("esIds", (MP4Property**)&pEsProperty);
|
|
431 pEsProperty->SetTags(MP4ESDescrTag);
|
|
432
|
|
433 // Add ES Descriptors
|
|
434
|
|
435 // Scene
|
|
436 CreateIsmaSceneCommand(
|
|
437 (audioProfile != 0xFF),
|
|
438 (videoProfile != 0xFF),
|
|
439 &pBytes,
|
|
440 &numBytes);
|
|
441
|
|
442 VERBOSE_ISMA(GetVerbosity(),
|
|
443 printf("Scene data =\n"); MP4HexDump(pBytes, numBytes));
|
|
444
|
|
445 char* sceneCmdBase64 = MP4ToBase64(pBytes, numBytes);
|
|
446
|
|
447 char* urlBuf =
|
|
448 (char*)MP4Malloc(strlen(sceneCmdBase64) + 64);
|
|
449 sprintf(urlBuf,
|
|
450 "data:application/mpeg4-bifs-au;base64,%s",
|
|
451 sceneCmdBase64);
|
|
452
|
|
453 VERBOSE_ISMA(GetVerbosity(),
|
|
454 printf("Scene data URL = \042%s\042\n", urlBuf));
|
|
455
|
|
456 /* MP4Descriptor* pSceneEsd = */
|
|
457 CreateESD(
|
|
458 pEsProperty,
|
|
459 201, // esid
|
|
460 MP4SystemsV2ObjectType,
|
|
461 MP4SceneDescriptionStreamType,
|
|
462 numBytes, // bufferSize
|
|
463 numBytes * 8, // bitrate
|
|
464 BifsV2Config,
|
|
465 sizeof(BifsV2Config),
|
|
466 urlBuf);
|
|
467
|
|
468 MP4Free(sceneCmdBase64);
|
|
469 sceneCmdBase64 = NULL;
|
|
470 MP4Free(urlBuf);
|
|
471 urlBuf = NULL;
|
|
472 MP4Free(pBytes);
|
|
473 pBytes = NULL;
|
|
474
|
|
475 // OD
|
|
476
|
|
477 // Video
|
|
478 MP4DescriptorProperty* pVideoEsdProperty =
|
|
479 new MP4DescriptorProperty();
|
|
480 pVideoEsdProperty->SetTags(MP4ESDescrTag);
|
|
481
|
|
482 /* MP4Descriptor* pVideoEsd = */
|
|
483 CreateESD(
|
|
484 pVideoEsdProperty,
|
|
485 20, // esid
|
|
486 MP4_MPEG4_VIDEO_TYPE,
|
|
487 MP4VisualStreamType,
|
|
488 videoBitrate / 8, // bufferSize
|
|
489 videoBitrate,
|
|
490 videoConfig,
|
|
491 videoConfigLength,
|
|
492 NULL);
|
|
493
|
|
494 // Audio
|
|
495 MP4DescriptorProperty* pAudioEsdProperty =
|
|
496 new MP4DescriptorProperty();
|
|
497 pAudioEsdProperty->SetTags(MP4ESDescrTag);
|
|
498
|
|
499 /* MP4Descriptor* pAudioEsd = */
|
|
500 CreateESD(
|
|
501 pAudioEsdProperty,
|
|
502 10, // esid
|
|
503 MP4_MPEG4_AUDIO_TYPE,
|
|
504 MP4AudioStreamType,
|
|
505 audioBitrate / 8, // bufferSize
|
|
506 audioBitrate,
|
|
507 audioConfig,
|
|
508 audioConfigLength,
|
|
509 NULL);
|
|
510
|
|
511 CreateIsmaODUpdateCommandForStream(
|
|
512 pAudioEsdProperty,
|
|
513 pVideoEsdProperty,
|
|
514 &pBytes,
|
|
515 &numBytes);
|
|
516
|
|
517 // cleanup temporary descriptor properties
|
|
518 delete pAudioEsdProperty;
|
|
519 delete pVideoEsdProperty;
|
|
520
|
|
521 VERBOSE_ISMA(GetVerbosity(),
|
201
|
522 printf("OD data = %llu bytes\n", numBytes); MP4HexDump(pBytes, numBytes));
|
61
|
523
|
|
524 char* odCmdBase64 = MP4ToBase64(pBytes, numBytes);
|
|
525
|
|
526 urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64);
|
|
527
|
|
528 sprintf(urlBuf,
|
|
529 "data:application/mpeg4-od-au;base64,%s",
|
|
530 odCmdBase64);
|
|
531
|
|
532 VERBOSE_ISMA(GetVerbosity(),
|
|
533 printf("OD data URL = \042%s\042\n", urlBuf));
|
|
534
|
|
535 /* MP4Descriptor* pOdEsd = */
|
|
536 CreateESD(
|
|
537 pEsProperty,
|
|
538 101,
|
|
539 MP4SystemsV1ObjectType,
|
|
540 MP4ObjectDescriptionStreamType,
|
|
541 numBytes, // bufferSize
|
|
542 numBytes * 8, // bitrate
|
|
543 NULL, // config
|
|
544 0, // configLength
|
|
545 urlBuf);
|
|
546
|
|
547 MP4Free(odCmdBase64);
|
|
548 odCmdBase64 = NULL;
|
|
549 MP4Free(pBytes);
|
|
550 pBytes = NULL;
|
|
551 MP4Free(urlBuf);
|
|
552 urlBuf = NULL;
|
|
553
|
|
554 // finally get the whole thing written to a memory
|
|
555 pIod->WriteToMemory(this, ppIodBytes, pIodNumBytes);
|
|
556
|
|
557 delete pIod;
|
|
558
|
|
559 VERBOSE_ISMA(GetVerbosity(),
|
|
560 printf("IOD data =\n"); MP4HexDump(*ppIodBytes, *pIodNumBytes));
|
|
561 }
|
|
562
|
|
563 MP4Descriptor* MP4File::CreateESD(
|
|
564 MP4DescriptorProperty* pEsProperty,
|
|
565 u_int32_t esid,
|
|
566 u_int8_t objectType,
|
|
567 u_int8_t streamType,
|
|
568 u_int32_t bufferSize,
|
|
569 u_int32_t bitrate,
|
|
570 const u_int8_t* pConfig,
|
|
571 u_int32_t configLength,
|
|
572 char* url)
|
|
573 {
|
|
574 MP4IntegerProperty* pInt;
|
|
575 MP4StringProperty* pString;
|
|
576 MP4BytesProperty* pBytes;
|
|
577 MP4BitfieldProperty* pBits;
|
|
578
|
|
579 MP4Descriptor* pEsd =
|
|
580 pEsProperty->AddDescriptor(MP4ESDescrTag);
|
|
581 pEsd->Generate();
|
|
582
|
|
583 pEsd->FindProperty("ESID",
|
|
584 (MP4Property**)&pInt);
|
|
585 pInt->SetValue(esid);
|
|
586
|
|
587 pEsd->FindProperty("decConfigDescr.objectTypeId",
|
|
588 (MP4Property**)&pInt);
|
|
589 pInt->SetValue(objectType);
|
|
590
|
|
591 pEsd->FindProperty("decConfigDescr.streamType",
|
|
592 (MP4Property**)&pInt);
|
|
593 pInt->SetValue(streamType);
|
|
594
|
|
595 pEsd->FindProperty("decConfigDescr.bufferSizeDB",
|
|
596 (MP4Property**)&pInt);
|
|
597 pInt->SetValue(bufferSize);
|
|
598
|
|
599 pEsd->FindProperty("decConfigDescr.maxBitrate",
|
|
600 (MP4Property**)&pInt);
|
|
601 pInt->SetValue(bitrate);
|
|
602
|
|
603 pEsd->FindProperty("decConfigDescr.avgBitrate",
|
|
604 (MP4Property**)&pInt);
|
|
605 pInt->SetValue(bitrate);
|
|
606
|
|
607 MP4DescriptorProperty* pConfigDescrProperty;
|
|
608 pEsd->FindProperty("decConfigDescr.decSpecificInfo",
|
|
609 (MP4Property**)&pConfigDescrProperty);
|
|
610
|
|
611 MP4Descriptor* pConfigDescr =
|
|
612 pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag);
|
|
613 pConfigDescr->Generate();
|
|
614
|
|
615 pConfigDescrProperty->FindProperty("decSpecificInfo[0].info",
|
|
616 (MP4Property**)&pBytes);
|
|
617 pBytes->SetValue(pConfig, configLength);
|
|
618
|
|
619 pEsd->FindProperty("slConfigDescr.predefined",
|
|
620 (MP4Property**)&pInt);
|
|
621 // changed 12/5/02 from plugfest to value 0
|
|
622 pInt->SetValue(0);
|
|
623
|
|
624 pEsd->FindProperty("slConfig.useAccessUnitEndFlag",
|
|
625 (MP4Property **)&pBits);
|
|
626 pBits->SetValue(1);
|
|
627
|
|
628 if (url) {
|
|
629 pEsd->FindProperty("URLFlag",
|
|
630 (MP4Property**)&pInt);
|
|
631 pInt->SetValue(1);
|
|
632
|
|
633 pEsd->FindProperty("URL",
|
|
634 (MP4Property**)&pString);
|
|
635 pString->SetValue(url);
|
|
636 }
|
|
637
|
|
638 return pEsd;
|
|
639 }
|
|
640
|
|
641 void MP4File::CreateIsmaODUpdateCommandFromFileForFile(
|
|
642 MP4TrackId odTrackId,
|
|
643 MP4TrackId audioTrackId,
|
|
644 MP4TrackId videoTrackId,
|
|
645 u_int8_t** ppBytes,
|
|
646 u_int64_t* pNumBytes)
|
|
647 {
|
|
648 MP4Descriptor* pCommand = CreateODCommand(MP4ODUpdateODCommandTag);
|
|
649 pCommand->Generate();
|
|
650
|
|
651 for (u_int8_t i = 0; i < 2; i++) {
|
|
652 MP4TrackId trackId;
|
|
653 u_int16_t odId;
|
|
654
|
|
655 if (i == 0) {
|
|
656 trackId = audioTrackId;
|
|
657 odId = 10;
|
|
658 } else {
|
|
659 trackId = videoTrackId;
|
|
660 odId = 20;
|
|
661 }
|
|
662
|
|
663 if (trackId == MP4_INVALID_TRACK_ID) {
|
|
664 continue;
|
|
665 }
|
|
666
|
|
667 MP4DescriptorProperty* pOdDescrProperty =
|
|
668 (MP4DescriptorProperty*)(pCommand->GetProperty(0));
|
|
669
|
|
670 pOdDescrProperty->SetTags(MP4FileODescrTag);
|
|
671
|
|
672 MP4Descriptor* pOd =
|
|
673 pOdDescrProperty->AddDescriptor(MP4FileODescrTag);
|
|
674
|
|
675 pOd->Generate();
|
|
676
|
|
677 MP4BitfieldProperty* pOdIdProperty = NULL;
|
|
678 pOd->FindProperty("objectDescriptorId",
|
|
679 (MP4Property**)&pOdIdProperty);
|
|
680 pOdIdProperty->SetValue(odId);
|
|
681
|
|
682 MP4DescriptorProperty* pEsIdsDescriptorProperty = NULL;
|
|
683 pOd->FindProperty("esIds",
|
|
684 (MP4Property**)&pEsIdsDescriptorProperty);
|
|
685 ASSERT(pEsIdsDescriptorProperty);
|
|
686
|
|
687 pEsIdsDescriptorProperty->SetTags(MP4ESIDRefDescrTag);
|
|
688
|
|
689 MP4Descriptor *pRefDescriptor =
|
|
690 pEsIdsDescriptorProperty->AddDescriptor(MP4ESIDRefDescrTag);
|
|
691 pRefDescriptor->Generate();
|
|
692
|
|
693 MP4Integer16Property* pRefIndexProperty = NULL;
|
|
694 pRefDescriptor->FindProperty("refIndex",
|
|
695 (MP4Property**)&pRefIndexProperty);
|
|
696 ASSERT(pRefIndexProperty);
|
|
697
|
|
698 u_int32_t mpodIndex = FindTrackReference(
|
|
699 MakeTrackName(odTrackId, "tref.mpod"), trackId);
|
|
700 ASSERT(mpodIndex != 0);
|
|
701
|
|
702 pRefIndexProperty->SetValue(mpodIndex);
|
|
703 }
|
|
704
|
|
705 pCommand->WriteToMemory(this, ppBytes, pNumBytes);
|
|
706
|
|
707 delete pCommand;
|
|
708 }
|
|
709
|
|
710 void MP4File::CreateIsmaODUpdateCommandFromFileForStream(
|
|
711 MP4TrackId audioTrackId,
|
|
712 MP4TrackId videoTrackId,
|
|
713 u_int8_t** ppBytes,
|
|
714 u_int64_t* pNumBytes)
|
|
715 {
|
|
716 MP4DescriptorProperty* pAudioEsd = NULL;
|
|
717 MP4Integer8Property* pAudioSLConfigPredef = NULL;
|
|
718 MP4BitfieldProperty* pAudioAccessUnitEndFlag = NULL;
|
|
719 int oldAudioUnitEndFlagValue = 0;
|
|
720 MP4DescriptorProperty* pVideoEsd = NULL;
|
|
721 MP4Integer8Property* pVideoSLConfigPredef = NULL;
|
|
722 MP4BitfieldProperty* pVideoAccessUnitEndFlag = NULL;
|
|
723 int oldVideoUnitEndFlagValue = 0;
|
|
724 MP4IntegerProperty* pAudioEsdId = NULL;
|
|
725 MP4IntegerProperty* pVideoEsdId = NULL;
|
|
726
|
|
727 if (audioTrackId != MP4_INVALID_TRACK_ID) {
|
|
728 // changed mp4a to * to handle enca case
|
|
729 MP4Atom* pEsdsAtom =
|
|
730 FindAtom(MakeTrackName(audioTrackId,
|
|
731 "mdia.minf.stbl.stsd.*.esds"));
|
|
732 ASSERT(pEsdsAtom);
|
|
733
|
|
734 pAudioEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2));
|
|
735 // ESID is 0 for file, stream needs to be non-ze
|
|
736 pAudioEsd->FindProperty("ESID",
|
|
737 (MP4Property**)&pAudioEsdId);
|
|
738
|
|
739 ASSERT(pAudioEsdId);
|
|
740 pAudioEsdId->SetValue(audioTrackId);
|
|
741
|
|
742 // SL config needs to change from 2 (file) to 1 (null)
|
|
743 pAudioEsd->FindProperty("slConfigDescr.predefined",
|
|
744 (MP4Property**)&pAudioSLConfigPredef);
|
|
745 ASSERT(pAudioSLConfigPredef);
|
|
746 pAudioSLConfigPredef->SetValue(0);
|
|
747
|
|
748 pAudioEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag",
|
|
749 (MP4Property **)&pAudioAccessUnitEndFlag);
|
|
750 oldAudioUnitEndFlagValue =
|
|
751 pAudioAccessUnitEndFlag->GetValue();
|
|
752 pAudioAccessUnitEndFlag->SetValue(1);
|
|
753 }
|
|
754
|
|
755 if (videoTrackId != MP4_INVALID_TRACK_ID) {
|
|
756 // changed mp4v to * to handle encv case
|
|
757 MP4Atom* pEsdsAtom =
|
|
758 FindAtom(MakeTrackName(videoTrackId,
|
|
759 "mdia.minf.stbl.stsd.*.esds"));
|
|
760 ASSERT(pEsdsAtom);
|
|
761
|
|
762 pVideoEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2));
|
|
763 pVideoEsd->FindProperty("ESID",
|
|
764 (MP4Property**)&pVideoEsdId);
|
|
765
|
|
766 ASSERT(pVideoEsdId);
|
|
767 pVideoEsdId->SetValue(videoTrackId);
|
|
768
|
|
769 // SL config needs to change from 2 (file) to 1 (null)
|
|
770 pVideoEsd->FindProperty("slConfigDescr.predefined",
|
|
771 (MP4Property**)&pVideoSLConfigPredef);
|
|
772 ASSERT(pVideoSLConfigPredef);
|
|
773 pVideoSLConfigPredef->SetValue(0);
|
|
774
|
|
775 pVideoEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag",
|
|
776 (MP4Property **)&pVideoAccessUnitEndFlag);
|
|
777 oldVideoUnitEndFlagValue =
|
|
778 pVideoAccessUnitEndFlag->GetValue();
|
|
779 pVideoAccessUnitEndFlag->SetValue(1);
|
|
780 }
|
|
781
|
|
782 CreateIsmaODUpdateCommandForStream(
|
|
783 pAudioEsd, pVideoEsd, ppBytes, pNumBytes);
|
|
784 VERBOSE_ISMA(GetVerbosity(),
|
201
|
785 printf("After CreateImsaODUpdateCommandForStream len %llu =\n", *pNumBytes); MP4HexDump(*ppBytes, *pNumBytes));
|
61
|
786 // return SL config values to 2 (file)
|
|
787 // return ESID values to 0
|
|
788 if (pAudioSLConfigPredef) {
|
|
789 pAudioSLConfigPredef->SetValue(2);
|
|
790 }
|
|
791 if (pAudioEsdId) {
|
|
792 pAudioEsdId->SetValue(0);
|
|
793 }
|
|
794 if (pAudioAccessUnitEndFlag) {
|
|
795 pAudioAccessUnitEndFlag->SetValue(oldAudioUnitEndFlagValue );
|
|
796 }
|
|
797 if (pVideoEsdId) {
|
|
798 pVideoEsdId->SetValue(0);
|
|
799 }
|
|
800 if (pVideoSLConfigPredef) {
|
|
801 pVideoSLConfigPredef->SetValue(2);
|
|
802 }
|
|
803 if (pVideoAccessUnitEndFlag) {
|
|
804 pVideoAccessUnitEndFlag->SetValue(oldVideoUnitEndFlagValue );
|
|
805 }
|
|
806 }
|
|
807
|
|
808 void MP4File::CreateIsmaODUpdateCommandForStream(
|
|
809 MP4DescriptorProperty* pAudioEsdProperty,
|
|
810 MP4DescriptorProperty* pVideoEsdProperty,
|
|
811 u_int8_t** ppBytes,
|
|
812 u_int64_t* pNumBytes)
|
|
813 {
|
|
814 MP4Descriptor* pAudioOd = NULL;
|
|
815 MP4Descriptor* pVideoOd = NULL;
|
|
816
|
|
817 MP4Descriptor* pCommand =
|
|
818 CreateODCommand(MP4ODUpdateODCommandTag);
|
|
819 pCommand->Generate();
|
|
820
|
|
821 for (u_int8_t i = 0; i < 2; i++) {
|
|
822 u_int16_t odId;
|
|
823 MP4DescriptorProperty* pEsdProperty = NULL;
|
|
824
|
|
825 if (i == 0) {
|
|
826 odId = 10;
|
|
827 pEsdProperty = pAudioEsdProperty;
|
|
828 } else {
|
|
829 odId = 20;
|
|
830 pEsdProperty = pVideoEsdProperty;
|
|
831 }
|
|
832
|
|
833 if (pEsdProperty == NULL) {
|
|
834 continue;
|
|
835 }
|
|
836
|
|
837 MP4DescriptorProperty* pOdDescrProperty =
|
|
838 (MP4DescriptorProperty*)(pCommand->GetProperty(0));
|
|
839
|
|
840 pOdDescrProperty->SetTags(MP4ODescrTag);
|
|
841
|
|
842 MP4Descriptor* pOd =
|
|
843 pOdDescrProperty->AddDescriptor(MP4ODescrTag);
|
|
844 pOd->Generate();
|
|
845
|
|
846 if (i == 0) {
|
|
847 pAudioOd = pOd;
|
|
848 } else {
|
|
849 pVideoOd = pOd;
|
|
850 }
|
|
851
|
|
852 MP4BitfieldProperty* pOdIdProperty = NULL;
|
|
853 pOd->FindProperty("objectDescriptorId",
|
|
854 (MP4Property**)&pOdIdProperty);
|
|
855 pOdIdProperty->SetValue(odId);
|
|
856
|
|
857 delete (MP4DescriptorProperty*)pOd->GetProperty(4);
|
|
858 pOd->SetProperty(4, pEsdProperty);
|
|
859 }
|
|
860
|
|
861 // serialize OD command
|
|
862 pCommand->WriteToMemory(this, ppBytes, pNumBytes);
|
|
863
|
|
864 // detach from esd descriptor params
|
|
865 if (pAudioOd) {
|
|
866 pAudioOd->SetProperty(4, NULL);
|
|
867 }
|
|
868 if (pVideoOd) {
|
|
869 pVideoOd->SetProperty(4, NULL);
|
|
870 }
|
|
871
|
|
872 // then destroy
|
|
873 delete pCommand;
|
|
874 }
|
|
875
|
|
876 void MP4File::CreateIsmaSceneCommand(
|
|
877 bool hasAudio,
|
|
878 bool hasVideo,
|
|
879 u_int8_t** ppBytes,
|
|
880 u_int64_t* pNumBytes)
|
|
881 {
|
|
882 // from ISMA 1.0 Tech Spec Appendix E
|
|
883 static const u_int8_t bifsAudioOnly[] = {
|
|
884 0xC0, 0x10, 0x12,
|
|
885 0x81, 0x30, 0x2A, 0x05, 0x6D, 0xC0
|
|
886 };
|
|
887 static const u_int8_t bifsVideoOnly[] = {
|
|
888 0xC0, 0x10, 0x12,
|
|
889 0x61, 0x04,
|
|
890 0x1F, 0xC0, 0x00, 0x00,
|
|
891 0x1F, 0xC0, 0x00, 0x00,
|
|
892 0x44, 0x28, 0x22, 0x82, 0x9F, 0x80
|
|
893 };
|
|
894 static const u_int8_t bifsAudioVideo[] = {
|
|
895 0xC0, 0x10, 0x12,
|
|
896 0x81, 0x30, 0x2A, 0x05, 0x6D, 0x26,
|
|
897 0x10, 0x41, 0xFC, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00,
|
|
898 0x04, 0x42, 0x82, 0x28, 0x29, 0xF8
|
|
899 };
|
|
900
|
|
901 if (hasAudio && hasVideo) {
|
|
902 *pNumBytes = sizeof(bifsAudioVideo);
|
|
903 *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
|
|
904 memcpy(*ppBytes, bifsAudioVideo, sizeof(bifsAudioVideo));
|
|
905
|
|
906 } else if (hasAudio) {
|
|
907 *pNumBytes = sizeof(bifsAudioOnly);
|
|
908 *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
|
|
909 memcpy(*ppBytes, bifsAudioOnly, sizeof(bifsAudioOnly));
|
|
910
|
|
911 } else if (hasVideo) {
|
|
912 *pNumBytes = sizeof(bifsVideoOnly);
|
|
913 *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
|
|
914 memcpy(*ppBytes, bifsVideoOnly, sizeof(bifsVideoOnly));
|
|
915 } else {
|
|
916 *pNumBytes = 0;
|
|
917 *ppBytes = NULL;
|
|
918 }
|
|
919 }
|
|
920
|