Программа зависает после вызова Dispose()

Я работаю над проектом iOS с С#. Программа захватывает изображения с подключенной веб-камеры и отправляет их через Socket на iPhone/iPad. Все это отлично работает, и я могу успешно отобразить свой поток на устройстве.

Но когда клиент отключается, веб-камеру приходится выключать и в этой функции программа просто зависает. Никаких сообщений об ошибках и вызовов исключений... просто зависает! Я считаю, что это проблема с несколькими потоками, но, к сожалению, я не настолько опытен в С#, чтобы найти решение. Я надеюсь, что кто-то здесь может направить меня на правильный путь...

Код:
функция onImageCaptured:

public void OnImageCaptured(Touchless.Vision.Contracts.IFrameSource frameSource, Touchless.Vision.Contracts.Frame frame, double fps)
{
    _latestFrame = frame.Image;
    Console.WriteLine("OnImageCaptured");
    if (isConnected)
    {
        Console.WriteLine("OnImageCaptured - isConnected");
        byteArray = new byte[0];
        MemoryStream stream = new MemoryStream();

        _latestFrame.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
        stream.Close();
        byteArray = stream.ToArray();

        if (byteArray.Length > 0)
        {
            string eof = "<EOF>";
            byte[] eofByte = Encoding.ASCII.GetBytes(eof);
            Console.WriteLine("OnImageCaptured - sendStream");
            this.onDataSend(byteArray);
            this.onDataSend(eofByte);
            stream.Flush();
        }

        System.Diagnostics.Debugger.Log(0, "1", "\nByte Array Length: " + byteArray.Length.ToString());
    }
    pictureBoxDisplay.Invalidate();
}

Определено так в классе камеры:

public event EventHandler<CameraEventArgs> OnImageCaptured;

И сработало:

OnImageCaptured.Invoke(this, new CameraEventArgs(bitmap, fps));

Итак, эта функция, как я считаю, работает в отдельной угрозе, поскольку пользовательский интерфейс не блокируется при поступлении изображений.

Итак, затем отключение клиента обрабатывается в этой функции:

public void onDataSend(byte[] data)
{
    clientReady = false;
    try
    {
        socketWorker.Send(data);
    }
    catch (SocketException se)
    {
        isConnected = false;
        Console.WriteLine("Error: Data Write - SocketException");
        Console.WriteLine(se.ErrorCode.ToString());
        thrashOldCamera() // THIS FUNCTION HANGS THE PROGRAM !!
        onDisconnectServer();

        // onDisconnectServer();
    }
    catch (ObjectDisposedException)
    {
        isConnected = false;
        Console.WriteLine("Error: Data Write - ObjectDisposedException");
        // onDisconnectServer();
    }

}

Клиент отключается, вызывается thrashOldCamera(). Пока работает нормально! Теперь:

private void thrashOldCamera()
{
    Console.WriteLine("TrashOldCamera");
    // Trash the old camera
    if (_frameSource != null)
    {
        try
        {
            _frameSource.NewFrame -= OnImageCaptured;
            Console.WriteLine("TrashOldCamera - 1");
            _frameSource.Camera.Dispose(); // HERE IT HANGS. IT NEVER GOES PAST HERE !!!
            Console.WriteLine("TrashOldCamera - 2");
            setFrameSource(null);
            Console.WriteLine("TrashOldCamera - 3");
            pictureBoxDisplay.Paint -= new PaintEventHandler(drawLatestImage);
        }
        catch (Exception ex)
        {
            Console.WriteLine("End Trash Camera Ex: " + ex);
        }
    }
    Console.WriteLine("End Trash Camera");
}

Программа зависает на _frameSource.Camera.Dispose();. Как указано выше, нет ошибок или исключений. Проблема может заключаться в том, что onDataReceive() вызывается внутри функции onImageCapture(). Я также добавил в форму кнопку, которая запускает thrashOldCamera(), и она отлично работает.

Любая помощь/подсказки действительно ценятся.


person Pascal    schedule 02.05.2012    source источник
comment
channel9 .msdn.com/Серии/   -  person Lex Li    schedule 02.05.2012


Ответы (4)


Это называется взаимоблокировкой, типичной проблемой многопоточности. Я не вижу, чтобы вы явно обращались к потоку пользовательского интерфейса где-либо во фрагменте, поэтому взаимоблокировка, возможно, находится в самой прошивке камеры. Основная проблема заключается в том, что вы пытаетесь закрыть камеру, в то время как ее обратный вызов все еще выполняется, не так много кода, который устойчив к этому. Вызов Release() не может завершиться, пока не завершится обратный вызов. Но обратный вызов не может завершиться, пока не завершится вызов Release(). Тупик город.

