static event_t * be_gmeplayer_play(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, int hold) { event_t *e; char *url, *p; int track; void *fh; url0 += strlen("gmeplayer:"); url = mystrdupa(url0); p = strrchr(url, '/'); if(p == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return NULL; } *p++= 0; track = atoi(p) - 1; if((fh = fa_open(url, errbuf, errlen)) == NULL) return NULL; e = fa_gme_playfile_internal(mp, fh, errbuf, errlen, hold, track); fa_close(fh); return e; }
static event_t * be_gmeplayer_play(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, int hold, const char *mimetype) { event_t *e; char *url, *p; int track; url0 += strlen("gmeplayer:"); url = mystrdupa(url0); p = strrchr(url, '/'); if(p == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return NULL; } *p++= 0; track = atoi(p) - 1; buf_t *b; if((b = fa_load(url, NULL, errbuf, errlen, NULL, 0, NULL, NULL)) == NULL) return NULL; e = fa_gme_playfile_internal(mp, b->b_ptr, b->b_size, errbuf, errlen, hold, track, url0); buf_release(b); return e; }
static int rtmp_probe(const char *url0, char *errbuf, size_t errlen, int timeout_ms) { RTMP *r; char *url = mystrdupa(url0); r = RTMP_Alloc(); RTMP_Init(r, NULL); if(!RTMP_SetupURL(r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); RTMP_Free(r); return BACKEND_PROBE_FAIL; } if(!RTMP_Connect(r, NULL, errbuf, errlen, timeout_ms)) { RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_FAIL; } RTMP_SetReadTimeout(r, timeout_ms); if(!RTMP_ConnectStream(r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_FAIL; } RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_OK; }
void metadata_filename_to_title(const char *filename, int *yearp, rstr_t **titlep) { int year = 0; char *s = mystrdupa(filename); url_deescape(s); int i = strlen(s); while(i > 0) { if(i > 5 && s[i-5] == '.' && isnum(s[i-4]) && isnum(s[i-3]) && isnum(s[i-2]) && isnum(s[i-1])) { year = atoi(s + i - 4); i -= 5; s[i] = 0; continue; } if(i > 7 && s[i-7] == ' ' && s[i-6] == '(' && isnum(s[i-5]) && isnum(s[i-4]) && isnum(s[i-3]) && isnum(s[i-2]) && s[i-1] == ')') { year = atoi(s + i - 5); i -= 7; s[i] = 0; continue; } int j; for(j = 0; stopstrings[j] != NULL; j++) { int len = strlen(stopstrings[j]); if(i > len+1 && (s[i-len-1] == '.' || s[i-len-1] == ' ') && !strncasecmp(s+i-len, stopstrings[j], len) && (s[i] == '.' || s[i] == ' ' || s[i] == '-' || s[i] == 0)) { i -= len+1; s[i] = 0; break; } } if(stopstrings[j] != NULL) continue; i--; } for(i = 0; s[i]; i++) { if(s[i] == '.') { s[i] = ' '; } } if(yearp != NULL) *yearp = year; if(titlep != NULL) *titlep = rstr_alloc(s); }
static int es_file_copy(duk_context *ctx) { char *cleanup; char path[URL_MAX]; char errbuf[256]; es_context_t *ec = es_get(ctx); const char *from = duk_to_string(ctx, 0); const char *to = duk_to_string(ctx, 1); cleanup = mystrdupa(to); fa_sanitize_filename(cleanup); snprintf(path, sizeof(path), "%s/copy/%s", ec->ec_storage, cleanup); TRACE(TRACE_DEBUG, "JS", "Copying file from '%s' to '%s'", from, path); if(fa_copy(path, from, errbuf, sizeof(errbuf))) duk_error(ctx, DUK_ERR_ERROR, "Copy failed: %s", errbuf); duk_push_string(ctx, path); return 1; }
static void notifications_update(void *opaque, prop_event_t event, ...) { statusbar_t *sb = opaque; prop_t *p, *txt; statusbar_entry_t *sbe; char *buf; rstr_t *msg; int i, l; va_list ap; va_start(ap, event); switch(event) { case PROP_ADD_CHILD: p = va_arg(ap, prop_t *); txt = prop_get_by_name(PNVEC("self", "text"), 1, PROP_TAG_NAMED_ROOT, p, "self", NULL); if(txt != NULL) { msg = prop_get_string(txt); if(msg != NULL) { buf = mystrdupa(rstr_get(msg)); l = strlen(buf); for(i = 0; i < l; i++) if(buf[i] < ' ') buf[i] = ' '; sbe = calloc(1, sizeof(statusbar_entry_t)); sbe->p = prop_ref_inc(p); sbe->id = gtk_statusbar_push(GTK_STATUSBAR(sb->bar), sb->ctxid, buf); LIST_INSERT_HEAD(&sb->entries, sbe, link); rstr_release(msg); } prop_ref_dec(txt); } break; case PROP_DEL_CHILD: p = va_arg(ap, prop_t *); LIST_FOREACH(sbe, &sb->entries, link) if(sbe->p == p) break; if(sbe == NULL) break; prop_ref_dec(sbe->p); gtk_statusbar_remove(GTK_STATUSBAR(sb->bar), sb->ctxid, sbe->id); LIST_REMOVE(sbe, link); free(sbe); break; default: break; } }
static pixmap_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; pixmap_t *pm = NULL; char cacheid[512]; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); const char *siz; *tim++ = 0; int secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex[0]); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex[0]); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex[0]); if(im->im_req_width < 100 && im->im_req_height < 100) { siz = "min"; } else if(im->im_req_width < 200 && im->im_req_height < 200) { siz = "mid"; } else { siz = "max"; } snprintf(cacheid, sizeof(cacheid), "%s-%s", url0, siz); buf_t *b = blobcache_get(cacheid, "videothumb", 0, 0, NULL, &mtime); if(b != NULL && mtime == stattime) { pm = pixmap_alloc_coded(b->b_ptr, b->b_size, PIXMAP_JPEG); buf_release(b); return pm; } buf_release(b); if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex[1]); pm = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, c); hts_mutex_unlock(&image_from_video_mutex[1]); return pm; }
static pixmap_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, fa_load_cb_t *cb, void *opaque) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; pixmap_t *pm = NULL; char cacheid[512]; void *data; size_t datasize; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); *tim++ = 0; int secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex); snprintf(cacheid, sizeof(cacheid), "%s-%d-%d-3", url0, im->im_req_width, im->im_req_height); data = blobcache_get(cacheid, "videothumb", &datasize, 0, 0, NULL, &mtime); if(data != NULL && mtime == stattime) { pm = pixmap_alloc_coded(data, datasize, PIXMAP_PNG); free(data); return pm; } if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex); pm = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, cb, opaque); hts_mutex_unlock(&image_from_video_mutex); return pm; }
void prop_set_from_tuple(GVariant *v, prop_t *parent) { const char *key = g_variant_get_string(g_variant_get_child_value(v, 0), NULL); GVariant *value = g_variant_get_variant(g_variant_get_child_value(v, 1)); char *k = mystrdupa(key); fixup_title(k); prop_set_from_gvariant(value, prop_create(parent, k)); }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, int flags, int priority, char *errbuf, size_t errlen, const char *mimetype) { rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); prop_set_string(mp->mp_prop_type, "video"); RTMP_LogSetLevel(RTMP_LOGINFO); r.r = RTMP_Alloc(); RTMP_Init(r.r); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_Connect(r.r, NULL)) { snprintf(errbuf, errlen, "Unable to connect RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_DEEP); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); e = rtmp_loop(&r, mp, url, errbuf, errlen); mp_flush(mp, 0); mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of stream"); rtmp_free(&r); return e; }
static int be_page_open(prop_t *root, const char *url0, int sync) { prop_t *src = prop_create(root, "model"); prop_t *metadata = prop_create(src, "metadata"); char *cap = mystrdupa(url0 + strlen("page:")); prop_set_string(prop_create(src, "type"), cap); cap[0] = toupper((int)cap[0]); prop_set_string(prop_create(metadata, "title"), cap); return 0; }
static int sidfile_scandir(fa_dir_t *fd, const char *url, char *errbuf, size_t errlen) { void *fh = NULL; char *p, *fpath = mystrdupa(url); char buf[128]; char name[32]; char turl[URL_MAX]; int tracks, i; fa_dir_entry_t *fde; rstr_t *album, *artist; if((p = strrchr(fpath, '/')) == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return -1; } *p = 0; if((fh = fa_open(fpath, errbuf, errlen)) == NULL) return -1; if(fa_read(fh, buf, 128) != 128) { snprintf(errbuf, errlen, "Unable to read file"); fa_close(fh); return -1; } album = rstr_alloc(utf8_from_bytes((char *)buf + 0x16, 32, NULL)); artist = rstr_alloc(utf8_from_bytes((char *)buf + 0x36, 32, NULL)); tracks = buf[0xf]; for(i = 0; i < tracks; i++) { snprintf(name, sizeof(name), "Track %02d", i + 1); snprintf(turl, sizeof(turl), "sidplayer:%s/%d", fpath, i + 1); fde = fa_dir_add(fd, turl, name, CONTENT_AUDIO); fde->fde_probestatus = FDE_PROBE_DEEP; fde->fde_metadata = prop_create_root("metadata"); prop_set_string(prop_create(fde->fde_metadata, "title"), name); prop_set_rstring(prop_create(fde->fde_metadata, "album"), album); prop_set_rstring(prop_create(fde->fde_metadata, "artist"), artist); } rstr_release(album); rstr_release(artist); fa_close(fh); return 0; }
static int es_file_dirname(duk_context *ctx) { es_context_t *ec = es_get(ctx); const char *filename = mystrdupa(get_filename(ctx, 0, ec, 0)); char *x = strrchr(filename, '/'); if(x) { *x = 0; duk_push_string(ctx, filename); } return 1; }
/** * Compiles a regexp matching all paths of existing file:// services. * E.g.: "^(path1|path2|path3...)". */ static int fa_create_paths_regex (regex_t *preg) { service_t *s; int len = 0; char *str, *t; int errcode; hts_mutex_lock(&service_mutex); /* First calculate the space needed for the regex. */ LIST_FOREACH(s, &services, s_link) if (!strncmp(s->s_url, "file://", strlen("file://"))) len += strlen(s->s_url + strlen("file://")) + strlen("/|"); if (len == 0) { /* No file:// services found. We should either flunk out * and do nothing here, or act as if this was a feature * and provide un-filtered 'locate' output. I really dont know * whats best. */ str = strdup(".*"); } else { str = t = malloc(len + strlen("^()") + 1); t += sprintf(str, "^("); /* Then construct the regex. */ LIST_FOREACH(s, &services, s_link) if (!strncmp(s->s_url, "file://", strlen("file://"))) t += sprintf(t, "%s%s|", deregex(mystrdupa(s->s_url + strlen("file://"))), (s->s_url[strlen(s->s_url)-1] == '/' ? "" : "/")); *(t-1) = ')'; } hts_mutex_unlock(&service_mutex); if ((errcode = regcomp(preg, str, REG_EXTENDED|REG_ICASE|REG_NOSUB))) { char buf[64]; regerror(errcode, preg, buf, sizeof(buf)); TRACE(TRACE_ERROR, "FA", "Search regex compilation of \"%s\" failed: %s", str, buf); free(str); return -1; } free(str); return 0; }
void prop_set_from_vardict(GVariant *v, prop_t *parent) { GVariantIter iter; GVariant *value; gchar *key; g_variant_iter_init(&iter, v); while(g_variant_iter_loop(&iter, "{sv}", &key, &value)) { char *k = mystrdupa(key); fixup_title(k); prop_set_from_gvariant(value, prop_create(parent, k)); } }
int metadata_filename_to_episode(const char *s, int *seasonp, int *episodep, rstr_t **titlep) { int i, j; int len = strlen(s); int season = -1; int episode = -1; for(i = 0; i < len; i++) { if((s[i] == 's' || s[i] == 'S') && isnum(s[i+1]) && isnum(s[i+2])) { int o = 3+i; if(s[o] == '.') o++; if((s[o] == 'e' || s[o] == 'E') && isnum(s[o+1]) && isnum(s[o+2])) { season = atoi(s+i+1); episode = atoi(s+o+1); break; } } } if(season == -1 || episode == -1) return -1; *seasonp = season; *episodep = episode; char *t = mystrdupa(s); url_deescape(t); for(j= 0; j < i; j++) { if(t[j] == '.') { t[j] = ' '; } } t[j] = 0; if(titlep != NULL) { if(j) *titlep = rstr_alloc(t); else *titlep = NULL; } return 0; }
int filebundle_load(const char *p, void **ptr, int *len, int *osize) { const struct filebundle_entry *fe; const struct filebundle *fb; char *path = mystrdupa(p); char *x = strchr(path, '/'); if(x == NULL) return ENOTDIR; *x++ = 0; for(fb = filebundles; fb != NULL; fb = fb->next) { if(!strcmp(path, fb->prefix)) break; } if(fb == NULL) return ENODEV; for(fe = fb->entries; fe->filename != NULL; fe++) { if(!strcmp(fe->filename, x)) break; } if(fe->filename == NULL) return ENOENT; if(ptr) *ptr = (void *)fe->data; if(len) *len = fe->size; if(osize) *osize = fe->original_size; return 0; }
/** * Play given track. * * We only expect this to be called from the playqueue system. */ static event_t * be_sid2player_play(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, int hold, const char *mimetype) { media_queue_t *mq = &mp->mp_audio; char *url, *p; int sample = 0; media_buf_t *mb = NULL; event_t *e; int subsong; int registered_play = 0; void *player; url0 += strlen("sidplayer:"); url = mystrdupa(url0); p = strrchr(url, '/'); if(p == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return NULL; } *p++= 0; subsong = atoi(p); buf_t *b; if((b = fa_load(url, NULL, errbuf, errlen, NULL, 0, NULL, NULL)) == NULL) return NULL; player = sidcxx_load(b->b_ptr, b->b_size, subsong, errbuf, errlen); buf_release(b); if(player == NULL) return NULL; mp_set_playstatus_by_hold(mp, hold, NULL); mp->mp_audio.mq_stream = 0; mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_NONE, 0); mp_become_primary(mp); while(1) { if(mb == NULL) { mb = media_buf_alloc_unlocked(mp, sizeof(int16_t) * CHUNK_SIZE * 2); mb->mb_data_type = MB_AUDIO; mb->mb_channels = 2; mb->mb_rate = 44100; mb->mb_pts = sample * 1000000LL / mb->mb_rate; mb->mb_drive_clock = 1; if(!registered_play && mb->mb_pts > METADB_AUDIO_PLAY_THRESHOLD) { registered_play = 1; metadb_register_play(url0, 1, CONTENT_AUDIO); } sample += CHUNK_SIZE; int16_t *samples = mb->mb_data; sidcxx_play(player, samples, CHUNK_SIZE * sizeof(int16_t) * mb->mb_channels); // Crossmix 25% int i, l, r, L, R; for(i = 0; i < CHUNK_SIZE; i++) { l = samples[i * 2 + 0]; r = samples[i * 2 + 1]; L = 3 * l + r * 2; R = 3 * r + l * 2; L = L / 4; R = R / 4; if(L > 32767) L = 32767; if(L < -32768) L = -32768; if(R > 32767) R = 32767; if(R < -32768) R = -32768; samples[i * 2 + 0] = L; samples[i * 2 + 1] = R; } } if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) { mb = NULL; /* Enqueue succeeded */ continue; } if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) { mp_flush(mp, 0); break; } else if(event_is_action(e, ACTION_SKIP_BACKWARD) || event_is_action(e, ACTION_SKIP_FORWARD) || event_is_action(e, ACTION_STOP)) { mp_flush(mp, 0); break; } event_release(e); } sidcxx_stop(player); return e; }
void metadata_filename_to_title(const char *filename, int *yearp, rstr_t **titlep) { int year = 0; //skip the chars between a starting '[' and ']'. char* start=NULL; if(filename[0]=='[') { start = strchr(filename,']'); if(start != NULL) { start++; if(start[0]=='.') start++; } if(start>=filename+strlen(filename)) //don't skip if the '[]' includes the whole filename start=NULL; } char *s ; if(start==NULL) s = mystrdupa(filename); else s = mystrdupa(start); url_deescape(s); int i = strlen(s); while(i > 0) { if(i > 5 && s[i-5] == '.' && isnum(s[i-4]) && isnum(s[i-3]) && isnum(s[i-2]) && isnum(s[i-1])) { year = atoi(s + i - 4); i -= 5; s[i] = 0; continue; } if(i > 7 && s[i-7] == ' ' && s[i-6] == '(' && isnum(s[i-5]) && isnum(s[i-4]) && isnum(s[i-3]) && isnum(s[i-2]) && s[i-1] == ')') { year = atoi(s + i - 5); i -= 7; s[i] = 0; continue; } int j; for(j = 0; stopstrings[j] != NULL; j++) { int len = strlen(stopstrings[j]); if(i > len+1 && (s[i-len-1] == '.' || s[i-len-1] == ' ') && !strncasecmp(s+i-len, stopstrings[j], len) && (s[i] == '.' || s[i] == ' ' || s[i] == '-' || s[i] == 0)) { i -= len+1; s[i] = 0; break; } } if(stopstrings[j] != NULL) continue; i--; } char *lastword = strrchr(s, ' '); if(lastword && lastword > s) { int y = atoi(lastword + 1); if(y > 1900 && y < 2040) { year = y; *lastword = 0; } } for(i = 0; s[i]; i++) { if(s[i] == '.') { s[i] = ' '; } } if(yearp != NULL) *yearp = year; if(titlep != NULL) *titlep = rstr_alloc(s); }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, video_queue_t *vq, struct vsource_list *vsl, const video_args_t *va0) { video_args_t va = *va0; rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); mp_set_url(mp, va0->canonical_url, va0->parent_url, va0->parent_title); usage_event("Play video", 1, USAGE_SEG("format", "RTMP")); prop_set(mp->mp_prop_metadata, "format", PROP_SET_STRING, "RTMP"); prop_set(mp->mp_prop_root, "loading", PROP_SET_INT, 1); va.flags |= BACKEND_VIDEO_NO_FS_SCAN; rtmp_log_level = RTMP_LOGINFO; RTMP_LogSetLevel(rtmp_log_level); r.r = RTMP_Alloc(); RTMP_Init(r.r, mp->mp_cancellable); int64_t start = playinfo_get_restartpos(va.canonical_url, va.title, va.resume_mode); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } r.r->Link.lFlags |= RTMP_LF_SWFV; if(!RTMP_Connect(r.r, NULL, errbuf, errlen, 5000)) { rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } if(start) RTMP_SendSeek(r.r, start); r.mp = mp; mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; if(start > 0) { r.seekpos_video = start * 1000; r.seekpos_audio = start * 1000; mp->mp_seek_base = r.seekpos_video; mp->mp_video.mq_seektarget = r.seekpos_video; mp->mp_audio.mq_seektarget = r.seekpos_video; } else { mp->mp_video.mq_seektarget = AV_NOPTS_VALUE; mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE; mp->mp_seek_base = 0; r.seekpos_audio = AV_NOPTS_VALUE; r.seekpos_video = AV_NOPTS_VALUE; } mp_configure(mp, MP_CAN_PAUSE, MP_BUFFER_DEEP, 0, "video"); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); playinfo_register_play(va.canonical_url, 0); r.canonical_url = va.canonical_url; r.restartpos_last = -1; r.url = url; r.va = &va; r.is_loading = 1; e = rtmp_loop(&r, mp, url, errbuf, errlen); if(r.ss) sub_scanner_destroy(r.ss); if(r.total_duration) { int p = mp->mp_seek_base / (r.total_duration * 10); if(p >= video_settings.played_threshold) { TRACE(TRACE_DEBUG, "RTMP", "Playback reached %d%%, counting as played", p); playinfo_register_play(va.canonical_url, 1); playinfo_set_restartpos(va.canonical_url, -1, 0); } else { playinfo_set_restartpos(va.canonical_url, mp->mp_seek_base / 1000, 0); } } mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of playback"); rtmp_free(&r); return e; }
void parse_opts(int argc, char **argv) { const char *argv0 = argv[0]; argv++; argc--; gconf.showtime_shell_fd = -1; while(argc > 0) { if(!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) { printf(APPNAMEUSER" %s\n" "Copyright (C) 2007-2015 Lonelycoder AB\n" "\n" "Usage: %s [options] [<url>]\n" "\n" " Options:\n" " -h, --help - This help text.\n" " -d - Enable debug output.\n" " --no-ui - Start without UI.\n" " --fullscreen - Start in fullscreen mode.\n" " --libav-log - Print libav log messages.\n" " --with-standby - Enable system standby.\n" " --with-poweroff - Enable system power-off.\n" " -s <path> - Non-default settings path.\n" " --ui <ui> - Use specified user interface.\n" " -L <ip:host> - Send log messages to remote <ip:host>.\n" " --syslog - Send log messages to syslog.\n" #if ENABLE_STDIN " --stdin - Listen on stdin for events.\n" #endif " -v <view> - Use specific view for <url>.\n" " --cache <path> - Set path for cache [%s].\n" " --persistent <path> - Set path for persistent stuff [%s].\n" #if ENABLE_HTTPSERVER " --disable-upnp - Disable UPNP/DLNA stack.\n" #endif " --disable-sd - Disable service discovery (mDNS, etc).\n" " -p - Path to plugin directory to load\n" " Intended for plugin development\n" " --plugin-repo - URL to plugin repository\n" " Intended for plugin development\n" " -j <path> Load javascript file\n" " --skin <skin> Select skin (for GLW ui)\n" "\n" " URL is any URL-type supported, " "e.g., \"file:///...\"\n" "\n", htsversion_full, argv0, gconf.cache_path, gconf.persistent_path); exit(0); argc--; argv++; } else if(!strcmp(argv[0], "-d")) { gconf.trace_level = TRACE_DEBUG; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--libav-log")) { gconf.libavlog = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--debug-glw")) { gconf.debug_glw = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--show-usage-events")) { gconf.show_usage_events = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--no-ui")) { gconf.noui = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--fullscreen")) { gconf.fullscreen = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--syslog")) { gconf.trace_to_syslog = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--stdin")) { gconf.listen_on_stdin = 1; argc -= 1; argv += 1; continue; #if ENABLE_HTTPSERVER } else if(!strcmp(argv[0], "--disable-upnp")) { gconf.disable_upnp = 1; argc -= 1; argv += 1; continue; #endif } else if(!strcmp(argv[0], "--disable-sd")) { gconf.disable_sd = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--disable-upgrades")) { gconf.disable_upgrades = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--with-standby")) { gconf.can_standby = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--with-poweroff")) { gconf.can_poweroff = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--with-logout")) { gconf.can_logout = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--with-openshell")) { gconf.can_open_shell = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--without-exit")) { gconf.can_not_exit = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "--with-restart")) { gconf.can_restart = 1; argc -= 1; argv += 1; continue; } else if(!strcmp(argv[0], "-p") && argc > 1) { strvec_addp(&gconf.devplugins,argv[1]); argc -= 2; argv += 2; continue; } else if(!strcmp(argv[0], "--plugin-repo") && argc > 1) { gconf.plugin_repo = argv[1]; argc -= 2; argv += 2; continue; } else if(!strcmp(argv[0], "--bypass-ecmascript-acl")) { gconf.bypass_ecmascript_acl = 1; argc -= 1; argv += 1; } else if(!strcmp(argv[0], "--ecmascript") && argc > 1) { gconf.load_ecmascript = argv[1]; argc -= 2; argv += 2; continue; } else if (!strcmp(argv[0], "-v") && argc > 1) { gconf.initial_view = argv[1]; argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--cache") && argc > 1) { mystrset(&gconf.cache_path, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--persistent") && argc > 1) { mystrset(&gconf.persistent_path, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--ui") && argc > 1) { mystrset(&gconf.ui, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--skin") && argc > 1) { mystrset(&gconf.skin, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--upgrade-path") && argc > 1) { mystrset(&gconf.upgrade_path, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--showtime-shell-fd") && argc > 1) { gconf.showtime_shell_fd = atoi(argv[1]); argc -= 2; argv += 2; } else if (!strcmp(argv[0], "--proxy") && argc > 1) { char *x = mystrdupa(argv[1]); char *pstr = strchr(x, ':'); if(pstr != NULL) { *pstr++ = 0; gconf.proxy_port = atoi(pstr); } else { gconf.proxy_port = 1080; } snprintf(gconf.proxy_host, sizeof(gconf.proxy_host), "%s", x); printf("Proxy set to %s:%d\n", gconf.proxy_host, gconf.proxy_port); argc -= 2; argv += 2; #ifdef __APPLE__ /* ignore -psn argument, process serial number */ } else if(!strncmp(argv[0], "-psn", 4)) { argc -= 1; argv += 1; continue; #endif } else break; } if(argc > 0) gconf.initial_url = argv[0]; }
/** * Try to figure out if we have a window manager and query some of its * capabilities */ static void probe_wm(glw_x11_t *gx11) { int i, format; Atom *items, type; unsigned long bytes_after, r, nitems; unsigned char *prop_return; int wm_window_id; char *wm_name; Atom NET_SUPPORTED = XInternAtom(gx11->display, "_NET_SUPPORTED", 0); Atom STATE_FS = XInternAtom(gx11->display, "_NET_WM_STATE_FULLSCREEN", 0); Atom NET_SUPPORTING_WM_CHECK = XInternAtom(gx11->display, "_NET_SUPPORTING_WM_CHECK", 0); Atom NET_WM_NAME = XInternAtom(gx11->display, "_NET_WM_NAME", 0); if(XGetWindowProperty(gx11->display, gx11->root, NET_SUPPORTING_WM_CHECK, 0, 16384, False, AnyPropertyType, &type, &format, &r, &bytes_after, &prop_return) != Success || r == 0 || prop_return == NULL) { TRACE(TRACE_INFO, "GLW", "No window manager found (NET_SUPPORTING_WM_CHECK not set)"); return; } wm_window_id = *(int *)prop_return; XFree(prop_return); prop_return = NULL; if(XGetWindowProperty(gx11->display, wm_window_id, NET_WM_NAME, 0, 16384, False, AnyPropertyType, &type, &format, &r, &bytes_after, &prop_return) != Success || r == 0 || prop_return == NULL) { TRACE(TRACE_INFO, "GLW", "No window manager found (NET_WM_NAME not set on wm window)"); return; } wm_name = mystrdupa((char *)prop_return); XFree(prop_return); prop_return = NULL; if(XGetWindowProperty(gx11->display, gx11->root, NET_SUPPORTED, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop_return) != Success || r == 0 || prop_return == NULL) { TRACE(TRACE_INFO, "GLW", "No window manager found (NET_SUPPORTING_WM_CHECK not set)"); return; } items = (Atom *)prop_return; gx11->wm_flags |= GX11_WM_DETECTED; for(i = 0; i < nitems; i++) { if(items[i] == STATE_FS) gx11->wm_flags |= GX11_WM_CAN_FULLSCREEN; } TRACE(TRACE_DEBUG, "GLW", "Window manager (%s) detected%s", wm_name, gx11->wm_flags & GX11_WM_CAN_FULLSCREEN ? ", can fullscreen" : ""); prop_set_string(prop_create(gx11->gr.gr_prop, "windowmanager"), wm_name); XFree(prop_return); }
event_t * fa_xmp_playfile(media_pipe_t *mp, FILE *f, char *errbuf, size_t errlen, int hold, const char *url, size_t size) { event_t *e = NULL; xmp_context ctx = xmp_create_context(); // struct xmp_module_info mi; struct xmp_frame_info fi; char *u = mystrdupa(url); mp->mp_audio.mq_stream = 0; mp_configure(mp, MP_CAN_PAUSE | MP_CAN_SEEK, MP_BUFFER_SHALLOW, 0, "tracks"); mp_become_primary(mp); if(xmp_load_modulef(ctx, f, u, size) >= 0) { if(xmp_start_player(ctx, 44100, 0) == 0) { media_buf_t *mb = NULL; media_queue_t *mq = &mp->mp_audio; while(1) { if(mb == NULL) { if(xmp_play_frame(ctx)) { e = event_create_type(EVENT_EOF); break; } xmp_get_frame_info(ctx, &fi); if(fi.loop_count > 0) { e = event_create_type(EVENT_EOF); break; } mb = media_buf_alloc_unlocked(mp, fi.buffer_size); mb->mb_data_type = MB_AUDIO; mb->mb_channels = 2; mb->mb_rate = 44100; mb->mb_pts = fi.time * 1000; mb->mb_drive_clock = 1; memcpy(mb->mb_data, fi.buffer, fi.buffer_size); mp_set_duration(mp, fi.total_time * 1000); } if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) { mb = NULL; /* Enqueue succeeded */ continue; } if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) { mp_flush(mp, 0); break; } else if(event_is_action(e, ACTION_SKIP_BACKWARD) || event_is_action(e, ACTION_SKIP_FORWARD) || event_is_action(e, ACTION_STOP)) { mp_flush(mp, 0); break; } event_release(e); } xmp_end_player(ctx); } else { snprintf(errbuf, errlen, "XMP failed to start"); } } else { snprintf(errbuf, errlen, "XMP Loading error"); } // prop_ref_dec(dur); xmp_free_context(ctx); return e; }
int metadata_filename_to_episode(const char *s, int *seasonp, int *episodep, rstr_t **titlep) { int i, j; int len = strlen(s); int season = -1; int episode = -1; // Parse S##E## format for(i = 0; i < len; i++) { if((s[i] == 's' || s[i] == 'S') && isnum(s[i+1]) && isnum(s[i+2])) { int o = 3+i; if(s[o] == '.') o++; if((s[o] == 'e' || s[o] == 'E') && isnum(s[o+1]) && isnum(s[o+2])) { season = atoi(s+i+1); episode = atoi(s+o+1); break; } } } if(season == -1 && episode == -1) { // Parse ' (#)#x## - ' format for(i = 3; i < len - 2; i++) { if(s[i] == 'x' && isnum(s[i + 1]) && isnum(s[i + 2]) && s[i + 3] == ' ' && s[i + 4] == '-' && s[i + 5] == ' ') { episode = atoi(s + i + 1); if(isnum(s[i - 1]) && s[i - 2] == ' ') { season = atoi(s + i - 1); i--; break; } else if(isnum(s[i - 1]) && isnum(s[i - 2]) && s[i - 3] == ' ') { season = atoi(s + i - 2); i-=2; break; } } } } if(season == -1 || episode == -1) return -1; *seasonp = season; *episodep = episode; char *t = mystrdupa(s); url_deescape(t); for(j= 0; j < i; j++) { if(t[j] == '.') { t[j] = ' '; } } t[j] = 0; if(titlep != NULL) { if(j) *titlep = rstr_alloc(t); else *titlep = NULL; } return 0; }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, int flags, int priority, char *errbuf, size_t errlen, const char *mimetype, const char *canonical_url) { rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); prop_set_string(mp->mp_prop_type, "video"); RTMP_LogSetLevel(RTMP_LOGINFO); r.r = RTMP_Alloc(); RTMP_Init(r.r); int64_t start = video_get_restartpos(canonical_url); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_Connect(r.r, NULL)) { snprintf(errbuf, errlen, "Unable to connect RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, start)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } r.seek_origin = start; r.mp = mp; r.hold = 0; r.lost_focus = 0; r.epoch = 1; mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; if(start > 0) { r.seekpos = start * 1000; r.seekbase = r.seekpos; mp->mp_video.mq_seektarget = r.seekpos; mp->mp_audio.mq_seektarget = r.seekpos; } else { mp->mp_video.mq_seektarget = AV_NOPTS_VALUE; mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE; r.seekbase = AV_NOPTS_VALUE; r.seekpos = AV_NOPTS_VALUE; } mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_DEEP); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); metadb_register_play(canonical_url, 0, CONTENT_VIDEO); r.canonical_url = canonical_url; r.restartpos_last = -1; e = rtmp_loop(&r, mp, url, errbuf, errlen); if(r.total_duration) { int p = r.seekbase / (r.total_duration * 10); if(p >= video_settings.played_threshold) { TRACE(TRACE_DEBUG, "RTMP", "Playback reached %d%%, counting as played", p); metadb_register_play(canonical_url, 1, CONTENT_VIDEO); metadb_set_video_restartpos(canonical_url, -1); } } mp_flush(mp, 0); mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of stream"); rtmp_free(&r); return e; }
static image_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; image_t *img = NULL; char cacheid[512]; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); const char *siz; *tim++ = 0; int secs; if(!strcmp(tim, "cover")) secs = -1; else secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex[0]); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex[0]); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex[0]); if(im->im_req_width < 100 && im->im_req_height < 100) { siz = "min"; } else if(im->im_req_width < 200 && im->im_req_height < 200) { siz = "mid"; } else { siz = "max"; } snprintf(cacheid, sizeof(cacheid), "%s-%s", url0, siz); buf_t *b = blobcache_get(cacheid, "videothumb", 0, 0, NULL, &mtime); if(b != NULL && mtime == stattime) { img = image_coded_create_from_buf(b, IMAGE_JPEG); buf_release(b); return img; } buf_release(b); if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex[1]); img = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, c); hts_mutex_unlock(&image_from_video_mutex[1]); if(img != NULL) img->im_flags |= IMAGE_ADAPTED; return img; }
static int gmefile_scandir(fa_dir_t *fd, const char *url, char *errbuf, size_t errlen) { char *p, *fpath = mystrdupa(url); char name[32]; char turl[URL_MAX]; int tracks, i; fa_dir_entry_t *fde; const char *title; Music_Emu *emu; gme_info_t *info; gme_err_t err; if((p = strrchr(fpath, '/')) == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return -1; } *p = 0; buf_t *b; if((b = fa_load(fpath, NULL, errbuf, errlen, NULL, 0, NULL, NULL)) == NULL) return -1; err = gme_open_data(b->b_ptr, b->b_size, &emu, gme_info_only); buf_release(b); if(err != NULL) return 0; tracks = gme_track_count(emu); for(i = 0; i < tracks; i++) { snprintf(turl, sizeof(turl), "gmeplayer:%s/%d", fpath, i + 1); err = gme_track_info(emu, &info, i); if(err == NULL && info->song[0]) { title = info->song; } else { snprintf(name, sizeof(name), "Track %02d", i + 1); title = name; } fde = fa_dir_add(fd, turl, title, CONTENT_AUDIO); fde->fde_probestatus = FDE_PROBE_DEEP; fde->fde_metadata = prop_create_root("metadata"); prop_set_string(prop_create(fde->fde_metadata, "title"), title); if(err == NULL) { if(info->game[0]) prop_set_string(prop_create(fde->fde_metadata, "album"), info->game); if(info->author[0]) prop_set_string(prop_create(fde->fde_metadata, "artist"), info->author); prop_set_float(prop_create(fde->fde_metadata, "duration"), info->play_length / 1000.0); gme_free_info(info); } } gme_delete(emu); return 0; }
void load_site_news(void) { #if ENABLE_WEBPOPUP struct http_header_list response_headers; buf_t *b; char errbuf[512]; b = fa_load("https://movian.tv/projects/movian/news.json", FA_LOAD_FLAGS(FA_DISABLE_AUTH | FA_COMPRESSION), FA_LOAD_RESPONSE_HEADERS(&response_headers), FA_LOAD_ERRBUF(errbuf, sizeof(errbuf)), NULL); if(b == NULL) { TRACE(TRACE_DEBUG, "News", "Unable to load news -- %s", errbuf); return; } const char *dateheader = http_header_get(&response_headers, "date"); if(dateheader == NULL) { buf_release(b); http_headers_free(&response_headers); return; } dateheader = mystrdupa(dateheader); http_headers_free(&response_headers); htsmsg_t *newsinfo = htsmsg_store_load("sitenews"); time_t no_news_before; if(newsinfo == NULL) newsinfo = htsmsg_create_map(); no_news_before = htsmsg_get_u32_or_default(newsinfo, "nothingbefore", 0); if(no_news_before == 0) { if(http_ctime(&no_news_before, dateheader)) { buf_release(b); htsmsg_release(newsinfo); return; } htsmsg_add_u32(newsinfo, "nothingbefore", no_news_before); htsmsg_store_save(newsinfo, "sitenews"); htsmsg_release(newsinfo); } htsmsg_t *doc = htsmsg_json_deserialize(buf_cstr(b)); buf_release(b); if(doc == NULL) { return; } hts_mutex_lock(&news_mutex); htsmsg_t *news = htsmsg_get_list(doc, "news"); if(news != NULL) { htsmsg_field_t *f; HTSMSG_FOREACH(f, news) { htsmsg_t *entry; if((entry = htsmsg_get_map_by_field(f)) == NULL) continue; const char *title = htsmsg_get_str(entry, "title"); const char *created_on = htsmsg_get_str(entry, "created_on"); int id = htsmsg_get_u32_or_default(entry, "id", 0); if(created_on == NULL || title == NULL || id == 0) continue; time_t t; if(parse_created_on_time(&t, created_on)) continue; if(t < no_news_before) continue; char idstr[64]; snprintf(idstr, sizeof(idstr), "sitenews:%d", id); prop_t *p = add_news_locked(idstr, title, NULL, "Read more", idstr); if(p != NULL) { prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, open_news, p, PROP_TAG_ROOT, prop_create(p, "eventSink"), PROP_TAG_MUTEX, &news_mutex, NULL); } }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, video_queue_t *vq, struct vsource_list *vsl, const video_args_t *va0) { video_args_t va = *va0; rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); va.flags |= BACKEND_VIDEO_NO_FS_SCAN; prop_set_string(mp->mp_prop_type, "video"); rtmp_log_level = RTMP_LOGINFO; RTMP_LogSetLevel(rtmp_log_level); r.r = RTMP_Alloc(); RTMP_Init(r.r); int64_t start = 0; if(va.flags & BACKEND_VIDEO_RESUME || (video_settings.resume_mode == VIDEO_RESUME_YES && !(va.flags & BACKEND_VIDEO_START_FROM_BEGINNING))) start = video_get_restartpos(va.canonical_url); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } r.r->Link.lFlags |= RTMP_LF_SWFV; if(!RTMP_Connect(r.r, NULL)) { snprintf(errbuf, errlen, "Unable to connect RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } if(start) RTMP_SendSeek(r.r, start); r.mp = mp; mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; if(start > 0) { r.seekpos_video = start * 1000; r.seekpos_audio = start * 1000; mp->mp_seek_base = r.seekpos_video; mp->mp_video.mq_seektarget = r.seekpos_video; mp->mp_audio.mq_seektarget = r.seekpos_video; } else { mp->mp_video.mq_seektarget = AV_NOPTS_VALUE; mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE; mp->mp_seek_base = 0; r.seekpos_audio = AV_NOPTS_VALUE; r.seekpos_video = AV_NOPTS_VALUE; } mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_DEEP, 0); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); metadb_register_play(va.canonical_url, 0, CONTENT_VIDEO); r.canonical_url = va.canonical_url; r.restartpos_last = -1; sub_scanner_t *ss = sub_scanner_create(url, mp->mp_prop_subtitle_tracks, &va, 0); e = rtmp_loop(&r, mp, url, errbuf, errlen); sub_scanner_destroy(ss); if(r.total_duration) { int p = mp->mp_seek_base / (r.total_duration * 10); if(p >= video_settings.played_threshold) { TRACE(TRACE_DEBUG, "RTMP", "Playback reached %d%%, counting as played", p); metadb_register_play(va.canonical_url, 1, CONTENT_VIDEO); metadb_set_video_restartpos(va.canonical_url, -1); } } mp_flush(mp, 0); mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of stream"); rtmp_free(&r); return e; }
/** * Play given track. * * We only expect this to be called from the playqueue system. */ static event_t * be_sid2player_play(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, int hold, const char *mimetype) { media_queue_t *mq = &mp->mp_audio; char *url, *p; int sample = 0, lost_focus = 0; media_buf_t *mb = NULL; event_t *e; int subsong; void *player; void *data; struct fa_stat fs; url0 += strlen("sidplayer:"); url = mystrdupa(url0); p = strrchr(url, '/'); if(p == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return NULL; } *p++= 0; subsong = atoi(p); if((data = fa_quickload(url, &fs, NULL, errbuf, errlen)) == NULL) return NULL; player = sidcxx_load(data, fs.fs_size, subsong, errbuf, errlen); free(data); if(player == NULL) return NULL; mp_set_playstatus_by_hold(mp, hold, NULL); mp->mp_audio.mq_stream = 0; mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_NONE); mp_become_primary(mp); while(1) { if(mb == NULL) { mb = media_buf_alloc(); mb->mb_data_type = MB_AUDIO; mb->mb_channels = 2; mb->mb_size = sizeof(int16_t) * CHUNK_SIZE * mb->mb_channels; mb->mb_data = malloc(mb->mb_size); mb->mb_rate = 44100; mb->mb_time = sample * 1000000LL / mb->mb_rate; sample += CHUNK_SIZE; int16_t *samples = mb->mb_data; sidcxx_play(player, samples, CHUNK_SIZE * sizeof(int16_t) * mb->mb_channels); // Crossmix 25% int i, l, r, L, R; for(i = 0; i < CHUNK_SIZE; i++) { l = samples[i * 2 + 0]; r = samples[i * 2 + 1]; L = 3 * l + r * 2; R = 3 * r + l * 2; L = L / 4; R = R / 4; if(L > 32767) L = 32767; if(L < -32768) L = -32768; if(R > 32767) R = 32767; if(R < -32768) R = -32768; samples[i * 2 + 0] = L; samples[i * 2 + 1] = R; } } if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) { mb = NULL; /* Enqueue succeeded */ continue; } if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) { mp_flush(mp, 0); break; } else if(event_is_action(e, ACTION_PLAYPAUSE) || event_is_action(e, ACTION_PLAY) || event_is_action(e, ACTION_PAUSE)) { hold = action_update_hold_by_event(hold, e); mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY); lost_focus = 0; mp_set_playstatus_by_hold(mp, hold, NULL); } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) { hold = 1; lost_focus = 1; mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE); mp_set_playstatus_by_hold(mp, hold, e->e_payload); } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) { if(lost_focus) { hold = 0; lost_focus = 0; mp_send_cmd_head(mp, mq, MB_CTRL_PLAY); mp_set_playstatus_by_hold(mp, hold, NULL); } } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) { hold = 1; lost_focus = 0; mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE); mp_set_playstatus_by_hold(mp, hold, e->e_payload); } else if(event_is_action(e, ACTION_PREV_TRACK) || event_is_action(e, ACTION_NEXT_TRACK) || event_is_action(e, ACTION_STOP)) { mp_flush(mp, 0); break; } event_release(e); } sidcxx_stop(player); return e; }