[FFmpeg-trac] #1977(undetermined:new): dshow hang when device is unplugged
FFmpeg
trac at avcodec.org
Thu Nov 29 13:54:10 CET 2012
#1977: dshow hang when device is unplugged
-------------------------------------+-------------------------------------
Reporter: DonMoir | Type: defect
Status: new | Priority: normal
Component: | Version:
undetermined | unspecified
Keywords: | Blocked By:
Blocking: | Reproduced by developer: 0
Analyzed by developer: 0 |
-------------------------------------+-------------------------------------
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,¶m1,¶m2,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;
}
}}}
--
Ticket URL: <https://ffmpeg.org/trac/ffmpeg/ticket/1977>
FFmpeg <http://ffmpeg.org>
FFmpeg issue tracker
More information about the FFmpeg-trac
mailing list