root/src/media_tools/html5_media.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gf_html_timeranges_new
  2. gf_html_timeranges_add_time
  3. gf_html_timeranges_add_start
  4. gf_html_timeranges_add_end
  5. gf_html_timeranges_reset
  6. gf_html_timeranges_del
  7. gf_html_timeranges_merge
  8. gf_html_timeranges_union
  9. gf_html_timeranges_intersection
  10. html_media_add_new_track_to_list
  11. html_media_tracklist_has_track
  12. html_media_tracklist_get_track
  13. gf_html_tracklist_del
  14. gf_html_media_track_new
  15. gf_html_track_del
  16. gf_html_media_element_new
  17. gf_html_media_element_del
  18. gf_html_media_controller_new
  19. gf_html_mediacontroller_del

/*
*                       GPAC - Multimedia Framework C SDK
*
 *                      Authors: Cyril COncolato
 *                      Copyright (c) Telecom ParisTech 2013-
*                                       All rights reserved
*
*  This file is part of GPAC / Media Source
*
*  GPAC is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation; either version 2, or (at your option)
*  any later version.
*
*  GPAC is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; see the file COPYING.  If not, write to
*  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

#include <gpac/setup.h>
#include <gpac/internal/scenegraph_dev.h>

#ifdef GPAC_HAS_SPIDERMONKEY

#include <gpac/html5_media.h>
#include <gpac/html5_mse.h>

GF_HTML_MediaTimeRanges *gf_html_timeranges_new(u32 timescale)
{
        GF_HTML_MediaTimeRanges *ranges;
        GF_SAFEALLOC(ranges, GF_HTML_MediaTimeRanges);
        if (!ranges) return NULL;
        ranges->times = gf_list_new();
        ranges->timescale = timescale;
        return ranges;
}

static GF_Err gf_html_timeranges_add_time(GF_HTML_MediaTimeRanges *timeranges, u64 time)
{
        u64 *t;
        if (!timeranges) return GF_BAD_PARAM;
        t = (u64 *)gf_malloc(sizeof(u64));
        *t = time;
        gf_list_add(timeranges->times, t);
        return GF_OK;
}

GF_Err gf_html_timeranges_add_start(GF_HTML_MediaTimeRanges *timeranges, u64 start)
{
        return gf_html_timeranges_add_time(timeranges, start);
}

GF_Err gf_html_timeranges_add_end(GF_HTML_MediaTimeRanges *timeranges, u64 end)
{
        return gf_html_timeranges_add_time(timeranges, end);
}

void gf_html_timeranges_reset(GF_HTML_MediaTimeRanges *ranges)
{
        while (gf_list_count(ranges->times))
        {
                u64 *d = (u64 *)gf_list_get(ranges->times, 0);
                gf_free(d);
                gf_list_rem(ranges->times, 0);
        }
}

void gf_html_timeranges_del(GF_HTML_MediaTimeRanges *ranges)
{
        gf_html_timeranges_reset(ranges);
        gf_list_del(ranges->times);
        ranges->times = NULL;
        gf_free(ranges);
}

void gf_html_timeranges_merge(GF_HTML_MediaTimeRanges *ranges) {
        u32 i, count;
        u64 *start;
        u64 *end;
        u64 *prev_end;
        GF_List *merged = gf_list_new();

        prev_end = NULL;
        count = gf_list_count(ranges->times);
        for (i = 0; i < count; i+=2) {
                start = (u64 *)gf_list_get(ranges->times, i);
                end = (u64 *)gf_list_get(ranges->times, i+1);
                if (prev_end == NULL || *start > *prev_end) {
                        if (prev_end) {
                                gf_list_add(merged, prev_end);
                        }
                        gf_list_add(merged, start);
                } else if (*start == *prev_end) {
                        gf_free(start);
                }
                prev_end = end;
        }
        if (prev_end) {
                gf_list_add(ranges->times, prev_end);
        }
        gf_list_del(ranges->times);
        ranges->times = merged;
}


GF_HTML_MediaTimeRanges *gf_html_timeranges_union(GF_HTML_MediaTimeRanges *a, GF_HTML_MediaTimeRanges *b)
{
        GF_HTML_MediaTimeRanges *union_ranges;
        u32 i, j, count_a, count_b;
        union_ranges = gf_html_timeranges_new(a->timescale);
        union_ranges->c = a->c;
        union_ranges->_this = a->_this;

        count_a = gf_list_count(a->times);
        if (b) {
                count_b = gf_list_count(b->times);
        } else {
                count_b = 0;
        }
        if (count_a == 0 && count_b == 0) {
                return NULL;
        } else if (count_a == 0) {
                GF_HTML_MediaTimeRanges *tmp = a;
                a = b;
                b = tmp;
                count_a = count_b;
                count_b = 0;
        }
        i = 0;
        j = 0;
        while (i < count_a) {
                Bool add_a = GF_TRUE;
                u64 *starta = (u64 *)gf_list_get(a->times, i);
                u64 *enda = (u64 *)gf_list_get(a->times, i+1);
                while (j < count_b) {
                        u64 *startb = (u64 *)gf_list_get(b->times, j);
                        u64 *endb = (u64 *)gf_list_get(b->times, j+1);
                        if (*enda*b->timescale < *startb*a->timescale) {
                                /* a ends before b starts, there is no overlap, we can add a to the union */
                                gf_list_add(union_ranges->times, starta);
                                gf_list_add(union_ranges->times, enda);
                                add_a = GF_FALSE;
                                /* force to get the next a */
                                i+=2;
                                break;
                        } else if (*endb*a->timescale < *starta*b->timescale) {
                                /* b ends before a starts, there is no overlap, we can add b to the union */
                                *startb = (u64)((*startb * a->timescale)*1.0 / b->timescale);
                                gf_list_add(union_ranges->times, startb);
                                *endb = (u64)((*endb * a->timescale)*1.0 / b->timescale);
                                gf_list_add(union_ranges->times, endb);
                                j+=2;
                        } else { /* there is some overlap */
                                if (*starta*b->timescale <= *startb*a->timescale) { /* the overlap is at the end of a */
                                        if (*endb*a->timescale <= *enda*b->timescale) { /* b is contained in a */
                                                /* ignore b, move on to the next b */
                                                j+=2;
                                        } else { /* *endb > *enda, the overlap is only at the start of b */
                                                /* update start of b */
                                                *startb = (u64)((*starta * b->timescale)*1.0 / a->timescale);
                                                /* ignore a, move on to the next a */
                                                i+=2;
                                                break;
                                        }
                                } else { /* *starta > *startb, the overlap is at the end of b */
                                        if (*enda*b->timescale <= *endb*a->timescale) { /* a is contained in b */
                                                /* ignore a */
                                                add_a = GF_FALSE;
                                                /* force to get the next a */
                                                i+=2;
                                                break;
                                        } else { /* *enda > *endb, the overlap is at the beginning of a */
                                                /* update start of a */
                                                *starta = (u64)((*startb * a->timescale)*1.0 / b->timescale);
                                                /* ignore b, move on to the next b */
                                                j+=2;
                                        }
                                }
                        }
                }
                /* we've processed all b, but a has not been added */
                /* first check if the next a is not contiguous */
                if (add_a == GF_TRUE && i+2 < count_a) {
                        u64 *next_starta = (u64 *)gf_list_get(a->times, i+2);
                        //u64 *next_enda = (u64 *)gf_list_get(a->times, i+3);
                        if (*enda == *next_starta) {
                                *next_starta = *starta;
                        }
                        add_a = GF_FALSE;
                }
                if (add_a) {
                        gf_list_add(union_ranges->times, starta);
                        gf_list_add(union_ranges->times, enda);
                }
                i+=2;
        }
        gf_html_timeranges_merge(union_ranges);
        return union_ranges;
}

