id	summary	reporter	owner	description	type	status	priority	component	version	resolution	keywords	cc	blockedby	blocking	reproduced	analyzed
1977	dshow hang when device is unplugged	DonMoir		"dshow.c doesn't do any event handling so when you unplug a currently opened device, it will hang in dshow_read_packet since the ctx->event will never be signaled.

See WaitForSingleObject (ctx->event,INFINITE) in dshow_read_packet.

Since there are not going to be anymore packets after the device is unplugged then ctx->event will not be signaled and it will wait forever.

All changes are in dshow.c I tested the following with 2 different cameras and still testing more.

struct dshow_ctx needs to change:

{{{
struct dshow_ctx {
    const AVClass *class;

    IGraphBuilder *graph;

    char *device_name[2];
    int video_device_number;
    int audio_device_number;

    int   list_options;
    int   list_devices;
    int   audio_buffer_size;

    IBaseFilter *device_filter[2];
    IPin        *device_pin[2];
    libAVFilter *capture_filter[2];
    libAVPin    *capture_pin[2];

    HANDLE mutex;
    HANDLE event;
    AVPacketList *pktl;

    int64_t curbufsize;
    unsigned int video_frame_num;

    IMediaControl *control;
+   IMediaEvent *media_event;
+   int eof; // signal to stop trying to read packets

    enum AVPixelFormat pixel_format;
    enum AVCodecID video_codec_id;
    char *framerate;

    int requested_width;
    int requested_height;
    AVRational requested_framerate;

    int sample_rate;
    int sample_size;
    int channels;
};

}}}

dshow_read_header changes. Adds code to retrieve IMediaEvent and fixes return value on open failure. (see ticket #1976)


{{{
static int dshow_read_header(AVFormatContext *avctx)
{
    struct dshow_ctx *ctx = avctx->priv_data;
    IGraphBuilder *graph = NULL;
    ICreateDevEnum *devenum = NULL;
    IMediaControl *control = NULL;
+   IMediaEvent *media_event = NULL;
    int ret = AVERROR(EIO);
    int r;

    if (!ctx->list_devices && !parse_device_name(avctx)) {
        av_log(avctx, AV_LOG_ERROR, ""Malformed dshow input string.\n"");
        goto error;
    }

    ctx->video_codec_id = avctx->video_codec_id ? avctx->video_codec_id
                                                : AV_CODEC_ID_RAWVIDEO;
    if (ctx->pixel_format != AV_PIX_FMT_NONE) {
        if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) {
            av_log(avctx, AV_LOG_ERROR, ""Pixel format may only be set when ""
                              ""video codec is not set or set to rawvideo\n"");
            ret = AVERROR(EINVAL);
            goto error;
        }
    }
    if (ctx->framerate) {
        r = av_parse_video_rate(&ctx->requested_framerate, ctx->framerate);
        if (r < 0) {
            av_log(avctx, AV_LOG_ERROR, ""Could not parse framerate '%s'.\n"", ctx->framerate);
            goto error;
        }
    }

    CoInitialize(0);

    r = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                         &IID_IGraphBuilder, (void **) &graph);
    if (r != S_OK) {
        av_log(avctx, AV_LOG_ERROR, ""Could not create capture graph.\n"");
        goto error;
    }
    ctx->graph = graph;

    r = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                         &IID_ICreateDevEnum, (void **) &devenum);
    if (r != S_OK) {
        av_log(avctx, AV_LOG_ERROR, ""Could not enumerate system devices.\n"");
        goto error;
    }

    if (ctx->list_devices) {
        av_log(avctx, AV_LOG_INFO, ""DirectShow video devices\n"");
        dshow_cycle_devices(avctx, devenum, VideoDevice, NULL);
        av_log(avctx, AV_LOG_INFO, ""DirectShow audio devices\n"");
        dshow_cycle_devices(avctx, devenum, AudioDevice, NULL);
        ret = AVERROR_EXIT;
        goto error;
    }
    if (ctx->list_options) {
        if (ctx->device_name[VideoDevice])
            dshow_list_device_options(avctx, devenum, VideoDevice);
        if (ctx->device_name[AudioDevice])
            dshow_list_device_options(avctx, devenum, AudioDevice);
        ret = AVERROR_EXIT;
        goto error;
    }

    if (ctx->device_name[VideoDevice]) {
        ret = dshow_open_device(avctx, devenum, VideoDevice);
        if (ret < 0)
            goto error;
        ret = dshow_add_device(avctx, VideoDevice);
        if (ret < 0)
            goto error;
    }
    if (ctx->device_name[AudioDevice]) {
        ret = dshow_open_device(avctx, devenum, AudioDevice);
        if (ret < 0)
            goto error;
        ret = dshow_add_device(avctx, AudioDevice);
        if (ret < 0)
            goto error;
    }

+   int ret = AVERROR(EIO); // fix return value

    ctx->mutex = CreateMutex(NULL, 0, NULL);
    if (!ctx->mutex) {
        av_log(avctx, AV_LOG_ERROR, ""Could not create Mutex\n"");
        goto error;
    }
    ctx->event = CreateEvent(NULL, 1, 0, NULL);
    if (!ctx->event) {
        av_log(avctx, AV_LOG_ERROR, ""Could not create Event\n"");
        goto error;
    }

    r = IGraphBuilder_QueryInterface(graph, &IID_IMediaControl, (void **) &control);
    if (r != S_OK) {
        av_log(avctx, AV_LOG_ERROR, ""Could not get media control.\n"");
        goto error;
    }
    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 = IMediaControl_Run(control);
    if (r == S_FALSE) {
        OAFilterState pfs;
        r = IMediaControl_GetState(control, 0, &pfs);
    }
    if (r != S_OK) {
        av_log(avctx, AV_LOG_ERROR, ""Could not run filter\n"");
        goto error;
    }

    ret = 0;

error:

    if (ret < 0)
        dshow_read_close(avctx);

    if (devenum)
        ICreateDevEnum_Release(devenum);

    return ret;
}
}}}

