Fading groups
Fading groups make it possible to fade in / fade out the main video, the audio and a set of text/image overlays in a synchronized way.
Principle
The principle consists in creating a fading group (e.g. FadingGroupScene1) with FadingGroup_Create, which returns the group identifier used by all the other functions, then assigning the overlays to this group by their index:
- image overlays with FadingGroup_AssignImageOverlay (FadingGroupScene1, image overlay index),
- text overlays with FadingGroup_AssignTextOverlay (FadingGroupScene1, text overlay index).
A given overlay belongs to at most one group.
No function creates an overlay: each text or image overlay is identified by its index, passed as the first parameter of the SetTextOverlay... / SetImageOverlay... functions (0 for the 1st text overlay, 1 for the 2nd text overlay, etc., and likewise 0 for the 1st image overlay, 1 for the 2nd image overlay, etc.). The overlay is defined by these functions, e.g. SetTextOverlay_String (Index, ...) or SetImageOverlayFromImageFile2 (Index, ...), and displayed once enabled with SetTextOverlay_Enabled (Index, true) or SetImageOverlay_Enabled (Index, true). The same index is then passed to FadingGroup_AssignTextOverlay / FadingGroup_AssignImageOverlay. See Text overlays and Image overlays.
Once set up, the fading is started by invoking:
VideoGrabber.FadingGroup_FadeOut (FadingGroupScene1);
or
VideoGrabber.FadingGroup_FadeIn (FadingGroupScene1);
By default a group fades only its assigned overlays. To fade the main video as well (to a solid color, black by default), enable FadingGroup_IncludeMainVideo and optionally set the color with FadingGroup_SetFadeColor.
The audio can be included in the fade as well with FadingGroup_IncludeAudio: the audio then fades to silence (and back) in synchronization with the visuals, following the same duration and easing curve. When several groups include the audio, their gains are combined.
Fading in and out
Level semantics: 0% = fully visible, 100% = fully faded out. The reached level is held until the next trigger.
The fade is started with FadingGroup_FadeOut or FadingGroup_FadeIn, runs asynchronously over the duration set by FadingGroup_SetDuration, and follows the easing curve selected by FadingGroup_SetCurve (smoothstep by default, or linear). The transitions are interruptible: a trigger received while a transition is in progress reverses or redirects the fade seamlessly from the current level. A trigger toward the level where the group already is (e.g. FadeOut on a group already faded out) does nothing and raises no completion event.
FadingGroup_FadeToLevelPercent animates the group from its current level to any intermediate level and holds it there: FadeIn and FadeOut are the 0 and 100 special cases, so e.g. a value of 50 dims the group half way.
The completion of a transition is notified by the OnFadingCompleted event, whose FadedOut parameter returns true after a complete fade out (level 100%), false otherwise (fade in or partial fade).
The current level can be set immediately (without animation) with FadingGroup_SetLevelPercent, typically to hide an incoming scene before a cross-fade, or to start in black, and polled with FadingGroup_GetLevelPercent and FadingGroup_IsFading, which return silent defaults (-1 / false) for unknown group identifiers (no error logging, so they can be polled freely from a timer).
The fade progresses as the video frames are processed: the level is computed from the elapsed time each time a frame is rendered. When no frame flows (e.g. player paused) the picture does not change, and after a stall of more than 1 second the transition resumes where it stopped.
The other functions validate their parameters: an unknown or deleted group identifier, an out-of-range overlay index or an out-of-range value raises an LERROR log entry naming the offending function and parameter, then the call is ignored.
Deleting a group with FadingGroup_Delete is thread-safe: if a fade is in progress the deletion is deferred until the current rendering step completes.
Main video, overlays and transparency
The main video is faded (toward the color of the group) before the overlays are drawn: the overlays assigned to other groups remain visible over the faded video, each one at the level of its own group. E.g. the video can fade to black while a logo stays on screen.
When several groups include the main video, their effects add up: each one fades the video by its own level, so the video is fully visible only when all these groups are at level 0%. In practice, include the main video in a single group.
An image overlay loaded from a PNG file having an alpha channel keeps its per-pixel transparency only when SetImageOverlay_Transparent (Index, true) is enabled, with SetImageOverlay_UseTransparentColor disabled (default). The fading then modulates this transparency. Otherwise the transparent pixels of the PNG are drawn with their own color (usually black). FadingGroup_IncludeMainVideo has no effect on the transparency of the overlays.
Sample code: fading the main video, the audio and 2 overlays together
// 1st text overlay (index 0): "Hello" drawn at the (10, 10) location
VideoGrabber.SetTextOverlay_String (0, 'Hello');
VideoGrabber.SetTextOverlay_Left (0, 10);
VideoGrabber.SetTextOverlay_Top (0, 10);
VideoGrabber.SetTextOverlay_Enabled (0, true);
// 1st image overlay (index 0): logo.png
VideoGrabber.SetImageOverlayFromImageFile2 (0, 'logo.png');
VideoGrabber.SetImageOverlay_Enabled (0, true);
// Create a 1500 ms fading group, fading the main video to black
FadingGroupId := VideoGrabber.FadingGroup_Create (1500);
VideoGrabber.FadingGroup_IncludeMainVideo (FadingGroupId, true);
VideoGrabber.FadingGroup_IncludeAudio (FadingGroupId, true); // also fade the audio to silence
VideoGrabber.FadingGroup_SetCurve (FadingGroupId, fcv_SmoothStep);
// Assign the 1st text overlay (index 0) and the 1st image overlay (index 0) to the group
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupId, 0);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupId, 0);
// Fade everything out, then later back in
VideoGrabber.FadingGroup_FadeOut (FadingGroupId);
...
VideoGrabber.FadingGroup_FadeIn (FadingGroupId);
// Or dim the group to 50% and hold it there
VideoGrabber.FadingGroup_FadeToLevelPercent (FadingGroupId, 50);
Cross-fading two scenes
FadingGroup_CrossFade (FadeOutFadingGroupId, FadeInFadingGroupId) fades out the 1st group while fading in the 2nd one, both transitions starting at the same time. The incoming group is expected to be at level 100% (previously faded out, or preset with FadingGroup_SetLevelPercent): an incoming group already fully visible (level 0%) does not change.
To replace scene 1 with scene 2 invoke:
VideoGrabber.FadingGroup_CrossFade (FadingGroupScene1, FadingGroupScene2);
and to go back to scene 1:
VideoGrabber.FadingGroup_CrossFade (FadingGroupScene2, FadingGroupScene1);
After the 1st cross-fade scene 1 is at level 100%, so the reverse cross-fade needs no preset. To cross-fade 2 single overlays, assign each one to its own group.
A cross-fade replaces a scene with another one. To add an element over the current picture, invoke FadingGroup_FadeIn on its group alone; by contrast, FadingGroup_CrossFade (FadingGroupVideo, FadingGroupText), where FadingGroupVideo includes the main video, fades the video to black and leaves only the text over a black frame.
Sample code: 2 scenes of image overlays, scene 1 visible at startup, scene 2 hidden, then cross-faded on button clicks:
var
FadingGroupScene1: LongInt;
FadingGroupScene2: LongInt;
const
FADE_DURATION_MS = 1200;
procedure TMainForm.SetupScenes;
var
i: LongInt;
begin
// scene 1: 1st, 2nd and 3rd image overlays (indexes 0, 1 and 2), top row
VideoGrabber.SetImageOverlayFromImageFile2 (0, 'scene1_left.jpg');
VideoGrabber.SetImageOverlay_LeftLocation (0, 10);
VideoGrabber.SetImageOverlay_TopLocation (0, 10);
VideoGrabber.SetImageOverlayFromImageFile2 (1, 'scene1_center.bmp');
VideoGrabber.SetImageOverlay_LeftLocation (1, 300);
VideoGrabber.SetImageOverlay_TopLocation (1, 10);
VideoGrabber.SetImageOverlayFromImageFile2 (2, 'scene1_right.bmp');
VideoGrabber.SetImageOverlay_LeftLocation (2, 600);
VideoGrabber.SetImageOverlay_TopLocation (2, 10);
// scene 2: 4th and 5th image overlays (indexes 3 and 4), second row
VideoGrabber.SetImageOverlayFromImageFile2 (3, 'scene2_left.bmp');
VideoGrabber.SetImageOverlay_LeftLocation (3, 10);
VideoGrabber.SetImageOverlay_TopLocation (3, 300);
VideoGrabber.SetImageOverlayFromImageFile2 (4, 'scene2_right.bmp');
VideoGrabber.SetImageOverlay_LeftLocation (4, 300);
VideoGrabber.SetImageOverlay_TopLocation (4, 300);
FadingGroupScene1 := VideoGrabber.FadingGroup_Create (FADE_DURATION_MS);
VideoGrabber.FadingGroup_SetCurve (FadingGroupScene1, fcv_SmoothStep); // optional: default curve, or fcv_Linear
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupScene1, 0);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupScene1, 1);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupScene1, 2);
FadingGroupScene2 := VideoGrabber.FadingGroup_Create (FADE_DURATION_MS);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupScene2, 3);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupScene2, 4);
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupScene2, 100); // scene 2 hidden, ready to be cross-faded in
// enable the overlays once scene 2 is hidden, so that it never flashes on screen
for i := 0 to 4 do begin
VideoGrabber.SetImageOverlay_Enabled (i, true);
end;
VideoGrabber.PlayerFileName := 'my video clip.mp4';
VideoGrabber.OpenPlayer;
end;
procedure TMainForm.btnScene2Click (Sender: TObject);
begin
VideoGrabber.FadingGroup_CrossFade (FadingGroupScene1, FadingGroupScene2); // scene 1 out, scene 2 in
end;
procedure TMainForm.btnScene1Click (Sender: TObject);
begin
VideoGrabber.FadingGroup_CrossFade (FadingGroupScene2, FadingGroupScene1); // scene 2 out, scene 1 in
end;
Starting from black, then dropping a single element
The elements that always move together go in the same group, the others in their own group. E.g. a clip starting from black, with a PNG logo and a title:
- FadingGroupMain: the main video (FadingGroup_IncludeMainVideo) and the PNG logo; they fade in together at the start and fade out together at the end,
- FadingGroupText: the title alone, so that it can be dropped on its own.
Sequence:
- button 1: the overlays are defined, the groups created, and both groups preset to level 100% with FadingGroup_SetLevelPercent before OpenPlayer: the very first frames are rendered fully faded (black, without overlays),
- OnPlayerOpened event: FadingGroup_FadeIn on both groups. The fade progresses as the frames are processed, so starting it there makes it ride the very first frames. Two consecutive FadeIn calls start within microseconds of each other and their levels are computed on the same frame: the video, the logo and the title rise as one. FadingGroup_CrossFade is not needed here: it swaps a scene for another one, it does not synchronize fade-ins,
- button 2: FadingGroup_FadeOut on the title group only; the video and the logo are untouched (a later FadingGroup_FadeIn brings the title back),
- button 3: FadingGroup_FadeOut on the main group (the video fades to black and the logo fades out together), and on the title group in case the title is still displayed (ignored if it is already faded out).
var
FadingGroupMain: LongInt; // main video + PNG logo
FadingGroupText: LongInt; // title (text overlay) only
const
FADE_MS = 1000;
procedure TMainForm.btnStartClick (Sender: TObject); // button 1
begin
// 1st image overlay (index 0): PNG logo with alpha channel
VideoGrabber.SetImageOverlayFromImageFile2 (0, 'logo.png');
VideoGrabber.SetImageOverlay_Transparent (0, true); // uses the alpha channel of the PNG
VideoGrabber.SetImageOverlay_LeftLocation (0, 20);
VideoGrabber.SetImageOverlay_TopLocation (0, 20);
VideoGrabber.SetImageOverlay_Enabled (0, true);
// 1st text overlay (index 0): title
VideoGrabber.SetTextOverlay_String (0, 'On now: the evening show');
VideoGrabber.SetTextOverlay_Left (0, 20);
VideoGrabber.SetTextOverlay_Top (0, 400);
VideoGrabber.SetTextOverlay_Enabled (0, true);
FadingGroupMain := VideoGrabber.FadingGroup_Create (FADE_MS, $000000);
VideoGrabber.FadingGroup_IncludeMainVideo (FadingGroupMain, true); // the video fades to black
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupMain, 0); // the logo fades with it
FadingGroupText := VideoGrabber.FadingGroup_Create (FADE_MS);
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupText, 0);
// start in black: both groups fully faded BEFORE opening, so that the very
// first rendered frames are already black and without overlays
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupMain, 100);
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupText, 100);
VideoGrabber.PlayerFileName := 'my video clip.mp4';
VideoGrabber.OpenPlayer;
end;
procedure TMainForm.VideoGrabberPlayerOpened (Sender: TObject);
begin
// the frames are about to flow: fade the video, the logo and the title up together
VideoGrabber.FadingGroup_FadeIn (FadingGroupMain);
VideoGrabber.FadingGroup_FadeIn (FadingGroupText);
end;
procedure TMainForm.btnDropTitleClick (Sender: TObject); // button 2
begin
// the title fades out on its own, the video and the logo are untouched
VideoGrabber.FadingGroup_FadeOut (FadingGroupText);
end;
procedure TMainForm.btnFadeAllClick (Sender: TObject); // button 3
begin
// the video fades to black and the logo fades out, together
VideoGrabber.FadingGroup_FadeOut (FadingGroupMain);
// the title too, if it is still displayed
VideoGrabber.FadingGroup_FadeOut (FadingGroupText);
end;
Variant: if the logo may later have to be faded alone (without the video), use one group per element instead (video / logo / title) and invoke FadeIn / FadeOut on them one after the other: consecutive calls are visually simultaneous. Grouping only fixes what always moves together.
Several scenes: program, station ident, logo and information texts
A typical broadcast setup: the program (main video), a full-frame station ident (PNG with alpha channel + caption) shown over black, a full-frame channel logo (PNG with alpha channel) and information texts, each one in its own group and all hidden at startup except the program:
var
FadingGroupVideo: LongInt; // program (main video)
FadingGroupIdent: LongInt; // station ident: 2nd image overlay + 4th text overlay
FadingGroupLogo: LongInt; // channel logo: 1st image overlay
FadingGroupInfo: LongInt; // information texts: 1st, 2nd and 3rd text overlays
const
FADE_MS = 800;
procedure TMainForm.SetupScenes;
var
i: LongInt;
begin
// 1st image overlay (index 0): channel logo, full-frame PNG with alpha channel
VideoGrabber.SetImageOverlayFromImageFile2 (0, 'channel_logo.png');
VideoGrabber.SetImageOverlay_StretchToVideoSize (0, true);
VideoGrabber.SetImageOverlay_Transparent (0, true); // uses the alpha channel of the PNG
// 2nd image overlay (index 1): station ident, full-frame PNG with alpha channel
VideoGrabber.SetImageOverlayFromImageFile2 (1, 'station_ident.png');
VideoGrabber.SetImageOverlay_StretchToVideoSize (1, true);
VideoGrabber.SetImageOverlay_Transparent (1, true);
// 1st, 2nd and 3rd text overlays (indexes 0, 1 and 2): information texts
VideoGrabber.SetTextOverlay_String (0, 'Headline');
VideoGrabber.SetTextOverlay_String (1, 'Weather');
VideoGrabber.SetTextOverlay_String (2, 'Coming up next');
// 4th text overlay (index 3): caption of the station ident
VideoGrabber.SetTextOverlay_String (3, 'Station ident caption');
// ... location, font and colors of the text overlays ...
// the only group including the main video
FadingGroupVideo := VideoGrabber.FadingGroup_Create (FADE_MS);
VideoGrabber.FadingGroup_IncludeMainVideo (FadingGroupVideo, true);
FadingGroupIdent := VideoGrabber.FadingGroup_Create (FADE_MS);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupIdent, 1);
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupIdent, 3);
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupIdent, 100);
FadingGroupLogo := VideoGrabber.FadingGroup_Create (FADE_MS);
VideoGrabber.FadingGroup_AssignImageOverlay (FadingGroupLogo, 0);
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupLogo, 100);
FadingGroupInfo := VideoGrabber.FadingGroup_Create (FADE_MS);
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupInfo, 0);
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupInfo, 1);
VideoGrabber.FadingGroup_AssignTextOverlay (FadingGroupInfo, 2);
VideoGrabber.FadingGroup_SetLevelPercent (FadingGroupInfo, 100);
// enable the overlays once their groups are hidden
VideoGrabber.SetImageOverlay_Enabled (0, true);
VideoGrabber.SetImageOverlay_Enabled (1, true);
for i := 0 to 3 do begin
VideoGrabber.SetTextOverlay_Enabled (i, true);
end;
end;
procedure TMainForm.btnLogoAndInfoClick (Sender: TObject);
begin
// adds the logo and the information texts over the program
VideoGrabber.FadingGroup_FadeIn (FadingGroupLogo);
VideoGrabber.FadingGroup_FadeIn (FadingGroupInfo);
end;
procedure TMainForm.btnIdentClick (Sender: TObject);
begin
// the program fades to black while the station ident appears
VideoGrabber.FadingGroup_CrossFade (FadingGroupVideo, FadingGroupIdent);
end;
procedure TMainForm.btnProgramClick (Sender: TObject);
begin
// back to the program
VideoGrabber.FadingGroup_CrossFade (FadingGroupIdent, FadingGroupVideo);
end;
The logo and the information texts, in their own groups, stay on screen over the station ident: fade them out as well (FadingGroup_FadeOut) if the ident must be displayed alone.
Do not include the main video in FadingGroupIdent: preset to level 100% at startup, that group would keep the video black until the ident is faded in (see "Main video, overlays and transparency" above). The transparency of the PNG files comes from SetImageOverlay_Transparent, not from the main video settings. SetImageOverlay_AlphaBlend and SetImageOverlay_AlphaBlendValue are needed only to make a whole overlay semi-transparent.