Две ошибки раздельного звука
Первая: при выключенных дорожках картинки не играло вообще ничего. Поток картинки опустевал, уплощение возвращало неудачу, и предпросмотр останавливался вместе со звуком, которого выключение не касалось. Теперь длина берётся по обоим видам дорожек сразу, и недостающее заполняется чёрным и тишиной. Оба потока всегда одной длины. Вторая: звук был слышен там, где на монтаже его нет. mpv добавляет внешний список кусков ещё одной дорожкой, а какую играть, решает сам, и мог взять звук, встроенный в саму картинку. Теперь после загрузки внешняя дорожка выбирается явно. Если её не оказалось, звук выключается совсем: играть вместо него звук из картинки нельзя — на монтаже его там нет. Проверено: при выключенных дорожках картинки получается чёрный кадр и сохранившийся звук (-21 dB); внешний список кусков выбирается верно и в ту, и в другую сторону.
This commit is contained in:
parent
3acf79d174
commit
cf20a0164e
3 changed files with 91 additions and 13 deletions
|
|
@ -63,9 +63,17 @@ type
|
|||
|
||||
Дорожки другого вида в проходе не участвуют вовсе — ни как источники,
|
||||
ни как границы кусков. }
|
||||
{ AMinLength — до какой длины дотянуть поток. Нужно, чтобы картинка и звук
|
||||
всегда были одной длины: если выключить все дорожки картинки, поток
|
||||
картинки опустеет, и без этого предпросмотр погас бы вместе со звуком.
|
||||
Недостающее заполняется чёрным и тишиной. }
|
||||
function FlattenStream(AProject: TProject; AKind: TTrackKind;
|
||||
const AMinLength: Double;
|
||||
out ASegments: TFlatSegmentArray; out AMissing: TGenRequests): Boolean;
|
||||
|
||||
{ Длина слышимого материала одного вида дорожек. }
|
||||
function AudibleEnd(AProject: TProject; AKind: TTrackKind): Double;
|
||||
|
||||
{ Оба потока разом. Список того, что надо создать, общий: чёрные вставки
|
||||
годятся и картинке, и звуку — в них есть и то, и другое. }
|
||||
function FlattenProject(AProject: TProject;
|
||||
|
|
@ -203,7 +211,28 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function AudibleEnd(AProject: TProject; AKind: TTrackKind): Double;
|
||||
var
|
||||
T: Integer;
|
||||
Track: TTrack;
|
||||
begin
|
||||
Result := 0;
|
||||
if AProject = nil then
|
||||
Exit;
|
||||
for T := 0 to AProject.TrackCount - 1 do
|
||||
begin
|
||||
Track := AProject.Tracks[T];
|
||||
if Track.Kind <> AKind then
|
||||
Continue;
|
||||
if not AProject.TrackAudible(Track) then
|
||||
Continue;
|
||||
if Track.EndTime > Result then
|
||||
Result := Track.EndTime;
|
||||
end;
|
||||
end;
|
||||
|
||||
function FlattenStream(AProject: TProject; AKind: TTrackKind;
|
||||
const AMinLength: Double;
|
||||
out ASegments: TFlatSegmentArray; out AMissing: TGenRequests): Boolean;
|
||||
var
|
||||
Bounds: array of Double;
|
||||
|
|
@ -334,10 +363,13 @@ begin
|
|||
N := 0;
|
||||
for T := 0 to AProject.TrackCount - 1 do
|
||||
Inc(N, AProject.Tracks[T].Count * 2);
|
||||
SetLength(Bounds, N + 2);
|
||||
SetLength(Bounds, N + 3);
|
||||
BoundCount := 0;
|
||||
|
||||
AddBoundary(Bounds, BoundCount, 0);
|
||||
{ Конец задан снаружи: оба потока должны быть одной длины. }
|
||||
if AMinLength > 0 then
|
||||
AddBoundary(Bounds, BoundCount, AMinLength);
|
||||
for T := 0 to AProject.TrackCount - 1 do
|
||||
begin
|
||||
Track := AProject.Tracks[T];
|
||||
|
|
@ -600,15 +632,20 @@ function FlattenProject(AProject: TProject;
|
|||
var
|
||||
MissA: TGenRequests;
|
||||
I: Integer;
|
||||
Total: Double;
|
||||
begin
|
||||
AAudio := nil;
|
||||
MissA := nil;
|
||||
Result := FlattenStream(AProject, tkVideo, AVideo, AMissing);
|
||||
{ Длина берётся по обоим видам дорожек сразу: иначе выключенная
|
||||
картинка укоротила бы ролик, хотя звук ещё идёт. }
|
||||
Total := Max(AudibleEnd(AProject, tkVideo), AudibleEnd(AProject, tkAudio));
|
||||
if Total <= 0 then
|
||||
Exit;
|
||||
|
||||
Result := FlattenStream(AProject, tkVideo, Total, AVideo, AMissing);
|
||||
if not Result then
|
||||
Exit;
|
||||
{ Звук уплощается всегда, даже если звуковых дорожек нет: тогда выйдет
|
||||
один кусок тишины на всю длину, и это правильный ответ. }
|
||||
FlattenStream(AProject, tkAudio, AAudio, MissA);
|
||||
FlattenStream(AProject, tkAudio, Total, AAudio, MissA);
|
||||
|
||||
for I := 0 to High(MissA) do
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@ type
|
|||
procedure PumpEvents(Sender: TObject);
|
||||
procedure HandlePropertyChange(AProp: Pmpv_event_property);
|
||||
procedure HandleLogMessage(AMsg: Pmpv_event_log_message);
|
||||
{ Выбирает внешнюю звуковую дорожку, если она есть.
|
||||
|
||||
Без этого mpv играет звук, встроенный в саму картинку: внешний файл
|
||||
он лишь добавляет ещё одной дорожкой, а выбирает по своему
|
||||
усмотрению. Получалось, что звук слышен там, где на монтаже его нет
|
||||
вовсе — он приходил из видеофайла, а не с звуковой дорожки. }
|
||||
procedure SelectExternalAudio;
|
||||
function SetOption(const AName, AValue: string): Integer;
|
||||
public
|
||||
constructor Create(AVideoParent: TWinControl);
|
||||
|
|
@ -737,6 +744,38 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TMpvPlayer.SelectExternalAudio;
|
||||
var
|
||||
Count, I: Integer;
|
||||
Kind, Ext, Id: string;
|
||||
begin
|
||||
if FHandle = nil then
|
||||
Exit;
|
||||
|
||||
{ Своего звука нет — пусть играет как есть. }
|
||||
if FAudioSource = '' then
|
||||
Exit;
|
||||
|
||||
Count := StrToIntDef(GetPropString('track-list/count'), 0);
|
||||
for I := 0 to Count - 1 do
|
||||
begin
|
||||
Kind := GetPropString(Format('track-list/%d/type', [I]));
|
||||
if Kind <> 'audio' then
|
||||
Continue;
|
||||
Ext := GetPropString(Format('track-list/%d/external', [I]));
|
||||
if (Ext <> 'yes') and (Ext <> 'true') then
|
||||
Continue;
|
||||
Id := GetPropString(Format('track-list/%d/id', [I]));
|
||||
if Id <> '' then
|
||||
mpv_set_property_string(FHandle, 'aid', PAnsiChar(Id));
|
||||
Exit;
|
||||
end;
|
||||
|
||||
{ Внешней дорожки не нашлось — значит наш список кусков не открылся.
|
||||
Играть вместо него звук из картинки нельзя: на монтаже его там нет. }
|
||||
mpv_set_property_string(FHandle, 'aid', 'no');
|
||||
end;
|
||||
|
||||
procedure TMpvPlayer.HandleLogMessage(AMsg: Pmpv_event_log_message);
|
||||
begin
|
||||
if Assigned(FOnLog) then
|
||||
|
|
@ -766,6 +805,8 @@ begin
|
|||
HandlePropertyChange(Pmpv_event_property(Ev^.data));
|
||||
MPV_EVENT_LOG_MESSAGE:
|
||||
HandleLogMessage(Pmpv_event_log_message(Ev^.data));
|
||||
MPV_EVENT_FILE_LOADED:
|
||||
SelectExternalAudio;
|
||||
MPV_EVENT_SHUTDOWN:
|
||||
begin
|
||||
FPump.Enabled := False;
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ begin
|
|||
try
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 10, 5, 0);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 15, 5, 5);
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 1, 'склеились в один кусок');
|
||||
if Length(Segs) = 1 then
|
||||
|
|
@ -583,7 +583,7 @@ begin
|
|||
try
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 100, 5, 0);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 200, 5, 5);
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 2, 'остались двумя кусками');
|
||||
finally
|
||||
|
|
@ -598,7 +598,7 @@ begin
|
|||
{ Дорожка 0 — верхняя, она побеждает. }
|
||||
Proj.AddClip(Proj.Tracks[1], Media, 0, 30, 0);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 100, 10, 10);
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 3, 'получилось три куска');
|
||||
if Length(Segs) = 3 then
|
||||
|
|
@ -622,7 +622,7 @@ begin
|
|||
try
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 0, 5, 3);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 50, 5, 12);
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 4, 'четыре куска: чёрное, видео, чёрное, видео');
|
||||
if Length(Segs) = 4 then
|
||||
|
|
@ -652,7 +652,7 @@ begin
|
|||
try
|
||||
Proj.AddClip(Proj.Tracks[1], Media, 0, 10, 0);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 200, 10, 0);
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 1, 'остался один кусок');
|
||||
if Length(Segs) = 1 then
|
||||
|
|
@ -669,7 +669,7 @@ begin
|
|||
Proj.AddClip(Proj.Tracks[1], Media, 0, 10, 0);
|
||||
Proj.AddClip(Proj.Tracks[0], Media, 200, 10, 0);
|
||||
Proj.Tracks[0].Muted := True;
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 1, 'остался один кусок');
|
||||
if Length(Segs) = 1 then
|
||||
|
|
@ -689,7 +689,7 @@ begin
|
|||
Proj.Tracks[0][0].EdgeIn := edPrecise;
|
||||
Proj.RippleMode := rmAllTracks;
|
||||
Proj.RippleEnabled := True;
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
Check(Length(Segs) = 3, 'до записи три куска');
|
||||
Check(Proj.SaveToFile(TempFile, Err), 'записан: ' + Err);
|
||||
finally
|
||||
|
|
@ -709,7 +709,7 @@ begin
|
|||
Check(Proj.RippleEnabled, 'состояние схлопывания сохранилось');
|
||||
if Proj.TrackCount = 2 then
|
||||
Check(Proj.Tracks[0][0].EdgeIn = edPrecise, 'признак точного реза сохранился');
|
||||
FlattenStream(Proj, tkVideo, Segs, Missing);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
DumpSegments(Segs);
|
||||
Check(Length(Segs) = 3, 'после чтения снова три куска');
|
||||
if Length(Segs) = 3 then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue