Bagikan melalui


Mendukung Pencarian di Filter Sumber

[Fitur yang terkait dengan halaman ini, DirectShow, adalah fitur warisan. Ini telah digantikan oleh MediaPlayer, IMFMediaEngine, dan Pengambilan Audio/Video di Media Foundation. Fitur-fitur tersebut telah dioptimalkan untuk Windows 10 dan Windows 11. Microsoft sangat menyarankan agar kode baru menggunakan MediaPlayer, IMFMediaEngine , dan Pengambilan Audio/Video di Media Foundation alih-alih DirectShow, jika memungkinkan. Microsoft menyarankan agar kode yang ada yang menggunakan API warisan ditulis ulang untuk menggunakan API baru jika memungkinkan.]

Topik ini menjelaskan cara menerapkan pencarian di filter sumber Microsoft DirectShow. Ini menggunakan sampel Filter Bola sebagai titik awal dan menjelaskan kode tambahan yang diperlukan untuk mendukung pencarian dalam filter ini.

Sampel Ball Filter adalah filter sumber yang membuat bola pantulan animasi. Artikel ini menjelaskan cara menambahkan fungsionalitas pencarian ke filter ini. Setelah menambahkan fungsionalitas ini, Anda dapat merender filter di GraphEdit dan mengontrol bola dengan menyeret penggerak GraphEdit.

Topik ini berisi bagian berikut:

Gambaran Umum Pencarian di DirectShow

Aplikasi mencari grafik filter dengan memanggil metode IMediaSeeking pada Filter Graph Manager. Filter Graph Manager kemudian mendistribusikan panggilan ke setiap perender dalam grafik. Setiap perender mengirimkan panggilan upstream, melalui pin output filter upstram berikutnya. Panggilan berjalan di hulu hingga mencapai filter yang dapat menjalankan perintah pencarian, biasanya filter sumber atau filter pengurai. Secara umum, filter yang berasal dari stempel waktu juga menangani pencarian.

Filter merespons perintah pencarian sebagai berikut:

  1. Filter menghapus grafik. Ini menghapus data kedaluarsa dari grafik, yang meningkatkan responsivitas. Jika tidak, sampel yang di-buffer sebelum perintah pencarian mungkin dikirimkan.
  2. Filter memanggil IPin::NewSegment untuk menginformasikan filter hilir tentang waktu berhenti, waktu mulai, dan laju pemutaran baru.
  3. Filter kemudian mengatur bendera penghentian pada sampel pertama setelah perintah pencarian.

Stempel waktu dimulai dari nol setelah perintah pencarian apa pun (termasuk perubahan laju).

Gambaran Umum Cepat Filter Bola

Filter Bola adalah sumber pendorongan, yang berarti menggunakan utas pekerja untuk mengirimkan sampel di hilir, dibandingkan dengan sumber penarikan, yang secara pasif menunggu filter hilir untuk meminta sampel. Filter Ball dibangun dari kelas CSource , dan pin outputnya dibangun dari kelas CSourceStream . Kelas CSourceStream membuat utas pekerja yang mendorong aliran data. Utas ini memasukkan perulangan yang mendapatkan sampel dari alokator, mengisinya dengan data, dan mengirimkannya ke hilir.

Sebagian besar tindakan di CSourceStream terjadi dalam metode CSourceStream::FillBuffer , yang diterapkan kelas turunan. Argumen untuk metode ini adalah penunjuk ke sampel yang akan dikirimkan. Implementasi FillBuffer filter Bola mengambil alamat buffer sampel dan menggambar langsung ke buffer dengan mengatur nilai piksel individual. (Gambar dilakukan oleh kelas pembantu, CBall, sehingga Anda dapat mengabaikan detail mengenai kedalaman bit, palet, dan sebagainya.)

Memodifikasi Filter Bola untuk Mencari

Agar filter Ball dapat dicari, gunakan kelas CSourceSeeking , yang dirancang untuk menerapkan pencarian dalam filter dengan satu pin output. Tambahkan kelas CSourceSeeking ke daftar warisan untuk kelas CBallStream:

class CBallStream :  // Defines the output pin.
    public CSourceStream, public CSourceSeeking

