Frage

Ich habe eine DirectShow -Anwendung in Delphi 6 mit der DSpack -Komponentenbibliothek geschrieben. Ich habe zwei Filterdiagramme, die miteinander kooperieren.

Das primär Die Filtergrafik hat diese Struktur:

  1. Erfassungsfilter mit einer Puffergröße von 100 ms.
  2. (verbunden mit) ein Probengrabberfilter.

Das "sekundäre" Filterdiagramm hat diese Struktur.

  1. Benutzerdefinierte Push -Quellfilter, der Audio direkt in ein von ihm verwaltetes Audio -Puffer -Lagerhaus akzeptiert.
  2. (verbunden mit) einem Renderfilter.

Der Push -Quellfilter verwendet ein Ereignis, um die Lieferung von Audio zu steuern. Der Befehl fillBuffer () wartet auf der Veranstaltung. Das Ereignis wird signalisiert, wenn dem Puffer neue Audiodaten hinzugefügt werden.

Wenn ich die Filterdiagramme ausführe, höre ich winzige "Lücken" im Audio. Normalerweise verbinde ich diese Bedingung mit einer nicht ordnungsgemäß konstruierten Audiopuffer, die nicht ausgefüllt oder "Lücken" in ihnen gefüllt sind. Als Test habe ich jedoch einen T -Shirt -Filter hinzugefügt und einen WAV -Zielfilter angeschlossen, gefolgt von einem Dateischreiberfilter. Wenn ich die Ausgangs -WAV -Datei untersuche, ist sie perfekt glatt und angrenzend. Mit anderen Worten, die Lücken, die ich vom Sprecher höre, sind in der Ausgabedatei nicht erkennbar.

Dies würde darauf hinweisen, dass sich das Audio aus dem Erfassungsfilter erfolgreich ausbreitet, aber die Abgabe der Audiopuffer erhält regelmäßige Interferenzen. Die "Lücken", die ich höre, sind nicht zehnmal pro Sekunde, sondern eher 2 oder dreimal pro Sekunde, manchmal mit kurzen Perioden ohne Lücken. Es geschieht also nicht jeden Puffer, sonst hörte ich 10 Mal Lücken pro Sekunde.

Meine erste Vermutung wäre, dass es sich um ein Sperrenproblem handelt, aber ich habe eine Auszeit beim Ereignis von 150 ms und wenn dies geschieht, wird eine Ausnahme ausgelöst. Es werden keine Ausnahmen geworfen. Ich habe auch eine Auszeit von 40 ms eingestellt jeder Kritischer Abschnitt, der in der Anwendung verwendet wird und keiner von diesen auslösen auch. Ich überprüfte meine Outputdebugstring () -Mumpen und die Zeit zwischen dem nicht signalisiert (blockiert) und signalisiert (Entsperrte) Vorkommen zeigt ein ziemlich konstantes Muster zwischen 94 ms und 140 ms. Mit anderen Worten, der Aufruf von FillBuffer () in meinem Push -Quellfilter bleibt für 94 ms, dann 140 ms und wiederholt sich. Beachten Sie, dass die Dauer ein wenig driftet, aber es ist ziemlich regelmäßig. Dieses Muster scheint mit einem Thread übereinzustimmen, der auf den Erfassungsfilter wartet, um seinen Audiopuffer in den Push -Quellfilter in einem 100 -ms -Intervall zu entfernen, da die Wechsel des Windows -Fadenschalters.

ich denken Ich verwende Doppelpuffer in meinem Push-Quellfilter. Ich bin der Überzeugung, dass ich den Audiostrom nicht unterbrechen sollte, wenn keiner der Verriegelungsmechanismen eine kombinierte Zeit von 200 ms oder mehr in Anspruch nimmt. Aber ich kann mir nichts anderes als ein Locking -Problem vorstellen, das diese Symptome verursachen würde. Ich habe den Code aus meiner Methode für detaBufferSize () in meinen Push -Quellfilter unten aufgenommen, falls ich etwas falsch mache. Obwohl es etwas lang ist, habe ich auch den folgenden FillBuffer () -Anruf aufgenommen, um zu zeigen, wie ich Zeitstempel generiere, falls dies einen Effekt hat.

Was könnte mein Audio -Stream sonst noch dazu bringen, den Renderfilter zu stottern, obwohl alle Audiopuffer intakt geliefert werden?