Adds check_event_code to check for device lost or other failures.


{{{
// note: EC_DEVICE_LOST is not defined in MinGW dshow headers.

+#ifndef EC_DEVICE_LOST
+#define EC_DEVICE_LOST 0x1f
+#endif
+
+static void check_event_code (struct dshow_ctx *ctx)
+{
+    long event_code, param1, param2;
+    while (IMediaEvent_GetEvent (ctx->media_event,&event_code,&param1,&param2,0)
+      != E_ABORT)
+    {
+        if (event_code == EC_COMPLETE || event_code == EC_DEVICE_LOST ||
+          event_code == EC_ERRORABORT)
+            ctx->eof = 1;
+        IMediaEvent_FreeEventParams (ctx->media_event,event_code,param1,param2);
+    }
+}
}}}

Changes dshow_read_packet to call check_event_code and to check ctx->eof


{{{
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) {
            *pkt = pktl->pkt;
            ctx->pktl = ctx->pktl->next;
            av_free(pktl);
            ctx->curbufsize -= pkt->size;
        }
        ResetEvent(ctx->event);
        ReleaseMutex(ctx->mutex);
        if (!pktl) {
-           if (s->flags & AVFMT_FLAG_NONBLOCK) {
-               return AVERROR(EAGAIN);
-           } else {
-               WaitForSingleObject(ctx->event, INFINITE);
-           }
+           if (s->flags & AVFMT_FLAG_NONBLOCK)
+               return AVERROR(EAGAIN);
+           else if (WaitForSingleObject(ctx->event, 200) == WAIT_TIMEOUT)
+                check_event_code (ctx);
        }
    }
    return ctx->eof ? -1 : pkt->size;
}
}}}

add check for ctx->eof in callback function as a sanity check


