Toon posts:

[d5] Video problemen MsgWaitForMultipleObjects()

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik kom er niet meer uit :?

Het probleem is als volgt:
Ik ben bezig met een movie player in Delphi5 mbv DelphiX, ik decode zelf de video bestanden. Ik maak een DirectSound buffer aan van 1 seconde. Hierin geef ik voor elke frame een notify event marker aan (SetNotificationPositions(), om de 1470 bytes), in totaal 15 markers (15 fps)

Ik maak een thread aan die zorgt voor het decoderen en renderen van het video/geluid. Na veel frustraties heb ik 't programma zo ver gekregen dat het geluid goed loopt (en BLIJFT lopen), maar nu zit ik nog met 1 probleem, en ik weet het echt niet meer :(

Vaak blijft het video spontaan steken (geluid loopt wel goed door tot het einde van het filmpje), soms na 1 seconde, soms bijna op het einde, soms een keer gewoon niet (maar das uniek). Het enige wat ik dan kan doen is het programma afsluiten (hij reageerd verder nog wel), en opnieuw openen.

Nu ben ik er 99% zeker van dat het ergens in deze routine in de soep loopt (uitleg volgt):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Procedure Render;
var
  CurrentEvent : DWord;
  Msg       : TMgs;
begin
EventNr := FrameNr mod EventCount;

CurrentEvent := MsgWaitForMultipleObjects(EventCount, SoundEventArray[0], 
            False, INFINITE, QS_ALLINPUT);
If (CurrentEvent >= WAITOBJECT_0 + EventCount) then
  While PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
    begin
    If Msg.Message = WM_QUIT then BREAK;
    TranslateMessage(Msg);
    DispatchMessage(Msg);
    end;
If (CurrentEvent - WAIT_OBJECT_0) = EventNr then
  begin
  If FrameNr > 0 then ShowFrame;
  RenderFrame;
  end;
end;

EventNr is het index nummer uit de event array waar het programma eigenlijk op wacht. Doe ik dit niet (dus reageert op elke index uit de array als de play positie voorbij een marker komt) dan raakt het filmpje uit sync na pauseren. Vraag me niet waarom maar zo werkt het prima.

Get bovenste gedeelte (While PeekMessage...) ben ik tegengekomen op vele sites hierover. Ik heb heel veel varianten hierop geprobeerd. Sommigen zorgden voor minder freezes dan anderen, maar geen enkele loste het probleem op

Ik heb geprobeerd met MsgWaitForSingleObject, dat zorgde ervoor dat ie m'n filmpje in sprongetjes afspeelde.

Ik heb geprobeerd zonder...laat ik daar maar over zwijgen

Anyways, m'n filmpjes freezen nog steeds, het ligt bijna zeker aan de code hierboven. Voor alle duidelijkheid: Hij decodeerd WEL alle frames tijdens de freeze, hij laat ze alleen niet zien :(

edit:

De code wat browser-vriendelijker gemaakt :)

Verwijderd

Okay, kan je niet direct een oplossing geven,maar in een geval als dit willen een stel TRACE/printf/oid statements nog weleens helpen de fout te lokaliseren, aangezien je dus kennelijk nog niet precies weet wat er fout gaat.

Het kan bijvoorbeeld helpen om te weten of die PeekMessage while loop blijft hangen, of die ShowFrame/RenderFrame methodes wel worden uitgevoerd of dat er misschien een andere event wordt getriggered die in die array staat die je nu niet afhandelt.

Ik snap trouwens ook niet waarom je die MsgWaitForMultipleObjects methode gebruikt...Zelf vindt ik het eigenlijk altijd gemakkelijker om dit soort dingen gewoon in een worker thread te doen (en dan moet je dus WaitForMultipleObjects gebruiken) zodat je UI thread gewoon lekker user input e.d. af kan handelen, hoef je tenminste ook niet zelf met die messages gaan zitten klooien en ze zelf dispatchen.

Verwijderd

Topicstarter
Ik had al heel wat checks uitgevoerd. De ShowFrame en RenderFrame worden wel degelijk uitgevoerd, ook de DXDraw.CanDraw property staat op TRUE als ik wil renderen op de secundary canvas.

Ik heb wel gemerkt dat het gedeelte in 'While Peekmessage... ' eigenlijk nooit uitgevoerd wordt. Nu weet ik dus niet of dat zo hoort of dat hier dus iets mis is...

Ik had het ook al geprobeerd met WaitForMultipleObjects(), zonder resultaat. Ik kreeg zo'n beetje hetzelfde effect als met MsgWaitForSingleObject() en WaitForSingleObject() : Schokkerig, en nog steeds freeze

Vage boel hoor dat DirectX :)

Verwijderd

