static void ifv_autoclose(callout_t *c, void *aux) { if(hts_mutex_trylock(&image_from_video_mutex[1])) { callout_arm(&thumb_flush_callout, ifv_autoclose, NULL, 5); } else { TRACE(TRACE_DEBUG, "Thumb", "Closing movie for thumb sources"); ifv_close(); hts_mutex_unlock(&image_from_video_mutex[1]); } }
static pixmap_t * fa_image_from_video2(const char *url, const image_meta_t *im, const char *cacheid, char *errbuf, size_t errlen, int sec, time_t mtime, cancellable_t *c) { pixmap_t *pm = NULL; if(ifv_url == NULL || strcmp(url, ifv_url)) { // Need to open int i; AVFormatContext *fctx; fa_handle_t *fh = fa_open_ex(url, errbuf, errlen, FA_BUFFERED_BIG, NULL); if(fh == NULL) return NULL; AVIOContext *avio = fa_libav_reopen(fh, 0); if((fctx = fa_libav_open_format(avio, url, NULL, 0, NULL, 0, 0, 0)) == NULL) { fa_libav_close(avio); snprintf(errbuf, errlen, "Unable to open format"); return NULL; } if(!strcmp(fctx->iformat->name, "avi")) fctx->flags |= AVFMT_FLAG_GENPTS; AVCodecContext *ctx = NULL; for(i = 0; i < fctx->nb_streams; i++) { if(fctx->streams[i]->codec != NULL && fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { ctx = fctx->streams[i]->codec; break; } } if(ctx == NULL) { fa_libav_close_format(fctx); return NULL; } AVCodec *codec = avcodec_find_decoder(ctx->codec_id); if(codec == NULL) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to find codec"); return NULL; } if(avcodec_open2(ctx, codec, NULL) < 0) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to open codec"); return NULL; } ifv_close(); ifv_stream = i; ifv_url = strdup(url); ifv_fctx = fctx; ifv_ctx = ctx; } AVPacket pkt; AVFrame *frame = av_frame_alloc(); int got_pic; AVStream *st = ifv_fctx->streams[ifv_stream]; int64_t ts = av_rescale(sec, st->time_base.den, st->time_base.num); if(av_seek_frame(ifv_fctx, ifv_stream, ts, AVSEEK_FLAG_BACKWARD) < 0) { ifv_close(); snprintf(errbuf, errlen, "Unable to seek to %"PRId64, ts); return NULL; } avcodec_flush_buffers(ifv_ctx); #define MAX_FRAME_SCAN 500 int cnt = MAX_FRAME_SCAN; while(1) { int r; r = av_read_frame(ifv_fctx, &pkt); if(r == AVERROR(EAGAIN)) continue; if(r == AVERROR_EOF) break; if(cancellable_is_cancelled(c)) { snprintf(errbuf, errlen, "Cancelled"); av_free_packet(&pkt); break; } if(r != 0) { ifv_close(); break; } if(pkt.stream_index != ifv_stream) { av_free_packet(&pkt); continue; } cnt--; int want_pic = pkt.pts >= ts || cnt <= 0; ifv_ctx->skip_frame = want_pic ? AVDISCARD_DEFAULT : AVDISCARD_NONREF; avcodec_decode_video2(ifv_ctx, frame, &got_pic, &pkt); av_free_packet(&pkt); if(got_pic == 0 || !want_pic) { continue; } int w,h; if(im->im_req_width != -1 && im->im_req_height != -1) { w = im->im_req_width; h = im->im_req_height; } else if(im->im_req_width != -1) { w = im->im_req_width; h = im->im_req_width * ifv_ctx->height / ifv_ctx->width; } else if(im->im_req_height != -1) { w = im->im_req_height * ifv_ctx->width / ifv_ctx->height; h = im->im_req_height; } else { w = im->im_req_width; h = im->im_req_height; } pm = pixmap_create(w, h, PIXMAP_BGR32, 0); if(pm == NULL) { ifv_close(); snprintf(errbuf, errlen, "Out of memory"); av_free(frame); return NULL; } struct SwsContext *sws; sws = sws_getContext(ifv_ctx->width, ifv_ctx->height, ifv_ctx->pix_fmt, w, h, AV_PIX_FMT_BGR32, SWS_BILINEAR, NULL, NULL, NULL); if(sws == NULL) { ifv_close(); snprintf(errbuf, errlen, "Scaling failed"); pixmap_release(pm); av_free(frame); return NULL; } uint8_t *ptr[4] = {0,0,0,0}; int strides[4] = {0,0,0,0}; ptr[0] = pm->pm_pixels; strides[0] = pm->pm_linesize; sws_scale(sws, (const uint8_t **)frame->data, frame->linesize, 0, ifv_ctx->height, ptr, strides); sws_freeContext(sws); write_thumb(ifv_ctx, frame, w, h, cacheid, mtime); break; } av_frame_free(&frame); if(pm == NULL) snprintf(errbuf, errlen, "Frame not found (scanned %d)", MAX_FRAME_SCAN - cnt); avcodec_flush_buffers(ifv_ctx); callout_arm(&thumb_flush_callout, ifv_autoclose, NULL, 5); return pm; }
static pixmap_t * fa_image_from_video2(const char *url, const image_meta_t *im, const char *cacheid, char *errbuf, size_t errlen, int sec, time_t mtime, fa_load_cb_t *cb, void *opaque) { pixmap_t *pm = NULL; if(ifv_url == NULL || strcmp(url, ifv_url)) { // Need to open int i; AVFormatContext *fctx; fa_handle_t *fh = fa_open_ex(url, errbuf, errlen, FA_BUFFERED_BIG, NULL); if(fh == NULL) return NULL; AVIOContext *avio = fa_libav_reopen(fh); if((fctx = fa_libav_open_format(avio, url, NULL, 0, NULL)) == NULL) { fa_libav_close(avio); snprintf(errbuf, errlen, "Unable to open format"); return NULL; } if(!strcmp(fctx->iformat->name, "avi")) fctx->flags |= AVFMT_FLAG_GENPTS; AVCodecContext *ctx = NULL; for(i = 0; i < fctx->nb_streams; i++) { if(fctx->streams[i]->codec != NULL && fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { ctx = fctx->streams[i]->codec; break; } } if(ctx == NULL) { fa_libav_close_format(fctx); return NULL; } AVCodec *codec = avcodec_find_decoder(ctx->codec_id); if(codec == NULL) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to find codec"); return NULL; } if(avcodec_open(ctx, codec) < 0) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to open codec"); return NULL; } ifv_close(); ifv_stream = i; ifv_url = strdup(url); ifv_fctx = fctx; ifv_ctx = ctx; } AVPacket pkt; AVFrame *frame = avcodec_alloc_frame(); int got_pic; AVStream *st = ifv_fctx->streams[ifv_stream]; int64_t ts = av_rescale(sec, st->time_base.den, st->time_base.num); if(av_seek_frame(ifv_fctx, ifv_stream, ts, AVSEEK_FLAG_BACKWARD) < 0) { ifv_close(); snprintf(errbuf, errlen, "Unable to seek to %"PRId64, ts); return NULL; } avcodec_flush_buffers(ifv_ctx); #define MAX_FRAME_SCAN 500 int cnt = MAX_FRAME_SCAN; while(1) { int r; r = av_read_frame(ifv_fctx, &pkt); if(r == AVERROR(EAGAIN)) continue; if(r == AVERROR_EOF) { break; } if(cb != NULL && cb(opaque, 0, 1)) { snprintf(errbuf, errlen, "Aborted"); break; } if(r != 0) { ifv_close(); break; } if(pkt.stream_index != ifv_stream) { av_free_packet(&pkt); continue; } cnt--; int want_pic = pkt.pts >= ts || cnt <= 0; ifv_ctx->skip_frame = want_pic ? AVDISCARD_DEFAULT : AVDISCARD_NONREF; avcodec_decode_video2(ifv_ctx, frame, &got_pic, &pkt); av_free_packet(&pkt); if(got_pic == 0 || !want_pic) { continue; } int w,h; if(im->im_req_width != -1 && im->im_req_height != -1) { w = im->im_req_width; h = im->im_req_height; } else if(im->im_req_width != -1) { w = im->im_req_width; h = im->im_req_width * ifv_ctx->height / ifv_ctx->width; } else if(im->im_req_height != -1) { w = im->im_req_height * ifv_ctx->width / ifv_ctx->height; h = im->im_req_height; } else { w = im->im_req_width; h = im->im_req_height; } pm = pixmap_create(w, h, PIXMAP_RGB24, #ifdef __PPC__ 16 #else 1 #endif ); if(pm == NULL) { ifv_close(); snprintf(errbuf, errlen, "Out of memory"); return NULL; } struct SwsContext *sws; sws = sws_getContext(ifv_ctx->width, ifv_ctx->height, ifv_ctx->pix_fmt, w, h, PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL); if(sws == NULL) { ifv_close(); snprintf(errbuf, errlen, "Scaling failed"); pixmap_release(pm); return NULL; } uint8_t *ptr[4] = {0,0,0,0}; int strides[4] = {0,0,0,0}; ptr[0] = pm->pm_pixels; strides[0] = pm->pm_linesize; sws_scale(sws, (const uint8_t **)frame->data, frame->linesize, 0, ifv_ctx->height, ptr, strides); sws_freeContext(sws); if(pngencoder != NULL) { AVFrame *oframe = avcodec_alloc_frame(); memset(&frame, 0, sizeof(frame)); oframe->data[0] = pm->pm_pixels; oframe->linesize[0] = pm->pm_linesize; size_t outputsize = MAX(pm->pm_linesize * h, FF_MIN_BUFFER_SIZE); void *output = malloc(outputsize); pngencoder->width = w; pngencoder->height = h; pngencoder->pix_fmt = PIX_FMT_RGB24; r = avcodec_encode_video(pngencoder, output, outputsize, oframe); if(r > 0) blobcache_put(cacheid, "videothumb", output, r, INT32_MAX, NULL, mtime); free(output); av_free(oframe); } break; } av_free(frame); if(pm == NULL) snprintf(errbuf, errlen, "Frame not found (scanned %d)", MAX_FRAME_SCAN - cnt); return pm; }
static image_t * fa_image_from_video2(const char *url, const image_meta_t *im, const char *cacheid, char *errbuf, size_t errlen, int sec, time_t mtime, cancellable_t *c) { image_t *img = NULL; if(ifv_url == NULL || strcmp(url, ifv_url)) { // Need to open int i; AVFormatContext *fctx; fa_handle_t *fh = fa_open_ex(url, errbuf, errlen, FA_BUFFERED_BIG, NULL); if(fh == NULL) return NULL; AVIOContext *avio = fa_libav_reopen(fh, 0); if((fctx = fa_libav_open_format(avio, url, NULL, 0, NULL, 0, 0, 0)) == NULL) { fa_libav_close(avio); snprintf(errbuf, errlen, "Unable to open format"); return NULL; } if(!strcmp(fctx->iformat->name, "avi")) fctx->flags |= AVFMT_FLAG_GENPTS; AVCodecContext *ctx = NULL; int vstream = 0; for(i = 0; i < fctx->nb_streams; i++) { AVStream *st = fctx->streams[i]; AVCodecContext *c = st->codec; AVDictionaryEntry *mt; if(c == NULL) continue; switch(c->codec_type) { case AVMEDIA_TYPE_VIDEO: if(ctx == NULL) { vstream = i; ctx = fctx->streams[i]->codec; } break; case AVMEDIA_TYPE_ATTACHMENT: mt = av_dict_get(st->metadata, "mimetype", NULL, AV_DICT_IGNORE_SUFFIX); if(sec == -1 && mt != NULL && (!strcmp(mt->value, "image/jpeg") || !strcmp(mt->value, "image/png"))) { int64_t offset = st->attached_offset; int size = st->attached_size; fa_libav_close_format(fctx); return thumb_from_attachment(url, offset, size, errbuf, errlen, cacheid, mtime); } break; default: break; } } if(ctx == NULL) { fa_libav_close_format(fctx); return NULL; } AVCodec *codec = avcodec_find_decoder(ctx->codec_id); if(codec == NULL) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to find codec"); return NULL; } if(avcodec_open2(ctx, codec, NULL) < 0) { fa_libav_close_format(fctx); snprintf(errbuf, errlen, "Unable to open codec"); return NULL; } ifv_close(); ifv_stream = vstream; ifv_url = strdup(url); ifv_fctx = fctx; ifv_ctx = ctx; } AVPacket pkt; AVFrame *frame = av_frame_alloc(); int got_pic; #define MAX_FRAME_SCAN 500 int cnt = MAX_FRAME_SCAN; AVStream *st = ifv_fctx->streams[ifv_stream]; if(sec == -1) { // Automatically try to find a good frame int duration_in_seconds = ifv_fctx->duration / 1000000; sec = MAX(1, duration_in_seconds * 0.05); // 5% of duration sec = MIN(sec, 150); // , buy no longer than 2:30 in sec = MAX(0, MIN(sec, duration_in_seconds - 1)); cnt = 1; } int64_t ts = av_rescale(sec, st->time_base.den, st->time_base.num); int delayed_seek = 0; if(ifv_ctx->codec_id == AV_CODEC_ID_RV40 || ifv_ctx->codec_id == AV_CODEC_ID_RV30) { // Must decode one frame delayed_seek = 1; } else { if(av_seek_frame(ifv_fctx, ifv_stream, ts, AVSEEK_FLAG_BACKWARD) < 0) { ifv_close(); snprintf(errbuf, errlen, "Unable to seek to %"PRId64, ts); return NULL; } } avcodec_flush_buffers(ifv_ctx); while(1) { int r; r = av_read_frame(ifv_fctx, &pkt); if(r == AVERROR(EAGAIN)) continue; if(r == AVERROR_EOF) break; if(cancellable_is_cancelled(c)) { snprintf(errbuf, errlen, "Cancelled"); av_free_packet(&pkt); break; } if(r != 0) { ifv_close(); break; } if(pkt.stream_index != ifv_stream) { av_free_packet(&pkt); continue; } cnt--; int want_pic = pkt.pts >= ts || cnt <= 0; ifv_ctx->skip_frame = want_pic ? AVDISCARD_DEFAULT : AVDISCARD_NONREF; avcodec_decode_video2(ifv_ctx, frame, &got_pic, &pkt); av_free_packet(&pkt); if(delayed_seek) { delayed_seek = 0; if(av_seek_frame(ifv_fctx, ifv_stream, ts, AVSEEK_FLAG_BACKWARD) < 0) { ifv_close(); break; } continue; } if(got_pic == 0 || !want_pic) { continue; } int w,h; if(im->im_req_width != -1 && im->im_req_height != -1) { w = im->im_req_width; h = im->im_req_height; } else if(im->im_req_width != -1) { w = im->im_req_width; h = im->im_req_width * ifv_ctx->height / ifv_ctx->width; } else if(im->im_req_height != -1) { w = im->im_req_height * ifv_ctx->width / ifv_ctx->height; h = im->im_req_height; } else { w = im->im_req_width; h = im->im_req_height; } pixmap_t *pm = pixmap_create(w, h, PIXMAP_BGR32, 0); if(pm == NULL) { ifv_close(); snprintf(errbuf, errlen, "Out of memory"); av_free(frame); return NULL; } struct SwsContext *sws; sws = sws_getContext(ifv_ctx->width, ifv_ctx->height, ifv_ctx->pix_fmt, w, h, AV_PIX_FMT_BGR32, SWS_BILINEAR, NULL, NULL, NULL); if(sws == NULL) { ifv_close(); snprintf(errbuf, errlen, "Scaling failed"); pixmap_release(pm); av_free(frame); return NULL; } uint8_t *ptr[4] = {0,0,0,0}; int strides[4] = {0,0,0,0}; ptr[0] = pm->pm_data; strides[0] = pm->pm_linesize; sws_scale(sws, (const uint8_t **)frame->data, frame->linesize, 0, ifv_ctx->height, ptr, strides); sws_freeContext(sws); write_thumb(ifv_ctx, frame, w, h, cacheid, mtime); img = image_create_from_pixmap(pm); pixmap_release(pm); break; } av_frame_free(&frame); if(img == NULL) snprintf(errbuf, errlen, "Frame not found (scanned %d)", MAX_FRAME_SCAN - cnt); if(ifv_ctx != NULL) { avcodec_flush_buffers(ifv_ctx); callout_arm(&thumb_flush_callout, ifv_autoclose, NULL, 5); } return img; }
static pixmap_t * fa_image_from_video2(const char *url0, const image_meta_t *im, const char *cacheid) { pixmap_t *pm = NULL; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); *tim++ = 0; if(ifv_url == NULL || strcmp(url, ifv_url)) { // Need to open int i; AVFormatContext *fctx; AVIOContext *avio; if((avio = fa_libav_open(url, 65536, NULL, 0, 0)) == NULL) return NULL; if((fctx = fa_libav_open_format(avio, url, NULL, 0, NULL)) == NULL) { fa_libav_close(avio); return NULL; } if(!strcmp(fctx->iformat->name, "avi")) fctx->flags |= AVFMT_FLAG_GENPTS; AVCodecContext *ctx = NULL; for(i = 0; i < fctx->nb_streams; i++) { if(fctx->streams[i]->codec != NULL && fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { ctx = fctx->streams[i]->codec; break; } } if(ctx == NULL) { fa_libav_close_format(fctx); return NULL; } AVCodec *codec = avcodec_find_decoder(ctx->codec_id); if(codec == NULL) { fa_libav_close_format(fctx); return NULL; } if(avcodec_open(ctx, codec) < 0) { fa_libav_close_format(fctx); return NULL; } ifv_close(); ifv_stream = i; ifv_url = strdup(url); ifv_fctx = fctx; ifv_ctx = ctx; } AVPacket pkt; AVFrame *frame = avcodec_alloc_frame(); int got_pic; int secs = atoi(tim); AVStream *st = ifv_fctx->streams[ifv_stream]; int64_t ts = av_rescale(secs, st->time_base.den, st->time_base.num); if(av_seek_frame(ifv_fctx, ifv_stream, ts, AVSEEK_FLAG_BACKWARD) < 0) { ifv_close(); return NULL; } avcodec_flush_buffers(ifv_ctx); int cnt = 500; while(1) { int r; r = av_read_frame(ifv_fctx, &pkt); if(r == AVERROR(EAGAIN)) continue; if(r == AVERROR_EOF) break; if(r != 0) { ifv_close(); break; } if(pkt.stream_index != ifv_stream) { av_free_packet(&pkt); continue; } cnt--; int want_pic = pkt.pts >= ts || cnt <= 0; ifv_ctx->skip_frame = want_pic ? AVDISCARD_DEFAULT : AVDISCARD_NONREF; avcodec_decode_video2(ifv_ctx, frame, &got_pic, &pkt); if(got_pic == 0 || !want_pic) continue; int w,h; if(im->req_width != -1 && im->req_height != -1) { w = im->req_width; h = im->req_height; } else if(im->req_width != -1) { w = im->req_width; h = im->req_width * ifv_ctx->height / ifv_ctx->width; } else if(im->req_height != -1) { w = im->req_height * ifv_ctx->width / ifv_ctx->height; h = im->req_height; } else { w = im->req_width; h = im->req_height; } pm = pixmap_create(w, h, PIX_FMT_RGB24); struct SwsContext *sws; sws = sws_getContext(ifv_ctx->width, ifv_ctx->height, ifv_ctx->pix_fmt, w, h, PIX_FMT_RGB24, SWS_LANCZOS, NULL, NULL, NULL); if(sws == NULL) { ifv_close(); return NULL; } uint8_t *ptr[4] = {0,0,0,0}; int strides[4] = {0,0,0,0}; ptr[0] = pm->pm_pixels; strides[0] = pm->pm_linesize; sws_scale(sws, (const uint8_t **)frame->data, frame->linesize, 0, ifv_ctx->height, ptr, strides); sws_freeContext(sws); if(pngencoder != NULL) { AVFrame *oframe = avcodec_alloc_frame(); memset(&frame, 0, sizeof(frame)); oframe->data[0] = pm->pm_pixels; oframe->linesize[0] = pm->pm_linesize; size_t outputsize = pm->pm_linesize * h; void *output = malloc(outputsize); pngencoder->width = w; pngencoder->height = h; pngencoder->pix_fmt = PIX_FMT_RGB24; r = avcodec_encode_video(pngencoder, output, outputsize, oframe); if(r > 0) blobcache_put(cacheid, "videothumb", output, outputsize, 86400 * 5); free(output); av_free(oframe); } break; } av_free(frame); return pm; }