{{{
static void
callback(void *priv_data, int index, uint8_t *buf, int buf_size, int64_t time)
{
    AVFormatContext *s = (AVFormatContext *)priv_data;
    struct dshow_ctx *ctx = (struct dshow_ctx *)s->priv_data;
    AVPacketList **ppktl, *pktl_next;

    WaitForSingleObject(ctx->mutex, INFINITE);
-   if(shall_we_drop(s))
+   if(ctx->eof || shall_we_drop(s))
        goto fail;

    pktl_next = (AVPacketList *)av_mallocz(sizeof(AVPacketList));
    if(!pktl_next)
        goto fail;

    if(av_new_packet(&pktl_next->pkt, buf_size) < 0) {
        av_free(pktl_next);
        goto fail;
    }

    pktl_next->pkt.stream_index = index;
    pktl_next->pkt.pts = time;
    memcpy(pktl_next->pkt.data, buf, buf_size);

    for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next);
    *ppktl = pktl_next;

    ctx->curbufsize += buf_size;

    SetEvent(ctx->event);
    ReleaseMutex(ctx->mutex);

    return;
fail:
    ReleaseMutex(ctx->mutex);
    return;
}
}}}

release media_event in dshow_read_close and set ctx->eof for sanity.


{{{
static int
dshow_read_close(AVFormatContext *s)
{
    struct dshow_ctx *ctx = s->priv_data;
    AVPacketList *pktl;
+   ctx->eof = 1;
    if (ctx->control) {
        IMediaControl_Stop(ctx->control);
        IMediaControl_Release(ctx->control);
    }
+   if (ctx->media_event)
+       IMediaEvent_Release (ctx->media_event);

    if (ctx->graph) {
        IEnumFilters *fenum;
        int r;
        r = IGraphBuilder_EnumFilters(ctx->graph, &fenum);
        if (r == S_OK) {
            IBaseFilter *f;
            IEnumFilters_Reset(fenum);
            while (IEnumFilters_Next(fenum, 1, &f, NULL) == S_OK) {
                if (IGraphBuilder_RemoveFilter(ctx->graph, f) == S_OK)
                    IEnumFilters_Reset(fenum); /* When a filter is removed,
                                                * the list must be reset. */
                IBaseFilter_Release(f);
            }
            IEnumFilters_Release(fenum);
        }
        IGraphBuilder_Release(ctx->graph);
    }

    if (ctx->capture_pin[VideoDevice])
        libAVPin_Release(ctx->capture_pin[VideoDevice]);
    if (ctx->capture_pin[AudioDevice])
        libAVPin_Release(ctx->capture_pin[AudioDevice]);
    if (ctx->capture_filter[VideoDevice])
        libAVFilter_Release(ctx->capture_filter[VideoDevice]);
    if (ctx->capture_filter[AudioDevice])
        libAVFilter_Release(ctx->capture_filter[AudioDevice]);

    if (ctx->device_pin[VideoDevice])
        IPin_Release(ctx->device_pin[VideoDevice]);
    if (ctx->device_pin[AudioDevice])
        IPin_Release(ctx->device_pin[AudioDevice]);
    if (ctx->device_filter[VideoDevice])
        IBaseFilter_Release(ctx->device_filter[VideoDevice]);
    if (ctx->device_filter[AudioDevice])
        IBaseFilter_Release(ctx->device_filter[AudioDevice]);

    if (ctx->device_name[0])
        av_free(ctx->device_name[0]);
    if (ctx->device_name[1])
        av_free(ctx->device_name[1]);

    if(ctx->mutex)
        CloseHandle(ctx->mutex);
    if(ctx->event)
        CloseHandle(ctx->event);

    pktl = ctx->pktl;
    while (pktl) {
        AVPacketList *next = pktl->next;
        av_destruct_packet(&pktl->pkt);
        av_free(pktl);
        pktl = next;
    }

    return 0;
}
}}}






"	defect	closed	normal	avdevice	unspecified	fixed	win dshow				0	0