Вам нужно будет реструктурировать свой код, чтобы этого не произошло. Отсрочка выпуска камеры является ключевым моментом, лучше всего делать это в той же ветке, которая открывала камеру. Вы, вероятно, решите это, например, выпустив его в событии FormClosed. Или вообще не выпускать его и оставлять на усмотрение Windows автоматическое закрытие дескрипторов.

person Hans Passant    schedule 02.05.2012

Не знаю, действительно ли это должен быть комментарий или ответ, но, по крайней мере, я могу направить вас по правильному пути.

Я нашел исходный код используемой вами библиотеки. Это содержимое Camera.Dispose()

public void Dispose()
{
    StopCapture();
}

Хорошо, тут мало что поможет, вот Camera.StopCapture()

internal void StopCapture()
{
    _cameraMethods.StopCamera();
}

Еще раз, мало что поможет. _cameraMethods — это тип CameraMethods, который исходит из библиотеки WebCamLib, которая является вспомогательной библиотекой C++ для связи с прямым показом.

Вот CameraMethods::StopCamera()

void CameraMethods::StopCamera()
{
    if (g_pMediaControl != NULL)
    {
        g_pMediaControl->Stop();
        g_pMediaControl->Release();
        g_pMediaControl = NULL;
    }

    g_pfnCaptureCallback = NULL;

    if (g_pIBaseFilterNullRenderer != NULL)
    {
        g_pIBaseFilterNullRenderer->Release();
        g_pIBaseFilterNullRenderer = NULL;
    }

    if (g_pIBaseFilterSampleGrabber != NULL)
    {
        g_pIBaseFilterSampleGrabber->Release();
        g_pIBaseFilterSampleGrabber = NULL;
    }

    if (g_pIBaseFilterCam != NULL)
    {
        g_pIBaseFilterCam->Release();
        g_pIBaseFilterCam = NULL;
    }

    if (g_pGraphBuilder != NULL)
    {
        g_pGraphBuilder->Release();
        g_pGraphBuilder = NULL;
    }

    if (g_pCaptureGraphBuilder != NULL)
    {
        g_pCaptureGraphBuilder->Release();
        g_pCaptureGraphBuilder = NULL;
    }

    this->activeCameraIndex = -1;
}

Похоже, ваше исключение съедается во время границы между родным и управляемым. Я не знаю, насколько это поможет вам, но, по крайней мере, это даст вам представление о том, где искать.

Включите отладку неуправляемого кода и посмотрите, сможете ли вы пройтись по исходному коду библиотек и увидеть, в чем заключается реальная проблема.

введите здесь описание изображения

Я делаю это вики сообщества, если кто-то, у кого больше опыта взаимодействия с C++/C#, захочет отредактировать это и добавить больше того, что делать дальше, чтобы отладить проблему.

person Community    schedule 02.05.2012
comment
Привет Скотт. Спасибо за ваш ответ. Где-то там что-то ломается, но это намного превышает мой визуальный уровень С# - С++, чтобы найти решение. Я думаю, мне нужно искать другой скрипт веб-камеры, который не использует DirectShow. - person Pascal; 03.05.2012

Найдите событие для вызова, когда устройство отключено. Напишите туда код для отключения веб-камеры.

person MACMAN    schedule 02.05.2012
comment
Привет, Гиджо, не совсем уверен, что ты имеешь в виду? - person Pascal; 02.05.2012
comment
Может быть событие, которое вы можете использовать для написания кода для отключения веб-камеры. Может быть, вы можете проверить список событий, доступный с помощью элемента управления. - person MACMAN; 02.05.2012
comment
Итак, что я только что заметил, в классе Camera есть функция под названием «void Dispose()». Если я использую кнопку для включения камеры, которая работает нормально, этот метод не срабатывает. Однако, если клиент отключается, он запускает эту функцию удаления. - person Pascal; 02.05.2012
comment
хорошо, вы можете написать код для функции void Dispose, чтобы выключить камеру. - person MACMAN; 02.05.2012

На всякий случай, если проблема все еще существует, используйте Touchless.RefreshCameraList для отключения от камеры. Вы также можете добавить Touchless.CurrentCamera.Dispose, если хотите. . Возможно, вам по-прежнему потребуется включить неуправляемую отладку кода.

person kosi    schedule 23.02.2015