Selain itu, Anda perlu menambahkan penginisialisasi untuk CSourceSeeking ke konstruktor CBallStream:

    CSourceSeeking(NAME("SeekBall"), (IPin*)this, phr, &m_cSharedState),

Pernyataan ini memanggil konstruktor dasar untuk CSourceSeeking. Parameternya adalah nama, penunjuk ke pin pemilik, nilai HRESULT , dan alamat objek bagian penting. Nama hanya digunakan untuk penelusuran kesalahan, dan makro NAME dikompilasi ke string kosong dalam build ritel. Pin langsung mewarisi CSourceSeeking, sehingga parameter kedua adalah penunjuk ke dirinya sendiri, ditransmisikan ke pointer IPin . Nilai HRESULT diabaikan dalam versi kelas dasar saat ini; tetap untuk kompatibilitas dengan versi sebelumnya. Bagian penting melindungi data bersama, seperti waktu mulai saat ini, waktu berhenti, dan laju pemutaran.

Tambahkan pernyataan berikut di dalam konstruktor CSourceSeeking :

m_rtStop = 60 * UNITS;

Variabel m_rtStop menentukan waktu berhenti. Secara default, nilainya adalah _I64_MAX / 2, yaitu sekitar 14.600 tahun. Pernyataan sebelumnya menetapkannya ke 60 detik yang lebih konservatif.

Dua variabel anggota tambahan harus ditambahkan ke CBallStream:

BOOL            m_bDiscontinuity; // If true, set the discontinuity flag.
REFERENCE_TIME  m_rtBallPosition; // Position of the ball. 

Setelah setiap perintah pencarian, filter harus mengatur bendera penghentian pada sampel berikutnya dengan memanggil IMediaSample::SetDiscontinuity. Variabel m_bDiscontinuity akan melacak hal ini. Variabel m_rtBallPosition akan menentukan posisi bola dalam bingkai video. Filter Bola asli menghitung posisi dari waktu streaming, tetapi waktu streaming diatur ulang ke nol setelah setiap perintah pencarian. Dalam aliran yang dapat dicari, posisi absolut independen dari waktu streaming.

QueryInterface

Kelas CSourceSeeking mengimplementasikan antarmuka IMediaSeeking . Untuk mengekspos antarmuka ini ke klien, ganti metode NonDelegatingQueryInterface :

STDMETHODIMP CBallStream::NonDelegatingQueryInterface
    (REFIID riid, void **ppv)
{
    if( riid == IID_IMediaSeeking ) 
    {
        return CSourceSeeking::NonDelegatingQueryInterface( riid, ppv );
    }
    return CSourceStream::NonDelegatingQueryInterface(riid, ppv);
}

Metode ini disebut "NonDelegating" karena cara kelas dasar DirectShow mendukung agregasi Model Objek Komponen (COM). Untuk informasi selengkapnya, lihat topik "Cara Menerapkan IUnknown" di DirectShow SDK.

Mencari Metode

Kelas CSourceSeeking mempertahankan beberapa variabel anggota yang berkaitan dengan pencarian.

Variabel Deskripsi Nilai Default
m_rtStart Waktu mulai Nol
m_rtStop Waktu henti _I64_MAX / 2
m_dRateSeeking Laju pemutaran 1,0

 

Implementasi CSourceSeeking dari IMediaSeeking::SetPositions memperbarui waktu mulai dan berhenti, lalu memanggil dua metode virtual murni pada kelas turunan, CSourceSeeking::ChangeStart dan CSourceSeeking::ChangeStop. Implementasi IMediaSeeking::SetRate serupa: Ini memperbarui laju pemutaran dan kemudian memanggil metode virtual murni CSourceSeeking::ChangeRate. Dalam setiap metode virtual ini, pin harus melakukan hal berikut:

  1. Panggil IPin::BeginFlush untuk mulai membersihkan data.
  2. Hentikan utas streaming.
  3. Hubungi IPin::EndFlush.
  4. Mulai ulang utas streaming.
  5. Panggil IPin::NewSegment.
  6. Atur bendera penghentian pada sampel berikutnya.

