Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Передача видео и звука с помощью Indy [D7]


Страницы:   1 2 3 4 5 6 7 8 9 10

3asys ©   (2011-11-26 15:48:00) 

На клиенте получаю видео и звук с web-камеры (с встроенным микрофоном) с помощью DirectShow.
Нужно передать их серверу для рассылки другим клиентам.
Видео и звук с Web-камеры получаю стандартным способом, описанным в примерах к DSPack, - примерно так:

var
 multiplexer: IBaseFilter;
 Writer: IFileSinkFilter;
 PinList: TPinList;
 i: integer;
begin
with MainForm do
begin
 CaptureGraph.Active := true;
 if AudioSourceFilter.FilterGraph <> nil then
 begin
   PinList := TPinList.Create(AudioSourceFilter as IBaseFilter);
   i := 0;
   while i < PinList.Count do
     if PinList.PinInfo[i].dir = PINDIR_OUTPUT then
       begin
         if AudioFormats.ItemIndex <> -1 then
           with (PinList.Items[i] as IAMStreamConfig) do
             SetFormat(AudioMediaTypes.Items[AudioFormats.ItemIndex].AMMediaType^);
         PinList.Delete(i);
       end else inc(i);
   if InputLines.ItemIndex <> -1 then
     with (PinList.Items[InputLines.ItemIndex] as IAMAudioInputMixer) do
       put_Enable(true);
   PinList.Free;
 end;

 if VideoSourceFilter.FilterGraph <> nil then
 begin
   PinList := TPinList.Create(VideoSourceFilter as IBaseFilter);
   if VideoFormats.ItemIndex <> -1 then
     with (PinList.First as IAMStreamConfig) do
       SetFormat(VideoMediaTypes.Items[VideoFormats.ItemIndex].AMMediaType^);
   PinList.Free;
 end;

 with CaptureGraph as IcaptureGraphBuilder2 do
 begin
   // set the output filename
   SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CapFile), multiplexer, Writer);

   if VideoSourceFilter.BaseFilter.DataLength > 0 then
     RenderStream(@PIN_CATEGORY_PREVIEW, nil, VideoSourceFilter as IBaseFilter,
       nil , form1.VideoWindow as IBaseFilter);

   if VideoSourceFilter.FilterGraph <> nil then
     RenderStream(@PIN_CATEGORY_CAPTURE, nil, VideoSourceFilter as IBaseFilter,
       nil, multiplexer as IBaseFilter);

   if AudioSourceFilter.FilterGraph <> nil then
   begin

     RenderStream(nil, nil, AudioSourceFilter as IBaseFilter,
       nil, multiplexer as IBaseFilter);
   end;
 end;
 CaptureGraph.Play;

В RenderStream получаем потоки, но как их передать через IdTCPClient, IdTCPServer я сообразить не могу.
Как это сделать?


Германн   (2011-11-29 00:28:00) [141]


> Это не я, это Delphi XE2. Пространства имен такие теперь
> там.

Хм. Спасибо за ответ.


DVM   (2011-11-29 00:19:00) [142]


> Dennis I. Komarov ©   (28.11.11 23:58) [44]


> Вот 50 - (http)серверов каждый из которых будет слать 50-
> и клиентам видео, и не сжатый поток, а полные кадры...

Да дался вам этот HTTP и 50 клиентов. Ну кто заставляет слать полные JPEG кадры? Можно сжимать в MPEG4 или H264 и слать P, I кадры, указывая, где какой в HTTP подзаголовках. Принцип тот же - multipart/x-mixed-replace. HTTP не такой медленный как многие думают, он же бинарный по сути, так что скорость сопоставима с просто TCP.
UDP же требует кучи кода, предназначенного для контроля ошибок и собственно в RTP и RTCP это и делается.

50 клиентов вполне реально, только не надо гнать все через один сервер. Пусть гоняют между собой видео.


> Германн ©   (29.11.11 00:02) [45]


> Зачем вы, Дмитрий, добавили эти префиксы?

Это не я, это Delphi XE2. Пространства имен такие теперь там.


Германн   (2011-11-29 00:02:00) [143]


> winapi.windows не находит что-то


> DVM ©   (28.11.11 23:10) [38]

А в самом деле. Зачем вы, Дмитрий, добавили эти префиксы?


Dennis I. Komarov   (2011-11-28 23:58:00) [144]


> У тебя задача пока хотя бы передать данные между 2-мя пользователями.
>  А там дальше будешь думать.

А потом он все с нуля переделвать будет... :)

> мне необходимо реализовать конференцию в минимальные сроки
> и чтобы она работала с приемлемым качеством человек на 50.
>  Как я понял, самый быстрый способ - http - поэтому HTTP

