static event_t * rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen) { RTMPPacket p = {0}; int pos = -1, ret; uint32_t dts; event_t *e = NULL; while(1) { if(pos == -1) { mp->mp_eof = 0; ret = RTMP_GetNextMediaPacket(r->r, &p); if(ret == 2) { /* Wait for queues to drain */ mp->mp_eof = 1; again: e = mp_wait_for_empty_queues(mp); if(e != NULL) { e = rtmp_process_event(r, e, NULL); if(e == NULL) goto again; } if(e == NULL) e = event_create_type(EVENT_EOF); break; } if(ret == 0) { int64_t restartpos = r->seekpos_video; if(cancellable_is_cancelled(mp->mp_cancellable)) { snprintf(errbuf, errlen, "Cancelled"); return NULL; } TRACE(TRACE_ERROR, "RTMP", "Disconnected"); sleep(1); if(restartpos == AV_NOPTS_VALUE) { snprintf(errbuf, errlen, "Giving up restart since nothing was decoded"); return NULL; } RTMP_Close(r->r); RTMP_Init(r->r, mp->mp_cancellable); memset(&p, 0, sizeof(p)); TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %ld", restartpos); if(!RTMP_SetupURL(r->r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP session"); return NULL; } if(!RTMP_Connect(r->r, NULL, errbuf, errlen, 5000)) { return NULL; } if(!RTMP_ConnectStream(r->r, 0)) { snprintf(errbuf, errlen, "Unable to stream RTMP session"); return NULL; } if(mp->mp_flags & MP_CAN_SEEK) RTMP_SendSeek(r->r, restartpos / 1000); continue; } dts = p.m_nTimeStamp; switch(p.m_packetType) { case RTMP_PACKET_TYPE_INFO: if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) { RTMPPacket_Free(&p); return NULL; } break; case RTMP_PACKET_TYPE_VIDEO: e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp); break; case RTMP_PACKET_TYPE_AUDIO: e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp); break; case 0x16: pos = 0; break; default: TRACE(TRACE_DEBUG, "RTMP", "Got unknown packet type %d\n", p.m_packetType); break; } if(pos == -1) RTMPPacket_Free(&p); } if(pos != -1) { if(pos + 11 < p.m_nBodySize) { uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1); if(pos + 11 + ds + 4 > p.m_nBodySize) { snprintf(errbuf, errlen, "Corrupt stream"); RTMPPacket_Free(&p); return NULL; } dts = AMF_DecodeInt24(p.m_body + pos + 4); dts |= (p.m_body[pos + 7] << 24); if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) { if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) { RTMPPacket_Free(&p); return NULL; } } else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) { e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp); } else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) { e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp); } else { TRACE(TRACE_DEBUG, "RTMP", "Got unknown packet type %d\n", p.m_body[pos]); } pos += 11 + ds + 4; } else { pos = -1; RTMPPacket_Free(&p); } } if(e != NULL) break; } return e; }
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 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; }