Диаграмата на филтъра DirectShow никога не завършва на някои машини

Работя върху филтърна графика на DirectShow, за да извлека IMediaSample от видео файл. Преди известно време получихме оригиналната реализация от разработчик по договор и аз си блъсках главата в стената, опитвайки се да разбера защо този код работи на моята машина за разработка, но не и на два други тестови сървъра, които имам.

Най-добре мога да кажа, че филтърната графика никога не се завършва на "счупените" машини. Винаги получавам E_ABORT от обаждането IMediaEvent->WaitForCompletion(). Въпреки това на "работещата" машина това извикване обикновено връща S_OK след около два цикъла.

Актуализация: DirectShow Spy изглежда не работи за мен . Може би това е така, защото имаме обичай, нерегистриран, CTransInPlaceFilter за събиране на IMediaSample във веригата? Няма грешка, но и GraphEdit, и GraphStudio просто висят, когато се опитват да се свържат с отдалечената графика. (‹-- Както беше предложено изпомпването на съобщения разреши това)

С помощта на GraphStudio успях да получа медийния подтип от MPEG-4 декодера който се свързва с нашия CTransInPlaceFilter. На моята машина е MEDIASUBTYPE_YV12, но на "счупената" машина е MEDIASUBTYPE_IYUV. В метода CheckInputType на нашия CTransInPlaceFilter ние приемаме само MEDIASUBTYPE_RGB24, което ме кара да вярвам, че има един или повече "магически филтри", които се вмъкват в графиката.

Актуализация: Благодарение на Roman R. успях да накарам DirectShow Spy да работи. Поне на "счупената" машина. На "работещата" машина получавам нарушение на достъпа, но графиката на филтъра работи бързо и е съборена, така че е трудно да се свържа с нея.

Открих също, че имаме конвертор на цветово пространство, който можеше да обработва MEDIASUBTYPE_IYUV в MEDIASUBTYPE_RGB24 навън. Добавих това към графиката и сега трябва да е правилно.

DirectShow Spy показва това като филтърна графика (изглежда ми завършена):

File Source -> MPEG Demux -> MPEG4 Decoder -> Color Space Converter -> CTransInPlaceFilter -> Null Render

Обаче извикването IMediaEvent->WaitForCompletion() никога не връща S_OK и филтърната графика просто работи вечно. Така че сега съм объркан какво се случва. Има ли нещо друго, което трябва да проверя за състояние на грешка или нещо подобно?

Актуализация: Модифицирах цикъла, за да изброя филтрите в графиката и да направя заявка за състоянието им:

char debugString[512];

int count = 0;
long EvCode;
mediaFilter->SetSyncSource(NULL);
hr = mediaControl->Run();

sprintf(debugString, "mediaControl->Run() %d", hr);
DebugLog(debugString);

while (!m_ThreadKill)
{
    hr = mediaEvent->WaitForCompletion(200, &EvCode);

    sprintf(debugString, "mediaEvent->WaitForCompletion() %d, %d", hr, count);
    DebugLog(debugString);
    count++;

    IEnumFilters *pEnum = NULL;
    IBaseFilter *pFilter;
    ULONG cFetched;

    graphBuilder->EnumFilters(&pEnum);

    while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
    {
        FILTER_INFO FilterInfo;
        FILTER_STATE FilterState;

        char szName[256];

        pFilter->GetState(200, &FilterState);
        pFilter->QueryFilterInfo(&FilterInfo);
        WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName, -1, szName, 256, 0, 0);

        sprintf(debugString, "Filter: %s, %d", szName, FilterState);

        DebugLog(debugString);

        SAFE_RELEASE(FilterInfo.pGraph);
        SAFE_RELEASE(pFilter);
    }

    SAFE_RELEASE(pEnum);

    if (hr == S_OK)
    {
        break;
    }
}

sprintf(debugString, "mediaControl->Stop()");
DebugLog(debugString);

mediaControl->Stop();

Всички те са в състояние "Работи". Така че, ако филтърът е свързан правилно и всички филтри работят, защо графиката никога не завършва на "счупените" машини?

Актуализация: Както беше предложено от Roman R. Премахнах нашия CTransInPlaceFilter от филтърната графика на повредената машина и графиката завърши успешно. Когато CTransInPlaceFilter е свързан, използването на процесора пада до нула. Така че сега не съм сигурен защо следният код работи на някои машини, но не и на други. Ще започна да добавям някакво регистриране за отстраняване на грешки към CTransInPlaceFilter, за да се опитам да разбера какво се случва (или не се случва).


Решение: Както беше предложено от Roman R. (имам чувството, че се повтарям :P), проблемът се оказа задънена улица. Всички счупени машини имаха един процесор/ядро, докато работещите машини имаха множество процесори/ядра. Приложението се състои от нишка за изходно видео, нишка за сливане и нишка на местоназначение.

Изходната нишка(и) изпълнява филтърна графика (предполагам, че филтърната графика също работи в собствената си нишка), за да извлече данните от IMediaSample и да ги постави в CQueue<BYTE*>.