Topicstarter
Ik begrijp er niks van.... :( :( :(

Ik werk nu wel met WaitForMultipleObjects, loopt minder schokkerig dan voorheen, maar het video-gedeelte bevriest nog steeds. Ik probeer het ook al met manual event resets e.d. maar nog steeds geen resultaten...

Ik zal even wat meer code posten in de hoop dat iemand het licht ziet en mij kan vertellen wat ik in gods naam fout doe...

Dit is mijn Render-threadje:
code:
1
2
3
4
Procedure TEventThread.Execute;
begin
While not Terminated do Render;
end;

Opwindend he?
Goed, en dit is wat er allemaal gebeurt in de Render routine:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Procedure Render;
Var
  CurrentEvent : DWord;
  Index   : DWord;
begin
{ Bereken het event-nr waar we op gaan wachten)
EventNr := FrameNr mod EventCount;

{ Wacht op een soundbuffer-event }
CurrentEvent := WaitForMultipleObjects(EventCount, @SoundEventArray, 
            False, INFINITE);
Index     := CurrentEvent - WAIT_OBJECT_0;
If (Index = EventNr) then  { Juiste event signaled, laat frame zien }
  begin
  ShowFrame;
  RenderFrame;
  end;

{ Reset het event (manual reset) }
ResetEvent(SoundNotifyArray[Index].hEventNotify);
end;

Heb er wat comments bij gezet voor jullie gemak...
Nogmaals, ik heb dit geprobeerd met WaitForSingleObject() zodat ik niet het EventNr hoef te berekenen, maar het video gaat hiervan behoorlijk schokken (Rendert 15 frames (= 1 seconde), en wacht tot het tijd is om de volgende 15 af te raffelen)

Dan hier het stukje code waar ik de notificatie posities in m'n geluidsbuffer defigneer:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Procedure CreateSoundNotify;
Var
  PositionNotify : TDSBPositionNotify;
  I       : Integer;
  Offset       : Cardinal;
begin
If SoundBuffer.IBuffer.QueryInterface(IID_IDirectSoundNotify, SoundNotify) = 0 then
  begin
  { We beginnen bij offset 0 (omdat we het buffer loopen) }
  Offset := 0;

  { EventCount is al berekend, normaal gespr. gelijk aan FPS }
  For I := 0 to EventCount-1 do
    begin
    PositionNotify.dwOffset     := Offset;
    PositionNotify.hEventNotify := CreateEvent(nil,True,False,nil); 
    SoundNotifyArray[I]    := PositionNotify;
    SoundEventArray[I]      := Pointer(PositionNotify.hEventNotify);
    
    { SoundFrameSize is gelijk aan het aantal bytes nodig voor 1/FPS seconden geluid }
    Inc(Offset, SoundFrameSize);
    end;
  SoundNotify.SetNotificationPositions(EventCount, SoundNotifyArray[0]);
  end;
end;

Het filmpje loops op de juiste snelheid, dus wordt er idd de juiste events gesignaled. Hier LIJKT mij dus niets fout te gaan. De CreateEvent() hierbij staat ingesteld op manual reset, wat ik dus ook doe in de code erboven.

Wat ik doe in het programma:
Ik open een filmbestand. Ik open 'm, initializeer 'm en dan wacht het programma tot ik op de 'play' knop klik.
In de UI-thread start ik dan het geluid, waarna ik een tweede thread start waarin ik ga tekenen op een DXDraw surface (nadat er een soundbuffer event wordt getriggerd)

Het pauseren, stoppen, etc van het geluid gebeurt allemaal in de hoofdthread. Hierop reageert het video. Geen geluidprogressie, geen triggers, geen video. Simpel. Werkt.
Ik resume het geluid. Geluidsprogressie, triggers, video. Ook simpel. Werkt ook.

Als het video eenmaal bevroren is, loopt de Frame counter wel gewoon door. Oftwel, hij leest+decodeert+schrijft nog steeds alle video/geluidsdata (Logisch, want het geluid bevriest niet, speelt het hele filmpje door). Het ENIGE wat ie dus niet doet het video op m'n DXDraw canvas laten zien.
De form buttons, positionbar, playtime display worden wel geupdate, dur de form wordt wel gerefreshed.

Misschien praat ik nu allemaal onzin, maar ik wil al het mogelijke proberen.

Maar het probleem moet ergens in het hierboven geplaatste code zitten. Als ik nl. een filmpje afspeel zonder synchronisatie, dus geen events, waits etc., loopt het video perfect en BLIJFT het perfect lopen.

Maargoed..om dit lange verhaal kort te maken: IK WEET HET NIET MEER!!! HEEEELP! :)

Verwijderd

Topicstarter
Waar o waar zijn de echte programmeurs? Ik ben wanhopig hier weetje :)

Verwijderd

Mm, aan de code die je geeft zie ik weinig fouts; als ik jouw verhaal goed lees, gaat het ook niet in die code fout; tenminste ik begrijp dat de ShowFrame en RenderFrame procedures nog steeds uitgevoerd worden.

Wel vraag ik mij af hoe/wanneer de events geset worden? En waarom gebruik je meerdere events en niet 1?

Ik zou het volgende doen: Vervang de ShowFrame en RenderFrame procedure door een simpele procedure die bv een teller op het scherm zet. Probeer dan het probleem [blijven steken van beeld] weer te krijgen.

Gebeurd dit nu niet, dan zit het probleem in de ShowFrame/RenderFrame; Gebeurd het wel dan zit het in deze code.

Zit het in de ShowFrame/RenderFrame, strip deze code dan zoveel mogelijk, totdat je weet welke code werkt en welke code fouten maakt.

Zorg ervoor dat de thread heel veel debug messages geeft aan de main-thread [bv door PostMessage(MainThreadHandle, CM_DEBUG, MSG_NuGebeurdDit, 0 ofzo] die de main thread dan bv in een listbox afdrukt.

Etc.

  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 18-08 10:58

Bergen

Spellingscontroleur

Een Application.ProcessMessages doet soms ook wonderen bij zulke loops. Zit die al ergens tussengestopt?

Verwijderd

Topicstarter
Wel vraag ik mij af hoe/wanneer de events geset worden? En waarom gebruik je meerdere events en niet 1?
Het geluidsbuffer wordt 8 frames vooruit gevuld. De events worden 15x per seconde gegenereerd. Een geluidsbuffer van 1/15de seconde is niet echt haalbaar. Ik moet dit buffer wel kunnen vullen zonder dat de nog niet afgespeelde data overschreven wordt, of dat ie begint te haperen als het wat drukker wordt voor de processor...
Ik zou het volgende doen: Vervang de ShowFrame en RenderFrame procedure door een simpele procedure die bv een teller op het scherm zet. Probeer dan het probleem [blijven steken van beeld] weer te krijgen.

Gebeurd dit nu niet, dan zit het probleem in de ShowFrame/RenderFrame; Gebeurd het wel dan zit het in deze code.

Zit het in de ShowFrame/RenderFrame, strip deze code dan zoveel mogelijk, totdat je weet welke code werkt en welke code fouten maakt
Goed plan.. Dit had ik al enigszins gedaan met enkele labels op m'n form, maar het kan nooit kwaad dit alles tot op het bot te monitoren :)

Ben al lang blij dat dit geen simpel foutje in de code was en dat ik aan m'n eigen intelligentie zou gaan moeten twijfelen :+
Een Application.ProcessMessages doet soms ook wonderen bij zulke loops. Zit die al ergens tussengestopt?
Had ik inderdaad al geprobeerd op deze manier:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Procedure Render;
Var
  CurrentEvent : DWord;
  Index        : DWord;
begin
{ Bereken het event-nr waar we op gaan wachten)
EventNr := FrameNr mod EventCount;
{ Wacht op een soundbuffer-event }
CurrentEvent := WaitForMultipleObjects(EventCount, @SoundEventArray,
                False, INFINITE);
Index        := CurrentEvent - WAIT_OBJECT_0;
If (Index = EventNr) then  { Juiste event signaled, laat frame zien }
  begin
  ShowFrame;
  RenderFrame;
  end else Application.ProcessMessages;

{ Reset het event (manual reset) }
ResetEvent(SoundNotifyArray[Index].hEventNotify);
end;

Ook zonder de 'else', zodat ie 'm altijd uitvoert. Haalde niks uit. :( Application.ProcessMessages() schijnt ook veel CPU cycles te vreten, in tegenstelling tot WaitForMultipleObjects...

I appreciate your input, keep them good ideas coming :)

Verwijderd

Oke, dat je een buffer wilt hebben begrijp ik. Maar ik haal niet uit jouw code wat het voordeel is om 15 verschillende events te hebben tegen bv 15x dezelfde event.

Volgens mij kan er ook iets fout gaan in jouw code: Stel de thread zit in een ShowFrame/RenderFrame; Deze duren lang en wel zo lang dat 2x een event afgevuurd wordt. Toevallig zijn dit eventnr 14 en eventnr 0. WaitForMultipleObjects zal dan retourneren met WAIT_OBJECT_0.

Maar jij zit te wachten op 14, dus je reset event 0 en gaat weer naar WaitForMultipleObjects. Deze retourneert meteen met WAIT_OBJECT_0 + 14, je doet de ShowFrame/RenderFrame etc en gaat nu wachten op event 0. Dit zal dan een seconde duren voordat deze afgevuurd wordt, omdat events 1..14 eerst afgevuurd moeten worden.

Zal misschien niet zo vaak gebeuren, maar toch :)

