This source file includes following definitions.
- movietexture_destroy
- movietexture_get_speed
- movietexture_get_loop
- movietexture_activate
- movietexture_deactivate
- movietexture_update
- movietexture_update_time
- compositor_init_movietexture
- mt_get_texture
- compositor_movietexture_modified
- imagetexture_destroy
- imagetexture_update
- compositor_init_imagetexture
- it_get_texture
- compositor_imagetexture_modified
- pixeltexture_destroy
- pixeltexture_update
- compositor_init_pixeltexture
- pt_get_texture
- matte_update
- compositor_init_mattetexture
#include "texturing.h"
#include <gpac/network.h>
#include <gpac/nodes_mpeg4.h>
#include <gpac/nodes_x3d.h>
#ifndef GPAC_DISABLE_VRML
#ifdef __cplusplus
extern "C" {
#endif
#include <gpac/avparse.h>
#include <gpac/crypt.h>
typedef struct
{
GF_TextureHandler txh;
GF_TimeNode time_handle;
Bool fetch_first_frame, first_frame_fetched, is_x3d;
Double start_time;
} MovieTextureStack;
static void movietexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
gf_sc_texture_destroy(&st->txh);
if (st->time_handle.is_registered) gf_sc_unregister_time_node(st->txh.compositor, &st->time_handle);
gf_free(st);
}
}
static Fixed movietexture_get_speed(MovieTextureStack *stack, M_MovieTexture *mt)
{
return gf_mo_get_speed(stack->txh.stream, mt->speed);
}
static Bool movietexture_get_loop(MovieTextureStack *stack, M_MovieTexture *mt)
{
return gf_mo_get_loop(stack->txh.stream, mt->loop);
}
static void movietexture_activate(MovieTextureStack *stack, M_MovieTexture *mt, Double scene_time)
{
mt->isActive = 1;
gf_node_event_out((GF_Node*)mt, 8);
if (!stack->txh.is_open) {
scene_time -= mt->startTime;
gf_sc_texture_play_from_to(&stack->txh, &mt->url, scene_time, -1, gf_mo_get_loop(stack->txh.stream, mt->loop), 0);
} else if (stack->first_frame_fetched) {
gf_mo_resume(stack->txh.stream);
}
gf_mo_set_speed(stack->txh.stream, mt->speed);
}
static void movietexture_deactivate(MovieTextureStack *stack, M_MovieTexture *mt)
{
mt->isActive = 0;
gf_node_event_out((GF_Node*)mt, 8);
stack->time_handle.needs_unregister = 1;
if (stack->txh.is_open) {
gf_sc_texture_stop(&stack->txh);
}
}
static void movietexture_update(GF_TextureHandler *txh)
{
M_MovieTexture *txnode = (M_MovieTexture *) txh->owner;
MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(txh->owner);
if (!txh->is_open) return;
if (!txnode->isActive && st->first_frame_fetched) return;
gf_sc_texture_update_frame(txh, 0);
if (txh->stream_finished) {
if (movietexture_get_loop(st, txnode)) {
gf_sc_texture_restart(txh);
}
else if (txnode->isActive && gf_mo_should_deactivate(st->txh.stream) ) {
movietexture_deactivate(st, txnode);
}
}
if (!st->first_frame_fetched && (txh->needs_refresh) ) {
st->first_frame_fetched = 1;
txnode->duration_changed = gf_mo_get_duration(txh->stream);
gf_node_event_out(txh->owner, 7);
if (!txnode->isActive && txh->is_open) {
gf_mo_pause(txh->stream);
txh->needs_refresh = 1;
gf_sc_invalidate(txh->compositor, NULL);
}
}
if (txh->needs_refresh) {
gf_node_dirty_parents(txh->owner);
}
}
static void movietexture_update_time(GF_TimeNode *st)
{
Double time;
M_MovieTexture *mt = (M_MovieTexture *)st->udta;
MovieTextureStack *stack = (MovieTextureStack *)gf_node_get_private(st->udta);
if ( ! mt->isActive) {
stack->start_time = mt->startTime;
}
time = gf_node_get_scene_time(st->udta);
if (time < stack->start_time ||
(!mt->isActive && (mt->stopTime > stack->start_time) && (time>=mt->stopTime))
) {
if (stack->fetch_first_frame) {
stack->fetch_first_frame = 0;
if (!stack->txh.is_open)
gf_sc_texture_play(&stack->txh, &mt->url);
else
gf_mo_resume(stack->txh.stream);
}
return;
}
if (movietexture_get_speed(stack, mt) && mt->isActive) {
if ((mt->stopTime > stack->start_time) && (time >= mt->stopTime) ) {
movietexture_deactivate(stack, mt);
return;
}
}
if (! mt->isActive) movietexture_activate(stack, mt, time);
stack->txh.stream_finished = GF_FALSE;
}
void compositor_init_movietexture(GF_Compositor *compositor, GF_Node *node)
{
MovieTextureStack *st;
GF_SAFEALLOC(st, MovieTextureStack);
if (!st) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate movie texture stack\n"));
return;
}
gf_sc_texture_setup(&st->txh, compositor, node);
st->txh.update_texture_fcnt = movietexture_update;
st->time_handle.UpdateTimeNode = movietexture_update_time;
st->time_handle.udta = node;
st->fetch_first_frame = 1;
st->txh.flags = 0;
if (((M_MovieTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
if (((M_MovieTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
#ifndef GPAC_DISABLE_X3D
st->is_x3d = (gf_node_get_tag(node)==TAG_X3D_MovieTexture) ? 1 : 0;
#endif
gf_node_set_private(node, st);
gf_node_set_callback_function(node, movietexture_destroy);
gf_sc_register_time_node(compositor, &st->time_handle);
}
GF_TextureHandler *mt_get_texture(GF_Node *node)
{
MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
return &st->txh;
}
void compositor_movietexture_modified(GF_Node *node)
{
M_MovieTexture *mt = (M_MovieTexture *)node;
MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
if (!st) return;
if (gf_sc_texture_check_url_change(&st->txh, &mt->url)) {
if (st->txh.is_open) gf_sc_texture_stop(&st->txh);
if (mt->isActive) gf_sc_texture_play(&st->txh, &mt->url);
}
else if (mt->isActive) {
movietexture_update_time(&st->time_handle);
if (!mt->isActive) return;
}
st->time_handle.needs_unregister = 0;
if (!st->time_handle.is_registered) gf_sc_register_time_node(st->txh.compositor, &st->time_handle);
}
static void imagetexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
if (gf_node_get_tag(node)==TAG_MPEG4_CacheTexture) {
char section[64];
const char *opt, *file;
Bool delete_file = 1;
M_CacheTexture *ct = (M_CacheTexture*)node;
sprintf(section, "@cache=%p", ct);
file = gf_cfg_get_key(txh->compositor->user->config, section, "cacheFile");
opt = gf_cfg_get_key(txh->compositor->user->config, section, "expireAfterNTP");
if (opt) {
u32 sec, frac, exp;
sscanf(opt, "%u", &exp);
gf_net_get_ntp(&sec, &frac);
if (!exp || (exp>sec)) delete_file=0;
}
if (delete_file) {
gf_delete_file((char*)file);
gf_cfg_del_section(txh->compositor->user->config, section);
}
if (txh->data) gf_free(txh->data);
txh->data = NULL;
}
gf_sc_texture_destroy(txh);
gf_free(txh);
}
}
static void imagetexture_update(GF_TextureHandler *txh)
{
if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
MFURL url = ((M_ImageTexture *) txh->owner)->url;
if (!txh->is_open && url.count) {
gf_sc_texture_play(txh, &url);
}
gf_sc_texture_update_frame(txh, 0);
if (
txh->needs_refresh) {
gf_node_dirty_parents(txh->owner);
gf_sc_invalidate(txh->compositor, NULL);
}
return;
}
else {
M_CacheTexture *ct = (M_CacheTexture *) txh->owner;
if ((ct->data || ct->image.buffer) && !txh->data) {
#ifndef GPAC_DISABLE_AV_PARSERS
u32 out_size;
GF_Err e;
if (ct->image.buffer) {
char *par = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
char *src_url = gf_url_concatenate(par, ct->image.buffer);
FILE *test = gf_fopen( src_url ? src_url : ct->image.buffer, "rb");
if (test) {
fseek(test, 0, SEEK_END);
ct->data_len = (u32) gf_ftell(test);
ct->data = gf_malloc(sizeof(char)*ct->data_len);
fseek(test, 0, SEEK_SET);
if (ct->data_len != fread(ct->data, 1, ct->data_len, test)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: IO err\n", src_url ? src_url : ct->image.buffer ) );
gf_free(ct->data);
ct->data = NULL;
ct->data_len = 0;
}
gf_fclose(test);
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: not found\n", src_url ? src_url : ct->image.buffer ) );
}
ct->image.buffer = NULL;
if (src_url) gf_free(src_url);
}
switch (ct->objectTypeIndication) {
case GPAC_OTI_IMAGE_JPEG:
out_size = 0;
e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3);
if (e==GF_BUFFER_TOO_SMALL) {
u32 BPP;
txh->data = gf_malloc(sizeof(char) * out_size);
if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1;
else BPP = 3;
e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP);
if (e==GF_OK) {
gf_sc_texture_allocate(txh);
gf_sc_texture_set_data(txh);
txh->needs_refresh = 1;
txh->stride = out_size / txh->height;
}
}
break;
case GPAC_OTI_IMAGE_PNG:
out_size = 0;
e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size);
if (e==GF_BUFFER_TOO_SMALL) {
txh->data = gf_malloc(sizeof(char) * out_size);
e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size);
if (e==GF_OK) {
gf_sc_texture_allocate(txh);
gf_sc_texture_set_data(txh);
txh->needs_refresh = 1;
txh->stride = out_size / txh->height;
}
}
break;
}
#endif
if (ct->cacheURL.buffer) {
u32 i;
u8 hash[20];
FILE *cached_texture;
char szExtractName[GF_MAX_PATH], section[64], *opt, *src_url;
opt = (char *) gf_cfg_get_key(txh->compositor->user->config, "General", "CacheDirectory");
if (opt) {
strcpy(szExtractName, opt);
} else {
opt = gf_get_default_cache_directory();
strcpy(szExtractName, opt);
gf_free(opt);
}
strcat(szExtractName, "/");
src_url = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
gf_sha1_csum((u8 *)src_url, (u32) strlen(src_url), hash);
for (i=0; i<20; i++) {
char t[3];
t[2] = 0;
sprintf(t, "%02X", hash[i]);
strcat(szExtractName, t);
}
strcat(szExtractName, "_");
strcat(szExtractName, ct->cacheURL.buffer);
cached_texture = gf_fopen(szExtractName, "wb");
if (cached_texture) {
gf_fwrite(ct->data, 1, ct->data_len, cached_texture);
gf_fclose(cached_texture);
}
if (ct->expirationDate!=0) {
sprintf(section, "@cache=%p", ct);
gf_cfg_set_key(txh->compositor->user->config, section, "serviceURL", src_url);
gf_cfg_set_key(txh->compositor->user->config, section, "cacheFile", szExtractName);
gf_cfg_set_key(txh->compositor->user->config, section, "cacheName", ct->cacheURL.buffer);
if (ct->expirationDate>0) {
char exp[50];
u32 sec, frac;
gf_net_get_ntp(&sec, &frac);
sec += ct->expirationDate;
sprintf(exp, "%u", sec);
gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", exp);
} else {
gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", "0");
}
}
}
if (ct->data) gf_free(ct->data);
ct->data = NULL;
ct->data_len = 0;
}
}
}
void compositor_init_imagetexture(GF_Compositor *compositor, GF_Node *node)
{
GF_TextureHandler *txh;
GF_SAFEALLOC(txh, GF_TextureHandler);
if (!txh) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate image texture stack\n"));
return;
}
gf_sc_texture_setup(txh, compositor, node);
txh->update_texture_fcnt = imagetexture_update;
gf_node_set_private(node, txh);
gf_node_set_callback_function(node, imagetexture_destroy);
txh->flags = 0;
if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
if (((M_ImageTexture*)node)->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
if (((M_ImageTexture*)node)->repeatT) txh->flags |= GF_SR_TEXTURE_REPEAT_T;
} else {
const char *url;
u32 i, count;
M_CacheTexture*ct = (M_CacheTexture*)node;
if (!ct->image.buffer) return;
if (ct->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
if (ct->repeatT) txh->flags |= GF_SR_TEXTURE_REPEAT_T;
url = gf_scene_get_service_url( gf_node_get_graph(node) );
count = gf_cfg_get_section_count(compositor->user->config);
for (i=0; i<count; i++) {
const char *opt;
const char *name = gf_cfg_get_section_name(compositor->user->config, i);
if (strncmp(name, "@cache=", 7)) continue;
opt = gf_cfg_get_key(compositor->user->config, name, "serviceURL");
if (!opt || stricmp(opt, url)) continue;
opt = gf_cfg_get_key(compositor->user->config, name, "cacheName");
if (opt && ct->cacheURL.buffer && !stricmp(opt, ct->cacheURL.buffer)) {
opt = gf_cfg_get_key(compositor->user->config, name, "cacheFile");
if (opt) gf_delete_file((char*)opt);
gf_cfg_del_section(compositor->user->config, name);
break;
}
}
}
}
GF_TextureHandler *it_get_texture(GF_Node *node)
{
return gf_node_get_private(node);
}
void compositor_imagetexture_modified(GF_Node *node)
{
MFURL url;
SFURL sfurl;
GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
if (!txh) return;
if (gf_node_get_tag(node)!=TAG_MPEG4_CacheTexture) {
url = ((M_ImageTexture *) node)->url;
} else {
url.count = 1;
sfurl.OD_ID=GF_MEDIA_EXTERNAL_ID;
sfurl.url = ((M_CacheTexture *) node)->image.buffer;
url.vals = &sfurl;
}
if (txh->is_open) {
if (! gf_sc_texture_check_url_change(txh, &url)) return;
gf_sc_texture_stop(txh);
gf_sc_texture_play(txh, &url);
return;
}
if (url.count)
gf_sc_texture_play(txh, &url);
}
typedef struct
{
GF_TextureHandler txh;
char *pixels;
} PixelTextureStack;
static void pixeltexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
if (st->pixels) gf_free(st->pixels);
gf_sc_texture_destroy(&st->txh);
gf_free(st);
}
}
static void pixeltexture_update(GF_TextureHandler *txh)
{
u32 pix_format, stride, i;
M_PixelTexture *pt = (M_PixelTexture *) txh->owner;
PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(txh->owner);
if (!gf_node_dirty_get(txh->owner)) return;
gf_node_dirty_clear(txh->owner, 0);
txh->transparent = 0;
stride = pt->image.width;
switch (pt->image.numComponents) {
case 1:
pix_format = GF_PIXEL_GREYSCALE;
break;
case 2:
pix_format = GF_PIXEL_ALPHAGREY;
txh->transparent = 1;
stride *= 2;
break;
case 3:
pix_format = GF_PIXEL_RGB_24;
txh->transparent = 0;
stride *= 3;
break;
case 4:
pix_format = GF_PIXEL_RGBA;
txh->transparent = 1;
stride *= 4;
break;
default:
return;
}
if (!txh->tx_io) {
gf_sc_texture_allocate(txh);
if (!txh->tx_io) return;
}
if (st->pixels) gf_free(st->pixels);
st->pixels = (char*)gf_malloc(sizeof(char) * stride * pt->image.height);
if (0) {
for (i=0; i<pt->image.height; i++) {
memcpy(st->pixels + i * stride, pt->image.pixels + i * stride, stride);
}
}
else {
for (i=0; i<pt->image.height; i++) {
memcpy(st->pixels + i * stride, pt->image.pixels + (pt->image.height - 1 - i) * stride, stride);
}
}
txh->width = pt->image.width;
txh->height = pt->image.height;
txh->stride = stride;
txh->pixelformat = pix_format;
txh->data = st->pixels;
gf_sc_texture_set_data(txh);
}
void compositor_init_pixeltexture(GF_Compositor *compositor, GF_Node *node)
{
PixelTextureStack *st;
GF_SAFEALLOC(st, PixelTextureStack);
if (!st) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate pixel texture stack\n"));
return;
}
gf_sc_texture_setup(&st->txh, compositor, node);
st->pixels = NULL;
st->txh.update_texture_fcnt = pixeltexture_update;
gf_node_set_private(node, st);
gf_node_set_callback_function(node, pixeltexture_destroy);
st->txh.flags = 0;
if (((M_PixelTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
if (((M_PixelTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
}
GF_TextureHandler *pt_get_texture(GF_Node *node)
{
PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
return &st->txh;
}
static void matte_update(GF_TextureHandler *txh)
{
}
void compositor_init_mattetexture(GF_Compositor *compositor, GF_Node *node)
{
GF_TextureHandler *txh;
GF_SAFEALLOC(txh, GF_TextureHandler);
if (!txh) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate matte texture stack\n"));
return;
}
gf_sc_texture_setup(txh, compositor, node);
txh->flags = GF_SR_TEXTURE_MATTE;
txh->update_texture_fcnt = matte_update;
gf_node_set_private(node, txh);
gf_node_set_callback_function(node, imagetexture_destroy);
}
#ifdef __cplusplus
}
#endif
#endif