2
* GPAC - Multimedia Framework C SDK
4
* Authors: Cyril COncolato
5
* Copyright (c) Telecom ParisTech 2013-
8
* This file is part of GPAC / Media Source
10
* GPAC is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU Lesser General Public License as published by
12
* the Free Software Foundation; either version 2, or (at your option)
15
* GPAC is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU Lesser General Public License for more details.
20
* You should have received a copy of the GNU Lesser General Public
21
* License along with this library; see the file COPYING. If not, write to
22
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25
#include <gpac/setup.h>
27
#ifdef GPAC_HAS_SPIDERMONKEY
28
#include <gpac/html5_mse.h>
29
#include <gpac/internal/isomedia_dev.h>
31
GF_HTML_MediaSource *gf_mse_media_source_new()
33
GF_HTML_MediaSource *ms;
34
GF_SAFEALLOC(ms, GF_HTML_MediaSource);
35
ms->sourceBuffers.list = gf_list_new();
36
ms->sourceBuffers.parent = ms;
37
ms->sourceBuffers.evt_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST, &ms->sourceBuffers);
38
ms->activeSourceBuffers.list = gf_list_new();
39
ms->activeSourceBuffers.parent = ms;
40
ms->activeSourceBuffers.evt_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST, &ms->activeSourceBuffers);
41
ms->reference_count = 1;
42
ms->evt_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE, ms);
47
void gf_mse_mediasource_del(GF_HTML_MediaSource *ms, Bool del_js)
51
/* finalize the object from the JS perspective */
55
/* only delete the object if it is not used elsewhere */
56
if (ms->reference_count) {
57
ms->reference_count--;
59
if (!ms->reference_count) {
61
for (i = 0; i < gf_list_count(ms->sourceBuffers.list); i++) {
62
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)gf_list_get(ms->sourceBuffers.list, i);
64
if (sb->parser && sb->parser_connected)
65
sb->parser->CloseService(sb->parser);
67
gf_mse_source_buffer_del(sb);
69
gf_list_del(ms->sourceBuffers.list);
70
/* all source buffer should have been deleted in the deletion of sourceBuffers */
71
gf_list_del(ms->activeSourceBuffers.list);
72
if (ms->blobURI) gf_free(ms->blobURI);
73
gf_free(ms->evt_target);
79
static void gf_mse_fire_event(GF_DOMEventTarget *target, GF_EventType event_type)
81
GF_SceneGraph *sg = NULL;
82
GF_DOM_Event mse_event;
83
memset(&mse_event, 0, sizeof(GF_DOM_Event));
84
mse_event.type = event_type;
85
mse_event.target = target->ptr;
86
mse_event.target_type = target->ptr_type;
87
switch(target->ptr_type) {
88
case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
90
GF_HTML_MediaSource *ms = (GF_HTML_MediaSource *)target->ptr;
94
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
96
GF_HTML_SourceBufferList *list = (GF_HTML_SourceBufferList *)target->ptr;
97
sg = list->parent->sg;
100
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
102
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)target->ptr;
103
sg = sb->mediasource->sg;
110
sg_fire_dom_event(target, &mse_event, sg, NULL);
114
void gf_mse_mediasource_open(GF_HTML_MediaSource *ms, struct _mediaobj *mo)
118
GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[MSE] Cannot open media source without a media object\n"));
121
ms->readyState = MEDIA_SOURCE_READYSTATE_OPEN;
122
/* getting the Scene Tree node attached to this media source */
123
ms->node = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(mo, 0));
124
ms->sg = gf_node_get_graph(ms->node);
125
gf_mse_fire_event(ms->evt_target, GF_EVENT_HTML_MSE_SOURCE_OPEN);
129
void gf_mse_mediasource_close(GF_HTML_MediaSource *ms)
132
ms->readyState = MEDIA_SOURCE_READYSTATE_CLOSED;
133
gf_mse_fire_event(ms->evt_target, GF_EVENT_HTML_MSE_SOURCE_CLOSE);
137
void gf_mse_mediasource_end(GF_HTML_MediaSource *ms)
140
ms->readyState = MEDIA_SOURCE_READYSTATE_ENDED;
141
gf_mse_fire_event(ms->evt_target, GF_EVENT_HTML_MSE_SOURCE_ENDED);
144
GF_HTML_SourceBuffer *gf_mse_source_buffer_new(GF_HTML_MediaSource *mediasource)
147
GF_HTML_SourceBuffer *source;
148
GF_SAFEALLOC(source, GF_HTML_SourceBuffer);
149
sprintf(name, "SourceBuffer_Thread_%p", source);
150
source->mediasource = mediasource;
151
source->buffered.times = gf_list_new();
152
source->input_buffer = gf_list_new();
153
source->tracks = gf_list_new();
154
source->parser_thread = gf_th_new(name);
155
source->remove_thread = gf_th_new(name);
156
source->append_mode = MEDIA_SOURCE_APPEND_MODE_SEGMENTS;
157
source->appendWindowStart = 0;
158
source->appendWindowEnd = GF_MAX_DOUBLE;
159
source->evt_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER, source);
163
void gf_mse_add_source_buffer(GF_HTML_MediaSource *ms, GF_HTML_SourceBuffer *sb)
165
gf_list_add(ms->sourceBuffers.list, sb);
166
gf_mse_fire_event(ms->sourceBuffers.evt_target, GF_EVENT_HTML_MSE_ADD_SOURCE_BUFFER);
169
static void gf_mse_reset_input_buffer(GF_List *input_buffer)
171
while (gf_list_count(input_buffer)) {
172
GF_HTML_ArrayBuffer *b = (GF_HTML_ArrayBuffer *)gf_list_get(input_buffer, 0);
173
gf_list_rem(input_buffer, 0);
174
gf_arraybuffer_del(b, GF_FALSE);
178
void gf_mse_source_buffer_del(GF_HTML_SourceBuffer *sb)
180
GF_HTML_TrackList tlist;
181
gf_html_timeranges_del(&sb->buffered);
182
gf_mse_reset_input_buffer(sb->input_buffer);
183
gf_list_del(sb->input_buffer);
185
if (sb->prev_buffer) gf_arraybuffer_del((GF_HTML_ArrayBuffer *)sb->prev_buffer, GF_FALSE);
187
tlist.tracks = sb->tracks;
188
gf_html_tracklist_del(&tlist);
189
gf_th_del(sb->parser_thread);
190
gf_th_del(sb->remove_thread);
192
if (sb->service_desc) gf_odf_desc_del((GF_Descriptor *)sb->service_desc);
197
void gf_mse_source_buffer_set_update(GF_HTML_SourceBuffer *sb, Bool update)
199
if (sb->updating == update) return;
200
sb->updating = update;
202
gf_mse_fire_event(sb->evt_target, GF_EVENT_HTML_MSE_UPDATE_START);
204
gf_mse_fire_event(sb->evt_target, GF_EVENT_HTML_MSE_UPDATE);
205
gf_mse_fire_event(sb->evt_target, GF_EVENT_HTML_MSE_UPDATE_END);
209
/*locates and loads an input service (demuxer, parser) for this source buffer based on mime type or segment name*/
210
GF_Err gf_mse_source_buffer_load_parser(GF_HTML_SourceBuffer *sourcebuffer, const char *mime)
212
GF_InputService *parser = NULL;
216
/* strip the 'codecs' and 'profile' parameters from the MIME type */
217
char *param = (char *)strchr(mime, ';');
222
/* Check MIME type to start the right InputService */
223
sPlug = gf_cfg_get_key(sourcebuffer->mediasource->service->term->user->config, "MimeTypes", mime);
224
if (sPlug) sPlug = strrchr(sPlug, '"');
227
parser = (GF_InputService *) gf_modules_load_interface_by_name(sourcebuffer->mediasource->service->term->user->modules, sPlug, GF_NET_CLIENT_INTERFACE);
235
sourcebuffer->parser = parser;
236
parser->query_proxy = gf_mse_proxy;
237
parser->proxy_udta = sourcebuffer;
238
parser->proxy_type = GF_TRUE;
241
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MSE] Error locating plugin for source - mime type %s\n", mime));
246
/* create a track based on the ESD and adds it to the source buffer */
247
static GF_HTML_Track *gf_mse_source_buffer_add_track(GF_HTML_SourceBuffer *sb, GF_ESD *esd)
249
GF_HTML_Track *track;
250
GF_HTML_TrackType type;
253
type = HTML_MEDIA_TRACK_TYPE_UNKNOWN;
254
if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) {
255
type = HTML_MEDIA_TRACK_TYPE_VIDEO;
256
} else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) {
257
type = HTML_MEDIA_TRACK_TYPE_AUDIO;
259
/* TODO: Text tracks */
262
track = gf_html_media_track_new(type, "", GF_TRUE, "", "", "", "");
265
track->bin_id = esd->ESID;
266
track->buffer = gf_list_new();
267
sprintf(mx_name, "track_mutex_%d", track->bin_id);
268
track->buffer_mutex = gf_mx_new(mx_name);
269
track->timescale = esd->slConfig->timestampResolution;
270
gf_list_add(sb->tracks, track);
275
/* Creates the different tracks based on the demuxer parser information
276
Needs the parser to be setup and the initialization segment passed */
277
static GF_Err gf_mse_source_buffer_setup_tracks(GF_HTML_SourceBuffer *sb)
279
if (!sb || !sb->parser || !sb->parser_connected) return GF_BAD_PARAM;
280
sb->service_desc = (GF_ObjectDescriptor *)sb->parser->GetServiceDescriptor(sb->parser, GF_MEDIA_OBJECT_UNDEF, NULL);
281
if (sb->service_desc) {
283
count = gf_list_count(sb->service_desc->ESDescriptors);
286
for (i = 0; i < count; i++) {
287
GF_ESD *esd = (GF_ESD *)gf_list_get(sb->service_desc->ESDescriptors, i);
288
gf_mse_source_buffer_add_track(sb, esd);
297
/* Adds the ObjectDescriptor to the associated track (creating a new track if needed)
298
Called in response to addmedia events received from the parser */
299
static GF_Err gf_mse_source_buffer_store_track_desc(GF_HTML_SourceBuffer *sb, GF_ObjectDescriptor *od)
304
Bool found = GF_FALSE;
305
GF_HTML_Track *track;
307
esd = (GF_ESD *)gf_list_get(od->ESDescriptors, 0);
308
count = gf_list_count(sb->tracks);
309
for (i = 0; i < count; i++) {
310
track = (GF_HTML_Track *)gf_list_get(sb->tracks, i);
311
if (track->bin_id == esd->ESID) {
318
track = gf_mse_source_buffer_add_track(sb, esd);
329
/* Traverses the list of Access Units already demuxed & parsed to update the buffered status */
330
void gf_mse_source_buffer_update_buffered(GF_HTML_SourceBuffer *sb) {
335
Bool start_set = GF_FALSE;
336
Bool end_set = GF_FALSE;
341
/* cleaning the current list */
342
gf_html_timeranges_reset(&(sb->buffered));
344
/* merging the start and end for all tracks */
345
track_count = gf_list_count(sb->tracks);
346
for (i = 0; i < track_count; i++) {
349
GF_HTML_Track *track = (GF_HTML_Track *)gf_list_get(sb->tracks, i);
350
gf_mx_p(track->buffer_mutex);
351
packet_count = gf_list_count(track->buffer);
353
for (j = 0; j < packet_count; j++) {
354
GF_MSE_Packet *packet = (GF_MSE_Packet *)gf_list_get(track->buffer, j);
356
packet_start = (packet->sl_header.compositionTimeStamp * 1.0 )/ track->timescale;
357
if (packet->sl_header.au_duration) {
358
au_dur = packet->sl_header.au_duration;
361
GF_MSE_Packet *prev = (GF_MSE_Packet *)gf_list_get(track->buffer, j-1);
362
au_dur = packet->sl_header.decodingTimeStamp - prev->sl_header.decodingTimeStamp;
365
packet_end = ((packet->sl_header.compositionTimeStamp + au_dur) * 1.0) / track->timescale;
367
start = packet_start;
370
if (start > packet_start) {
371
start = packet_start;
378
if (end < packet_end) {
384
gf_mx_v(track->buffer_mutex);
387
/* Creating only one range for now */
388
if (start_set && end_set) {
389
gf_media_time_ranges_add(&sb->buffered, start, end);
393
/* Deletes all unparsed data buffers from all tracks in the source buffer */
394
static void gf_mse_source_buffer_reset_parser(GF_HTML_SourceBuffer *sb)
397
track_count = gf_list_count(sb->tracks);
399
/* wait until all remaining entire AU are parsed and then flush the remaining bytes in the parser */
401
for (i = 0; i < track_count; i++)
403
GF_HTML_Track *track = (GF_HTML_Track *)gf_list_get(sb->tracks, i);
404
track->last_dts_set = GF_FALSE;
405
track->highest_pts_set = GF_FALSE;
406
track->needs_rap = GF_TRUE;
408
sb->highest_end_timestamp_set = GF_FALSE;
409
gf_mse_reset_input_buffer(sb->input_buffer);
410
sb->append_state = MEDIA_SOURCE_APPEND_STATE_WAITING_FOR_SEGMENT;
413
GF_Err gf_mse_source_buffer_abort(GF_HTML_SourceBuffer *sb)
416
if (sb->continuation_timestamp_flag == GF_FALSE)
418
if (sb->abort_mode == MEDIA_SOURCE_ABORT_MODE_CONTINUATION && !sb->highest_end_timestamp_set)
423
if (sb->highest_end_timestamp_set) {
424
sb->continuation_timestamp = sb->highest_end_timestamp;
425
sb->continuation_timestamp_flag = GF_TRUE;
428
// sb->abort_mode = mode;
430
gf_mse_source_buffer_set_update(sb, GF_FALSE);
431
sb->appendWindowStart = 0;
432
sb->appendWindowEnd = GF_MAX_DOUBLE;
433
/*fire abort event at the SourceBuffer */
434
gf_mse_source_buffer_reset_parser(sb);
438
void gf_mse_packet_del(GF_MSE_Packet *packet) {
439
gf_free(packet->data);
443
static GF_MSE_Packet *gf_mse_find_overlapped_packet(GF_HTML_Track *track,
444
GF_MSE_Packet *packet)
450
found_previous = GF_FALSE;
451
gf_mx_p(track->buffer_mutex);
452
count = gf_list_count(track->buffer);
453
for (i = 0; i < count; i++)
455
GF_MSE_Packet *p = (GF_MSE_Packet *)gf_list_get(track->buffer, i);
456
if (p->sl_header.compositionTimeStamp < packet->sl_header.compositionTimeStamp) {
457
found_previous = GF_TRUE;
459
if (found_previous == GF_TRUE && p->sl_header.compositionTimeStamp > packet->sl_header.compositionTimeStamp) {
463
gf_mx_v(track->buffer_mutex);
467
static void gf_mse_remove_frames_from_to(GF_HTML_Track *track,
474
gf_mx_p(track->buffer_mutex);
475
frame_count = gf_list_count(track->buffer);
476
for (i = 0; i < frame_count; i++) {
477
GF_MSE_Packet *frame = (GF_MSE_Packet *)gf_list_get(track->buffer, i);
478
if (frame->sl_header.compositionTimeStamp >= from && frame->sl_header.compositionTimeStamp < to) {
479
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Removing frame %g (%d frames)\n", (frame->sl_header.compositionTimeStamp*1.0)/track->timescale, gf_list_count(track->buffer)));
480
gf_list_rem(track->buffer, i);
483
gf_mx_v(track->buffer_mutex);
486
static GF_Err gf_mse_process_coded_frame(GF_HTML_SourceBuffer *sb,
487
GF_HTML_Track *track,
488
GF_MSE_Packet *frame,
492
if (sb->append_mode == MEDIA_SOURCE_APPEND_MODE_SEQUENCE && sb->group_start_timestamp_flag) {
493
sb->timestampOffset = sb->group_start_timestamp - (frame->sl_header.compositionTimeStamp*1.0/track->timescale);
494
sb->highest_end_timestamp = sb->group_start_timestamp;
495
track->needs_rap = GF_TRUE; /* fix: should be on all track buffers */
496
sb->group_start_timestamp_flag = GF_FALSE;
499
if (sb->timestampOffset != 0) {
500
u64 offset = (u64)((sb->timestampOffset)*track->timescale);
501
if (offset > frame->sl_header.compositionTimeStamp || offset > frame->sl_header.decodingTimeStamp) {
502
return GF_NON_COMPLIANT_BITSTREAM;
504
frame->sl_header.compositionTimeStamp += (u64)(sb->timestampOffset*track->timescale);
505
frame->sl_header.decodingTimeStamp += (u64)(sb->timestampOffset*track->timescale);
506
/* check if the new CTS/DTS are in range */
509
if (track->last_dts_set) {
510
if (track->last_dts*track->timescale > frame->sl_header.decodingTimeStamp) {
511
return GF_NON_COMPLIANT_BITSTREAM;
515
* If last decode timestamp for track buffer is set and decode timestamp is less than last decode timestamp
516
* or the difference between decode timestamp and last decode timestamp is greater than 100 milliseconds,
517
* then call endOfStream("decode") and abort these steps.
519
if (frame->sl_header.decodingTimeStamp - track->last_dts*track->timescale > 0.1*track->timescale) {
520
return GF_NON_COMPLIANT_BITSTREAM;
524
if (frame->sl_header.compositionTimeStamp < sb->appendWindowStart*track->timescale) {
525
track->needs_rap = GF_TRUE;
529
if (frame->sl_header.compositionTimeStamp /* + dur */ > sb->appendWindowEnd*track->timescale) {
530
track->needs_rap = GF_TRUE;
534
if (track->needs_rap) {
535
if (!frame->sl_header.randomAccessPointFlag) {
538
track->needs_rap = GF_FALSE;
541
if (!track->last_dts_set) {
542
/* find a frame in the track buffer whose PTS is less than the current packet and whose PTS + dur is greater than the current packet */
543
GF_MSE_Packet *overlapped_packet;
544
overlapped_packet = gf_mse_find_overlapped_packet(track, frame);
545
if (overlapped_packet) {
546
gf_mse_remove_frames_from_to(track, overlapped_packet->sl_header.compositionTimeStamp, overlapped_packet->sl_header.compositionTimeStamp + (u64)(0.000001*track->timescale));
550
if (!track->highest_pts_set) {
551
/* this is the first time a frame is processed in the append sequence */
552
gf_mse_remove_frames_from_to(track, frame->sl_header.compositionTimeStamp, frame->sl_header.compositionTimeStamp /* + dur */);
553
} else if (track->highest_pts*track->timescale <= frame->sl_header.compositionTimeStamp) {
554
/* the highest pts has already been set in this append sequence, so we just need to remove frames from that point on, it's safe */
555
gf_mse_remove_frames_from_to(track, (u64)(track->highest_pts*track->timescale), (u64)(track->highest_pts*track->timescale) /* + dur */);
558
/* remove dependencies: no way !! */
560
/* TODO: spliced frames */
563
gf_mx_p(track->buffer_mutex);
564
gf_list_add(track->buffer, frame);
565
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Adding frame %g (%d frames)\n", (frame->sl_header.compositionTimeStamp*1.0)/track->timescale, gf_list_count(track->buffer)));
566
gf_mx_v(track->buffer_mutex);
568
track->last_dts = (frame->sl_header.decodingTimeStamp*1.0/track->timescale);
569
track->last_dts_set = GF_TRUE;
571
if (!track->highest_pts_set || (frame->sl_header.compositionTimeStamp /* + dur */) > track->highest_pts*track->timescale) {
572
track->highest_pts_set = GF_TRUE;
573
track->highest_pts = (frame->sl_header.compositionTimeStamp*1.0/track->timescale /* + dur */);
576
if (!sb->highest_end_timestamp_set || (frame->sl_header.compositionTimeStamp*1.0 /* + dur */) > sb->highest_end_timestamp * track->timescale) {
577
sb->highest_end_timestamp_set = GF_TRUE;
578
sb->highest_end_timestamp = (frame->sl_header.compositionTimeStamp*1.0/track->timescale /* + dur */);
584
/* Thread run function: called as a result of an append buffer
585
* Parses/Demultiplexes media segments and places the parsed AU in the track buffers
587
* \param par the GF_HTML_SourceBuffer object used for the append
589
u32 gf_mse_parse_segment(void *par)
591
GF_MSE_Packet *packet;
592
GF_HTML_Track *track;
593
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)par;
597
if (!sb->parser_connected) {
598
GF_HTML_ArrayBuffer *buffer = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 0);
599
gf_list_rem(sb->input_buffer, 0);
601
/* we expect an initialization segment to connect the service */
602
if (!buffer->is_init) {
603
/* TODO: set the media element decode error and run the end of stream algo */
606
sb->parser->ConnectService(sb->parser, sb->mediasource->service, buffer->url);
607
sb->prev_buffer = buffer;
609
/* we expect a media segment */
610
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Starting to fetch AUs from parser\n"));
612
/* For each track, ask the parser for all the Access Unit, until it is empty
613
* AU are placed as GF_MSE_Packets in the track buffer
615
track_count = gf_list_count(sb->tracks);
617
u32 track_with_data = 0;
618
for (i = 0; i < track_count; i++) {
619
Bool stored = GF_FALSE;
621
track = (GF_HTML_Track *)gf_list_get(sb->tracks, i);
622
GF_SAFEALLOC(packet, GF_MSE_Packet);
623
assert(track->channel);
624
e = sb->parser->ChannelGetSLP(sb->parser, track->channel,
625
&packet->data, &packet->size, &packet->sl_header,
626
&packet->is_compressed, &packet->status, &packet->is_new_data);
628
if (packet->status == GF_OK && packet->is_new_data && packet->size) {
630
assert(packet->is_new_data && packet->size);
631
data = (char *)gf_malloc(packet->size);
632
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] New AU parsed %g\n", (packet->sl_header.compositionTimeStamp*1.0/track->timescale)));
633
memcpy(data, packet->data, packet->size);
635
gf_mse_process_coded_frame(sb, track, packet, &stored);
637
sb->parser->ChannelReleaseSLP(sb->parser, track->channel);
640
if (!stored) gf_free(packet);
642
if (!track_with_data) {
643
/* try to delete the previous buffer */
644
gf_arraybuffer_del((GF_HTML_ArrayBuffer *)sb->prev_buffer, GF_FALSE);
645
sb->prev_buffer = NULL;
646
/* get ready to receive a new segment and start a new thread */
650
/* reaching here, the parser does not have enough data to produce new AU */
651
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Done fetching AUs from parser\n"));
654
gf_mse_source_buffer_set_update(sb, GF_FALSE);
659
void gf_mse_source_buffer_append_arraybuffer(GF_HTML_SourceBuffer *sb, GF_HTML_ArrayBuffer *buffer)
662
gf_mse_source_buffer_set_update(sb, GF_TRUE);
664
buffer->url = (char *)gf_malloc(256);
665
sprintf(buffer->url, "gmem://%d@%p", buffer->length, buffer->data);
666
buffer->reference_count++;
667
buffer->is_init = (gf_isom_probe_file(buffer->url) == 2 ? GF_TRUE : GF_FALSE);
668
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Appending segment %s to SourceBuffer %p\n", buffer->url, sb));
670
gf_list_add(sb->input_buffer, buffer);
671
/* Call the parser (asynchronously) and return */
672
/* the updating attribute will be positioned back to 0 when the parser is done */
673
gf_th_run(sb->parser_thread, gf_mse_parse_segment, sb);
677
FIXME : Unused function, create warnings on debian
678
static void gf_mse_source_buffer_append_error(GF_HTML_SourceBuffer *sb)
680
sb->updating = GF_FALSE;
681
gf_mse_source_buffer_reset_parser(sb);
686
/* Threaded function called upon request from JS
687
- Removes data in each track buffer until the next RAP is found */
688
u32 gf_mse_source_buffer_remove(void *par)
690
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)par;
698
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Removing media until next RAP\n"));
700
track_count = gf_list_count(sb->tracks);
701
for (i = 0; i < track_count; i++) {
702
GF_HTML_Track *track = (GF_HTML_Track *)gf_list_get(sb->tracks, i);
703
//end_set = GF_FALSE;
705
/* find the next random access point */
706
gf_mx_p(track->buffer_mutex);
707
frame_count = gf_list_count(track->buffer);
708
for (j = 0; j < frame_count; j++) {
709
GF_MSE_Packet *frame = (GF_MSE_Packet *)gf_list_get(track->buffer, j);
710
if ((frame->sl_header.randomAccessPointFlag &&
711
frame->sl_header.compositionTimeStamp >= sb->remove_end*track->timescale) ||
712
(j == frame_count - 1)) {
713
end = frame->sl_header.compositionTimeStamp;
718
gf_mx_v(track->buffer_mutex);
720
/* remove up to the next RAP found */
721
gf_mse_remove_frames_from_to(track, (u64)sb->remove_start, end);
723
gf_mse_source_buffer_set_update(sb, GF_FALSE);
727
/* Callback functions used by a media parser when parsing events happens */
728
GF_Err gf_mse_proxy(GF_InputService *parser, GF_NetworkCommand *command)
730
if (!parser || !command || !parser->proxy_udta) {
733
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)parser->proxy_udta;
734
switch (command->command_type) {
735
case GF_NET_SERVICE_QUERY_INIT_RANGE:
737
case GF_NET_SERVICE_QUERY_NEXT:
738
/* The parser is asking for the next media segment in the buffer,
739
check for the media time and give the right one */
741
GF_HTML_ArrayBuffer *buffer;
742
/* The input buffer should not be modified by append operations at the same time, no need to protect access */
743
buffer = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 0);
745
command->url_query.discontinuity_type = 0;
746
command->url_query.current_download = GF_FALSE;
747
command->url_query.start_range = 0;
748
command->url_query.end_range = 0;
749
command->url_query.switch_start_range = 0;
750
command->url_query.switch_end_range = 0;
751
command->url_query.next_url_init_or_switch_segment = NULL;
752
if (buffer->is_init) {
753
GF_HTML_ArrayBuffer *next = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 1);
754
command->url_query.discontinuity_type = 1;
756
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s with init \n", next->url, buffer->url));
757
command->url_query.next_url = next->url;
758
command->url_query.next_url_init_or_switch_segment = buffer->url;
759
gf_list_rem(sb->input_buffer, 0);
760
gf_list_rem(sb->input_buffer, 0);
762
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Only one init segment to parse %s, need to wait\n", buffer->url));
763
command->url_query.next_url = NULL;
766
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s\n", buffer->url));
767
command->url_query.next_url = buffer->url;
768
gf_list_rem(sb->input_buffer, 0);
770
sb->prev_buffer = buffer;
772
command->url_query.next_url = NULL;
773
command->url_query.discontinuity_type = 0;
777
case GF_NET_SERVICE_STATUS_PROXY:
778
/* The parser is informing the proxy about its status changes:
783
if (command->status.is_add_media) {
784
if (command->status.desc) {
785
gf_mse_source_buffer_store_track_desc(sb, (GF_ObjectDescriptor *)command->status.desc);
787
/* this is the last add media, we can switch updating to false */
788
/* the first init segment was correctly processed */
789
gf_mse_source_buffer_set_update(sb, GF_FALSE);
790
/* TODO: set active tracks and send addsourcebuffer event */
791
/* TODO: send media loadedmetadata event */
793
gf_term_add_media(sb->mediasource->service, command->status.desc, (command->status.desc ? GF_TRUE : GF_FALSE));
795
/* general connection/disconnection messages from the media parser (not track related) */
796
else if (!command->status.channel) {
797
/* connection message */
798
if (!command->status.is_disconnect) {
799
if (command->status.e == GF_OK) {
800
/* nothing needs to be done. Setup is done with final add media */
801
sb->parser_connected = GF_TRUE;
802
sb->mediasource->durationType = DURATION_INFINITY;
803
gf_mse_source_buffer_setup_tracks(sb);
805
/* wrong first init segment */
806
/* TODO: fire an error event */
808
gf_term_on_connect(sb->mediasource->service, command->status.channel, command->status.e);
810
gf_term_on_disconnect(sb->mediasource->service, command->status.channel, command->status.e);
813
/* channel (track related) specific connection/disconnection messages from the media parser */
815
if (!command->status.is_disconnect) {
816
gf_term_on_connect(sb->mediasource->service, command->status.channel, command->status.e);
818
gf_term_on_disconnect(sb->mediasource->service, command->status.channel, command->status.e);
823
gf_term_on_command(sb->mediasource->service, command, GF_OK);
830
/* Track Buffer Managment:
831
* - Access Units are parsed/demultiplexed from the SourceBuffer input buffer and placed into individual track buffers
832
* - The parsing/demux is done in a separate thread (so access to the the track buffer is protected by a mutex)
834
* Track Buffer Length: number of Access Units */
835
#define MSE_TRACK_BUFFER_LENGTH 1000
837
/* When an Access Unit can be released, we check if it needs to be kept or not in the track buffer */
839
GF_Err gf_mse_track_buffer_release_packet(GF_HTML_Track *track) {
840
GF_MSE_Packet *packet;
841
gf_mx_p(track->buffer_mutex);
842
packet = (GF_MSE_Packet *)gf_list_get(track->buffer, track->packet_index);
844
track->packet_index++;
845
if (gf_list_count(track->buffer) > MSE_TRACK_BUFFER_LENGTH) {
846
packet = (GF_MSE_Packet *)gf_list_get(track->buffer, 0);
847
track->packet_index--;
848
gf_list_rem(track->buffer, 0);
849
gf_free(packet->data);
853
gf_mx_v(track->buffer_mutex);
857
/* Requests from the decoders to get the next Access Unit: we get it from the track buffer */
859
GF_Err gf_mse_track_buffer_get_next_packet(GF_HTML_Track *track,
860
char **out_data_ptr, u32 *out_data_size,
861
GF_SLHeader *out_sl_hdr, Bool *sl_compressed,
862
GF_Err *out_reception_status, Bool *is_new_data)
864
GF_MSE_Packet *packet;
866
gf_mx_p(track->buffer_mutex);
867
count = gf_list_count(track->buffer);
868
packet = (GF_MSE_Packet *)gf_list_get(track->buffer, track->packet_index);
870
*out_data_ptr = packet->data;
871
*out_data_size = packet->size;
872
*out_sl_hdr = packet->sl_header;
873
*sl_compressed = packet->is_compressed;
874
*out_reception_status = packet->status;
875
*is_new_data = packet->is_new_data;
876
packet->is_new_data = GF_FALSE;
877
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE_IN] Sending AU #%d/%d to decoder with TS: %g \n", track->packet_index, count, (packet->sl_header.compositionTimeStamp*1.0/track->timescale)));
879
*out_data_ptr = NULL;
881
*sl_compressed = GF_FALSE;
882
*out_reception_status = GF_OK;
883
*is_new_data = GF_FALSE;
884
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE_In] No AU for decoder\n"));
886
gf_mx_v(track->buffer_mutex);