Misschien zou je eens kunnen kijken hoe lang het gemiddeld duurt om een frame te renderen, en hoe groot de uitschieters zijn. Kan de thread achterstand inhalen? Zo als je het nu gemaakt hebt, zal de thread geen frames overslaan, dit is misschien toch slim om te doen..

Verder zeg je in je reply dat je geprobeerd hebt om Application.ProcessMessages aan te roepen in een thread. Dit is een vrij onzinnig idee [Je roept alleen maar PeekMessage aan in de context van je thread]; maar aangezien je erop ingaat weet je misschien ook niet dat de VCL niet echt thread-safe is.

Het is niet mogelijk/onverstandig om visuele componenten die op je form staan, te gaan updaten in een thread. Ik weet niet welke componenten je gebruikt, dus het zou kunnen dat deze componenten het canvas locken zodat het wel gaat werken. Iig ben ik benieuwd hoe je de data die je in de thread maakt [RenderFrame] over zet naar je main-thread [ShowFrame?]

Verwijderd

Topicstarter
Oke, dat je een buffer wilt hebben begrijp ik. Maar ik haal niet uit jouw code wat het voordeel is om 15 verschillende events te hebben tegen bv 15x dezelfde event.
Ik heb 15 verschillende nodig omdat ik moet kunnen nagaan welk van de 15 getriggerd werd. Leg ik hieronder uit...
Volgens mij kan er ook iets fout gaan in jouw code: Stel de thread zit in een ShowFrame/RenderFrame; Deze duren lang en wel zo lang dat 2x een event afgevuurd wordt. Toevallig zijn dit eventnr 14 en eventnr 0. WaitForMultipleObjects zal dan retourneren met WAIT_OBJECT_0.