Вот 50 - (http)серверов каждый из которых будет слать 50-и клиентам видео, и не сжатый поток, а полные кадры...

З.Ы. Я конечно не делал видеоконференции, но в такую реализацию мне чего-то не верится... :)


3asys   (2011-11-28 23:33:00) [145]

разобрался :)
СПАСИБО ВАМ ОГРОМНОЕ ЗА ВРЕМЯ И УСИЛИЯ


3asys   (2011-11-28 23:27:00) [146]

winapi.windows не находит что-то. что подключить?


DVM   (2011-11-28 23:23:00) [147]

Клиента завтра будем делать.
Ну и все объяснения тоже завтра.


DVM   (2011-11-28 23:19:00) [148]

Особые моменты в коде.

AResponseInfo.ContentLength := 1000000;
Indy всегда пытается всунуть в заголовок ответа сервера ContentLength, идеально было бы вообще без него, но от него не избавиться, поэтому ставим заведомо большое число, оно мало на что влияет, но 0 ставить нельзя.

Sleep(100);
Костыль. Ограничивает частоту кадров на клиенте. Частоту кадров стоит вычислять более умно. Хотя можно просто ограничить скажем величиной 25. Но все равно надо рассчитать тогда паузу между кадрами, чтоб получалось 25 кадров в секунду.


DVM   (2011-11-28 23:15:00) [149]

Итак, что тут к чему. Во-первых, это лишь пример, иллюстрирующий принцип. Не надо отсюда слепо копировать.
На форму кинуть таймер и TIdHTTPServer. Таймер нужен лишь для генерации картинок (типа кадры), интервал у таймера стоит 100.
TidHTTPServer слушает порт 8081.

Запускаем все это дело, берем Firefox (и только его, другие браузеры не понимают этот формат) и обращаемся в нем по адресу http://127.0.0.1:8081
Видим в окне браузера сменяющие друг друга картинки, фактически видео.


DVM   (2011-11-28 23:10:00) [150]


unit uMain;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.SyncObjs, Vcl.Imaging.jpeg,
 Vcl.ExtCtrls,

 IdBaseComponent, IdComponent, IdTCPServer, IdCustomHTTPServer, IdGlobal,
 IdHTTPServer, IdCustomTCPServer, IdContext, IdSchedulerOfThread, IdGlobalProtocols;