Urutan langkah-langkah ini sangat penting, karena alur streaming dapat memblokir saat menunggu untuk mengirimkan sampel atau mendapatkan sampel baru. Metode BeginFlush memastikan bahwa utas streaming tidak diblokir dan oleh karena itu tidak akan kebuntuan di langkah 2. Panggilan EndFlush menginformasikan filter hilir untuk mengharapkan sampel baru, sehingga mereka tidak menolaknya saat utas dimulai lagi di langkah 4.

Metode privat berikut melakukan langkah 1 hingga 4:

void CBallStream::UpdateFromSeek()
{
    if (ThreadExists()) 
    {
        DeliverBeginFlush();
        // Shut down the thread and stop pushing data.
        Stop();
        DeliverEndFlush();
        // Restart the thread and start pushing data again.
        Pause();
    }
}

Ketika utas streaming dimulai lagi, alur streaming memanggil metode CSourceStream::OnThreadStartPlay . Ambil alih metode ini untuk melakukan langkah 5 dan 6:

HRESULT CBallStream::OnThreadStartPlay()
{
    m_bDiscontinuity = TRUE;
    return DeliverNewSegment(m_rtStart, m_rtStop, m_dRateSeeking);
}

Dalam metode ChangeStart , atur waktu streaming ke nol dan posisi bola ke waktu mulai baru. Kemudian panggil CBallStream::UpdateFromSeek:

HRESULT CBallStream::ChangeStart( )
{
    {
        CAutoLock lock(CSourceSeeking::m_pLock);
        m_rtSampleTime = 0;
        m_rtBallPosition = m_rtStart;
    }
    UpdateFromSeek();
    return S_OK;
}

Dalam metode ChangeStop , panggil CBallStream::UpdateFromSeek jika waktu berhenti baru kurang dari posisi saat ini. Jika tidak, waktu berhenti masih di masa depan, jadi tidak perlu membersihkan grafik.

HRESULT CBallStream::ChangeStop( )
{
    {
        CAutoLock lock(CSourceSeeking::m_pLock);
        if (m_rtBallPosition < m_rtStop)
        {
            return S_OK;
        }
    }

    // We're already past the new stop time. Flush the graph.
    UpdateFromSeek();
    return S_OK;
}

Untuk perubahan tarif, metode CSourceSeeking::SetRate menetapkan m_dRateSeeking ke tarif baru (membuang nilai lama) sebelum memanggil ChangeRate. Sayangnya, jika penelepon memberikan tarif yang tidak valid—misalnya, kurang dari nol—sudah terlambat pada saat ChangeRate dipanggil. Salah satu solusinya adalah mengambil alih SetRate dan memeriksa tarif yang valid:

HRESULT CBallStream::SetRate(double dRate)
{
    if (dRate <= 1.0)
    {
        return E_INVALIDARG;
    }
    {
        CAutoLock lock(CSourceSeeking::m_pLock);
        m_dRateSeeking = dRate;
    }
    UpdateFromSeek();
    return S_OK;
}
// Now ChangeRate won't ever be called, but it's pure virtual, so it needs
// a dummy implementation.
HRESULT CBallStream::ChangeRate() { return S_OK; }

Menggambar di Buffer

Berikut adalah versi CSourceStream::FillBuffer yang dimodifikasi, rutinitas yang menggambar bola pada setiap bingkai:

HRESULT CBallStream::FillBuffer(IMediaSample *pMediaSample)
{
    BYTE *pData;
    long lDataLen;
    pMediaSample->GetPointer(&pData);
    lDataLen = pMediaSample->GetSize();
    {
        CAutoLock cAutoLockShared(&m_cSharedState);
        if (m_rtBallPosition >= m_rtStop) 
        {
            // End of the stream.
            return S_FALSE;
        }
        // Draw the ball in its current position.
        ZeroMemory( pData, lDataLen );
        m_Ball->MoveBall(m_rtBallPosition);
        m_Ball->PlotBall(pData, m_BallPixel, m_iPixelSize);
        
        // The sample times are modified by the current rate.
        REFERENCE_TIME rtStart, rtStop;
        rtStart = static_cast<REFERENCE_TIME>(
                      m_rtSampleTime / m_dRateSeeking);
        rtStop  = rtStart + static_cast<int>(
                      m_iRepeatTime / m_dRateSeeking);
        pMediaSample->SetTime(&rtStart, &rtStop);

        // Increment for the next loop.
        m_rtSampleTime += m_iRepeatTime;
        m_rtBallPosition += m_iRepeatTime;
    }
    pMediaSample->SetSyncPoint(TRUE);
    if (m_bDiscontinuity) 
    {
        pMediaSample->SetDiscontinuity(TRUE);
        m_bDiscontinuity = FALSE;
    }
    return NOERROR;
}

