25
25
26
26
#include "lib/filters/filters.h"
27
27
#include "lib/memio.h"
28
+ #include <gpac/internal/isomedia_dev.h>
29
+
30
+ static guint32 INIT_BOXES [] = { GF_ISOM_BOX_TYPE_FTYP ,
31
+ GF_ISOM_BOX_TYPE_FREE ,
32
+ GF_ISOM_BOX_TYPE_MOOV ,
33
+ 0 };
34
+ static guint32 HEADER_BOXES [] = { GF_ISOM_BOX_TYPE_STYP ,
35
+ GF_ISOM_BOX_TYPE_MOOF ,
36
+ GF_ISOM_BOX_TYPE_MDAT ,
37
+ 0 };
38
+
39
+ typedef enum _BufferType
40
+ {
41
+ INIT = 0 ,
42
+ HEADER ,
43
+ DATA ,
44
+
45
+ LAST
46
+ } BufferType ;
47
+
48
+ struct BoxMapping
49
+ {
50
+ BufferType type ;
51
+ guint32 * boxes ;
52
+ } box_mapping [LAST ] = {
53
+ { INIT , INIT_BOXES },
54
+ { HEADER , HEADER_BOXES },
55
+ { DATA , NULL },
56
+ };
28
57
29
58
typedef struct
30
59
{
60
+ GstBuffer * buffer ;
61
+ gboolean is_complete ;
62
+ guint32 expected_size ;
63
+ guint64 pts , dts ;
64
+ guint32 duration ;
65
+ } BufferContents ;
66
+
67
+ typedef struct
68
+ {
69
+ // Output queue for complete GOPs
31
70
GQueue * output_queue ;
71
+
72
+ // State
73
+ BufferType current_type ;
74
+
75
+ // Buffer contents for the init, header, and data
76
+ BufferContents * contents [3 ];
77
+
78
+ // Global state
79
+ gboolean is_discontinuity ;
80
+
81
+ // Reader context
82
+ guint64 offset ;
83
+ guint64 size ;
84
+ guint32 leftover ;
32
85
} Mp4mxCtx ;
33
86
34
87
void
35
88
mp4mx_ctx_init (void * * process_ctx )
36
89
{
37
90
* process_ctx = g_new0 (Mp4mxCtx , 1 );
38
91
Mp4mxCtx * ctx = (Mp4mxCtx * )* process_ctx ;
92
+
93
+ // Initialize the context
39
94
ctx -> output_queue = g_queue_new ();
95
+ ctx -> current_type = INIT ;
96
+ ctx -> is_discontinuity = TRUE;
97
+
98
+ // Initialize the buffer contents
99
+ for (guint i = 0 ; i < LAST ; i ++ ) {
100
+ ctx -> contents [i ] = g_new0 (BufferContents , 1 );
101
+ ctx -> contents [i ]-> is_complete = FALSE;
102
+ }
40
103
}
41
104
42
105
void
@@ -49,6 +112,13 @@ mp4mx_ctx_free(void* process_ctx)
49
112
gst_buffer_unref ((GstBuffer * )g_queue_pop_head (ctx -> output_queue ));
50
113
g_queue_free (ctx -> output_queue );
51
114
115
+ // Free the buffer contents
116
+ for (guint i = 0 ; i < LAST ; i ++ ) {
117
+ if (ctx -> contents [i ]-> buffer )
118
+ gst_buffer_unref (ctx -> contents [i ]-> buffer );
119
+ g_free (ctx -> contents [i ]);
120
+ }
121
+
52
122
// Free the context
53
123
g_free (ctx );
54
124
}
@@ -58,25 +128,208 @@ mp4mx_post_process(GF_Filter* filter, GF_FilterPacket* pck)
58
128
{
59
129
GPAC_MemIoContext * ctx = (GPAC_MemIoContext * )gf_filter_get_rt_udta (filter );
60
130
Mp4mxCtx * mp4mx_ctx = (Mp4mxCtx * )ctx -> process_ctx ;
131
+ if (!pck )
132
+ return GF_OK ;
61
133
62
134
// Get the data
63
135
u32 size ;
64
136
const u8 * data = gf_filter_pck_get_data (pck , & size );
65
- gf_filter_pck_ref (& pck );
66
-
67
- // Create a new buffer
68
- GstBuffer * buffer =
69
- gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY , // flags
70
- (u8 * )data , // data
71
- size , // maxsize
72
- 0 , // offset
73
- size , // size
74
- pck , // user_data
75
- (GDestroyNotify )gf_filter_pck_unref // notify
76
- );
77
-
78
- // Enqueue the buffer
79
- g_queue_push_tail (mp4mx_ctx -> output_queue , buffer );
137
+
138
+ mp4mx_ctx -> size += size ;
139
+ guint32 offset = 0 ;
140
+
141
+ // If we have leftover data, append it to the current buffer
142
+ if (mp4mx_ctx -> leftover ) {
143
+ guint32 leftover = MIN (mp4mx_ctx -> leftover , size );
144
+ GstMemory * mem =
145
+ gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY ,
146
+ (gpointer )data ,
147
+ leftover ,
148
+ 0 ,
149
+ leftover ,
150
+ pck ,
151
+ (GDestroyNotify )gf_filter_pck_unref );
152
+ gf_filter_pck_ref (& pck );
153
+
154
+ // Append the memory to the buffer
155
+ g_assert (mp4mx_ctx -> contents [mp4mx_ctx -> current_type ]-> buffer );
156
+ gst_buffer_append_memory (
157
+ mp4mx_ctx -> contents [mp4mx_ctx -> current_type ]-> buffer , mem );
158
+
159
+ // Update the leftover and data
160
+ mp4mx_ctx -> leftover -= leftover ;
161
+ offset += leftover ;
162
+ }
163
+
164
+ // Iterate over the boxes
165
+ while (offset < size ) {
166
+ guint32 box_size = GUINT32_FROM_BE (* (guint32 * )(data + offset ));
167
+ guint32 box_type = GUINT32_FROM_BE (* (guint32 * )(data + offset + 4 ));
168
+ GST_WARNING ("Box: %s, size: %u" , gf_4cc_to_str (box_type ), box_size );
169
+
170
+ // Special handling for mdat
171
+ if (box_type == GF_ISOM_BOX_TYPE_MDAT ) {
172
+ // Create a new memory for the mdat header
173
+ GstMemory * mem =
174
+ gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY ,
175
+ (gpointer )data + offset ,
176
+ 8 ,
177
+ 0 ,
178
+ 8 ,
179
+ pck ,
180
+ (GDestroyNotify )gf_filter_pck_unref );
181
+ gf_filter_pck_ref (& pck );
182
+
183
+ // Append the memory to the buffer
184
+ g_assert (mp4mx_ctx -> contents [HEADER ]-> buffer );
185
+ gst_buffer_append_memory (mp4mx_ctx -> contents [HEADER ]-> buffer , mem );
186
+
187
+ // Complete the header
188
+ mp4mx_ctx -> contents [HEADER ]-> is_complete = TRUE;
189
+ mp4mx_ctx -> current_type = DATA ;
190
+
191
+ // Set the expected size
192
+ mp4mx_ctx -> contents [DATA ]-> expected_size = box_size - 8 ;
193
+
194
+ // Move the offset
195
+ offset += 8 ;
196
+ box_size -= 8 ;
197
+ goto append_memory ;
198
+ }
199
+
200
+ guint32 last_type ;
201
+ for (last_type = mp4mx_ctx -> current_type ; last_type < LAST ; last_type ++ ) {
202
+ // Check if the current box is related to current type
203
+ gboolean found = FALSE;
204
+ for (guint i = 0 ; box_mapping [last_type ].boxes [i ]; i ++ ) {
205
+ if (box_mapping [last_type ].boxes [i ] == box_type ) {
206
+ found = TRUE;
207
+ break ;
208
+ }
209
+ }
210
+ if (found )
211
+ break ;
212
+
213
+ // If not found, current type is completed
214
+ mp4mx_ctx -> contents [last_type ]-> is_complete = TRUE;
215
+ }
216
+
217
+ // Check if the box is unknown
218
+ if (last_type == LAST ) {
219
+ GST_ERROR ("Unknown box type: %s" , gf_4cc_to_str (box_type ));
220
+ g_assert_not_reached ();
221
+ }
222
+
223
+ // Set the current type
224
+ mp4mx_ctx -> current_type = last_type ;
225
+
226
+ append_memory :
227
+ // Create a new memory
228
+ GstMemory * mem =
229
+ gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY ,
230
+ (gpointer )data + offset ,
231
+ box_size ,
232
+ 0 ,
233
+ box_size ,
234
+ pck ,
235
+ (GDestroyNotify )gf_filter_pck_unref );
236
+ gf_filter_pck_ref (& pck );
237
+
238
+ // Append the memory to the buffer
239
+ BufferType type = mp4mx_ctx -> current_type ;
240
+ if (!mp4mx_ctx -> contents [type ]-> buffer ) {
241
+ GstBuffer * buf = mp4mx_ctx -> contents [type ]-> buffer = gst_buffer_new ();
242
+
243
+ // Set the flags
244
+ switch (type ) {
245
+ case INIT :
246
+ if (mp4mx_ctx -> is_discontinuity ) {
247
+ GST_BUFFER_FLAG_SET (buf , GST_BUFFER_FLAG_DISCONT );
248
+ mp4mx_ctx -> is_discontinuity = FALSE;
249
+ }
250
+ // fallthrough
251
+ case HEADER :
252
+ GST_BUFFER_FLAG_SET (buf , GST_BUFFER_FLAG_HEADER );
253
+ break ;
254
+ case DATA :
255
+ GST_BUFFER_FLAG_SET (buf , GST_BUFFER_FLAG_MARKER );
256
+ GST_BUFFER_FLAG_SET (buf , GST_BUFFER_FLAG_DELTA_UNIT );
257
+ break ;
258
+
259
+ default :
260
+ break ;
261
+ }
262
+
263
+ // Set the times
264
+ GST_BUFFER_PTS (buf ) = gf_filter_pck_get_cts (pck );
265
+ GST_BUFFER_DTS (buf ) = gf_filter_pck_get_dts (pck );
266
+ GST_BUFFER_DURATION (buf ) = gf_filter_pck_get_duration (pck );
267
+ }
268
+
269
+ // Append the memory to the buffer
270
+ gst_buffer_append_memory (mp4mx_ctx -> contents [type ]-> buffer , mem );
271
+
272
+ // Move to the next box
273
+ offset += box_size ;
274
+ }
275
+
276
+ // Update leftover and global offset
277
+ if (mp4mx_ctx -> offset + offset > mp4mx_ctx -> size ) {
278
+ mp4mx_ctx -> leftover = mp4mx_ctx -> offset + offset - mp4mx_ctx -> size ;
279
+ } else {
280
+ g_assert (mp4mx_ctx -> leftover == 0 );
281
+ mp4mx_ctx -> offset = MIN (mp4mx_ctx -> offset + offset , mp4mx_ctx -> size );
282
+ }
283
+
284
+ // Check if the data is complete
285
+ if (mp4mx_ctx -> contents [DATA ]-> buffer ) {
286
+ guint32 expected_size = mp4mx_ctx -> contents [DATA ]-> expected_size ;
287
+ guint32 buffer_size =
288
+ gst_buffer_get_size (mp4mx_ctx -> contents [DATA ]-> buffer );
289
+
290
+ // Check if the buffer is complete
291
+ if (buffer_size >= expected_size ) {
292
+ g_assert (buffer_size == expected_size );
293
+
294
+ // Update the state
295
+ mp4mx_ctx -> current_type = HEADER ;
296
+ mp4mx_ctx -> contents [DATA ]-> is_complete = TRUE;
297
+ }
298
+ }
299
+
300
+ // Check if the GOP is complete
301
+ if (mp4mx_ctx -> contents [HEADER ]-> is_complete &&
302
+ mp4mx_ctx -> contents [DATA ]-> is_complete ) {
303
+ // Create a buffer list
304
+ GstBufferList * buffer_list = gst_buffer_list_new ();
305
+
306
+ // Init only if it's present
307
+ if (mp4mx_ctx -> contents [INIT ]-> is_complete ) {
308
+ gst_buffer_list_add (buffer_list , mp4mx_ctx -> contents [INIT ]-> buffer );
309
+
310
+ // Init won't have timings set, set it using header
311
+ GST_BUFFER_PTS (mp4mx_ctx -> contents [INIT ]-> buffer ) =
312
+ GST_BUFFER_PTS (mp4mx_ctx -> contents [HEADER ]-> buffer ) -
313
+ GST_BUFFER_DURATION (mp4mx_ctx -> contents [HEADER ]-> buffer );
314
+ GST_BUFFER_DTS (mp4mx_ctx -> contents [INIT ]-> buffer ) =
315
+ GST_BUFFER_DTS (mp4mx_ctx -> contents [HEADER ]-> buffer ) -
316
+ GST_BUFFER_DURATION (mp4mx_ctx -> contents [HEADER ]-> buffer );
317
+ }
318
+
319
+ gst_buffer_list_add (buffer_list , mp4mx_ctx -> contents [HEADER ]-> buffer );
320
+ gst_buffer_list_add (buffer_list , mp4mx_ctx -> contents [DATA ]-> buffer );
321
+
322
+ // Enqueue the buffer
323
+ g_queue_push_tail (mp4mx_ctx -> output_queue , buffer_list );
324
+
325
+ // Reset the buffer contents
326
+ for (guint i = 0 ; i < LAST ; i ++ ) {
327
+ mp4mx_ctx -> contents [i ]-> buffer = NULL ;
328
+ mp4mx_ctx -> contents [i ]-> is_complete = FALSE;
329
+ mp4mx_ctx -> contents [i ]-> expected_size = 0 ;
330
+ }
331
+ }
332
+
80
333
return GF_OK ;
81
334
}
82
335
@@ -93,5 +346,5 @@ mp4mx_consume(GF_Filter* filter, void** outptr)
93
346
94
347
// Assign the output
95
348
* outptr = g_queue_pop_head (mp4mx_ctx -> output_queue );
96
- return GPAC_FILTER_PP_RET_BUFFER ;
349
+ return GPAC_FILTER_PP_RET_BUFFER_LIST ;
97
350
}
0 commit comments