Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
[Fitur yang terkait dengan halaman ini, DirectShow, adalah fitur warisan. Ini telah digantikan oleh MediaPlayer, IMFMediaEngine, dan Perekaman 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.]
[API ini tidak didukung dan mungkin diubah atau tidak tersedia di masa mendatang.]
Filter Sample Grabber adalah filter transform yang dapat digunakan untuk mengambil sampel media dari aliran ketika melewati grafik filter.
Jika Anda hanya ingin mengambil bitmap dari file video, lebih mudah untuk menggunakan Media Detector (MediaDet) objek. Lihat Mengambil Bingkai Poster untuk detailnya. Sampel Grabber lebih fleksibel, namun, karena bekerja dengan hampir semua jenis media (lihat ISampleGrabber::SetMediaType untuk beberapa pengecualian), dan menawarkan kontrol lebih besar kepada aplikasi.
- Membuat Filter Graph Manager
- Tambahkan Sampel Grabber ke Filter Graph
- Atur Jenis Media
- Membangun Graf Filter
- Menjalankan Grafik
- Ambil Sampel
- Kode Contoh
- Topik terkait
Membuat Manajer Grafik Filter
Untuk memulai, buatlah Filter Graph Manager, kemudian kueri untuk antarmuka IMediaControl dan IMediaEventEx.
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
Menambahkan Sampel Grabber ke Grafik Filter
Buat instans filter Sampel Grabber dan tambahkan ke grafik filter. Kueri filter Sample Grabber untuk antarmuka ISampleGrabber .
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
Mengatur Jenis Media
Ketika Anda pertama kali membuat Sampel Grabber, ia tidak memiliki jenis media pilihan. Ini berarti Anda dapat terhubung ke hampir semua filter dalam grafik, tetapi Anda tidak akan memiliki kontrol atas jenis data yang diterimanya. Sebelum membangun grafik lainnya, oleh karena itu, Anda harus mengatur jenis media untuk Sampel Grabber, dengan memanggil metode ISampleGrabber::SetMediaType.
Ketika Grabber Sampel terhubung, itu akan membandingkan jenis media ini dengan jenis media yang ditawarkan oleh filter lain. Satu-satunya bidang yang diperiksa adalah jenis utama, subjenis, dan jenis format. Untuk salah satu dari nilai-nilai ini, nilai GUID_NULL berarti "terima nilai apa pun." Umumnya, Anda ingin mengatur jenis dan subjenis utama. Misalnya, kode berikut menentukan video RGB 24-bit yang tidak dikompresi:
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
Membangun Graf Filter
Sekarang Anda dapat membangun grafik filter lainnya. Karena Sample Grabber hanya akan terhubung menggunakan jenis media yang telah Anda tentukan, ini memungkinkan Anda memanfaatkan mekanisme Intelligent Connect Filter Graph Manager saat Anda membuat grafik.
Misalnya, jika Anda menentukan video yang tidak dikompresi, Anda dapat menyambungkan filter sumber ke Sampel Grabber, dan Filter Graph Manager akan secara otomatis menambahkan pengurai file dan dekoder. Contoh berikut menggunakan fungsi pembantu ConnectFilters, yang tercantum dalam Sambungkan Dua Filter:
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
Sampel Grabber adalah filter transformasi, sehingga pin output harus terhubung ke filter lain. Seringkali, Anda mungkin hanya ingin membuang sampel setelah Anda selesai dengan sampel tersebut. Dalam hal ini, sambungkan Sample Grabber ke Penyaring Perender Null , yang membuang data yang diterimanya.
Contoh berikut menghubungkan Sampel Grabber ke filter Null Renderer:
IBaseFilter *pNullF = NULL;
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
Ketahuilah bahwa menempatkan Sampel Grabber antara dekoder video dan perender video dapat secara signifikan merusak performa penyajian. Sample Grabber adalah filter trans-in-place, yang berarti buffer output sama dengan buffer input. Untuk penyajian video, buffer output kemungkinan terletak pada kartu grafis, di mana operasi baca jauh lebih lambat, dibandingkan dengan operasi baca di memori utama.
Jalankan Grafik
Sample Grabber beroperasi dalam salah satu dari dua mode:
- Mode buffering membuat salinan setiap sampel sebelum mengirimkan sampel di hilir.
- Mode panggilan balik memanggil fungsi panggilan balik yang ditentukan aplikasi pada setiap sampel.
Artikel ini menjelaskan mode buffering. (Sebelum menggunakan mode panggilan balik, ketahuilah bahwa fungsi panggilan balik harus cukup terbatas. Jika tidak, itu dapat secara drastis mengurangi performa atau bahkan menyebabkan kebuntuan. Untuk informasi selengkapnya, lihat ISampleGrabber::SetCallback.) Untuk mengaktifkan mode buffering, panggil metode ISampleGrabber::SetBufferSamples dengan nilai TRUE.
Secara opsional, panggil metode ISampleGrabber::SetOneShot dengan nilai TRUE. Hal ini menyebabkan Sample Grabber berhenti setelah menerima sampel media pertama, yang bermanfaat jika Anda ingin mengambil satu bingkai dari aliran. Cari ke waktu yang diinginkan, jalankan grafik, dan tunggu peristiwa EC_COMPLETE. Perhatikan bahwa tingkat akurasi bingkai tergantung pada sumbernya. Misalnya, mencari file MPEG sering kali tidak akurat secara frame.
Untuk menjalankan grafik secepat mungkin, matikan jam grafik seperti yang dijelaskan di Mengatur jam grafik.
Contoh berikut memungkinkan mode satu bidikan dan mode buffering, menjalankan grafik filter, dan menunggu penyelesaian.
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
Ambil Sampel
Dalam mode buffering, Sample Grabber menyimpan salinan setiap sampel. Metode ISampleGrabber::GetCurrentBuffer menyalin buffer ke dalam array yang dialokasikan pemanggil. Untuk menentukan ukuran array yang diperlukan, pertama-tama panggil GetCurrentBuffer dengan penunjuk NULL untuk alamat array. Kemudian alokasikan array dan panggil metode untuk kedua kalinya untuk menyalin buffer. Contoh berikut menunjukkan langkah-langkah ini.
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
Anda perlu mengetahui format data yang tepat dalam buffer. Untuk mendapatkan informasi ini, panggil metode ISampleGrabber::GetConnectedMediaType. Metode ini mengisi format ke dalam struktur AM_MEDIA_TYPE.
Untuk aliran video yang tidak dikompresi, informasi format terkandung dalam strukturVIDEOINFOHEADER. Contoh berikut menunjukkan cara mendapatkan informasi format untuk aliran video yang tidak dikompresi.
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
Nota
Grabber Sampel tidak mendukung VIDEOINFOHEADER2.
Contoh Kode
Berikut adalah kode lengkap untuk contoh sebelumnya.
#include <windows.h>
#include <dshow.h>
#include "qedit.h"
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
HRESULT WriteBitmap(PCWSTR, BITMAPINFOHEADER*, size_t, BYTE*, size_t);
HRESULT GrabVideoBitmap(PCWSTR pszVideoFile, PCWSTR pszBitmapFile)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
BYTE *pBuffer = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
done:
CoTaskMemFree(pBuffer);
SafeRelease(&pPin);
SafeRelease(&pEnum);
SafeRelease(&pNullF);
SafeRelease(&pSourceF);
SafeRelease(&pGrabber);
SafeRelease(&pGrabberF);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
return hr;
};
// Writes a bitmap file
// pszFileName: Output file name.
// pBMI: Bitmap format information (including pallete).
// cbBMI: Size of the BITMAPINFOHEADER, including palette, if present.
// pData: Pointer to the bitmap bits.
// cbData Size of the bitmap, in bytes.
HRESULT WriteBitmap(PCWSTR pszFileName, BITMAPINFOHEADER *pBMI, size_t cbBMI,
BYTE *pData, size_t cbData)
{
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
BITMAPFILEHEADER bmf = { };
bmf.bfType = 'MB';
bmf.bfSize = cbBMI+ cbData + sizeof(bmf);
bmf.bfOffBits = sizeof(bmf) + cbBMI;
DWORD cbWritten = 0;
BOOL result = WriteFile(hFile, &bmf, sizeof(bmf), &cbWritten, NULL);
if (result)
{
result = WriteFile(hFile, pBMI, cbBMI, &cbWritten, NULL);
}
if (result)
{
result = WriteFile(hFile, pData, cbData, &cbWritten, NULL);
}
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hFile);
return hr;
}
Topik terkait