GF_HTML_MediaTimeRanges *gf_html_timeranges_intersection(GF_HTML_MediaTimeRanges *a, GF_HTML_MediaTimeRanges *b)
{
        GF_HTML_MediaTimeRanges *intersection_ranges;
        u32 i, j, count_a, count_b;
        intersection_ranges = gf_html_timeranges_new(a->timescale);
        intersection_ranges->c = a->c;
        intersection_ranges->_this = a->_this;
        count_a = 0;
        count_b = 0;
        if (a) count_a = gf_list_count(a->times);
        if (b) count_b = gf_list_count(b->times);
        if (count_a != 0 && count_b != 0) {
                i = 0;
                j = 0;
                while (i < count_a) {
                        u64 *starta = (u64 *)gf_list_get(a->times, i);
                        u64 *enda = (u64 *)gf_list_get(a->times, i+1);
                        while (j < count_b) {
                                u64 *startb = (u64 *)gf_list_get(b->times, j);
                                u64 *endb = (u64 *)gf_list_get(b->times, j+1);
                                if (*enda*b->timescale < *startb*a->timescale) {
                                        /* this is no intersection with this a */
                                        /* force to get the next a */
                                        i+=2;
                                        break;
                                } else if (*endb*a->timescale < *starta*b->timescale) {
                                        /* this is no intersection with this b */
                                        j+=2;
                                } else { /* there is an intersection */
                                        if (*starta*b->timescale <= *startb*a->timescale) { /* the intersection starts at the beginning of b */
                                                gf_list_add(intersection_ranges->times, startb);
                                                if (*endb*a->timescale <= *enda*b->timescale) { /* b is contained in a */
                                                        gf_list_add(intersection_ranges->times, endb);
                                                        *starta = (u64)((*endb * a->timescale)*1.0 / b->timescale);
                                                        /* move on to the next b */
                                                        j+=2;
                                                } else { /* *endb > *enda, the intersection ends at the end of a */
                                                        gf_list_add(intersection_ranges->times, enda);
                                                        /* update start of b */
                                                        *startb = (u64)((*enda * b->timescale)*1.0 / a->timescale);
                                                        /* move on to the next a */
                                                        i+=2;
                                                        break;
                                                }
                                        } else { /* *starta > *startb, the intersection starts at the beginning of a */
                                                gf_list_add(intersection_ranges->times, startb);
                                                if (*enda*b->timescale <= *endb*a->timescale) { /* a is contained in b */
                                                        gf_list_add(intersection_ranges->times, enda);
                                                        *startb = (u64)((*enda * b->timescale)*1.0 / a->timescale);
                                                        /* move on to the next a */
                                                        i+=2;
                                                        break;
                                                } else { /* *enda > *endb, the intersection ends at the end of b */
                                                        gf_list_add(intersection_ranges->times, endb);
                                                        /* update start of a */
                                                        *starta = (u64)((*endb * a->timescale)*1.0 / b->timescale);
                                                        /* move on to the next b */
                                                        j+=2;
                                                }
                                        }
                                }
                        }
                }
        }
        return intersection_ranges;
}