Perbedaan utama antara versi ini dan yang asli adalah sebagai berikut:

  • Seperti disebutkan sebelumnya, variabel m_rtBallPosition digunakan untuk mengatur posisi bola, daripada waktu streaming.
  • Setelah menahan bagian penting, metode memeriksa apakah posisi saat ini melebihi waktu berhenti. Jika demikian, ia mengembalikan S_FALSE, yang memberi sinyal kelas dasar untuk berhenti mengirim data dan mengirimkan pemberitahuan end-of-stream.
  • Stempel waktu dibagi dengan laju saat ini.
  • Jika m_bDiscontinuityTRUE, metode menetapkan bendera penghentian pada sampel.

Ada perbedaan kecil lainnya. Karena versi aslinya bergantung pada memiliki tepat satu buffer, versi ini mengisi seluruh buffer dengan nol sekali, saat streaming dimulai. Setelah itu, itu hanya menghapus bola dari posisi sebelumnya. Namun, pengoptimalan ini mengungkapkan sedikit bug di filter Bola. Ketika metode CBaseOutputPin::D ecideAllocator memanggil IMemInputPin::NotifyAllocator, metode ini mengatur bendera baca-saja ke FALSE. Akibatnya, filter hilir bebas untuk ditulis pada buffer. Salah satu solusinya adalah mengambil alih DecideAllocator sehingga mengatur bendera baca-saja ke TRUE. Namun, untuk kesederhanaan, versi baru hanya menghapus pengoptimalan sama sekali. Sebaliknya, versi ini mengisi buffer dengan nol setiap kali.

Perubahan Lain-lain

Dalam versi baru, kedua baris ini dihapus dari konstruktor CBall:

    m_iRandX = rand();
    m_iRandY = rand();

Filter Bola asli menggunakan nilai-nilai ini untuk menambahkan beberapa keacakan ke posisi bola awal. Untuk tujuan kita, kita ingin bola menjadi deterministik. Selain itu, beberapa variabel telah diubah dari objek CRefTime menjadi variabel REFERENCE_TIME . (Kelas CRefTime adalah pembungkus tipis untuk nilai REFERENCE_TIME .) Terakhir, implementasi IQualityControl::Notify sedikit dimodifikasi; Anda dapat merujuk ke kode sumber untuk detailnya.

Batasan Kelas CSourceSeeking

Kelas CSourceSeeking tidak dimaksudkan untuk filter dengan beberapa pin output, karena masalah dengan komunikasi pin silang. Misalnya, bayangkan filter pengurai yang menerima aliran video audio yang saling diselingi, membagi aliran menjadi komponen audio dan videonya, dan mengirimkan video dari satu pin output dan audio dari yang lain. Kedua pin output akan menerima setiap perintah pencarian, tetapi filter harus mencari hanya sekali per perintah pencarian. Solusinya adalah menunjuk salah satu pin untuk mengontrol pencarian dan mengabaikan perintah pencarian yang diterima oleh pin lainnya.

Namun, setelah perintah pencarian, kedua pin harus menghapus data. Untuk mempersulit masalah lebih lanjut, perintah pencarian terjadi pada utas aplikasi, bukan utas streaming. Oleh karena itu, Anda harus memastikan bahwa tidak ada pin yang diblokir dan menunggu IMemInputPin::Terima panggilan untuk kembali, atau dapat menyebabkan kebuntuan. Untuk informasi selengkapnya tentang pembilasan aman utas dalam pin, lihat Utas dan Bagian Penting.

Menulis Filter Sumber