From 190f6135b48a97dadd7586f154640bec6468df1b Mon Sep 17 00:00:00 2001 From: Ramiro Polla Date: Mon, 17 Dec 2012 03:44:12 -0200 Subject: [PATCH] dshow: handle events in graph Handling DirectShow events prevents infinite loops when there is an error in the graph, such as a device being disconnected. This makes it possible for dshow to return an error to the caller and run the cleanup code. Based on patch by Don Moir . --- libavdevice/dshow.c | 76 +++++++++++++++++++++++++++++++------ libavdevice/dshow_capture.h | 5 +++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/libavdevice/dshow.c b/libavdevice/dshow.c index d57323e46e..6f7e95b1bc 100644 --- a/libavdevice/dshow.c +++ b/libavdevice/dshow.c @@ -45,13 +45,17 @@ struct dshow_ctx { libAVPin *capture_pin[2]; HANDLE mutex; - HANDLE event; + HANDLE event[2]; /* event[0] is set by DirectShow + * event[1] is set by callback() */ AVPacketList *pktl; + int eof; + int64_t curbufsize; unsigned int video_frame_num; IMediaControl *control; + IMediaEvent *media_event; enum AVPixelFormat pixel_format; enum AVCodecID video_codec_id; @@ -118,6 +122,9 @@ dshow_read_close(AVFormatContext *s) IMediaControl_Release(ctx->control); } + if (ctx->media_event) + IMediaEvent_Release(ctx->media_event); + if (ctx->graph) { IEnumFilters *fenum; int r; @@ -161,8 +168,10 @@ dshow_read_close(AVFormatContext *s) if(ctx->mutex) CloseHandle(ctx->mutex); - if(ctx->event) - CloseHandle(ctx->event); + if(ctx->event[0]) + CloseHandle(ctx->event[0]); + if(ctx->event[1]) + CloseHandle(ctx->event[1]); pktl = ctx->pktl; while (pktl) { @@ -233,7 +242,7 @@ callback(void *priv_data, int index, uint8_t *buf, int buf_size, int64_t time) ctx->curbufsize += buf_size; - SetEvent(ctx->event); + SetEvent(ctx->event[1]); ReleaseMutex(ctx->mutex); return; @@ -868,6 +877,9 @@ static int dshow_read_header(AVFormatContext *avctx) IGraphBuilder *graph = NULL; ICreateDevEnum *devenum = NULL; IMediaControl *control = NULL; + IMediaEvent *media_event = NULL; + HANDLE media_event_handle; + HANDLE proc; int ret = AVERROR(EIO); int r; @@ -948,8 +960,8 @@ static int dshow_read_header(AVFormatContext *avctx) av_log(avctx, AV_LOG_ERROR, "Could not create Mutex\n"); goto error; } - ctx->event = CreateEvent(NULL, 1, 0, NULL); - if (!ctx->event) { + ctx->event[1] = CreateEvent(NULL, 1, 0, NULL); + if (!ctx->event[1]) { av_log(avctx, AV_LOG_ERROR, "Could not create Event\n"); goto error; } @@ -961,6 +973,26 @@ static int dshow_read_header(AVFormatContext *avctx) } ctx->control = control; + r = IGraphBuilder_QueryInterface(graph, &IID_IMediaEvent, (void **) &media_event); + if (r != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not get media event.\n"); + goto error; + } + ctx->media_event = media_event; + + r = IMediaEvent_GetEventHandle(media_event, (void *) &media_event_handle); + if (r != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not get media event handle.\n"); + goto error; + } + proc = GetCurrentProcess(); + r = DuplicateHandle(proc, media_event_handle, proc, &ctx->event[0], + 0, 0, DUPLICATE_SAME_ACCESS); + if (!r) { + av_log(avctx, AV_LOG_ERROR, "Could not duplicate media event handle.\n"); + goto error; + } + r = IMediaControl_Run(control); if (r == S_FALSE) { OAFilterState pfs; @@ -984,12 +1016,32 @@ error: return ret; } +/** + * Checks media events from DirectShow and returns -1 on error or EOF. Also + * purges all events that might be in the event queue to stop the trigger + * of event notification. + */ +static int dshow_check_event_queue(IMediaEvent *media_event) +{ + LONG_PTR p1, p2; + long code; + int ret = 0; + + while (IMediaEvent_GetEvent(media_event, &code, &p1, &p2, 0) != E_ABORT) { + if (code == EC_COMPLETE || code == EC_DEVICE_LOST || code == EC_ERRORABORT) + ret = -1; + IMediaEvent_FreeEventParams(media_event, code, p1, p2); + } + + return ret; +} + static int dshow_read_packet(AVFormatContext *s, AVPacket *pkt) { struct dshow_ctx *ctx = s->priv_data; AVPacketList *pktl = NULL; - while (!pktl) { + while (!ctx->eof && !pktl) { WaitForSingleObject(ctx->mutex, INFINITE); pktl = ctx->pktl; if (pktl) { @@ -998,18 +1050,20 @@ static int dshow_read_packet(AVFormatContext *s, AVPacket *pkt) av_free(pktl); ctx->curbufsize -= pkt->size; } - ResetEvent(ctx->event); + ResetEvent(ctx->event[1]); ReleaseMutex(ctx->mutex); if (!pktl) { - if (s->flags & AVFMT_FLAG_NONBLOCK) { + if (dshow_check_event_queue(ctx->media_event) < 0) { + ctx->eof = 1; + } else if (s->flags & AVFMT_FLAG_NONBLOCK) { return AVERROR(EAGAIN); } else { - WaitForSingleObject(ctx->event, INFINITE); + WaitForMultipleObjects(2, ctx->event, 0, INFINITE); } } } - return pkt->size; + return ctx->eof ? AVERROR(EIO) : pkt->size; } #define OFFSET(x) offsetof(struct dshow_ctx, x) diff --git a/libavdevice/dshow_capture.h b/libavdevice/dshow_capture.h index 2446c7cd5f..aff5019b30 100644 --- a/libavdevice/dshow_capture.h +++ b/libavdevice/dshow_capture.h @@ -32,6 +32,11 @@ #include #include +/* EC_DEVICE_LOST is not defined in MinGW dshow headers. */ +#ifndef EC_DEVICE_LOST +#define EC_DEVICE_LOST 0x1f +#endif + long ff_copy_dshow_media_type(AM_MEDIA_TYPE *dst, const AM_MEDIA_TYPE *src); void ff_print_VIDEO_STREAM_CONFIG_CAPS(const VIDEO_STREAM_CONFIG_CAPS *caps); void ff_print_AUDIO_STREAM_CONFIG_CAPS(const AUDIO_STREAM_CONFIG_CAPS *caps);