Frage: Muss ich selbst doppelte Puffer implementieren? Ich dachte, DirectShow -Render -Filter tun dies für Sie, sonst hätten die anderen Filterdiagramme, die ich ohne meinen benutzerdefinierten Push -Quellfilter erstellt habe, nicht ordnungsgemäß funktioniert. Aber vielleicht muss ich, da ich eine weitere Lock-/Entsperr -Situation im Filterdiagramm erstelle, meine eigene doppelte Pufferung hinzufügen? Ich möchte das natürlich vermeiden, um zusätzliche Latenz zu vermeiden. Wenn es also eine weitere Lösung für meine Situation gibt, würde ich gerne wissen.

function TPushSourcePinBase_wavaudio.DecideBufferSize(Allocator: IMemAllocator; Properties: PAllocatorProperties): HRESULT;
var
    // pvi: PVIDEOINFOHEADER;
    errMsg: string;
    Actual: ALLOCATOR_PROPERTIES;
    sampleSize, numBytesPerBuffer: integer;
    // ourOwnerFilter: TPushSourceFilterBase_wavaudio;
begin
    if (Allocator = nil) or (Properties = nil) then
    begin
        Result := E_POINTER;
        // =========================== EXIT POINT ==============
        Exit;
    end; // if (Allocator = nil) or (Properties = nil) then

    FFilter.StateLock.Lock;
    try
        // Allocate enough space for the desired amount of milliseconds
        //  we want to buffer (approximately).
        numBytesPerBuffer := Trunc((FOurOwnerFilter.WaveFormatEx.nAvgBytesPerSec / 1000) * FBufferLatencyMS);

        // Round it up to be an even multiple of the size of a sample in bytes.
        sampleSize := bytesPerSample(FOurOwnerFilter.WaveFormatEx);

        // Round it down to the nearest increment of sample size.
        numBytesPerBuffer := (numBytesPerBuffer div sampleSize) * sampleSize;

        if gDebug then OutputDebugString(PChar(
            '(TPushSourcePinBase_wavaudio.DecideBufferSize) Resulting buffer size for audio is: ' + IntToStr(numBytesPerBuffer)
        ));

        // Sanity check on the buffer size.
        if numBytesPerBuffer < 1 then
        begin
            errMsg := '(TPushSourcePinBase_wavaudio.DecideBufferSize) The calculated number of bytes per buffer is zero or less.';

            if gDebug then OutputDebugString(PChar(errMsg));
            MessageBox(0, PChar(errMsg), 'PushSource Play Audio File filter error', MB_ICONERROR or MB_OK);

            Result := E_FAIL;
            // =========================== EXIT POINT ==============
            Exit;
        end;

        // --------------- Do the buffer allocation -----------------

        // Ensure a minimum number of buffers
        if (Properties.cBuffers = 0) then
            Properties.cBuffers := 2;

        Properties.cbBuffer := numBytesPerBuffer;

        Result := Allocator.SetProperties(Properties^, Actual);

        if Failed(Result) then
            // =========================== EXIT POINT ==============
            Exit;

        // Is this allocator unsuitable?
        if (Actual.cbBuffer < Properties.cbBuffer) then
            Result := E_FAIL
        else
            Result := S_OK;

    finally
        FFilter.StateLock.UnLock;
    end; // try()
end;

// *******************************************************


// This is where we provide the audio data.
function TPushSourcePinBase_wavaudio.FillBuffer(Sample: IMediaSample): HResult;
    // Given a Wave Format and a Byte count, convert the Byte count
    //  to a REFERENCE_TIME value.
    function byteCountToReferenceTime(waveFormat: TWaveFormat; numBytes: LongInt): REFERENCE_TIME;
    var
        durationInSeconds: Extended;
    begin
        if waveFormat.nAvgBytesPerSec <= 0 then
            raise Exception.Create('(TPushSourcePinBase_wavaudio.FillBuffer::byteCountToReferenceTime) Invalid average bytes per second value found in the wave format parameter: ' + IntToStr(waveFormat.nAvgBytesPerSec));

        // Calculate the duration in seconds given the audio format and the
        //  number of bytes requested.
        durationInSeconds := numBytes / waveFormat.nAvgBytesPerSec;

        // Convert it to increments of 100ns since that is the unit value
        //  for DirectShow timestamps (REFERENCE_TIME).
        Result :=
            Trunc(durationInSeconds * REFTIME_ONE_SECOND);
    end;

    // ---------------------------------------------------------------

    function min(v1, v2: DWord): DWord;
    begin
        if v1 <= v2 then
            Result := v1
        else
            Result := v2;
    end;

    // ---------------------------------------------------------------

