These changes allow testing Bluray IGS menu with avplay: - add support for display overlay pictures, - add support for openning a second input file as subpath, - add some basic navigation commands to simulate and view result of pages and buttons
One thing don't work well now, backup/restore of YUV buffer to allow display of main menu using still image (one GOP video or png source) that reach eof fast. This may be a SDL problem, another player using GL texture may work well. Signed-off-by: David Girault <[email protected]> --- avplay.c | 600 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 549 insertions(+), 51 deletions(-) diff --git a/avplay.c b/avplay.c index d291ba3..e64e4a7 100644 --- a/avplay.c +++ b/avplay.c @@ -117,6 +117,11 @@ typedef struct SubPicture { AVSubtitle sub; } SubPicture; +typedef struct OverlayPicture { + double pts; /* presentation time stamp for this picture */ + AVOverlay overlay; +} OverlayPicture; + enum { AV_SYNC_AUDIO_MASTER, /* default choice */ AV_SYNC_VIDEO_MASTER, @@ -187,6 +192,23 @@ typedef struct VideoState { SDL_mutex *subpq_mutex; SDL_cond *subpq_cond; + char overlay_subpath[1024]; + int overlay_use_subpath; + AVFormatContext *overlay_ic; + + SDL_Thread *overlay_tid; + int overlay_stream; + int overlay_stream_changed; + AVStream *overlay_st; + SDL_Overlay *overlay_bmp; ///< Used to display an overlay picture when no new video picture is available + double overlay_last_pts; + PacketQueue overlayq; + OverlayPicture ovepq[SUBPICTURE_QUEUE_SIZE]; + int ovepq_size, ovepq_rindex, ovepq_windex; + SDL_mutex *ovepq_mutex; + SDL_cond *ovepq_cond; + + double aspect_ratio; ///< calculated when video picture is displayed double frame_timer; double frame_last_pts; double frame_last_delay; @@ -207,6 +229,7 @@ typedef struct VideoState { // QETimer *video_timer; char filename[1024]; + int width, height, xleft, ytop; PtsCorrectionContext pts_ctx; @@ -221,10 +244,12 @@ typedef struct VideoState { } VideoState; static void show_help(void); +static void stream_handle_event(VideoState *is, SDL_Event *event); /* options specified by the user */ static AVInputFormat *file_iformat; static const char *input_filename; +static const char *subpath_filename; static const char *window_title; static int fs_screen_width; static int fs_screen_height; @@ -236,6 +261,7 @@ static int wanted_stream[AVMEDIA_TYPE_NB] = { [AVMEDIA_TYPE_AUDIO] = -1, [AVMEDIA_TYPE_VIDEO] = -1, [AVMEDIA_TYPE_SUBTITLE] = -1, + [AVMEDIA_TYPE_OVERLAY] = -1, }; static int seek_by_bytes = -1; static int display_disable; @@ -438,7 +464,9 @@ static inline void fill_rectangle(SDL_Surface *screen, #define BPP 1 -static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, int imgh) +static void blend_rect(AVPicture *dst, int imgw, int imgh, + int rw, int rh, int rx, int ry, + Uint8 *pixels, Uint8 *palette, int linesize) { int wrap, wrap3, width2, skip2; int y, u, v, a, u1, v1, a1, w, h; @@ -447,10 +475,10 @@ static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, const uint32_t *pal; int dstx, dsty, dstw, dsth; - dstw = av_clip(rect->w, 0, imgw); - dsth = av_clip(rect->h, 0, imgh); - dstx = av_clip(rect->x, 0, imgw - dstw); - dsty = av_clip(rect->y, 0, imgh - dsth); + dstw = av_clip(rw, 0, imgw); + dsth = av_clip(rh, 0, imgh); + dstx = av_clip(rx, 0, imgw - dstw); + dsty = av_clip(ry, 0, imgh - dsth); lum = dst->data[0] + dsty * dst->linesize[0]; cb = dst->data[1] + (dsty >> 1) * dst->linesize[1]; cr = dst->data[2] + (dsty >> 1) * dst->linesize[2]; @@ -458,9 +486,9 @@ static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, width2 = ((dstw + 1) >> 1) + (dstx & ~dstw & 1); skip2 = dstx >> 1; wrap = dst->linesize[0]; - wrap3 = rect->pict.linesize[0]; - p = rect->pict.data[0]; - pal = (const uint32_t *)rect->pict.data[1]; /* Now in YCrCb! */ + wrap3 = linesize; + p = pixels; + pal = (const uint32_t *)palette; /* Now in YCrCb! */ if (dsty & 1) { lum += dstx; @@ -638,17 +666,177 @@ static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, } } +static inline void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, int imgh) +{ + blend_rect(dst, imgw, imgh, + rect->w, rect->h, rect->x, rect->y, + rect->pict.data[0], + rect->pict.data[1], + rect->pict.linesize[0]); +} + +static inline void blend_overlayrect(AVPicture *dst, const AVOverlayRect *rect, int imgw, int imgh) +{ + blend_rect(dst, imgw, imgh, + rect->w, rect->h, rect->x, rect->y, + rect->pict.data[0], + rect->pict.data[1], + rect->pict.linesize[0]); +} + static void free_subpicture(SubPicture *sp) { avsubtitle_free(&sp->sub); } +static void free_overlaypicture(OverlayPicture *op) +{ + avoverlay_free(&op->overlay); +} + + +static inline void overlay_img_backup(SDL_Overlay *bmp, Uint8 **bakpixels) +{ + int i; + for (i=0;i<bmp->planes;i++) { + bakpixels[i] = av_malloc(bmp->pitches[i]); + memcpy(bakpixels[i], bmp->pixels[i], bmp->pitches[i]); + } +} +static inline void overlay_img_restore(SDL_Overlay *bmp, Uint8 **bakpixels) +{ + int i; + for (i=0;i<bmp->planes;i++) { + memcpy(bmp->pixels[i], bakpixels[i], bmp->pitches[i]); + av_freep(&bakpixels[i]); + } +} + +static void overlay_image_display(VideoState *is, SDL_Overlay *bmp, SDL_Rect *rectp) +{ + // if rectp not NULL, we are called from video_image_display, after video picture was displayed. + // need to backup altered surfaces but don't require to restore previous saved surfaces. + // if rectp is NULL, we need to restore previous saved surfaces before drawing new overlay pic. + OverlayPicture *sp; + AVPicture pict; + SDL_Rect rect; + int i, deleted=0; + + if (rectp == NULL) { + int width, height, x, y; + // + height = is->height; + width = ((int)rint(height * is->aspect_ratio)) & ~1; + if (width > is->width) { + width = is->width; + height = ((int)rint(width / is->aspect_ratio)) & ~1; + } + x = (is->width - width) / 2; + y = (is->height - height) / 2; + rect.x = is->xleft + x; + rect.y = is->ytop + y; + rect.w = width; + rect.h = height; + rectp = ▭ + } + + if (is->overlay_st) + { + int num = 2; // number of overlay pictures to keep + 1 + if (is->overlay_stream_changed) num--; // purge all on changed stream + + while (is->ovepq_size >= num) { + if (is->ovepq_size == num && !bmp && num == 1) { + /* previous displayed overlay pictures need to be removed from display */ + fprintf(stderr, "Remove older overlay picture by restoring original saved one: %dx%d@%d:%d\n", rectp->w,rectp->h,rectp->x,rectp->y); + SDL_DisplayYUVOverlay(is->overlay_bmp, rectp); + } + free_overlaypicture(&is->ovepq[is->ovepq_rindex]); + /* update queue size and signal for next picture */ + if (++is->ovepq_rindex == SUBPICTURE_QUEUE_SIZE) + is->ovepq_rindex = 0; + is->ovepq_size--; + deleted++; + } + + if (is->ovepq_size > 0) + { + sp = &is->ovepq[is->ovepq_rindex]; + + /*if (is->video_current_pts > (sp->pts + ((float) sp->overlay.end_display_time / 1000))) { + if (!bmp) { + // restore last backup + fprintf(stderr, "Overlay picture timed-out, !\n"); + SDL_DisplayYUVOverlay(is->overlay_bmp, rectp); + } + else { + // do nothing, caller will display unmodified picture + } + } + else */ + if (is->video_current_pts >= sp->pts + ((float) sp->overlay.start_display_time / 1000)) + { + Uint8 *bakpixels[3]; // 3 pixels planes max + if (bmp) { + // we have a new source, backup it then use it directly + //fprintf(stderr, "Display new overlay picture on new video picture (%dx%d@%d:%d)\n", rectp->w,rectp->h,rectp->x,rectp->y); + SDL_LockYUVOverlay(bmp); + overlay_img_backup(bmp, bakpixels); // first, save unmodified picture + + // apply overlay pictures + pict.data[0] = bmp->pixels[0]; + pict.data[1] = bmp->pixels[2]; + pict.data[2] = bmp->pixels[1]; + pict.linesize[0] = bmp->pitches[0]; + pict.linesize[1] = bmp->pitches[2]; + pict.linesize[2] = bmp->pitches[1]; + for (i = 0; i < sp->overlay.num_rects; i++) + blend_overlayrect(&pict, sp->overlay.rects[i], bmp->w, bmp->h); + SDL_UnlockYUVOverlay (bmp); + + // modified bmp displayed by caller, don't do it now + + // store backup in our overlay_bmp + SDL_LockYUVOverlay(is->overlay_bmp); + overlay_img_restore(is->overlay_bmp, bakpixels); + SDL_UnlockYUVOverlay(is->overlay_bmp); + is->overlay_last_pts = is->video_current_pts; + } + else if (deleted) { + // we don't have a source, duplicate backup and use it + fprintf(stderr, "Display new overlay picture on old video picture (%dx%d@%d:%d)\n", rectp->w,rectp->h,rectp->x,rectp->y); + SDL_LockYUVOverlay (is->overlay_bmp); + overlay_img_backup(is->overlay_bmp, bakpixels); + + // apply overlay pictures + pict.data[0] = is->overlay_bmp->pixels[0]; + pict.data[1] = is->overlay_bmp->pixels[2]; + pict.data[2] = is->overlay_bmp->pixels[1]; + pict.linesize[0] = is->overlay_bmp->pitches[0]; + pict.linesize[1] = is->overlay_bmp->pitches[2]; + pict.linesize[2] = is->overlay_bmp->pitches[1]; + for (i = 0; i < sp->overlay.num_rects; i++) + blend_overlayrect(&pict, sp->overlay.rects[i], is->overlay_bmp->w, is->overlay_bmp->h); + SDL_UnlockYUVOverlay (is->overlay_bmp); + + // display it right now + SDL_DisplayYUVOverlay(is->overlay_bmp, rectp); + + // restore backup in our overlay_bmp + SDL_LockYUVOverlay (is->overlay_bmp); + overlay_img_restore(is->overlay_bmp, bakpixels); + SDL_UnlockYUVOverlay (is->overlay_bmp); + } + } + } + } +} + static void video_image_display(VideoState *is) { VideoPicture *vp; SubPicture *sp; AVPicture pict; - float aspect_ratio; int width, height, x, y; SDL_Rect rect; int i; @@ -657,22 +845,22 @@ static void video_image_display(VideoState *is) if (vp->bmp) { #if CONFIG_AVFILTER if (vp->picref->video->pixel_aspect.num == 0) - aspect_ratio = 0; + is->aspect_ratio = 0; else - aspect_ratio = av_q2d(vp->picref->video->pixel_aspect); + is->aspect_ratio = av_q2d(vp->picref->video->pixel_aspect); #else /* XXX: use variable in the frame */ if (is->video_st->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->sample_aspect_ratio); + is->aspect_ratio = av_q2d(is->video_st->sample_aspect_ratio); else if (is->video_st->codec->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio); + is->aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio); else - aspect_ratio = 0; + is->aspect_ratio = 0; #endif - if (aspect_ratio <= 0.0) - aspect_ratio = 1.0; - aspect_ratio *= (float)vp->width / (float)vp->height; + if (is->aspect_ratio <= 0.0) + is->aspect_ratio = 1.0; + is->aspect_ratio *= (float)vp->width / (float)vp->height; if (is->subtitle_st) { @@ -701,13 +889,12 @@ static void video_image_display(VideoState *is) } } - /* XXX: we suppose the screen has a 1.0 pixel ratio */ height = is->height; - width = ((int)rint(height * aspect_ratio)) & ~1; + width = ((int)rint(height * is->aspect_ratio)) & ~1; if (width > is->width) { width = is->width; - height = ((int)rint(width / aspect_ratio)) & ~1; + height = ((int)rint(width / is->aspect_ratio)) & ~1; } x = (is->width - width) / 2; y = (is->height - height) / 2; @@ -716,7 +903,11 @@ static void video_image_display(VideoState *is) rect.y = is->ytop + y; rect.w = width; rect.h = height; + + overlay_image_display(is, vp->bmp, &rect); + SDL_DisplayYUVOverlay(vp->bmp, &rect); + } } @@ -1086,7 +1277,7 @@ static void video_refresh_timer(void *opaque) if (is->video_st) { retry: if (is->pictq_size == 0) { - // nothing to do, no picture to display in the que + // nothing to do, no picture to display in the queue } else { double time = av_gettime() / 1000000.0; double next_target; @@ -1178,6 +1369,12 @@ retry: SDL_CondSignal(is->pictq_cond); SDL_UnlockMutex(is->pictq_mutex); } + + /* overlay may work even if no more video picture to display */ + if (is->overlay_st) { + overlay_image_display(is, NULL, NULL); + } + } else if (is->audio_st) { /* draw the next audio frame */ @@ -1191,7 +1388,7 @@ retry: if (show_status) { static int64_t last_time; int64_t cur_time; - int aqsize, vqsize, sqsize; + int aqsize, vqsize, sqsize, oqsize; double av_diff; cur_time = av_gettime(); @@ -1199,18 +1396,21 @@ retry: aqsize = 0; vqsize = 0; sqsize = 0; + oqsize = 0; if (is->audio_st) aqsize = is->audioq.size; if (is->video_st) vqsize = is->videoq.size; if (is->subtitle_st) sqsize = is->subtitleq.size; + if (is->overlay_st) + oqsize = is->overlayq.size; av_diff = 0; if (is->audio_st && is->video_st) av_diff = get_audio_clock(is) - get_video_clock(is); - printf("%7.2f A-V:%7.3f s:%3.1f aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \r", + printf("%7.2f A-V:%7.3f s:%3.1f aq=%5dKB vq=%5dKB sq=%5dB oq=%5dB f=%"PRId64"/%"PRId64" \r", get_master_clock(is), av_diff, FFMAX(is->skip_frames - 1, 0), aqsize / 1024, - vqsize / 1024, sqsize, is->pts_ctx.num_faulty_dts, is->pts_ctx.num_faulty_pts); + vqsize / 1024, sqsize, oqsize, is->pts_ctx.num_faulty_dts, is->pts_ctx.num_faulty_pts); fflush(stdout); last_time = cur_time; } @@ -1244,6 +1444,12 @@ static void stream_close(VideoState *is) SDL_DestroyCond(is->pictq_cond); SDL_DestroyMutex(is->subpq_mutex); SDL_DestroyCond(is->subpq_cond); + SDL_DestroyMutex(is->ovepq_mutex); + SDL_DestroyCond(is->ovepq_cond); + if (is->overlay_bmp) { + SDL_FreeYUVOverlay(is->overlay_bmp); + is->overlay_bmp = NULL; + } #if !CONFIG_AVFILTER if (is->img_convert_ctx) sws_freeContext(is->img_convert_ctx); @@ -1307,6 +1513,20 @@ static void alloc_picture(void *opaque) do_exit(); } + if (!is->overlay_bmp && is->overlay_stream>=0) { + is->overlay_bmp = SDL_CreateYUVOverlay(vp->width, vp->height, + SDL_YV12_OVERLAY, + screen); + if (!is->overlay_bmp || is->overlay_bmp->pitches[0] < vp->width) { + fprintf(stderr, "Error: the video system does not support an image\n" + "size of %dx%d pixels. Try using -vf \"scale=w:h\"\n" + "to reduce the image size.\n", vp->width, vp->height ); + do_exit(); + } + + } + + SDL_LockMutex(is->pictq_mutex); vp->allocated = 1; SDL_CondSignal(is->pictq_cond); @@ -1927,6 +2147,77 @@ static int subtitle_thread(void *arg) return 0; } +static int overlay_thread(void *arg) +{ + VideoState *is = arg; + OverlayPicture *sp; + AVPacket pkt1, *pkt = &pkt1; + int got_overlay; + double pts; + int i, j; + int r, g, b, y, u, v, a; + + for (;;) { + while (is->paused && !is->overlayq.abort_request) { + SDL_Delay(10); + } + if (packet_queue_get(&is->overlayq, pkt, 1) < 0) + break; + + if (pkt->data == flush_pkt.data) { + avcodec_flush_buffers(is->overlay_st->codec); + continue; + } + SDL_LockMutex(is->ovepq_mutex); + while (is->ovepq_size >= SUBPICTURE_QUEUE_SIZE && + !is->overlayq.abort_request) { + SDL_CondWait(is->ovepq_cond, is->ovepq_mutex); + } + SDL_UnlockMutex(is->ovepq_mutex); + + if (is->overlayq.abort_request) + return 0; + + sp = &is->ovepq[is->ovepq_windex]; + + /* NOTE: ipts is the PTS of the _first_ picture beginning in + this packet, if any */ + pts = 0; + if (pkt->pts != AV_NOPTS_VALUE) + pts = av_q2d(is->overlay_st->time_base) * pkt->pts; + + avcodec_decode_overlay2(is->overlay_st->codec, &sp->overlay, + &got_overlay, pkt); + + if (got_overlay && sp->overlay.format == 0) { + sp->pts = pts; + + /* RGBA->YUVA palette convertion */ + for (i = 0; i < sp->overlay.num_rects; i++) + { + for (j = 0; j < sp->overlay.rects[i]->nb_colors; j++) + { + RGBA_IN(r, g, b, a, (uint32_t*)sp->overlay.rects[i]->pict.data[1] + j); + y = RGB_TO_Y_CCIR(r, g, b); + u = RGB_TO_U_CCIR(r, g, b, 0); + v = RGB_TO_V_CCIR(r, g, b, 0); + YUVA_OUT((uint32_t*)sp->overlay.rects[i]->pict.data[1] + j, y, u, v, a); + } + } + + /* now we can update the picture count */ + if (++is->ovepq_windex == SUBPICTURE_QUEUE_SIZE) + is->ovepq_windex = 0; + + SDL_LockMutex(is->ovepq_mutex); + is->ovepq_size++; + SDL_UnlockMutex(is->ovepq_mutex); + } + av_free_packet(pkt); + } + return 0; +} + /* copy samples for viewing in editor window */ static void update_sample_display(VideoState *is, short *samples, int samples_size) { @@ -2217,17 +2508,22 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) static int stream_component_open(VideoState *is, int stream_index) { AVFormatContext *ic = is->ic; + AVFormatContext *sc = is->overlay_ic; AVCodecContext *avctx; AVCodec *codec; + AVStream *stream; SDL_AudioSpec wanted_spec, spec; AVDictionary *opts; AVDictionaryEntry *t = NULL; - if (stream_index < 0 || stream_index >= ic->nb_streams) + if (stream_index < 0 || stream_index >= (ic->nb_streams + (sc?sc->nb_streams:0))) return -1; - avctx = ic->streams[stream_index]->codec; - - opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index]); + if (stream_index >= ic->nb_streams) + stream = sc->streams[stream_index-ic->nb_streams]; + else + stream = ic->streams[stream_index]; + avctx = stream->codec; + opts = filter_codec_opts(codec_opts, avctx->codec_id, (stream_index>=ic->nb_streams ? sc : ic), stream); codec = avcodec_find_decoder(avctx->codec_id); avctx->debug_mv = debug_mv; @@ -2283,11 +2579,12 @@ static int stream_component_open(VideoState *is, int stream_index) is->resample_channel_layout = is->sdl_channel_layout; } - ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; + stream->discard = AVDISCARD_DEFAULT; + switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: is->audio_stream = stream_index; - is->audio_st = ic->streams[stream_index]; + is->audio_st = stream; is->audio_buf_size = 0; is->audio_buf_index = 0; @@ -2304,18 +2601,22 @@ static int stream_component_open(VideoState *is, int stream_index) break; case AVMEDIA_TYPE_VIDEO: is->video_stream = stream_index; - is->video_st = ic->streams[stream_index]; - + is->video_st = stream; packet_queue_init(&is->videoq); is->video_tid = SDL_CreateThread(video_thread, is); break; case AVMEDIA_TYPE_SUBTITLE: is->subtitle_stream = stream_index; - is->subtitle_st = ic->streams[stream_index]; + is->subtitle_st = stream; packet_queue_init(&is->subtitleq); - is->subtitle_tid = SDL_CreateThread(subtitle_thread, is); break; + case AVMEDIA_TYPE_OVERLAY: + is->overlay_stream = stream_index; + is->overlay_st = stream; + packet_queue_init(&is->overlayq); + is->overlay_tid = SDL_CreateThread(overlay_thread, is); + break; default: break; } @@ -2325,11 +2626,18 @@ static int stream_component_open(VideoState *is, int stream_index) static void stream_component_close(VideoState *is, int stream_index) { AVFormatContext *ic = is->ic; + AVFormatContext *sc = is->overlay_ic; AVCodecContext *avctx; + AVStream *stream; - if (stream_index < 0 || stream_index >= ic->nb_streams) + if (stream_index < 0 || stream_index >= (ic->nb_streams + (sc?sc->nb_streams:0))) return; - avctx = ic->streams[stream_index]->codec; + if (stream_index >= ic->nb_streams) + stream = sc->streams[stream_index-ic->nb_streams]; + else + stream = ic->streams[stream_index]; + + avctx = stream->codec; switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: @@ -2380,11 +2688,26 @@ static void stream_component_close(VideoState *is, int stream_index) packet_queue_end(&is->subtitleq); break; + case AVMEDIA_TYPE_OVERLAY: + packet_queue_abort(&is->overlayq); + + /* note: we also signal this mutex to make sure we deblock the + video thread in all cases */ + SDL_LockMutex(is->ovepq_mutex); + is->overlay_stream_changed = 1; + + SDL_CondSignal(is->ovepq_cond); + SDL_UnlockMutex(is->ovepq_mutex); + + SDL_WaitThread(is->overlay_tid, NULL); + + packet_queue_end(&is->overlayq); + break; default: break; } - ic->streams[stream_index]->discard = AVDISCARD_ALL; + stream->discard = AVDISCARD_ALL; avcodec_close(avctx); switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: @@ -2399,6 +2722,10 @@ static void stream_component_close(VideoState *is, int stream_index) is->subtitle_st = NULL; is->subtitle_stream = -1; break; + case AVMEDIA_TYPE_OVERLAY: + is->overlay_st = NULL; + is->overlay_stream = -1; + break; default: break; } @@ -2418,6 +2745,7 @@ static int decode_thread(void *arg) { VideoState *is = arg; AVFormatContext *ic = NULL; + AVFormatContext *sc = NULL; int err, i, ret; int st_index[AVMEDIA_TYPE_NB]; AVPacket pkt1, *pkt = &pkt1; @@ -2431,6 +2759,7 @@ static int decode_thread(void *arg) is->video_stream = -1; is->audio_stream = -1; is->subtitle_stream = -1; + is->overlay_stream = -1; global_video_state = is; @@ -2465,6 +2794,36 @@ static int decode_thread(void *arg) av_dict_free(&opts[i]); av_freep(&opts); + if (is->overlay_use_subpath) { + sc = avformat_alloc_context(); + sc->interrupt_callback.callback = decode_interrupt_cb; + err = avformat_open_input(&sc, is->overlay_subpath, NULL, &format_opts); + if (err < 0) { + print_error(is->overlay_subpath, err); + ret = -1; + goto fail; + } + if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { + av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key); + ret = AVERROR_OPTION_NOT_FOUND; + goto fail; + } + is->overlay_ic = sc; + + opts = setup_find_stream_info_opts(sc, codec_opts); + orig_nb_streams = sc->nb_streams; + + err = avformat_find_stream_info(sc, opts); + if (err < 0) { + fprintf(stderr, "%s: could not find codec parameters\n", is->filename); + ret = -1; + goto fail; + } + for (i = 0; i < orig_nb_streams; i++) + av_dict_free(&opts[i]); + av_freep(&opts); + } + if (ic->pb) ic->pb->eof_reached = 0; // FIXME hack, avplay maybe should not use url_feof() to test for the end @@ -2488,6 +2847,10 @@ static int decode_thread(void *arg) for (i = 0; i < ic->nb_streams; i++) ic->streams[i]->discard = AVDISCARD_ALL; + if (sc) + for (i = 0; i < sc->nb_streams; i++) + sc->streams[i]->discard = AVDISCARD_ALL; + if (!video_disable) st_index[AVMEDIA_TYPE_VIDEO] = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, @@ -2498,7 +2861,7 @@ static int decode_thread(void *arg) wanted_stream[AVMEDIA_TYPE_AUDIO], st_index[AVMEDIA_TYPE_VIDEO], NULL, 0); - if (!video_disable) + if (!video_disable) { st_index[AVMEDIA_TYPE_SUBTITLE] = av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, wanted_stream[AVMEDIA_TYPE_SUBTITLE], @@ -2506,8 +2869,35 @@ static int decode_thread(void *arg) st_index[AVMEDIA_TYPE_AUDIO] : st_index[AVMEDIA_TYPE_VIDEO]), NULL, 0); + if (!is->overlay_use_subpath) { + st_index[AVMEDIA_TYPE_OVERLAY] = + av_find_best_stream(ic, AVMEDIA_TYPE_OVERLAY, + wanted_stream[AVMEDIA_TYPE_OVERLAY], + (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? + st_index[AVMEDIA_TYPE_AUDIO] : + st_index[AVMEDIA_TYPE_VIDEO]), + NULL, 0); + } + else { + st_index[AVMEDIA_TYPE_OVERLAY] = + av_find_best_stream(sc, AVMEDIA_TYPE_OVERLAY, + wanted_stream[AVMEDIA_TYPE_OVERLAY], + (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? + st_index[AVMEDIA_TYPE_AUDIO] : + st_index[AVMEDIA_TYPE_VIDEO]), + NULL, 0); + // found one, adjust index + if (st_index[AVMEDIA_TYPE_OVERLAY] >= 0) { + st_index[AVMEDIA_TYPE_OVERLAY] += ic->nb_streams; + } + } + } + if (show_status) { av_dump_format(ic, 0, is->filename, 0); + if (is->overlay_use_subpath) { + av_dump_format(sc, 1, is->overlay_subpath, 0); + } } /* open the streams */ @@ -2529,6 +2919,10 @@ static int decode_thread(void *arg) stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]); } + if (st_index[AVMEDIA_TYPE_OVERLAY] >= 0) { + stream_component_open(is, st_index[AVMEDIA_TYPE_OVERLAY]); + } + if (is->video_stream < 0 && is->audio_stream < 0) { fprintf(stderr, "%s: could not open codecs\n", is->filename); ret = -1; @@ -2564,6 +2958,13 @@ static int decode_thread(void *arg) if (ret < 0) { fprintf(stderr, "%s: error while seeking\n", is->ic->filename); } else { + if (is->overlay_ic) { + // also seek subpath if + ret = avformat_seek_file(is->overlay_ic, -1, seek_min, seek_target, seek_max, is->seek_flags); + if (ret < 0) { + fprintf(stderr, "%s: error while seeking\n", is->ic->filename); + } + } if (is->audio_stream >= 0) { packet_queue_flush(&is->audioq); packet_queue_put(&is->audioq, &flush_pkt); @@ -2572,20 +2973,26 @@ static int decode_thread(void *arg) packet_queue_flush(&is->subtitleq); packet_queue_put(&is->subtitleq, &flush_pkt); } + if (is->overlay_stream >= 0) { + packet_queue_flush(&is->overlayq); + packet_queue_put(&is->overlayq, &flush_pkt); + } if (is->video_stream >= 0) { packet_queue_flush(&is->videoq); packet_queue_put(&is->videoq, &flush_pkt); } } + is->seek_req = 0; eof = 0; } /* if the queue are full, no need to read more */ - if ( is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE + if ( is->audioq.size + is->videoq.size + is->subtitleq.size + is->overlayq.size > MAX_QUEUE_SIZE || ( (is->audioq .size > MIN_AUDIOQ_SIZE || is->audio_stream < 0) && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0) - && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0))) { + && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0) + && (is->overlayq.nb_packets > MIN_FRAMES || is->overlay_stream < 0))) { /* wait 10 ms */ SDL_Delay(10); continue; @@ -2607,7 +3014,7 @@ static int decode_thread(void *arg) packet_queue_put(&is->audioq, pkt); } SDL_Delay(10); - if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) { + if (is->audioq.size + is->videoq.size + is->subtitleq.size + is->overlayq.size == 0) { if (loop != 1 && (!loop || --loop)) { stream_seek(cur_stream, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0); } else if (autoexit) { @@ -2638,9 +3045,33 @@ static int decode_thread(void *arg) packet_queue_put(&is->videoq, pkt); } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) { packet_queue_put(&is->subtitleq, pkt); + } else if (pkt->stream_index == is->overlay_stream && pkt_in_play_range) { + packet_queue_put(&is->overlayq, pkt); } else { av_free_packet(pkt); } + + if (is->overlay_ic) { + ret = av_read_frame(is->overlay_ic, pkt); + if (ret < 0) { + if (is->overlay_ic->pb && is->overlay_ic->pb->error) + break; + continue; + } + /* check if packet is in play range specified by user, then queue, otherwise discard */ + pkt_in_play_range = duration == AV_NOPTS_VALUE || + (pkt->pts - ic->streams[pkt->stream_index]->start_time) * + av_q2d(ic->streams[pkt->stream_index]->time_base) - + (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000 + <= ((double)duration / 1000000); + if (pkt->stream_index == (is->subtitle_stream-ic->nb_streams) && pkt_in_play_range) { + packet_queue_put(&is->subtitleq, pkt); + } else if (pkt->stream_index == (is->overlay_stream-ic->nb_streams) && pkt_in_play_range) { + packet_queue_put(&is->overlayq, pkt); + } else { + av_free_packet(pkt); + } + } } /* wait until the end */ while (!is->abort_request) { @@ -2659,9 +3090,14 @@ static int decode_thread(void *arg) stream_component_close(is, is->video_stream); if (is->subtitle_stream >= 0) stream_component_close(is, is->subtitle_stream); + if (is->overlay_stream >= 0) + stream_component_close(is, is->overlay_stream); if (is->ic) { avformat_close_input(&is->ic); } + if (is->overlay_ic) { + avformat_close_input(&is->overlay_ic); + } if (ret != 0) { SDL_Event event; @@ -2673,7 +3109,7 @@ static int decode_thread(void *arg) return 0; } -static VideoState *stream_open(const char *filename, AVInputFormat *iformat) +static VideoState *stream_open(const char *filename, const char *subpath, AVInputFormat *iformat) { VideoState *is; @@ -2681,6 +3117,10 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) if (!is) return NULL; av_strlcpy(is->filename, filename, sizeof(is->filename)); + if (subpath) { + av_strlcpy(is->overlay_subpath, subpath, sizeof(is->overlay_subpath)); + is->overlay_use_subpath = 1; + } is->iformat = iformat; is->ytop = 0; is->xleft = 0; @@ -2692,6 +3132,9 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) is->subpq_mutex = SDL_CreateMutex(); is->subpq_cond = SDL_CreateCond(); + is->ovepq_mutex = SDL_CreateMutex(); + is->ovepq_cond = SDL_CreateCond(); + is->av_sync_type = av_sync_type; is->parse_tid = SDL_CreateThread(decode_thread, is); if (!is->parse_tid) { @@ -2701,6 +3144,34 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) return is; } +static void stream_handle_event(VideoState *is, SDL_Event *event) +{ + AVPacket pkt1, *pkt = &pkt1; + char key = 0; + if (event != NULL && event->type == SDL_KEYDOWN) switch(event->key.keysym.sym) { + case SDLK_RETURN: key = 13; break; // activate current button + case SDLK_u: key = 'u'; break; // up + case SDLK_d: key = 'd'; break; // down + case SDLK_l: key = 'l'; break; // left + case SDLK_r: key = 'r'; break; // right + case SDLK_p: key = 'p'; break; // roll pages + } + + //pass key to decoder using a fake packet + if (is->overlay_stream >= 0) { + av_init_packet(pkt); + pkt->size = (key != 0 ? 4 : 3); + pkt->data = av_mallocz(pkt->size); + *(pkt->data) = 0x80; + *(pkt->data+1) = 0x00; + *(pkt->data+2) = (key != 0 ? 1 : 0); + if (key != 0) + *(char*)(pkt->data+3) = key; + pkt->stream_index = is->overlay_stream; + packet_queue_put(&is->overlayq, pkt); + } +} + static void stream_cycle_channel(VideoState *is, int codec_type) { AVFormatContext *ic = is->ic; @@ -2711,15 +3182,17 @@ static void stream_cycle_channel(VideoState *is, int codec_type) start_index = is->video_stream; else if (codec_type == AVMEDIA_TYPE_AUDIO) start_index = is->audio_stream; - else + else if (codec_type == AVMEDIA_TYPE_SUBTITLE) start_index = is->subtitle_stream; - if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE ? -1 : 0)) + else + start_index = is->overlay_stream; + if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE || codec_type == AVMEDIA_TYPE_OVERLAY ? -1 : 0)) return; stream_index = start_index; for (;;) { if (++stream_index >= is->ic->nb_streams) { - if (codec_type == AVMEDIA_TYPE_SUBTITLE) + if (codec_type == AVMEDIA_TYPE_SUBTITLE || codec_type == AVMEDIA_TYPE_OVERLAY) { stream_index = -1; goto the_end; @@ -2739,6 +3212,7 @@ static void stream_cycle_channel(VideoState *is, int codec_type) break; case AVMEDIA_TYPE_VIDEO: case AVMEDIA_TYPE_SUBTITLE: + case AVMEDIA_TYPE_OVERLAY: goto the_end; default: break; @@ -2815,7 +3289,6 @@ static void event_loop(void) case SDLK_f: toggle_full_screen(); break; - case SDLK_p: case SDLK_SPACE: toggle_pause(); break; @@ -2834,6 +3307,20 @@ static void event_loop(void) if (cur_stream) stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE); break; + case SDLK_o: + if (cur_stream) + stream_cycle_channel(cur_stream, AVMEDIA_TYPE_OVERLAY); + break; + case SDLK_RETURN: + case SDLK_u: + case SDLK_d: + case SDLK_l: + case SDLK_r: + case SDLK_p: + if (cur_stream) + stream_handle_event(cur_stream, &event); + break; + case SDLK_w: toggle_audio_display(); break; @@ -3084,24 +3571,35 @@ static void show_help(void) printf("\nWhile playing:\n" "q, ESC quit\n" "f toggle full screen\n" - "p, SPC pause\n" + "SPC pause\n" "a cycle audio channel\n" "v cycle video channel\n" "t cycle subtitle channel\n" + "o cycle overlay channel\n" "w show audio waves\n" "s activate frame-step mode\n" "left/right seek backward/forward 10 seconds\n" "down/up seek backward/forward 1 minute\n" "mouse click seek to percentage in file corresponding to fraction of width\n" + "u navigate 'up' in overlay\n" + "d navigate 'down' in overlay\n" + "l navigate 'left' in overlay\n" + "r navigate 'right' in overlay\n" + "ENTER activate selected overlay button\n" + "p toggle overlay display (popup menu)\n" ); } static void opt_input_file(void *optctx, const char *filename) { if (input_filename) { - fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n", - filename, input_filename); - exit(1); + if (subpath_filename) { + fprintf(stderr, "Argument '%s' provided as subpath filename, but '%s' was already specified.\n", + filename, subpath_filename); + exit(1); + } + subpath_filename = filename; + return; } if (!strcmp(filename, "-")) filename = "pipe:"; @@ -3167,7 +3665,7 @@ int main(int argc, char **argv) av_init_packet(&flush_pkt); flush_pkt.data = "FLUSH"; - cur_stream = stream_open(input_filename, file_iformat); + cur_stream = stream_open(input_filename, subpath_filename, file_iformat); event_loop(); -- 1.7.9.5 _______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