Maar jij zit te wachten op 14, dus je reset event 0 en gaat weer naar WaitForMultipleObjects. Deze retourneert meteen met WAIT_OBJECT_0 + 14, je doet de ShowFrame/RenderFrame etc en gaat nu wachten op event 0. Dit zal dan een seconde duren voordat deze afgevuurd wordt, omdat events 1..14 eerst afgevuurd moeten worden.

Zal misschien niet zo vaak gebeuren, maar toch :)
Ik heb inderdaad eerst niet op specifieke events gewacht, dus gewoon op elk willekeurig buffer event gewacht. In afspeelmodus werkt het goed, maar zodra ik 'm pauseer, en daarna weer uit pause haal, lijkt het erop dat ie alle voorgaande buffer events triggert tot het punt waar ie aan het afspelen is

voorbeeld: Ik pauseer 'm als het afspeelbuffer halverwege is (net voorbij trigger 7). Als ik 'm nu uit pause haal, triggert ie 0 t/m 7 (rendert dus 7 frames) en speelt dan gewoon verder. gevolg: frames lopen nu 7 frames VOOR op het geluid. Daarom zoek ik nu dus op specifieke events. Waarom dit zo werkt weet ik niet. Wie het weet mag het zeggen :)

Maar ook in bovenstaande situatie freezed het video regelmatig.

In de situatie die jij schetst hierboven lijkt mij niet dat het beeld freezed maar dan hooguit op 1 FPS gaat spelen.
Ik hou nu de frame count bij tijdens het afspelen en als het beeld freezed loopt de counter op de juiste snelheid door
Misschien zou je eens kunnen kijken hoe lang het gemiddeld duurt om een frame te renderen, en hoe groot de uitschieters zijn. Kan de thread achterstand inhalen? Zo als je het nu gemaakt hebt, zal de thread geen frames overslaan, dit is misschien toch slim om te doen..
Dat is waar. Gaat ook gebeuren. Normaal gesproken kan de thread 4 a 5 frames renderen in 1/15de seconde. Dit kan ik nog wel wat opkrikken als ik m'n code ga optimaliseren. Maar ik heb wel gezien dat de player soms even stilstaat, en dat weer snel bijrendert tot ie weer in sync loopt...
Het is niet mogelijk/onverstandig om visuele componenten die op je form staan, te gaan updaten in een thread. Ik weet niet welke componenten je gebruikt, dus het zou kunnen dat deze componenten het canvas locken zodat het wel gaat werken. Iig ben ik benieuwd hoe je de data die je in de thread maakt [RenderFrame] over zet naar je main-thread [ShowFrame?]
Je bedoelt dat ik beter een Msg kan sturen naar de main UI-thread, die dan m'n frame gaat renderen en plaatst op m'n canvas? Dit is idd nog niet het geval..

Ik wist ook niet dat dit zo onverstandig was. Klinkt op zich wel logisch nu je het zegt. Ik heb ook nog niet zo ontzettend veel ervaring met multithreads maar ik leer iig weer genoeg :) Thnx :)

Ik ga hier in ieder geval even mee stoeien, laten we hopen dat dit de oplossing biedt.. Je hoort het wel :)
Pagina: 1