var
    pData: PByte;
    cbData: Longint;
    pwfx: PWaveFormat;
    aryOutOfDataIDs: TDynamicStringArray;
    intfAudFiltNotify: IAudioFilterNotification;
    i: integer;
    errMsg: string;
    bIsShuttingDown: boolean;

    // MSDN: The REFERENCE_TIME data type defines the units for reference times
    //  in DirectShow. Each unit of reference time is 100 nanoseconds.
    Start, Stop: REFERENCE_TIME;
    durationInRefTime, ofsInRefTime: REFERENCE_TIME;
    wfOutputPin: TWaveFormat;

    aryDebug: TDynamicByteArray;
begin
    aryDebug := nil;

    if (Sample = nil) then
    begin
        Result := E_POINTER;
        // =========================== EXIT POINT ==============
        Exit;
    end; // if (Sample = nil) then

    // Quick lock to get sample size.
    FSharedState.Lock;
    try
        cbData := Sample.GetSize;
    finally
        // Don't want to have our filter state locked when calling
        //  isEnoughDataOrBlock() since that call can block.
        FSharedState.UnLock;
    end; // try

    aryOutOfDataIDs := nil;
    intfAudFiltNotify := nil;

    // This call will BLOCK until have enough data to satisfy the request
    //  or the buffer storage collection is freed.
    if FOurOwnerFilter.bufferStorageCollection.isEnoughDataOrBlock(cbData, bIsShuttingDown) then
    begin
        // If we are shutting down, just exit with S_FALSE as the return to
        //   tell the caller we are done streaming.
        if bIsShuttingDown then
        begin
            Result := S_FALSE;

            // =========================== EXIT POINT ==============
            exit;
        end; // if bIsShuttingDown then

        // Re-acquire the filter state lock.
        FSharedState.Lock;

        try
            // Get the data and return it.

            // Access the sample's data buffer
            cbData := Sample.GetSize;
            Sample.GetPointer(pData);

            // Make sure this format matches the media type we are supporting.
            pwfx := AMMediaType.pbFormat;       // This is the format that our Output pin is set to.
            wfOutputPin := waveFormatExToWaveFormat(FOurOwnerFilter.waveFormatEx);

            if not isEqualWaveFormat(pwfx^, wfOutputPin) then
            begin
                Result := E_FAIL;

                errMsg :=
                    '(TPushSourcePinBase_wavaudio.FillBuffer) The wave format of the incoming media sample does not match ours.'
                    + CRLF
                    + ' > Incoming sample: ' + waveFormatToString(pwfx^)
                    + CRLF
                    + ' > Our output pin: ' + waveFormatToString(wfOutputPin);
                OutputDebugString(PChar(errMsg));

                postComponentLogMessage_error(errMsg, FOurOwnerFilter.FFilterName);

                MessageBox(0, PChar(errMsg), 'PushSource Play Audio File filter error', MB_ICONERROR or MB_OK);

                Result := E_FAIL;

                // =========================== EXIT POINT ==============
                exit;
            end; // if not isEqualWaveFormatEx(pwfx^, FOurOwnerFilter.waveFormatEx) then

            // Convert the Byte index into the WAV data array into a reference
            //  time value in order to offset the start and end timestamps.
            ofsInRefTime := byteCountToReferenceTime(pwfx^, FWaveByteNdx);

            // Convert the number of bytes requested to a reference time vlaue.
            durationInRefTime := byteCountToReferenceTime(pwfx^, cbData);

            // Now I can calculate the timestamps that will govern the playback
            //  rate.
            Start := ofsInRefTime;
            Stop := Start + durationInRefTime;

            {
            OutputDebugString(PChar(
                '(TPushSourcePinBase_wavaudio.FillBuffer) Wave byte index, start time, stop time: '
                + IntToStr(FWaveByteNdx)
                + ', '
                + IntToStr(Start)
                + ', '
                + IntToStr(Stop)
            ));
            }

            Sample.SetTime(@Start, @Stop);

            // Set TRUE on every sample for uncompressed frames
            Sample.SetSyncPoint(True);

            // Check that we're still using audio
            Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_WaveFormatEx));

