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 void bwlimit_close(fa_handle_t *h) { bwlimit_t *s = (bwlimit_t *)h; fa_close(s->s_src); free(s); }
void fa_libav_close(AVIOContext *avio) { fa_close(avio->opaque); av_free(avio->buffer); av_free(avio); }
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; }
void load_syms(void) { char sympath[256]; char errbuf[256]; snprintf(sympath, sizeof(sympath), "%s/showtime.syms", showtime_dataroot()); my_trace("sympath: %s\n", sympath); fa_handle_t *fh = fa_open(sympath, errbuf, sizeof(errbuf)); if(fh == NULL) { my_trace("Unable to open symbol file %s -- %s", sympath, errbuf); return; } int size = fa_fsize(fh); char *buf = halloc(size + 1); int r = fa_read(fh, buf, size); if(r != size) { my_trace("Unable to read %d bytes", size); hfree(buf, size+1); } else { buf[size] = 0; my_trace("Loaded symbol table %d bytes to %p", size, buf); symbuf = buf; } fa_close(fh); }
static void cmp_close(fa_handle_t *h) { cmp_t *s = (cmp_t *)h; fa_close(s->s_src); close(s->s_fd); free(s); }
static void es_fd_destroy(es_resource_t *eres) { es_fd_t *efd = (es_fd_t *)eres; if(efd->efd_fh != NULL) { fa_close(efd->efd_fh); efd->efd_fh = NULL; } free(efd->efd_path); es_resource_unlink(&efd->super); }
metadata_t * fa_probe_metadata(const char *url, char *errbuf, size_t errsize, const char *filename) { AVFormatContext *fctx; fa_handle_t *fh = fa_open_ex(url, errbuf, errsize, FA_BUFFERED_SMALL, NULL); if(fh == NULL) return NULL; metadata_t *md = metadata_create(); #if ENABLE_LIBGME if(gme_probe(md, url, fh)) return md; #endif #if ENABLE_XMP if(xmp_probe(md, url, fh)) return md; #endif fa_seek(fh, 0, SEEK_SET); if(fa_probe_header(md, url, fh, filename)) { fa_close(fh); return md; } AVIOContext *avio = fa_libav_reopen(fh); if((fctx = fa_libav_open_format(avio, url, errbuf, errsize, NULL)) == NULL) { fa_libav_close(avio); metadata_destroy(md); return NULL; } fa_lavf_load_meta(md, fctx, filename); fa_libav_close_format(fctx); return md; }
static event_t * openspc_play(media_pipe_t *mp, AVIOContext *avio, char *errbuf, size_t errlen) { media_queue_t *mq = &mp->mp_audio; #error fa_fsize can return -1 .. deal with it size_t r, siz = fa_fsize(fh); uint8_t *buf = malloc(siz); media_buf_t *mb = NULL; event_t *e; int hold = 0, lost_focus = 0; int sample = 0; unsigned int duration = INT32_MAX; mp_set_playstatus_by_hold(mp, hold, NULL); mp->mp_audio.mq_stream = 0; fa_seek(fh, 0, SEEK_SET); r = fa_read(fh, buf, siz); fa_close(fh); if(r != siz) { free(buf); snprintf(errbuf, errlen, "openspc: Unable to read file"); return NULL; } if(OSPC_Init(buf, siz)) { free(buf); snprintf(errbuf, errlen, "openspc: Unable to initialize file"); return NULL; } if(!memcmp("v0.30", buf + 0x1c, 4) && buf[0x23] == 0x1a) { char str[4]; memcpy(str, buf + 0xa9, 3); str[3] = 0; duration = atoi(str) * 32000; } mp_set_play_caps(mp, MP_PLAY_CAPS_PAUSE); mp_become_primary(mp); while(1) { if(mb == NULL) { if(sample > duration) { while((e = mp_wait_for_empty_queues(mp, 0)) != NULL) { if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) || 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); } if(e == NULL) e = event_create_type(EVENT_EOF); break; } mb = media_buf_alloc(); mb->mb_data_type = MB_AUDIO; mb->mb_size = sizeof(int16_t) * 2048 * 2; mb->mb_data = malloc(mb->mb_size); mb->mb_size = OSPC_Run(-1, mb->mb_data, mb->mb_size); mb->mb_channels = 2; mb->mb_rate = 32000; mb->mb_time = sample * 1000000LL / mb->mb_rate; sample += 2048; } 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); } free(buf); return e; }
event_t * be_file_playaudio(const char *url, media_pipe_t *mp, char *errbuf, size_t errlen, int hold, const char *mimetype) { AVFormatContext *fctx; AVCodecContext *ctx; AVPacket pkt; media_format_t *fw; int i, r, si; media_buf_t *mb = NULL; media_queue_t *mq; event_ts_t *ets; int64_t ts, seekbase = 0; media_codec_t *cw; event_t *e; int lost_focus = 0; int registered_play = 0; mp_set_playstatus_by_hold(mp, hold, NULL); fa_handle_t *fh = fa_open_ex(url, errbuf, errlen, FA_BUFFERED_SMALL, NULL); if(fh == NULL) return NULL; // First we need to check for a few other formats #if ENABLE_LIBOPENSPC || ENABLE_LIBGME uint8_t pb[128]; size_t psiz; psiz = fa_read(fh, pb, sizeof(pb)); if(psiz < sizeof(pb)) { fa_close(fh); snprintf(errbuf, errlen, "Fill too small"); return NULL; } #if ENABLE_LIBGME if(*gme_identify_header(pb)) return fa_gme_playfile(mp, fh, errbuf, errlen, hold, url); #endif #if ENABLE_LIBOPENSPC if(!memcmp(pb, "SNES-SPC700 Sound File Data", 27)) return openspc_play(mp, fh, errbuf, errlen); #endif #endif AVIOContext *avio = fa_libav_reopen(fh); if(avio == NULL) { fa_close(fh); return NULL; } if((fctx = fa_libav_open_format(avio, url, errbuf, errlen, mimetype)) == NULL) { fa_libav_close(avio); return NULL; } TRACE(TRACE_DEBUG, "Audio", "Starting playback of %s", url); mp_configure(mp, MP_PLAY_CAPS_SEEK | MP_PLAY_CAPS_PAUSE, MP_BUFFER_SHALLOW); mp->mp_audio.mq_stream = -1; mp->mp_video.mq_stream = -1; fw = media_format_create(fctx); cw = NULL; for(i = 0; i < fctx->nb_streams; i++) { ctx = fctx->streams[i]->codec; if(ctx->codec_type != AVMEDIA_TYPE_AUDIO) continue; cw = media_codec_create(ctx->codec_id, 0, fw, ctx, NULL, mp); mp->mp_audio.mq_stream = i; break; } if(cw == NULL) { media_format_deref(fw); snprintf(errbuf, errlen, "Unable to open codec"); return NULL; } mp_become_primary(mp); mq = &mp->mp_audio; while(1) { /** * Need to fetch a new packet ? */ if(mb == NULL) { mp->mp_eof = 0; r = av_read_frame(fctx, &pkt); if(r == AVERROR(EAGAIN)) continue; if(r == AVERROR_EOF || r == AVERROR(EIO)) { mb = MB_SPECIAL_EOF; mp->mp_eof = 1; continue; } if(r != 0) { char msg[100]; fa_ffmpeg_error_to_txt(r, msg, sizeof(msg)); TRACE(TRACE_ERROR, "Audio", "Playback error: %s", msg); while((e = mp_wait_for_empty_queues(mp)) != NULL) { if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) || 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); } if(e == NULL) e = event_create_type(EVENT_EOF); break; } si = pkt.stream_index; if(si != mp->mp_audio.mq_stream) { av_free_packet(&pkt); continue; } mb = media_buf_alloc_unlocked(mp, pkt.size); mb->mb_data_type = MB_AUDIO; mb->mb_pts = rescale(fctx, pkt.pts, si); mb->mb_dts = rescale(fctx, pkt.dts, si); mb->mb_duration = rescale(fctx, pkt.duration, si); mb->mb_cw = media_codec_ref(cw); /* Move the data pointers from ffmpeg's packet */ mb->mb_stream = pkt.stream_index; memcpy(mb->mb_data, pkt.data, pkt.size); if(mb->mb_pts != AV_NOPTS_VALUE) { if(fctx->start_time == AV_NOPTS_VALUE) mb->mb_time = mb->mb_pts; else mb->mb_time = mb->mb_pts - fctx->start_time; } else mb->mb_time = AV_NOPTS_VALUE; mb->mb_send_pts = 1; av_free_packet(&pkt); } /* * Try to send the buffer. If mb_enqueue() returns something we * catched an event instead of enqueueing the buffer. In this case * 'mb' will be left untouched. */ if(mb == MB_SPECIAL_EOF) { // We have reached EOF, drain queues e = mp_wait_for_empty_queues(mp); if(e == NULL) { e = event_create_type(EVENT_EOF); break; } } else 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_type(e, EVENT_CURRENT_PTS)) { ets = (event_ts_t *)e; seekbase = ets->ts; if(registered_play == 0) { if(ets->ts - fctx->start_time > METADB_AUDIO_PLAY_THRESHOLD) { registered_play = 1; metadb_register_play(url, 1, CONTENT_AUDIO); } } } else if(event_is_type(e, EVENT_SEEK)) { ets = (event_ts_t *)e; ts = ets->ts + fctx->start_time; if(ts < fctx->start_time) ts = fctx->start_time; av_seek_frame(fctx, -1, ts, AVSEEK_FLAG_BACKWARD); seekflush(mp, &mb); } else if(event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) { av_seek_frame(fctx, -1, seekbase - 60000000, AVSEEK_FLAG_BACKWARD); seekflush(mp, &mb); } else if(event_is_action(e, ACTION_SEEK_BACKWARD)) { av_seek_frame(fctx, -1, seekbase - 15000000, AVSEEK_FLAG_BACKWARD); seekflush(mp, &mb); } else if(event_is_action(e, ACTION_SEEK_FAST_FORWARD)) { av_seek_frame(fctx, -1, seekbase + 60000000, 0); seekflush(mp, &mb); } else if(event_is_action(e, ACTION_SEEK_FORWARD)) { av_seek_frame(fctx, -1, seekbase + 15000000, 0); seekflush(mp, &mb); #if 0 } else if(event_is_action(e, ACTION_RESTART_TRACK)) { av_seek_frame(fctx, -1, 0, AVSEEK_FLAG_BACKWARD); seekflush(mp, &mb); #endif } 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); } if(mb != NULL && mb != MB_SPECIAL_EOF) media_buf_free_unlocked(mp, mb); media_codec_deref(cw); media_format_deref(fw); if(hold) { // If we were paused, release playback again. mp_send_cmd(mp, mq, MB_CTRL_PLAY); mp_set_playstatus_by_hold(mp, 0, NULL); } return e; }
pixmap_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { uint8_t p[16]; int r; int width = -1, height = -1, orientation = 0; fa_handle_t *fh; pixmap_t *pm; pixmap_type_t fmt; #if ENABLE_LIBAV if(strchr(url, '#')) return fa_image_from_video(url, im, errbuf, errlen, cache_control, c); #endif if(!im->im_want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen, cache_control, c); fa_open_extra_t foe = { .foe_c = c }; if((fh = fa_open_vpaths(url, vpaths, errbuf, errlen, FA_BUFFERED_SMALL, &foe)) == NULL) return NULL; if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } if(fa_read(fh, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "File too short"); fa_close(fh); return NULL; } /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, fh, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->im_want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_close(fh); return NULL; } if(im->im_want_thumb && ji.ji_thumbnail) { pixmap_t *pm = pixmap_dup(ji.ji_thumbnail); fa_close(fh); jpeg_info_clear(&ji); return pm; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = PIXMAP_SVG; } else { snprintf(errbuf, errlen, "Unknown format"); fa_close(fh); return NULL; } int64_t s = fa_fsize(fh); if(s < 0) { snprintf(errbuf, errlen, "Can't read from non-seekable file"); fa_close(fh); return NULL; } pm = pixmap_alloc_coded(NULL, s, fmt); if(pm == NULL) { snprintf(errbuf, errlen, "Out of memory"); fa_close(fh); return NULL; } pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; fa_seek(fh, SEEK_SET, 0); r = fa_read(fh, pm->pm_data, pm->pm_size); fa_close(fh); if(r != pm->pm_size) { pixmap_release(pm); snprintf(errbuf, errlen, "Read error"); return NULL; } return pm; }
static void screenshot_process(void *task) { pixmap_t *pm = task; if(pm == NULL) { screenshot_response(NULL, "Screenshot not supported on this platform"); return; } TRACE(TRACE_DEBUG, "Screenshot", "Processing image %d x %d", pm->pm_width, pm->pm_height); int codecid = AV_CODEC_ID_PNG; if(screenshot_connection) codecid = AV_CODEC_ID_MJPEG; buf_t *b = screenshot_compress(pm, codecid); pixmap_release(pm); if(b == NULL) { screenshot_response(NULL, "Unable to compress image"); return; } if(!screenshot_connection) { char path[512]; char errbuf[512]; snprintf(path, sizeof(path), "%s/screenshot.png", gconf.cache_path); fa_handle_t *fa = fa_open_ex(path, errbuf, sizeof(errbuf), FA_WRITE, NULL); if(fa == NULL) { TRACE(TRACE_ERROR, "SCREENSHOT", "Unable to open %s -- %s", path, errbuf); buf_release(b); return; } fa_write(fa, buf_data(b), buf_len(b)); fa_close(fa); TRACE(TRACE_INFO, "SCREENSHOT", "Written to %s", path); buf_release(b); return; } buf_t *result = NULL; htsbuf_queue_t hq; htsbuf_queue_init(&hq, 0); htsbuf_append(&hq, "image=", 6); htsbuf_append_and_escape_url_len(&hq, buf_cstr(b), buf_len(b)); char errbuf[256]; int ret = http_req("https://api.imgur.com/3/upload", HTTP_FLAGS(FA_CONTENT_ON_ERROR), HTTP_REQUEST_HEADER("Authorization", "Client-ID 7c79b311d4797ed"), HTTP_RESULT_PTR(&result), HTTP_POSTDATA(&hq, "application/x-www-form-urlencoded"), HTTP_ERRBUF(errbuf, sizeof(errbuf)), NULL); if(ret) { screenshot_response(NULL, errbuf); } else { htsmsg_t *response = htsmsg_json_deserialize(buf_cstr(result)); if(response == NULL) { screenshot_response(NULL, "Unable to parse imgur response"); } else { if(htsmsg_get_u32_or_default(response, "success", 0)) { const char *url = htsmsg_get_str_multi(response, "data", "link", NULL); screenshot_response(url, "No link in imgur response"); } else { const char *msg = htsmsg_get_str_multi(response, "data", "error", NULL); if(msg == NULL) { screenshot_response(NULL, "Unkown imgur error"); } else { snprintf(errbuf, sizeof(errbuf), "Imgur error: %s", msg); screenshot_response(NULL, errbuf); } } htsmsg_release(response); } buf_release(result); } buf_release(b); }
static int gmefile_scandir(fa_dir_t *fd, const char *url, char *errbuf, size_t errlen) { void *fh = NULL; char *p, *fpath = mystrdupa(url); char name[32]; char turl[URL_MAX]; int tracks, i, size; fa_dir_entry_t *fde; const char *title; char *buf; Music_Emu *emu; gme_info_t *info; gme_err_t err; size_t r; if((p = strrchr(fpath, '/')) == NULL) { snprintf(errbuf, errlen, "Invalid filename"); return -1; } *p = 0; if((fh = fa_open(fpath, errbuf, errlen)) == NULL) return -1; size = fa_fsize(fh); buf = malloc(size); r = fa_read(fh, buf, size); fa_close(fh); if(r != size) { snprintf(errbuf, errlen, "Unable to read file"); free(buf); return -1; } err = gme_open_data(buf, size, &emu, gme_info_only); free(buf); 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; }
image_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { uint8_t p[16]; int r; int width = -1, height = -1, orientation = 0; fa_handle_t *fh; image_t *img; image_coded_type_t fmt; #if ENABLE_LIBAV if(strchr(url, '#')) return fa_image_from_video(url, im, errbuf, errlen, cache_control, c); #endif if(!im->im_want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen, cache_control, c); fa_open_extra_t foe = { .foe_cancellable = c }; if((fh = fa_open_vpaths(url, vpaths, errbuf, errlen, FA_BUFFERED_SMALL, &foe)) == NULL) return NULL; if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } if(fa_read(fh, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "File too short"); fa_close(fh); return NULL; } /* Probe format */ if(p[0] == 0xff && p[1] == 0xd8 && p[2] == 0xff) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, fh, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->im_want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_close(fh); return NULL; } if(im->im_want_thumb && ji.ji_thumbnail) { image_t *im = image_retain(ji.ji_thumbnail); fa_close(fh); jpeg_info_clear(&ji); im->im_flags |= IMAGE_ADAPTED; return im; } fmt = IMAGE_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = IMAGE_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = IMAGE_GIF; } else if(p[0] == 'B' && p[1] == 'M') { fmt = IMAGE_BMP; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = IMAGE_SVG; } else { snprintf(errbuf, errlen, "Unknown format"); fa_close(fh); return NULL; } int64_t s = fa_fsize(fh); if(s < 0) { snprintf(errbuf, errlen, "Can't read from non-seekable file"); fa_close(fh); return NULL; } void *ptr; img = image_coded_alloc(&ptr, s, fmt); if(img == NULL) { snprintf(errbuf, errlen, "Out of memory"); fa_close(fh); return NULL; } img->im_width = width; img->im_height = height; img->im_orientation = orientation; fa_seek(fh, SEEK_SET, 0); r = fa_read(fh, ptr, s); fa_close(fh); if(r != s) { image_release(img); snprintf(errbuf, errlen, "Read error"); return NULL; } return img; }
static int cookie_close(void *fh) { fa_close(fh); return 0; }