This source file includes following definitions.
- vtt_check_download
- VTT_ProcessData
- VTT_UpdateSizeInfo
- VTT_load_script
- VTT_AttachScene
- VTT_CleanExtraScene
- VTT_ReleaseScene
- VTT_ReadFileNameFromDSI
- VTT_ReadConfigFromDSI
- VTT_AttachStream
- VTT_DetachStream
- VTT_GetName
- VTT_CanHandleStream
- VTT_GetCapabilities
- VTT_SetCapabilities
- NewVTTDec
- DeleteVTTDec
- LoadInterface
- ShutdownInterface
- QueryInterfaces
#include <gpac/constants.h>
#include <gpac/internal/terminal_dev.h>
#include <gpac/internal/isomedia_dev.h>
#include <gpac/internal/media_dev.h>
#include <gpac/internal/scenegraph_dev.h>
#include <gpac/internal/compositor_dev.h>
#include <gpac/nodes_svg.h>
#include <gpac/webvtt.h>
#if !defined(GPAC_DISABLE_VTT) && !defined(GPAC_DISABLE_SVG)
typedef struct {
GF_BaseInterface *module;
u32 oti;
Bool is_stream_attached;
u32 base_es_id;
char *file_name;
u64 file_size;
u64 file_pos;
char *config;
Bool use_progressive;
GF_Scene *scene;
GF_Terminal *terminal;
GF_List *cues;
GF_SceneGraph *sg;
Bool has_rendering_script;
} VTTDec;
#define VTT_BUFFER_SIZE 4096
static Bool vtt_check_download(VTTDec *vttdec)
{
u64 size;
FILE *f = gf_fopen(vttdec->file_name, "rt");
if (!f) return GF_FALSE;
gf_fseek(f, 0, SEEK_END);
size = gf_ftell(f);
gf_fclose(f);
if (size==vttdec->file_size) return GF_TRUE;
return GF_FALSE;
}
static GF_Err VTT_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 inBufferLength,
u16 ES_ID, u32 stream_time, u32 mmlevel)
{
GF_Err e = GF_OK;
VTTDec *vttdec = (VTTDec *)plug->privateStack;
if (!vttdec->has_rendering_script) return GF_BAD_PARAM;
if (stream_time==(u32)-1) {
return GF_OK;
}
switch (vttdec->oti) {
case GPAC_OTI_PRIVATE_SCENE_VTT:
if (vttdec->file_size) {
if (!vttdec->use_progressive) {
if (!vtt_check_download(vttdec)) return GF_OK;
} else {
}
}
break;
case GPAC_OTI_SCENE_VTT:
break;
case GPAC_OTI_SCENE_VTT_MP4:
{
#ifndef GPAC_DISABLE_MEDIA_IMPORT
char start[100], end[100];
GF_List *cues;
cues = gf_webvtt_parse_cues_from_data(inBuffer, inBufferLength, 0);
gf_webvtt_js_removeCues(vttdec->sg->RootNode);
if (gf_list_count(cues)) {
while (gf_list_count(cues)) {
GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, 0);
gf_list_rem(cues, 0);
sprintf(start, "%02d:%02d:%02d.%03d", cue->start.hour, cue->start.min, cue->start.sec, cue->start.ms);
sprintf(end, "%02d:%02d:%02d.%03d", cue->end.hour, cue->end.min, cue->end.sec, cue->end.ms);
gf_webvtt_js_addCue(vttdec->sg->RootNode, cue->id, start, end, cue->settings, cue->text);
gf_webvtt_cue_del(cue);
}
}
gf_list_del(cues);
#endif
}
break;
default:
return GF_BAD_PARAM;
}
return e;
}
void VTT_UpdateSizeInfo(VTTDec *vttdec)
{
u32 w, h;
GF_FieldInfo info;
char szVB[100];
GF_Node *root = gf_sg_get_root_node(vttdec->sg);
if (!root) return;
#if 0
w = vttdec->scene->root_od->term->compositor->display_width;
h = vttdec->scene->root_od->term->compositor->display_height;
#else
w=1280;
h=720;
#endif
gf_sg_set_scene_size_info(vttdec->sg, w, h, GF_TRUE);
sprintf(szVB, "0 0 %d %d", w, h);
gf_node_get_attribute_by_tag(root, TAG_SVG_ATT_viewBox, GF_TRUE, GF_FALSE, &info);
gf_svg_parse_attribute(root, &info, szVB, 0);
}
void VTT_load_script(VTTDec *vttdec, GF_SceneGraph *graph)
{
GF_Node *n, *root;
GF_FieldInfo info;
const char *path;
FILE *jsfile;
if (!graph) return;
gf_sg_add_namespace(graph, "http://www.w3.org/2000/svg", NULL);
gf_sg_add_namespace(graph, "http://www.w3.org/1999/xlink", "xlink");
gf_sg_add_namespace(graph, "http://www.w3.org/2001/xml-events", "ev");
gf_sg_set_scene_size_info(graph, 800, 600, GF_TRUE);
n = root = gf_node_new(graph, TAG_SVG_svg);
gf_node_register(root, NULL);
gf_sg_set_root_node(graph, root);
gf_node_get_attribute_by_name(n, "xmlns", 0, GF_TRUE, GF_FALSE, &info);
gf_svg_parse_attribute(n, &info, "http://www.w3.org/2000/svg", 0);
VTT_UpdateSizeInfo(vttdec);
gf_node_init(n);
n = gf_node_new(graph, TAG_SVG_script);
gf_node_register(n, root);
gf_node_list_add_child(&((GF_ParentNode *)root)->children, n);
path = gf_modules_get_option((GF_BaseInterface *)vttdec->module, "WebVTT", "RenderingScript");
if (!path) {
const char *startuppath = gf_modules_get_option((GF_BaseInterface *)vttdec->module, "General", "StartupFile");
char *jspath = gf_url_concatenate(startuppath, "webvtt-renderer.js");
jsfile = gf_fopen(jspath, "rt");
if (jsfile) {
gf_modules_set_option((GF_BaseInterface *)vttdec->module, "WebVTT", "RenderingScript", jspath);
gf_fclose(jsfile);
gf_free(jspath);
path = gf_modules_get_option((GF_BaseInterface *)vttdec->module, "WebVTT", "RenderingScript");
} else {
gf_free(jspath);
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[WebVTT] Cannot find Rendering Script [WebVTT:RenderingScript] - check config file\n"));
return;
}
}
jsfile = gf_fopen(path, "rt");
if (jsfile) {
gf_fclose(jsfile);
gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, GF_TRUE, GF_FALSE, &info);
if (strstr(path, ":\\")) {
gf_svg_parse_attribute(n, &info, (char *) path, 0);
} else {
char szPath[GF_MAX_PATH];
strcpy(szPath, "file://");
strcat(szPath, path);
gf_svg_parse_attribute(n, &info, (char *) szPath, 0);
}
vttdec->has_rendering_script = GF_TRUE;
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[WebVTT] Cannot open Rendering Script - %s\n", path));
return;
}
gf_node_init(n);
}
static GF_Err VTT_AttachScene(GF_SceneDecoder *plug, GF_Scene *scene, Bool is_scene_decoder)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
vttdec->scene = scene;
vttdec->terminal = scene->root_od->term;
vttdec->sg = gf_sg_new_subscene(vttdec->scene->graph);
VTT_load_script(vttdec, vttdec->sg);
return GF_OK;
}
static void VTT_CleanExtraScene(VTTDec *vttdec)
{
if (vttdec->sg) {
gf_scene_register_extra_graph(vttdec->scene, vttdec->sg, GF_TRUE);
gf_sg_del(vttdec->sg);
vttdec->sg = NULL;
}
}
static GF_Err VTT_ReleaseScene(GF_SceneDecoder *plug)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
VTT_CleanExtraScene(vttdec);
vttdec->scene = NULL;
vttdec->terminal = NULL;
return GF_OK;
}
static void VTT_ReadFileNameFromDSI(VTTDec *vttdec, GF_DefaultDescriptor *dsi)
{
GF_BitStream *bs;
bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ);
vttdec->file_size = gf_bs_read_u32(bs);
vttdec->file_pos = 0;
gf_bs_del(bs);
vttdec->file_name = (char *) gf_malloc(sizeof(char)*(1 + dsi->dataLength - sizeof(u32)) );
memcpy(vttdec->file_name, dsi->data + sizeof(u32), dsi->dataLength - sizeof(u32) );
vttdec->file_name[dsi->dataLength - sizeof(u32) ] = 0;
}
static void VTT_ReadConfigFromDSI(VTTDec *vttdec, GF_DefaultDescriptor *dsi)
{
GF_BitStream *bs;
u32 entry_type;
bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ);
entry_type = gf_bs_read_u32(bs);
if (entry_type == GF_ISOM_BOX_TYPE_WVTT) {
GF_Box *b;
gf_isom_box_parse(&b, bs);
vttdec->config = ((GF_StringBox *)b)->string;
((GF_StringBox *)b)->string = NULL;
gf_isom_box_del(b);
}
gf_bs_del(bs);
}
static GF_Err VTT_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED;
if (vttdec->is_stream_attached) return GF_NOT_SUPPORTED;
switch (esd->decoderConfig->objectTypeIndication) {
case GPAC_OTI_SCENE_VTT:
break;
case GPAC_OTI_SCENE_VTT_MP4:
if (!esd->decoderConfig->decoderSpecificInfo) return GF_NON_COMPLIANT_BITSTREAM;
VTT_ReadConfigFromDSI(vttdec, esd->decoderConfig->decoderSpecificInfo);
break;
case GPAC_OTI_PRIVATE_SCENE_VTT:
default:
if (!esd->decoderConfig->decoderSpecificInfo) return GF_NON_COMPLIANT_BITSTREAM;
VTT_ReadFileNameFromDSI(vttdec, esd->decoderConfig->decoderSpecificInfo);
break;
}
vttdec->oti = esd->decoderConfig->objectTypeIndication;
vttdec->is_stream_attached = GF_TRUE;
if (!esd->dependsOnESID) vttdec->base_es_id = esd->ESID;
return GF_OK;
}
static GF_Err VTT_DetachStream(GF_BaseDecoder *plug, u16 ES_ID)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
if (vttdec->file_name) gf_free(vttdec->file_name);
VTT_CleanExtraScene(vttdec);
vttdec->file_name = NULL;
vttdec->is_stream_attached = GF_FALSE;
if (vttdec->config) {
gf_free(vttdec->config);
vttdec->config = NULL;
}
return GF_OK;
}
const char *VTT_GetName(struct _basedecoder *plug)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
if (vttdec->oti==GPAC_OTI_PRIVATE_SCENE_VTT) return "GPAC WebVTT Parser";
if (vttdec->oti==GPAC_OTI_SCENE_VTT) return "GPAC WebVTT Streaming Parser";
if (vttdec->oti==GPAC_OTI_SCENE_VTT_MP4) return "GPAC WebVTT/MP4 Parser";
return "INTERNAL ERROR";
}
static u32 VTT_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, GF_ESD *esd, u8 PL)
{
if (StreamType==GF_STREAM_TEXT) {
if (!esd) return GF_CODEC_STREAM_TYPE_SUPPORTED;
switch (esd->decoderConfig->objectTypeIndication) {
case GPAC_OTI_SCENE_VTT:
case GPAC_OTI_SCENE_VTT_MP4:
return GF_CODEC_SUPPORTED;
default:
return GF_CODEC_NOT_SUPPORTED;
}
}
return GF_CODEC_NOT_SUPPORTED;
}
static GF_Err VTT_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap)
{
cap->cap.valueInt = 0;
return GF_NOT_SUPPORTED;
}
static GF_Err VTT_SetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability capability)
{
VTTDec *vttdec = (VTTDec *)plug->privateStack;
if (capability.CapCode==GF_CODEC_ABORT) {
} else if (capability.CapCode==GF_CODEC_SHOW_SCENE) {
if (capability.cap.valueInt) {
VTT_UpdateSizeInfo(vttdec);
gf_scene_register_extra_graph(vttdec->scene, vttdec->sg, GF_FALSE);
} else {
gf_scene_register_extra_graph(vttdec->scene, vttdec->sg, GF_TRUE);
}
}
return GF_OK;
}
GF_BaseInterface *NewVTTDec()
{
VTTDec *vttdec;
GF_SceneDecoder *sdec;
GF_SAFEALLOC(sdec, GF_SceneDecoder)
if (!sdec) return NULL;
GF_REGISTER_MODULE_INTERFACE(sdec, GF_SCENE_DECODER_INTERFACE, "GPAC WebVTT Parser", "gpac distribution");
GF_SAFEALLOC(vttdec, VTTDec);
if (!vttdec) {
gf_free(sdec);
return NULL;
}
vttdec->cues = gf_list_new();
vttdec->module = (GF_BaseInterface *)sdec;
sdec->privateStack = vttdec;
sdec->CanHandleStream = VTT_CanHandleStream;
sdec->AttachStream = VTT_AttachStream;
sdec->DetachStream = VTT_DetachStream;
sdec->AttachScene = VTT_AttachScene;
sdec->ReleaseScene = VTT_ReleaseScene;
sdec->ProcessData = VTT_ProcessData;
sdec->GetName = VTT_GetName;
sdec->SetCapabilities = VTT_SetCapabilities;
sdec->GetCapabilities = VTT_GetCapabilities;
return (GF_BaseInterface *)sdec;
}
void DeleteVTTDec(GF_BaseDecoder *plug)
{
VTTDec *dec= (VTTDec *)plug->privateStack;
gf_list_del(dec->cues);
gf_free(dec);
gf_free(plug);
}
#endif
GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
switch (InterfaceType) {
#if !defined(GPAC_DISABLE_VTT) && !defined(GPAC_DISABLE_SVG)
case GF_SCENE_DECODER_INTERFACE:
return (GF_BaseInterface *)NewVTTDec();
#endif
default:
return NULL;
}
}
GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
switch (ifce->InterfaceType) {
#if !defined(GPAC_DISABLE_VTT) && !defined(GPAC_DISABLE_SVG)
case GF_SCENE_DECODER_INTERFACE:
DeleteVTTDec((GF_BaseDecoder *)ifce);
break;
#endif
}
}
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
static u32 si [] = {
#if !defined(GPAC_DISABLE_VTT) && !defined(GPAC_DISABLE_SVG)
GF_SCENE_DECODER_INTERFACE,
#endif
0
};
return si;
}
GPAC_MODULE_STATIC_DECLARATION( vtt_dec )