![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Разделение аудио потоков в AVI [D4, D5, D6, WinXP]
Noby © (2006-01-30 14:07:00)Подскажите, пожалуйста. Каким образом можно в своей программе не используя компонентов разделить аудио потоки в avi файлах и воспроизводить только тот, который нужен?
Нужен код. Заранее спасибо!
Noby (2006-02-06 01:28:00) [1]Огромное человеческое тебе СПАСИБО!!! выручил очень сильно:-)... буду пробовать
NailMan (2006-02-04 13:28:00) [2]Функция потрошения загововков AVI-RIFF(только модификаций AVI). Собрана с учетом нескольких модификаций структур заголовка, которые пользуют разные программы создания видео.
На выходе выходит заполненная структура, которая и используется далее для удобного выуживания нужной информации о файле.
В теле часто встречается ненужная строчкаmove(PD^,FCC,4);- это для того чтобы контролировать процесс навигации по секциям во время отладки парсинга нового вида заголовка(с какой-то левой неизвестной секцией). В целом прибить их можно или закоментить.
type
FCC_Type = array[0..3] of char;
Const
FCC_RIFF : FCC_Type = 'RIFF';
FCC_hdrl : FCC_Type = 'hdrl';
FCC_AVI : FCC_Type = 'AVI ';
FCC_avih : FCC_Type = 'avih';
FCC_strl : FCC_Type = 'strl';
FCC_strh : FCC_Type = 'strh';
FCC_strf : FCC_Type = 'strf';
FCC_strd : FCC_Type = 'strd';
FCC_strn : FCC_Type = 'strn';
FCC_MOVI : FCC_Type = 'movi';
FCC_LIST : FCC_Type = 'LIST';
FCC_JUNK : FCC_Type = 'JUNK';
FCC_indx : FCC_Type = 'indx';
FCC_vids : FCC_Type = 'vids';
FCC_MPEG : FCC_Type = chr($0)+ chr($0) + chr($01)+ chr($BA);
TYPE
TFOURCC = array[0..3] of char;
TVidsFormat = record
BMfmt : TBITMAPINFOHEADER;
DVFmt : TDVINFO;
end;
TVideoStream = record
st : TAVIStreamHeader;
FMT : TVidsFormat;
end;
TAudioStream = record
st : TAVIStreamHeader;
WAVFmt : TWaveFormatEx;
end;
TAviInfo = record
AVIHeader : TAVIMainHeader;
audstreams : DWORD;
vids : TvideoStream;
auds : Array[0..4] of TAudioStream;
CRC : Int64;
end;
Function ReadAVIHeader(Const FName:String;Var Avi:TAviInfo):Cardinal;
var f:Integer;
p,PD:pointer;
NulDD,i,leng:Cardinal;
audstreams:dword;
FCC,FCC2 : Array[0..3] of char;
MH : ^TAVIMainHeader;
SH : ^TAVIStreamHeader;
VFH : ^TVidsFormat;
WVH : ^TWaveFormatEx;
begin
Result:=1; //По умолчанию - AVIVideo
Zeromemory(@AVi,sizeof(TAviInfo));
if FileAutoDetection then avi.CRC:=getfilecrc(fname);
F:=Fileopen(fname,fmopenread or fmsharedenynone);
fileread(f,fcc,4); //RIFF
fileread(f,nuldd,4);
fileread(f,fcc2,4); //AVI_
if (fcc=fcc_riff) and (fcc2<>FCC_AVI) then
begin
result:=0;
Fileclose(F);
exit;
end;
if (fcc<>fcc_riff) then
begin
If FCC=FCC_mpeg then result:=2
else
result:=0;
Fileclose(F);
exit;
end;
Fileread(f,fcc,4); //LIST
fileread(f,nuldd,4);
getmem(p,nuldd+1024);
fileread(f,p^,nuldd);
fileclose(f);
PD:=P; //stay on 'hdrl'
inc(DWORD(PD),4); //stay on 'avih'
MH:=PD;
AVi.AVIHeader.dwMicroSecPerFrame:=MH^.dwMicroSecPerFrame;
avi.AVIHeader.dwMaxBytesPerSec:=mh^.dwMaxBytesPerSec;
avi.AVIHeader.dwFlags:=mh^.dwFlags;
avi.AVIHeader.dwTotalFrames:=mh^.dwTotalFrames;
avi.AVIHeader.dwStreams:=mh^.dwStreams;
inc(DWORD(PD),sizeof(TAVIMainHeader)); //Stay on LIST
audstreams:=0;
move(PD^,FCC,4);
For i:=0 to avi.AVIHeader.dwStreams-1 do
Begin
inc(DWORD(PD),4); //stay on 'strl' length
move(PD^,FCC,4);
inc(DWORD(PD),4); //Stay on 'strl'
move(PD^,FCC,4);
inc(DWORD(PD),4); //stay on 'strh'
move(PD^,FCC,4);
SH:=PD;
if SH^.fccType=ConvertFCCToDWORD(FCC_vids) then
begin
avi.vids.st:=sh^;
inc(DWORD(PD),sh^.cb+8); //stay on 'strf'
move(PD^,FCC,4);
inc(DWORD(PD),4); //stay on length 'strf'
move(PD^,leng,4);
inc(DWORD(PD),4); //stay on 'strf' data
VFH:=PD;
avi.vids.FMT.BMfmt:=VFH^.BMfmt;
if leng>56 then
avi.vids.FMT.DVFmt:=VFH^.DVFmt;
inc(DWORD(PD),leng);//stay on LIST
move(PD^,FCC,4);
end;
if SH^.fccType=ConvertFCCToDWORD('auds') then begin
avi.auds[audstreams].st:=SH^;
inc(DWORD(PD),sh^.cb+8); //stay on 'strf'
move(PD^,FCC,4);
inc(DWORD(PD),4); //stay on length 'strf'
move(PD^,leng,4);
inc(DWORD(PD),4); //stay on 'strf' data
WVH:=PD;
avi.auds[audstreams].WAVFmt:=WVH^;
inc(DWORD(PD),leng);
inc(audstreams);
end;
Move(PD^,FCC,4);//Stay on JUNK or 'strn'
If (FCC=FCC_JUNK) or (FCC=FCC_strn) or (FCC=FCC_strd) or (FCC=FCC_indx) then
begin
inc(DWORD(PD),4);
move(PD^,nuldd,4);
If nuldd mod 2<>0 then inc(nuldd);
inc(DWORD(PD),4+NULDD);
move(PD^,FCC,4);
end;
End;
avi.audstreams:=audstreams;
freemem(p);
end;
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
Noby (2006-02-04 05:22:00) [3]Не думал что это такая трудоемкая работа :( Скинь плыз AVI-RIFF, попробую что сделать.
Спасибо и на этом
NailMan (2006-02-03 11:22:00) [4]Noby
> Есть еще один вопросик относительно звука… можно ли вывести
> выбранный звук в буфер и в последствии сохранить его отдельно
> от видео в любой выбранный пользователем формат?
Можно выдрать фреймы аудиодорог и собрать их в любой файл или по крайней мере в Wav, но для этого придется по любому растормошить заголовок AVI-RIFF, таблицу индексов в конце файла и потом по таблице разобрать всю секцию данных выдергивая фреймы. Тут нужно знать формат и структуру WAV(это вобщем-то тот же AVI-RIFF только упрощенный).
Чтобы конвертить в другие форматы(в т.ч. и сжатие), можно сделать свой граф на основе исходного, повыкидывать оттуда все ненужное и внедрить на место аудиорендерера кодек сжатия и вобщем-то неизвестный фильтр записи файла на диск или фильтр-заглушку с которой можно получить зажатый фрейм аудио и потом его записывать в нужный формат(*.mp3 например).
Вобщем в этом деле много сложностей. самое простое это просто вырезать дородку по фреймам и в wav слить, как я предложил в первом варианте. Так делает VirtualDub например.
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
Noby (2006-02-03 02:27:00) [5]NailMan
Спасибо за инфу очень полезная для меня и нужная.
Есть еще один вопросик относительно звука… можно ли вывести выбранный звук в буфер и в последствии сохранить его отдельно от видео в любой выбранный пользователем формат?
Хотелось бы конечно выудить все инфу из файла но наверное мне не дано это сделать :( поскольку толком в сети я не смог талькового найти ничего
Noby (2006-02-03 02:22:00) [6]Дима
У меня та же проблема была… у меня нормально работает так: SetMultipleVolume(-1, 0). Глушатся только те что не должны работать, а воспроизведение сразу двух дорожек при этом не прослушивается :).
-1 это номер дорожки, а 0 глушим не нужное.
NailMan (2006-01-31 23:14:00) [7]Вот так попробуй:
Procedure TVideoEngine.SetMultipleVolume(TrackID:integer;vLevel:integer);
var
pEnum : IEnumFilters;
pFilter :IBaseFilter;
cFetched : dword;
CL:TGUID;
BA : IBasicAudio;
z : integer;
begin
repeat
// Get filter enumerator
dsGraphBuilder.EnumFilters(pEnum);
while pEnum.Next(1, pFilter, @cFetched) = S_OK do
begin
pFilter.GetClassID(CL);
if IsEqualGUID(CL,CLSID_DSoundRender) then
begin
If i<>TrackID then
z:=-10000 else z:=vLevel;
pFilter.QueryInterface(IID_IBasicAudio,BA);
BA.put_Volume(VolumeTable[z]);
BA:=nil;
end;
pFilter:=nil;
end;
pFilter:=nil;
pEnum:=nil;
end;
значение массива VolumeTable тоже что и было.
Указываешь номер дороги и громкость, остальные глушатся в ноль. Алгоритм тут жесткий, что не наше, то глушим.
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
NailMan (2006-01-31 23:10:00) [8]Noby
можно узнать несколькими способами.
Самый простой(имея файлик типа DirectX9_c.chm из SDK где описаны структуры AVI-RIFF) - это распотрошить заголовок AVI-RIFF. там все структурированно описано что за потоки, каких форматов, кодеки(номера в случае аудио, чтобы узнать название надо энумератором по реесту поковырять, если надо могу дать процедурку ;-) ) и пр.
В заголовке еще много всякой полезной инфы выудить можно, включая все кодеки всех потоков, длительности, частоты дискретизации, etc...
В общем случае на основе вышеприведенной процедуры делается следующая функция:
Function TVideoEngine.GetAudioTracks:Integer;
var
pEnum : IEnumFilters;
pFilter :IBaseFilter;
cFetched : dword;
CL:TGUID;
begin
result:=0;
// Get filter enumerator
dsGraphBuilder.EnumFilters(pEnum);
while pEnum.Next(1, pFilter, @cFetched) = S_OK do
begin
pFilter.GetClassID(CL);
if IsEqualGUID(CL,CLSID_DSoundRender) then inc(Result);
pFilter:=nil;
end;
pFilter:=nil;
pEnum:=nil;
end;
Другими словами сколько аудиорендереров в графе, стока и дорожек ;-)
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
Дима (2006-01-31 22:26:00) [9]я попробовал и у меня при смене воспроизводится отдельно только второе аудио а при переключении на первое воспроизводятся оба… что мне делать с этим как от этого избавиться?
С уважением Дима.
Noby (2006-01-31 16:01:00) [10]псиб:), а возможно ли определинть сколько аудио дорожек в файле? Предположим что пользователь не знает о том сколько там дорожек.
NailMan (2006-01-31 15:29:00) [11]Ну подчистить надо обязательною писалась процедура очень давно и частично не мной.
собсно, зачем добавил проверку на количество аудиорендереров не помню, был какой-то закидон, вот и добавил. Выброси если без этого работает.
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
Noby (2006-01-31 14:13:00) [12]Только, я вот одного не понял что вот это такое
dword(i)=avi.audstreams;? с чем его есть то...
NailMan (2006-01-30 21:11:00) [13]Ну тады все просто:
Procedure TVideoEngine.SetMultipleVolume(vLevel:integer);
var
pEnum : IEnumFilters;
pFilter :IBaseFilter;
cFetched : dword;
CL:TGUID;
i:integer;
BA : IBasicAudio;
z : integer;
begin
i:=0;
repeat
// Get filter enumerator
dsGraphBuilder.EnumFilters(pEnum);
while pEnum.Next(1, pFilter, @cFetched) = S_OK do
begin
pFilter.GetClassID(CL);
if IsEqualGUID(CL,CLSID_DSoundRender) then
begin
If i<>CurrentPlayedAUDS then
z:=AUDSVolume[i]
else
z:=vLevel;
pFilter.QueryInterface(IID_IBasicAudio,BA);
BA.put_Volume(VolumeTable[z]);
AUDSVolume[i]:=z;
BA:=nil;
inc(i);
end;
pFilter:=nil;
end;
pFilter:=nil;
pEnum:=nil;
until dword(i)=avi.audstreams;
end;
В данном случае здесь несколько расширенная функция установки громкости звуковых дорожек.
vLevel- уровень матер-дорожки(которая выбрана пользователем, ее номер CurrentPlayedAUDS).
AUDSVolume- это массив уровней громкости(0..16) всех дорожек в файле.
VolumeTable- массив-константа в которой хранятся реальные значения уровней громкости которые принимает DShow. Содержимое константы:
VolumeTable : Array[0..16] of Integer = (-10000,-9000,-7500,-6500,-5500,-4500,-4000,-3000,-2000,-1000,-750,-500,-250,-125,-100,-50,0);
расширение функционала тут как раз в исаользовании таблицы(массива) уровней для других дорожек. Типа чтобы можно было помимо самой громкой мастер-дорожки играть тихо(как пользователь выставил) другие. Если остальные надо заглушить, то нужно вместо
If i<>CurrentPlayedAUDS then
z:=AUDSVolume[i]
else
z:=vLevel;
сделать
If i<>CurrentPlayedAUDS then
z:=-10000
else
z:=vLevel;
Ну и соотвественно лишнее поудалять из тела процедуры.
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13
Noby (2006-01-30 16:58:00) [14]1. Да через DirectShow.
2. И Видео и аудои
NailMan (2006-01-30 16:41:00) [15]Уточни исходные данные.
1. Воспроизводишь чем? DirectShow?
2. Как я понял у видео несколько дорожек. Тебе надо воспроизводить одну из них вместе с видео? или только аудио?
---
P.L.U.R. and WBR, NailMan aka 2:5020/3337.13