type

 TSafeBuffer = class(TMemoryStream)
 strict private
   FLock: TCriticalSection;
 public
   constructor Create;
   destructor Destroy; override;
   procedure Lock;
   procedure Unlock;
 end;

 TfrmMin = class(TForm)
   tmrUpdateFrame: TTimer;
   idhtpsrvrMain: TIdHTTPServer;
   procedure tmrUpdateFrameTimer(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure idhtpsrvrMainCommandGet(AContext: TIdContext;
     ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
 private
   { Private declarations }
 public
   Buffer: TSafeBuffer;
 end;

var
 frmMin: TfrmMin;

implementation

{$R *.dfm}

constructor TSafeBuffer.Create;
begin
 FLock := TCriticalSection.Create;
 inherited Create;
end;

destructor TSafeBuffer.Destroy;
begin
 inherited Destroy;
 FLock.Free;
end;

procedure TSafeBuffer.Lock;
begin
 FLock.Enter;
end;

procedure TSafeBuffer.Unlock;
begin
 FLock.Leave;
end;

procedure TfrmMin.FormCreate(Sender: TObject);
begin
 Buffer := TSafeBuffer.Create;
end;

procedure TfrmMin.FormDestroy(Sender: TObject);
begin
 Buffer.Free;
end;

procedure TfrmMin.idhtpsrvrMainCommandGet(AContext: TIdContext;
 ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
const
 Boundary = '--myboundary';
 CRLF = #13#10;
var
 Stream: TMemoryStream;
 SubHeader: AnsiString;
begin
 Stream := TMemoryStream.Create;
 try
   AResponseInfo.FreeContentStream := false;
   AResponseInfo.Server := 'StreamServer';
   AResponseInfo.CacheControl := 'no-cache';
   AResponseInfo.Pragma := 'no-cache';
   AResponseInfo.Expires := Now;
   AResponseInfo.CharSet :='';
   AResponseInfo.Connection := 'close';
   AResponseInfo.ContentType := 'multipart/x-mixed-replace; boundary=' + Boundary;
   AResponseInfo.ContentLength := 1000000;
   AResponseInfo.WriteHeader;
   while ((not (AContext.Yarn as TIdYarnOfThread).Thread.Terminated) and (AContext.Connection.Connected)) do
     begin
       Buffer.Lock;
       try
         AResponseInfo.ContentLength := Buffer.Size;
         SubHeader := AnsiString(Boundary + CRLF +
                           'Content-Type: image/jpeg' + CRLF +
                           'Content-Length: ' + IntToStr(AResponseInfo.ContentLength) + CRLF + CRLF);
         Stream.Size := 0;
         Stream.Write(SubHeader[1], length(SubHeader));
         Stream.Write(Buffer.Memory^, Buffer.Size);
       finally
         Buffer.Unlock;
       end;
        Stream.Position := 0;
       AResponseInfo.ContentStream := Stream;
       AResponseInfo.WriteContent;

       
       Sleep(100);
     end;
 finally
   Stream.Free;

 end;

end;

procedure TfrmMin.tmrUpdateFrameTimer(Sender: TObject);
var
 Bmp: TBitmap;
 JPG: TJPEGImage;
begin
 Bmp := TBitmap.Create;
 try
   Bmp.Width := 320;
   Bmp.Height := 240;
   Bmp.PixelFormat :=pf24bit;
   Bmp.Canvas.TextOut(50, 50, FormatDateTime('hh:nn:ss.zzz', Now));
   JPG := TJPEGImage.Create;
   try
     JPG.Assign(Bmp);
     Buffer.Lock;
     try
       Buffer.Size := 0;
       JPG.SaveToStream(Buffer);
       Buffer.Position :=0;
     finally
       Buffer.Unlock;
     end;
   finally
     JPG.Free;
   end;
 finally
   Bmp.Free;
 end;
end;

end.


DVM   (2011-11-28 22:30:00) [151]


> 3asys ©   (28.11.11 22:25) [36]

Ну HTTP так HTTP. Щас набросаю тебе пример.


3asys   (2011-11-28 22:25:00) [152]

мне необходимо реализовать конференцию в минимальные сроки и чтобы она работала с приемлемым качеством человек на 50. Как я понял, самый быстрый способ - http - поэтому HTTP


DVM   (2011-11-28 22:18:00) [153]

Короче у тебя 3 пути:

1) Использовать самописный протокол для коммутации и передачи медиаданных.
2) Использовать HTTP и для передачи и для коммутации.
3) Использовать SIP для коммутации и RTP для передачи (вот этот вариант самый правильный, и вобщем то повсеместно используется в IP телефонии)
4) Использовать что-то типа http://ru.wikipedia.org/wiki/XMPP

Что выбираешь?


3asys   (2011-11-28 22:10:00) [154]

согласен


DVM   (2011-11-28 22:09:00) [155]


> я полагал, что должно быть N клиентов, каждый из которых
> передает свои видео и звук серверу, а сервер транслирует
> их всем остальным.  Это не так?

Это может быть и так, а можно и по другому. Если все будет проходить через сервер, то нагрузка на него будет большая при большом числе клиентов, если видео не будет идти через сервер, то нагрузка на него будет минимальная, но будет меньше контроля. Вообще все это - это вопрос коммутации, это отдельный разговор. Вот SIP или RTSP и есть протоколы, которые предназначены для коммутации. У тебя задача пока хотя бы передать данные между 2-мя пользователями. А там дальше будешь думать.


3asys   (2011-11-28 21:24:00) [156]

это видеоконференция и в ней каждый клиент передает другим видео и звук со своей web-камеры/микрофона.
я полагал, что должно быть N клиентов, каждый из которых передает свои видео и звук серверу, а сервер транслирует их всем остальным.  Это не так?


DVM   (2011-11-28 19:53:00) [157]

Если нужна двусторонняя передача видео, то для http каждый клиент должен быть одновременно сервером и клиентом для сервера другого клиента так как в http данные всегда передаются в основном канале в отличие от RTSP или SIP в котором RTP данные могут передаваться как поверх основного канала так и в независимых и в обоих направлениях.


3asys   (2011-11-28 19:25:00) [158]

:) про  IdHTTPServer  понятно (написал не посмотрев сообшения)


3asys   (2011-11-28 19:22:00) [159]

В качестве http сервера IdHTTPServer подойдет?
Если буфер обмена создается на клиенте, как данные от клиента попадут на сервер? или каждый клиент одновременно и сервер?


DVM   (2011-11-28 19:18:00) [160]

Как показывает практика не накладно. Мы же не будем для каждого кадра делать запрос, запрос будет сделан 1 раз.Остальное позже напишу - с телефона неудобно.


Страницы:   1 2 3 4 5 6 7 8 9 10

Наверх

Титульная страница Поиск, карта сайта Написать письмо