{
// Debugging.
FillChar(pData^, cbData, 0);
SetLength(aryDebug, cbData);
if not FOurOwnerFilter.bufferStorageCollection.mixData(@aryDebug[0], cbData, aryOutOfDataIDs) then
}
            // Grab the requested number of bytes from the audio data.
            if not FOurOwnerFilter.bufferStorageCollection.mixData(pData, cbData, aryOutOfDataIDs) then
            begin
                // We should not have had any partial copies since we
                //  called isEnoughDataOrBlock(), which is not supposed to
                //  return TRUE unless there is enough data.
                Result := E_FAIL;

                errMsg := '(TPushSourcePinBase_wavaudio.FillBuffer) The mix-data call returned FALSE despite our waiting for sufficient data from all participating buffer channels.';
                OutputDebugString(PChar(errMsg));

                postComponentLogMessage_error(errMsg, FOurOwnerFilter.FFilterName);

                MessageBox(0, PChar(errMsg), 'PushSource Play Audio File filter error', MB_ICONERROR or MB_OK);

                Result := E_FAIL;

                // =========================== EXIT POINT ==============
                exit;
            end; // if not FOurOwnerFilter.bufferStorageCollection.mixData(pData, cbData, aryOutOfDataIDs) then

            // ------------- OUT OF DATA NOTIFICATIONS -----------------

            {
                WARNING:  TBufferStorageCollection automatically posts
                AudioFilterNotification messages to any buffer storage
                that has a IRequestStep user data interface attached to
                it!.
            }

            if FOurOwnerFilter.wndNotify > 0 then
            begin
                // ----- Post Audio Notification to Filter level notify handle ---
                if Length(aryOutOfDataIDs) > 0 then
                begin
                    for i := Low(aryOutOfDataIDs) to High(aryOutOfDataIDs) do
                    begin
                        // Create a notification and post it.
                        intfAudFiltNotify := TAudioFilterNotification.Create(aryOutOfDataIDs[i], afnOutOfData);

                        // ourOwnerFilter.intfNotifyRequestStep.triggerResult(intfAudFiltNotify);
                        PostMessageWithUserDataIntf(FOurOwnerFilter.wndNotify, WM_PUSH_SOURCE_FILTER_NOTIFY, intfAudFiltNotify);
                    end; // for()
                end; // if Length(aryOutOfDataIDs) > 0 then
            end; // if FOurOwnerFilter.wndNotify > 0 then

            // Advance the Wave Byte index by the number of bytes requested.
            Inc(FWaveByteNdx, cbData);

            Result := S_OK;
        finally
            FSharedState.UnLock;
        end; // try
    end
    else
    begin
        // Tell DirectShow to stop streaming with us.  Something has
        //  gone seriously wrong with the audio streams feeding us.
        errMsg := '(TPushSourcePinBase_wavaudio.FillBuffer) Time-out occurred while waiting for sufficient data to accumulate in our audio buffer channels.';
        OutputDebugString(PChar(errMsg));

        postComponentLogMessage_error(errMsg, FFilter.filterName);
        MessageBox(0, PChar(errMsg), 'PushSource Play Audio File filter error', MB_ICONERROR or MB_OK);

        Result := E_FAIL;
    end;
end;
War es hilfreich?

Lösung

Um die Audioausgabe zu beheben, möchten Sie zunächst die Renderer -Eigenschaften überprüfen. Fortgeschrittene Registerkarten erhalten Ihnen diese und Sie können sie auch über abfragen IAMAudioRendererStats Schnittstelle programmatisch. Dinge, die sich von den Eigenschaften in der Datei -Wiedergabe unterscheiden, sollten eine Warnung für Sie sein, was die Korrektheit des Streamings ist.

Advanced Audio Renderer Properties

Da Audio -Immobilienseiten in Aktienfiltern nicht so felsig wie für DRIECTSHOW -Videofilter hergestellt werden, benötigen Sie möglicherweise einen Trick, um dies zu veröffentlichen. In Ihrer Anwendung beim aktiven Einsatz wird eingesetzt OleCreatePropertyFrame So zeigen Sie Filterprotperties direkt aus Ihrem Code aus dem GUI -Thread (z. B. eine Antwort auf das Drücken einer temporären Taste).

Was die typischen Ursachen von Wiedergabeproblemen angeht, würde ich Folgendes überprüfen:

  • Sie sind keine Zeitstempelmuster, und Thye werden in dem Tempo des Schubs wiedergegeben, und Sie schieben manchmal später Zeug als frühere Probe -Wiedergabe abgeschlossen ist
  • Ihre Zeitstempel sehen korrekt aus, aber sie sind von der aktuellen Wiedergabesteit zurückgezogen und erscheinen möglicherweise teilweise spät für den Renderer

Beide Szenarien sollten eine gewisse Reflexion über die Advanced TAB -Daten.

Andere Tipps

Können Sie versuchen, sich zu ändern?

    // Ensure a minimum number of buffers
    if (Properties.cBuffers = 0) then
        Properties.cBuffers := 2;

hinein

    // Ensure a minimum number of buffers
    if (Properties.cBuffers < 2) then
        Properties.cBuffers := 2;

Um sicherzustellen, dass Sie mindestens zwei Puffer haben. Wenn Sie nur einen Puffer haben, hören Sie Lücken.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top