GF_HTML_Track *html_media_add_new_track_to_list(GF_HTML_TrackList *tracklist,
        GF_HTML_TrackType type, const char *mime, Bool enable_or_selected,
        const char *id, const char *kind, const char *label, const char *lang)
{
        GF_HTML_Track *track;
        track = gf_html_media_track_new(type, mime, enable_or_selected, id, kind, label, lang);
        gf_list_add(tracklist->tracks, track);
        return track;
}

Bool html_media_tracklist_has_track(GF_HTML_TrackList *tracklist, const char *id)
{
        GF_HTML_Track *track;
        u32 i, count;
        count = gf_list_count(tracklist->tracks);
        for (i = 0; i < count; i++)
        {
                track = (GF_HTML_Track *)gf_list_get(tracklist->tracks, i);
                if (!strcmp(id, track->id))
                {
                        return 1;
                }
        }
        return 0;
}

GF_HTML_Track *html_media_tracklist_get_track(GF_HTML_TrackList *tracklist, const char *id)
{
        GF_HTML_Track *track = NULL;
        u32 i, count;
        count = gf_list_count(tracklist->tracks);
        for (i = 0; i < count; i++)
        {
                track = (GF_HTML_Track *)gf_list_get(tracklist->tracks, i);
                if (!strcmp(id, track->id))
                {
                        return track;
                }
        }
        return NULL;
}