Нишката за сливане преминава през източниците, извлича примерните данни от източника CQueue<BYTE*>, обединява кадрите в едно изображение и ги изпраща към CQueue<BYTE*>, която консумира целевата нишка.

Целевата нишка изпълнява друга филтърна графика, за да кодира видеото/аудиото.

CQueue<BYTE*> блокира на Put, докато има свободно място. Обикновено това е добре, защото нишката за сливане премахва елементи. Въпреки това на машините с един процесор/ядро нишката за сливане беше блокирана от нишките източник.

Накратко Sleep(0); тук и там позволи на изходните нишки да отстъпят на нишките за сливане и проблемът изглежда е разрешен.


person Cory Charlton    schedule 15.08.2013    source източник
comment
Кодовият фрагмент е малко вероятно да има отношение към проблема. Трябва да сте сигурни, че успешно създавате филтърна графика на първо място. След това, преди да стартирате графиката, трябва да проверите нейната ефективна топология, за да видите какво има вътре. Вероятно ще видите разлики между вашите системи на тази стъпка. Тогава ще видите какво прави стриймингът непълен.   -  person Roman R.    schedule 15.08.2013
comment
Ако видите графиката си, шпионирането работи. Инструментите за шпиониране и отдалечено управление на графики обаче ще замръзнат, ако не изпомпвате съобщения на прозореца, докато тече поточно предаване - така че това е по-скоро проблем с вашия код (не фатално, разбира се, но изпомпването на съобщения все пак е добра идея).   -  person Roman R.    schedule 16.08.2013


Отговори (2)


Вътрешното завършване на възпроизвеждането включва изпращане на известия за край на потока от източниците на потоци, които се предават от филтри надолу по веригата, събират се в рендери и след това, комбинирани, се докладват на приложението. Така че успешното завършване зависи от това дали всички участници във филтърната графика действат правилно.

Открихте топологията на вашата графика и трябва да сравните топологиите на различните си машини. Ако видите някакви разлики там, те може да предложат кой филтър може да губи известията за завършване.

Въпреки това, дори ако топологиите съвпадат точно, някои филтри могат да действат различно по други причини. По-специално, наличието на ваш собствен персонализиран филтър върху графиката има големи шансове да загуби известието и графиката никога да не бъде завършена. Той спира да обработва данни и просто работи на празен ход оттам (това е друго нещо, което искате да проверите - дали потреблението на процесора намалява до нула или все още се извършва някаква обработка, в който случай можете да преквалифицирате проблема в блокиране).

Нещо, което бихте могли повече или по-малко лесно да направите, за да подходите към този проблем, е да започнете да изрязвате филтри от графиката, за да идентифицирате кой точно филтър внася проблема. Опитването на тези графики вероятно ще открие нарушител:

File Source -> MPEG Demux -> MPEG4 Decoder -> Color Space Converter -> Null Render
File Source -> MPEG Demux -> MPEG4 Decoder -> Null Render
File Source -> MPEG Demux -> Null Render
person Roman R.    schedule 16.08.2013
comment
Добре, това ме отведе някъде. Премахнах нашия CTransInPlaceFilter от графиката и той завърши успешно. Все още не съм сигурен защо филтърът работи на една машина, но не и на друга. Въпросът е актуализиран с повече подробности. - person Cory Charlton; 17.08.2013
comment
Трябва да проверите нещата около IPin::EndOfStream на този филтър. Нещо не е наред там. - person Roman R.; 17.08.2013
comment
Доколкото мога да преценя, той не е внедрил IPin::EndOfStream никъде и просто е разчитал на базата. Мисля обаче, че може да сте на път за нещо относно безизходицата, тъй като виждам само метода CTransInPlaceFilter::Transform, наречен четири пъти и след това просто спира. На моята работеща машина виждам метода, наречен подходящ брой пъти, свързани с броя кадри. - person Cory Charlton; 17.08.2013

Много е трудно да се каже, без да се погледне цялото нещо. Когато разработвах DS филтри, често използвах GraphStudio и FilterGraph Spy.

Често срещана грешка е да се използват "автоматични филтри", които може да не са налични на целевите машини. Да предположим, че вашето видео е h264 и се опитате да прочетете необработен RGB от него, DS автоматично ще предостави декодиращи филтри и трансформации на цветовото пространство вместо вас. Ще бъдат генерирани много междинни филтри, без да забележите това от вашия код. Ето защо е много важно да изхвърлите графиката във визуален инструмент и да проверите как всичко е свързано.

Предполагам, че един или повече от тези „магически филтри“ не съществуват на вашия сървър за внедряване. Нещо, което можете да опитате, е да използвате GraphStudio директно на сървъра и да свържете всичко, както бихте направили програмно, и да видите как и защо се проваля оттам.

person Eric    schedule 15.08.2013
comment
Благодаря за бакшиша. Ще разгледам тези инструменти и ще видя какво мога да намеря. - person Cory Charlton; 15.08.2013