Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.137.192.3] |
|
Сообщ.
#1
,
|
|
|
Что-то меня заклинило на вроде бы простом вопросе
Итак, есть некая программа захвата видео, основанная на direct show. Она даже работает, но очень спагеттиобразно написана, поэтому стал разделять куски на отдельные классы с самостоятельным функционированием. Суть проблемы. В юните есть основной класс TDSCapturer = class(TObject) private public end; И есть класс, методы которого вызываются callback'ом после получения каждого кадра TVideoGrabCB = class(TComponent, ISampleGrabberCB) private function SampleCB(SampleTime: Double; pSample: IMediaSample): HResult; stdcall; function BufferCB(SampleTime: Double; pBuffer: PByte; BufferLen: longint): HResult; stdcall; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: integer; stdcall; function _Release: integer; stdcall; end; Понятно, что захваченный кадр мне надо передать из функции обработки callback в основной класс. И вот тут я начинаю тупить. Простейший способ - разместить буфер в разделе implementation. И это работает. Но это решение мне совершенно не нравится, так как а) всё, что находится в implementation, автоматом доступно и базовой программе, которая этот юнит вызывает. Что мне совершенно не надо (чревато всевозможными глюками в случае, если вдруг совпадёт какое-нибудь имя, например). б) если мне вдруг потребуется создать несколько экземпляров основного класса TDSCapturer (скажем, организовать многоканальный захват), то находящееся в implementation будет общим для всех экземпляров класса. И понять - какой именно граф захвата положил в общий буфер очередной кадр - будет невозможно в принципе. То есть в идеале - буфер лежит в разделе private основного класса. Но как тогда к нему обратиться из callback метода совершенно другого класса? Тут видится два подхода - либо каким-то образом сделать коллбек класс "подклассом" основного класса (не знаю - возможно ли это в принципе?). Либо при создании экземпляра коллбек класса передавать ему ссылку на основной класс и дальше заполнять буфер через эту ссылку. Либо еще как-то? Что-то заклинило меня... |
Сообщ.
#2
,
|
|
|
Пока выкрутился так:
Создаю callback'и с указанием self как owner. В самих callback пишу with Owner as TDSCapturer do begin end; Ну и внутри обращаюсь к private переменным класса. Корректен ли такой подход? Или есть какой-то подход лучше? |
Сообщ.
#3
,
|
|
|
Иметь в классе поле для хранения связанного класса и заполнять его в конструкторе или через свойство - нормальная практика. Тот же Owner или Parent, скажем.
TVideoGrabCB = class(TComponent, ISampleGrabberCB) private FCapturer: TDSCapturer; ... >аким-то образом сделать коллбек класс "подклассом" основного класса В свежих версиях Delphi есть вложенные (nested) классы, но полей внешнего они не видят, так что пользы тут нет. |
Сообщ.
#4
,
|
|
|
Цитата An_private @ Либо при создании экземпляра коллбек класса передавать ему ссылку на основной класс и дальше заполнять буфер через эту ссылку. Либо еще как-то? Что-то заклинило меня... Сразу предупреждаю, что не использую Delphi. Но поскольку вопрос структурный, это не важно. Возможен такой вариант: Допустим получили какие-то данные, хоть даже графические. Хоть в другом потоке. 1. Создадим в памяти объект для их хранения (если нужно) и заполним его данными. 2. Поместим указатель на объект в очередь. В общем случае - потокобезопасную. 3. Пошлём сообщение диспетчеру, о том, что получено. (Например, заранее предусмотренное сообщение главному окну. 4. Диспетчер запустит обработчик (или процедуру любого предусмотренного класса). Который извлечёт указатель на контейнер из очереди и дальше.. всё, что предусмотрено алгоритмом. 5. Уничтожим не нужный уже контейнер. 6. Чтобы не заниматься массовым созданием/уничтожением контейнеров в памяти, предусмотрим обратную очередь с указателями на уже созданные контейнеры. 7. Тогда модифицируем 1-й пункт - прежде, чем создавать новый контейнер, проверим обратную очередь, на предмет наличия пустых контейнеров. Если есть - не будем создавать новый, будем использовать готовый. Как-то так. |
Сообщ.
#5
,
|
|
|
Owner совершенно нормальная практика. Только логичнее наверно назвать его Destination - неочевидно, что класс коллбэка должен иметь владельца и какой класс у него должен быть.
Доступ к private делать можно, но тут чисто субъективно я выделяю "юнит-доступные" поля в отдельный protected раздел с комментарием: Tsample = class strict private ... protected // для доступа из других классов модуля ... public ... end Это держит структуру доступа в строгости и наглядности и облегчает обращение с кодом. Кстати, переменные в implementation как раз недоступны извне. |
Сообщ.
#6
,
|
|
|
Цитата MBo @ Иметь в классе поле для хранения связанного класса и заполнять его в конструкторе или через свойство - нормальная практика. Ясно. То есть я в очередной раз изобрел велосипед и это работать будет Спасибо Цитата ЫукпШ @ Чтобы не заниматься массовым созданием/уничтожением контейнеров в памяти, предусмотрим обратную очередь с указателями на уже созданные контейнеры. Ну, у меня классическая ситуация single writer - single reader. Поэтому у меня заранее создан массив на 250 кадров (10 секунд). В каждом кадре есть ссылка на кадровый буфер (выделяется сразу при создании экземпляра класса) и два boolean поля - used и filled. Соответственно, когда callback должен сбросить кадр - он ищет кадр с used=false, ставит в нём used в true и копирует в него данные. По завершению процесса ставит поле filled в true. Основной класс ищет кадры с filled=true, забирает из них данные, после чего ставит filled и used в false. |