void gf_html_tracklist_del(GF_HTML_TrackList *tlist)
{
        while (gf_list_count(tlist->tracks))
        {
                GF_HTML_Track *track = (GF_HTML_Track *)gf_list_get(tlist->tracks, 0);
                gf_html_track_del(track);
                gf_list_rem(tlist->tracks, 0);
        }
        gf_list_del(tlist->tracks);
}

GF_HTML_Track *gf_html_media_track_new(GF_HTML_TrackType type, const char *mime, Bool enable_or_selected,
                                       const char *id, const char *kind, const char *label, const char *lang)
{
        GF_HTML_Track *track;
        GF_SAFEALLOC(track, GF_HTML_Track);
        if (!track) return NULL;
        track->type     = type;
        track->mime     = gf_strdup(mime);
        track->id       = gf_strdup(id);
        track->kind     = gf_strdup(kind);
        track->label    = gf_strdup(label);
        track->language = gf_strdup(lang);
        track->enabled_or_selected = enable_or_selected;
        /* TODO: empty MSE data */
        return track;
}

void gf_html_track_del(GF_HTML_Track *track)
{
        if (track->id)          gf_free(track->id);
        if (track->kind)        gf_free(track->kind);
        if (track->label)       gf_free(track->label);
        if (track->language)    gf_free(track->language);
        if (track->mime)        gf_free(track->mime);

        if (track->buffer_mutex) {
                gf_mx_p(track->buffer_mutex);
                while (gf_list_count(track->buffer))
                {
                        GF_MSE_Packet *packet = (GF_MSE_Packet *)gf_list_get(track->buffer, 0);
                        gf_mse_packet_del(packet);
                        gf_list_rem(track->buffer, 0);
                }
                gf_mx_v(track->buffer_mutex);
                gf_list_del(track->buffer);
                gf_mx_del(track->buffer_mutex);
        } else {
                assert(track->buffer == NULL);
        }
        gf_free(track);
}

GF_HTML_MediaElement *gf_html_media_element_new(GF_Node *media_node, GF_HTML_MediaController *mc)
{
        GF_HTML_MediaElement *me;
        GF_SAFEALLOC(me, GF_HTML_MediaElement);
        if (!me) return NULL;
        me->node                    = media_node;
        me->controller              = mc;
        me->audioTracks.tracks      = gf_list_new();
        me->videoTracks.tracks      = gf_list_new();
        me->textTracks.tracks       = gf_list_new();
        me->buffered                                    = gf_html_timeranges_new(1);
        me->played                          = gf_html_timeranges_new(1);
        me->seekable                                    = gf_html_timeranges_new(1);
        return me;
}

void gf_html_media_element_del(GF_HTML_MediaElement *me)
{
        if (me->startDate) gf_free(me->startDate);
        if (me->currentSrc) gf_free(me->currentSrc);
        gf_html_tracklist_del(&me->audioTracks);
        gf_html_tracklist_del(&me->videoTracks);
        gf_html_tracklist_del(&me->textTracks);
        gf_html_timeranges_del(me->buffered);
        gf_html_timeranges_del(me->seekable);
        gf_html_timeranges_del(me->played);
        gf_free(me);
}

GF_HTML_MediaController *gf_html_media_controller_new()
{
        GF_HTML_MediaController *mc;
        GF_SAFEALLOC(mc, GF_HTML_MediaController);
        if (!mc) return NULL;
        mc->buffered          = gf_html_timeranges_new(1);
        mc->played            = gf_html_timeranges_new(1);
        mc->seekable          = gf_html_timeranges_new(1);
        return mc;
}

void gf_html_mediacontroller_del(GF_HTML_MediaController *mc)
{
        u32 i, count;
        /* removing the pointer to that controller from the media elements using it */
        count = gf_list_count(mc->media_elements);
        for (i = 0; i < count; i++) {
                GF_HTML_MediaElement *me = (GF_HTML_MediaElement *)gf_list_get(mc->media_elements, i);
                me->controller =  NULL;
        }
        gf_html_timeranges_del(mc->buffered);
        gf_html_timeranges_del(mc->seekable);
        gf_html_timeranges_del(mc->played);
        gf_free(mc);
}

#endif


/* [<][>][^][v][top][bottom][index][help] */