DirectShow Custom Source Pin
Я новичок в DirectShow. Я, как и многие другие, пытаюсь создать потоковое P2P-решение на основе сокетов для карточной игры на основе WPF. Я хочу, чтобы каждый игрок мог видеть друг друга через маленькие видеоокна.
Мои вопросы двоякие. Первый: Как мне снизить частоту дискретизации и разрешение кадра? Я считаю, что 320x200 x 15 до 20 кадров в секунду должно быть хорошо. Я использую обратный вызов SampleGrabber для захвата данных кадра и отправки его через сокет; который фактически работает без сжатия при разрешении 640x480.
Мой второй вопрос: поскольку каждый кадр содержит 921 600 байт, это действительно тормозит, и я получаю очень медленный рендеринг только через локальную локальную сеть, подключенную к WiFi. Я добавил простое сжатие MJPEG (чтобы позже перейти на h.264) и заметил, что количество байтов упало до 330-360k. Неплохое улучшение.
На принимающей стороне Нужно ли мне создавать собственный исходный пин-код DirectShow, чтобы обслуживать байты, полученные из сокета, чтобы я мог прикрепить декодер и отобразить байты в окне?
Я просто хотел спросить об этом в первую очередь, так как кажется, что много работы по созданию нового COM-объекта (не делали этого около 15 лет!), Его регистрации и использования / отладки.
Есть ли другой способ?
Также, если это так, должен ли я использовать SampleGrabber на принимающей стороне и создать BitmapSource из распакованных байтов, или я должен позволить DirectShow создать дочернее окно? Дело в том, что я хочу иметь более одного другого игрока, и я установил дополнительный байт в сокет, чтобы сказать, в какой позиции стола они находятся. Как мне рендерить каждую позицию по очереди?
1 ответ
Для тех, кто заинтересован, вот как вы устанавливаете разрешение и добавляете кодировщик / компрессор:
// Create a graph builder
int hr = captureGraphBuilder.SetFiltergraph(graphBuilder);
// Find a capture device (WebCam) and attach it to the graph
sourceFilter = FindCaptureDevice();
hr = graphBuilder.AddFilter(sourceFilter, "Video Capture");
// Get the source output Pin
IPin sourcePin = DsFindPin.ByDirection((IBaseFilter)sourceFilter, PinDirection.Output, 0);
IAMStreamConfig sc = (IAMStreamConfig)sourcePin;
int count;
int size;
sc.GetNumberOfCapabilities(out count, out size);
VideoInfoHeader v;
AMMediaType media2 = null;
IntPtr memPtr = Marshal.AllocCoTaskMem(size);
for (int i = 0; i < count; ++i)
{
sc.GetStreamCaps(i, out media2, memPtr);
v = (VideoInfoHeader)Marshal.PtrToStructure(media2.formatPtr, typeof(VideoInfoHeader));
// Break when width is 160
if (v.BmiHeader.Width == 160)
break;
}
// Set the new media format t0 160 x 120
hr = sc.SetFormat(media2);
Marshal.FreeCoTaskMem(memPtr);
DsUtils.FreeAMMediaType(media2);
// Create a FramGrabber
IBaseFilter grabberF = (IBaseFilter)new SampleGrabber();
ISampleGrabber grabber = (ISampleGrabber)grabberF;
// Set the media type
var media = new AMMediaType
{
majorType = MediaType.Video,
subType = MediaSubType.MJPG
//subType = MediaSubType.RGB24
};
// The media sub type will be MJPG
hr = grabber.SetMediaType(media);
DsUtils.FreeAMMediaType(media);
hr = grabber.SetCallback(this, 1);
hr = graphBuilder.AddFilter(grabberF, "Sample Grabber");
IPin grabberPin = DsFindPin.ByDirection(grabberF, PinDirection.Input, 0);
// Get the MPEG compressor
Guid iid = typeof(IBaseFilter).GUID;
object compressor = null;
foreach (DsDevice device in DsDevice.GetDevicesOfCat(FilterCategory.VideoCompressorCategory))//.MediaEncoderCategory))
{
if (device.Name == "MJPEG Compressor")
{
device.Mon.BindToObject(null, null, ref iid, out compressor);
hr = graphBuilder.AddFilter((IBaseFilter)compressor, "Compressor");
break;
}
string name = device.Name;
}
// This also works!
//IBaseFilter enc = (IBaseFilter)new MJPGEnc();
//graphBuilder.AddFilter(enc, "MJPEG Encoder");
// Get the input and out pins of the compressor
IBaseFilter enc = (IBaseFilter)compressor;
IPin encPinIn = DsFindPin.ByDirection(enc, PinDirection.Input, 0);
IPin encPinOut = DsFindPin.ByDirection(enc, PinDirection.Output, 0);
// Attach the pins: source to input, output to grabber
hr = graphBuilder.Connect(sourcePin, encPinIn);
hr = graphBuilder.Connect(encPinOut, grabberPin);
// Free the pin resources
Marshal.ReleaseComObject(sourcePin);
Marshal.ReleaseComObject(enc);
Marshal.ReleaseComObject(encPinIn);
Marshal.ReleaseComObject(encPinOut);
Marshal.ReleaseComObject(grabberPin);
// Create a render stream
hr = captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, grabberF);
Marshal.ReleaseComObject(sourceFilter);
Configure(grabber);