Самописная мозаика (система мониторинга IPTV)

В данной статье я приведу рабочий пример реализации системы мозаичного мониторинга ТВ-каналов - это когда много ТВ каналов одновременно в маленьких окошках выводятся на один большой монитор.

Профессиональные системы подобного типа стоят бешеных денег (но они правда того стоят - как правило они не просто делают такую мозаичную картинку - они дают еще и много нужной и важной аналитической информации о потоках, о качестве, потерях, ошибках и т.п. Но иногда достаточно просто смотреть сразу несколько каналов :) и решить подобную задачу подручными средствами и бесплатно - вполне реально.

IPTV Mozaic

Я решил использовать для этого старый добрый Delphi 7 и VLC, точнее библиотеку для libvlc.dll (которая идет в составе дистрибутива VLC player'а) и интерфейс для этой dll-ки - PasLibVlc. На момент написания статьи проект располагался по адресу http://sourceforge.net/projects/paslibvlc/ . Этот интерфейс дает доступ к функциям из библиотеки libvlc.dll который и позволил мне соорудить свой, на самом деле, по сути своей, мозаичный плеер, с зачаточными функциями сбора статистики. Моя программа работает в двух режимах - первый режим - если в плейлисте количество каналов совпадает с количеством установленных экранов, она статично отображает на экране заданные каналы и при этом позволяет посмотреть накопленную статистику по кадрам для одного из каналов. В случае каких либо ошибок - обрамляет красным канал с ошибками. И второй режим - в заданном количестве окошек она пробегает по кругу по всему заданному плейлисту. Плейлист задается как обычный .m3u

Ну пришло время кода :), и немножко с комментариями. В конце статьи будет ссылка на архив где будут все исходники и бинарник программы (разумеется необходима еще установленная библиотека libvlc.dll)

основной модуль unit1.pas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, StrUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, PasLibVlcPlayerUnit, PasLibVlcUnit;
 
type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Panel1: TPanel;
    Label1: TLabel;
    Timer2: TTimer;
    Edit1: TEdit;
    Label2: TLabel;
    Button1: TButton;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
  State_on: boolean= false;
  VlcPlayer: array[1..40] of TPasLibVlcPlayer;
  VP_x,VP_y,VP_n, Pause_switch, Num_Active: integer;
  TV_URLs, TV_Names: TStrings;
  Timer_first_finish: boolean;
  URL_Count, Last_URL, Last_MNTR, First_URL: integer;
  mntr: array[1..40] of record
    active: boolean;
    m_x,m_y: integer;
    url: string;
  end;
  urls: array[1..250] of record
    mntr_idx: integer;
    url: string;
    name: string;
  end;
  vlc_stats, prev_vlc_stats: array[1..40] of libvlc_media_stats_t;
 
 
implementation
 
uses unit2;
{$R *.dfm}
 
procedure ParseM3UPlayList(M3U: string; var Names: TStrings; var URLs: TStrings);
var
  TMPstr: TStrings;
  i: integer;
  s: string;
begin
  TMPstr := TStringList.Create;
  TMPstr.NameValueSeparator := ',';
 
  if ( M3U[1]=#$ef) and (M3U[2]=#$bb) and (M3U[3]=#$bf)
     then TMPstr.Text := UTF8ToAnsi(M3U)
     else TMPstr.Text := M3U;
 
  Names.Clear;
  URLs.Clear;
  for i := 0 to TMPstr.Count - 1 do begin
    s := TMPstr.Strings[i];
    if s = '' then continue;
    if AnsiContainsText(s,'#extm3u') then continue;
    if not AnsiContainsText(s,'#extinf') then begin
       URLs.Append(s);
    end
    else begin
      Names.Append(Trim(TMPstr.ValueFromIndex[i]));
    end;
  end;
end;
 
 
procedure TForm1.FormCreate(Sender: TObject);
var
  i, j: integer;
  TmpStr: TStrings;
begin
  VP_n := 0;
  Form2 := TForm2.Create(Self);
  if Form2.ShowModal = mrOk then begin
    VP_x := StrToInt(Form2.Edit1.Text);
    VP_y := StrToInt(Form2.Edit2.Text);
    Num_Active := VP_y * VP_x;
    if Num_Active > 40 then begin
      ShowMessage('Слишком много (максимум 40) мониторов: '+IntToStr(Num_Active));
      Halt;
    end;
    ShowMessage('Кол-во активных мониторов принудительно установлено: '+IntToStr(Num_Active));
 
    Pause_Switch := StrToInt(Form2.Edit5.Text);
    for i := 1 to VP_y do begin
      for j := 1 to VP_x do begin
        inc(VP_n);
        VlcPlayer[VP_n] := TPasLibVlcPlayer.CreateParented(Form1.Handle);
        VlcPlayer[VP_n].Parent := Form1;
        VlcPlayer[VP_n].Top := 5+245*(i-1);
        VlcPlayer[VP_n].Left := 5+325*(j-1);
        VlcPlayer[VP_n].Visible := true;
        mntr[VP_n].m_x := j;
        mntr[VP_n].m_y := i;
      end;
    end;
    ClientWidth := VP_x * 325 - 5;
    ClientHeight := VP_y * 245 + 50;
 
    TV_URLs := TStringList.Create;
    TV_Names := TStringList.Create;
    TmpStr := TStringList.Create;
 
    TmpStr.Clear;
    if FileExists(Form2.Edit4.Text) then begin
      TmpStr.LoadFromFile(Form2.Edit4.Text);
      TV_URLs.Clear;
      TV_Names.Clear;
      ParseM3UPlayList(TmpStr.Text, TV_Names, TV_URLs);
    end
    else begin
      ShowMessage('Плейлист не найден :(');
      Halt;
    end;
  end;
  URL_Count := TV_URLs.Count;
  Last_URL := 0;
  First_URL := 0;
  Last_MNTR := 0;
  for i := 1 to URL_Count do begin
    urls[i].mntr_idx := 0;
    urls[i].url := TV_URLs.Strings[i-1];
    urls[i].name := TV_Names.Strings[i-1];
  end;
 
  Timer1.Enabled := true;
  Timer1.Interval := 1000*Pause_switch;
  Timer_first_finish := false;
  Timer1Timer(Sender);
 
  for i := 1 to VP_n do begin
    mntr[i].url := '';
    mntr[i].active := false;
  end
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
var
  i, num_a: integer;
begin
  if Last_MNTR > 0 then begin
     Form1.Canvas.Pen.Color := clBtnFace;
     Form1.Canvas.Pen.Width := 5;
     Form1.Canvas.Rectangle(VlcPlayer[Last_MNTR].Left - 3, VlcPlayer[Last_MNTR].Top - 3,VlcPlayer[Last_MNTR].Left + 323, VlcPlayer[Last_MNTR].Top + 243);
  end;
 
// предполагаем что урлов больше чем мониторов
  if URL_Count > VP_n then begin
    num_a := 0;
// проходим по всем урлам - проверяем запущены ли все у нас мониторы или еще нет!
    for i := 1 to VP_n do if mntr[i].active then inc(num_a);
 
    // активных мониторов меньше ограничения - ищем первый свободный и запускаем там канал Last_URL+1
    if num_a < Num_Active then begin
       inc(Last_MNTR);
       if Last_MNTR > VP_n then Last_MNTR := 1;
       inc(Last_URL);
       if Last_URL > URL_Count then Last_URL := 1;
 
       mntr[Last_MNTR].active := true;
       mntr[Last_MNTR].url := urls[Last_URL].url;
       urls[Last_URL].mntr_idx := Last_MNTR;
       if Last_MNTR > 1 then VlcPlayer[Last_MNTR].SetAudioVolume(0)
                        else VlcPlayer[Last_MNTR].SetAudioVolume(50);
 
       Label1.Caption := 'запускаю: '+ IntToStr(Last_URL)+'. '+ urls[Last_URL].name + ' ('+ urls[Last_URL].url + ') в '+IntToStr(Last_MNTR)+' окне';
       Form1.Canvas.Pen.Color := clLime;
       Form1.Canvas.Pen.Width := 5;
       Form1.Canvas.Rectangle(VlcPlayer[Last_MNTR].Left - 3, VlcPlayer[Last_MNTR].Top - 3,VlcPlayer[Last_MNTR].Left + 323, VlcPlayer[Last_MNTR].Top + 243);
 
       VlcPlayer[Last_MNTR].Play(mntr[Last_MNTR].url);
       EXIT;
    end
    //все кол-во разрешенных мониторов уже запущено
    else begin
      // Если при этом разрешенных мониторов (Num_Active) столько же сколько всего доступных мониторов (VP_n)
      if Num_Active = VP_n then begin
       inc(Last_MNTR);
       if Last_MNTR > VP_n then Last_MNTR := 1;
       inc(Last_URL);
       if Last_URL > URL_Count then Last_URL := 1;
 
       mntr[Last_MNTR].active := true;
       mntr[Last_MNTR].url := urls[Last_URL].url;
       urls[Last_URL].mntr_idx := Last_MNTR;
       if Last_MNTR > 1 then VlcPlayer[Last_MNTR].SetAudioVolume(0)
                        else VlcPlayer[Last_MNTR].SetAudioVolume(50);
       Label1.Caption := 'запускаю: '+IntToStr(Last_URL)+'. '+ urls[Last_URL].name + ' ('+ urls[Last_URL].url + ') в '+IntToStr(Last_MNTR)+' окне';
       Form1.Canvas.Pen.Color := clLime;
       Form1.Canvas.Pen.Width := 5;
       Form1.Canvas.Rectangle(VlcPlayer[Last_MNTR].Left - 3, VlcPlayer[Last_MNTR].Top - 3,VlcPlayer[Last_MNTR].Left + 323, VlcPlayer[Last_MNTR].Top + 243);
 
       VlcPlayer[Last_MNTR].Play(mntr[Last_MNTR].url);
       EXIT;
      end
      else begin
       for i := 1 to URL_Count do if urls[i].mntr_idx = Last_MNTR then urls[i].mntr_idx := 0;
       inc(Last_MNTR);
       if Last_MNTR > VP_n then Last_MNTR := 1;
       inc(Last_URL);
       if Last_URL > URL_Count then Last_URL := 1;
 
       if Last_MNTR - Num_Active < 0 then begin
         mntr[Last_MNTR+VP_n-Num_Active].active := false;
         VlcPlayer[Last_MNTR+VP_n-Num_Active].Play('zastavka.ps');
       end
       else begin
         mntr[Last_MNTR-Num_Active].active := false;
         VlcPlayer[Last_MNTR-Num_Active].Play('zastavka.ps');
       end;
 
       mntr[Last_MNTR].active := true;
       mntr[Last_MNTR].url := urls[Last_URL].url;
       urls[Last_URL].mntr_idx := Last_MNTR;
       Label1.Caption := 'запускаю: '+IntToStr(Last_URL)+'. '+ urls[Last_URL].name + ' ('+ urls[Last_URL].url + ') в '+IntToStr(Last_MNTR)+' окне';
       Form1.Canvas.Pen.Color := clLime;
       Form1.Canvas.Pen.Width := 5;
       Form1.Canvas.Rectangle(VlcPlayer[Last_MNTR].Left - 3, VlcPlayer[Last_MNTR].Top - 3,VlcPlayer[Last_MNTR].Left + 323, VlcPlayer[Last_MNTR].Top + 243);
 
       VlcPlayer[Last_MNTR].Play(mntr[Last_MNTR].url);
      end;
 
    end;
  end
  else if URL_Count = VP_n then begin // отдельный случай - чисто для мониторинга
  // предполагаем что урлов стока же скока и мониторов - поэтому тупо все быстро запускаем - и ищем ошибки в потоках
    VlcPlayer[1].SetAudioVolume(50);
    VlcPlayer[1].Play(urls[1].url);
    // звук будет тока в первом окошке
    Sleep(2000);
    for i := 2 to URL_Count do begin
       VlcPlayer[i].SetAudioVolume(0);
       VlcPlayer[i].Play(urls[i].url);
       Sleep(300);
    end;
    for i := 1 to URL_Count do prev_vlc_stats[i] := VlcPlayer[i].GetStats;
 
    Timer1.Enabled := false;
 
    Edit1.Visible := true;
    Button1.Visible := true;
    for i := 2 to 18 do (FindComponent('Label'+IntToStr(i)) as TLabel).Visible := true;
 
    Timer2.Enabled := true;
  end
end;
 
 
procedure TForm1.Timer2Timer(Sender: TObject);
var
//  stat:libvlc_media_stats_t;
  i: integer;
begin
  for i := 1 to URL_Count do begin
    vlc_stats[i] := VlcPlayer[i].GetStats;
    if (vlc_stats[i].i_demux_corrupted > prev_vlc_stats[i].i_demux_corrupted) or
       (vlc_stats[i].i_demux_discontinuity > prev_vlc_stats[i].i_demux_discontinuity) or
       (vlc_stats[i].i_lost_pictures > prev_vlc_stats[i].i_lost_pictures) or
       (vlc_stats[i].i_lost_abuffers > prev_vlc_stats[i].i_lost_abuffers) then Form1.Canvas.Pen.Color := clRed;
 
    Form1.Canvas.Pen.Width := 3;
    Form1.Canvas.Rectangle(VlcPlayer[i].Left - 2, VlcPlayer[i].Top - 2,VlcPlayer[i].Left + 322, VlcPlayer[i].Top + 242);
    prev_vlc_stats[i] := vlc_stats[i];
  end;
  i := StrToInt(Label18.Caption);
  Label3.Caption := IntToStr(vlc_stats[i].i_read_bytes)+' Read bytes';
  Label4.Caption := IntToStr(vlc_stats[i].i_demux_read_bytes)+' Demux read bytes';
  Label5.Caption := IntToStr(vlc_stats[i].i_demux_corrupted)+' Demux corrupted';
  Label6.Caption := IntToStr(vlc_stats[i].i_demux_discontinuity)+' Demux discontinuity';
  Label7.Caption := IntToStr(vlc_stats[i].i_decoded_video)+' Decoded video';
  Label8.Caption := IntToStr(vlc_stats[i].i_decoded_audio)+' Decoded audio';
  Label9.Caption := IntToStr(vlc_stats[i].i_displayed_pictures)+' Displayed pictures';
  Label10.Caption := IntToStr(vlc_stats[i].i_lost_pictures)+' Lost pictures';
  Label11.Caption := IntToStr(vlc_stats[i].i_played_abuffers)+' Played ABuffers';
  Label12.Caption := IntToStr(vlc_stats[i].i_lost_abuffers)+' Lost ABuffers';
  Label13.Caption := IntToStr(vlc_stats[i].i_sent_packets)+' Sent packets';
  Label14.Caption := IntToStr(vlc_stats[i].i_sent_bytes)+' Sent bytes';
  Label15.Caption := FloatToStr(vlc_stats[i].f_input_bitrate)+' Input bitrate';
  Label16.Caption := FloatToStr(vlc_stats[i].f_demux_bitrate)+' Demux bitrate';
  Label17.Caption := FloatToStr(vlc_stats[i].f_send_bitrate)+' Send bitrate';
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  i := StrToInt(Edit1.Text);
  if (i > 0) and (i <= URL_Count) then begin
    VlcPlayer[StrToInt(Label18.Caption)].SetAudioVolume(0);
    Label18.Caption := IntToStr(i);
    VlcPlayer[i].SetAudioVolume(50);
  end;
end;
 
end.

модуль unit2.pas с показом настроек - совсем простенький

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
unit Unit2;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;
 
type
  TForm2 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Edit3: TEdit;
    Label3: TLabel;
    Edit4: TEdit;
    BitBtn1: TBitBtn;
    Label4: TLabel;
    Button1: TButton;
    Edit5: TEdit;
    Label5: TLabel;
    OpenDialog1: TOpenDialog;
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form2: TForm2;
 
implementation
 
{$R *.dfm}
 
procedure TForm2.BitBtn1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then Edit4.Text := OpenDialog1.FileName;
end;
 
end.

описание формы для unit1.dfm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
object Form1: TForm1
  Left = 366
  Top = 257
  Width = 679
  Height = 445
  Caption = 'Moza IC by IbZ(c)'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  OnMouseUp = FormMouseUp
  PixelsPerInch = 96
  TextHeight = 13
  object Panel1: TPanel
    Left = 0
    Top = 368
    Width = 671
    Height = 50
    Align = alBottom
    TabOrder = 0
    object Label1: TLabel
      Left = 8
      Top = 8
      Width = 7
      Height = 24
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clNavy
      Font.Height = -19
      Font.Name = 'MS Sans Serif'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label2: TLabel
      Left = 32
      Top = 4
      Width = 140
      Height = 13
      Caption = #8470' '#1082#1072#1085#1072#1083#1072' '#1076#1083#1103' '#1084#1086#1085#1080#1090#1086#1088#1080#1085#1075#1072
      Visible = False
    end
    object Label3: TLabel
      Left = 616
      Top = 0
      Width = 54
      Height = 13
      Caption = 'Read bytes'
      Visible = False
    end
    object Label4: TLabel
      Left = 616
      Top = 24
      Width = 85
      Height = 13
      Caption = 'Demux read bytes'
      Visible = False
    end
    object Label5: TLabel
      Left = 472
      Top = 0
      Width = 81
      Height = 13
      Caption = 'Demux corrupted'
      Visible = False
    end
    object Label6: TLabel
      Left = 472
      Top = 12
      Width = 94
      Height = 13
      Caption = 'Demux discontinuity'
      Visible = False
    end
    object Label7: TLabel
      Left = 336
      Top = 0
      Width = 73
      Height = 13
      Caption = 'Decoded video'
      Visible = False
    end
    object Label8: TLabel
      Left = 336
      Top = 24
      Width = 73
      Height = 13
      Caption = 'Decoded audio'
      Visible = False
    end
    object Label9: TLabel
      Left = 336
      Top = 12
      Width = 86
      Height = 13
      Caption = 'Displayed pictures'
      Visible = False
    end
    object Label10: TLabel
      Left = 472
      Top = 24
      Width = 60
      Height = 13
      Caption = 'Lost pictures'
      Visible = False
    end
    object Label11: TLabel
      Left = 336
      Top = 36
      Width = 75
      Height = 13
      Caption = 'Played ABuffers'
      Visible = False
    end
    object Label12: TLabel
      Left = 472
      Top = 36
      Width = 63
      Height = 13
      Caption = 'Lost ABuffers'
      Visible = False
    end
    object Label13: TLabel
      Left = 240
      Top = 8
      Width = 63
      Height = 13
      Caption = 'Sent packets'
      Visible = False
    end
    object Label14: TLabel
      Left = 240
      Top = 20
      Width = 53
      Height = 13
      Caption = 'Send bytes'
      Visible = False
    end
    object Label15: TLabel
      Left = 616
      Top = 12
      Width = 56
      Height = 13
      Caption = 'Input bitrate'
      Visible = False
    end
    object Label16: TLabel
      Left = 616
      Top = 36
      Width = 65
      Height = 13
      Caption = 'Demux bitrate'
      Visible = False
    end
    object Label17: TLabel
      Left = 240
      Top = 32
      Width = 57
      Height = 13
      Caption = 'Send bitrate'
      Visible = False
    end
    object Label18: TLabel
      Left = 64
      Top = 24
      Width = 17
      Height = 13
      Caption = '1'
      Visible = False
    end
    object Edit1: TEdit
      Left = 32
      Top = 20
      Width = 25
      Height = 21
      TabOrder = 0
      Text = '1'
      Visible = False
    end
    object Button1: TButton
      Left = 88
      Top = 20
      Width = 81
      Height = 25
      Caption = #1052#1086#1085#1080#1090#1086#1088#1080#1090#1100'!'
      TabOrder = 1
      Visible = False
      OnClick = Button1Click
    end
  end
  object Timer1: TTimer
    Enabled = False
    OnTimer = Timer1Timer
    Left = 88
    Top = 112
  end
  object Timer2: TTimer
    Enabled = False
    OnTimer = Timer2Timer
    Left = 128
    Top = 112
  end
end

описание формы для unit2.dfm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
object Form2: TForm2
  Left = 519
  Top = 337
  Width = 264
  Height = 257
  Caption = #1053#1072#1089#1090#1088#1086#1077#1095#1082#1080'...'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 12
    Width = 117
    Height = 13
    Caption = #1050#1086#1083#1080#1095#1077#1089#1090#1074#1086' '#1084#1086#1085#1080#1090#1086#1088#1086#1074
  end
  object Label2: TLabel
    Left = 200
    Top = 12
    Width = 9
    Height = 13
    Caption = 'X'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = [fsBold]
    ParentFont = False
  end
  object Label3: TLabel
    Left = 8
    Top = 44
    Width = 200
    Height = 13
    Caption = #1054#1076#1085#1086#1074#1088#1077#1084#1077#1085#1085#1086' '#1088#1072#1073#1086#1090#1072#1102#1097#1080#1093' '#1084#1086#1085#1080#1090#1086#1088#1086#1074
  end
  object Label4: TLabel
    Left = 8
    Top = 112
    Width = 191
    Height = 13
    Caption = #1054#1090#1082#1088#1099#1090#1100' '#1092#1072#1081#1083' '#1089' '#1087#1083#1077#1081#1083#1080#1089#1090#1086#1084' '#1082#1072#1085#1072#1083#1086#1074
  end
  object Label5: TLabel
    Left = 8
    Top = 76
    Width = 194
    Height = 13
    Caption = #1055#1072#1091#1079#1072' '#1084#1077#1078#1076#1091' '#1079#1072#1087#1091#1089#1082#1086#1084' '#1082#1072#1085#1072#1083#1086#1074' ('#1089#1077#1082'.)'
  end
  object Edit1: TEdit
    Left = 160
    Top = 8
    Width = 33
    Height = 21
    TabOrder = 0
    Text = '3'
  end
  object Edit2: TEdit
    Left = 216
    Top = 8
    Width = 33
    Height = 21
    TabOrder = 1
    Text = '3'
  end
  object Edit3: TEdit
    Left = 216
    Top = 40
    Width = 33
    Height = 21
    TabOrder = 2
    Text = '5'
  end
  object Edit4: TEdit
    Left = 8
    Top = 128
    Width = 209
    Height = 21
    TabOrder = 3
    Text = 'playlist.m3u'
  end
  object BitBtn1: TBitBtn
    Left = 224
    Top = 128
    Width = 25
    Height = 25
    TabOrder = 4
    OnClick = BitBtn1Click
    Glyph.Data = {
      76010000424D7601000000000000760000002800000020000000100000000100
      04000000000000010000120B0000120B00001000000000000000000000000000
      800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
      FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00555555555555
      55555555FFFFFFFFFF55555000000000055555577777777775F55500B8B8B8B8
      B05555775F555555575F550F0B8B8B8B8B05557F75F555555575550BF0B8B8B8
      B8B0557F575FFFFFFFF7550FBF0000000000557F557777777777500BFBFBFBFB
      0555577F555555557F550B0FBFBFBFBF05557F7F555555FF75550F0BFBFBF000
      55557F75F555577755550BF0BFBF0B0555557F575FFF757F55550FB700007F05
      55557F557777557F55550BFBFBFBFB0555557F555555557F55550FBFBFBFBF05
      55557FFFFFFFFF7555550000000000555555777777777755555550FBFB055555
      5555575FFF755555555557000075555555555577775555555555}
    NumGlyphs = 2
  end
  object Button1: TButton
    Left = 8
    Top = 168
    Width = 241
    Height = 57
    Caption = #1055' '#1040' '#1045' '#1061' '#1040' '#1051' '#1048'   !!!'
    Default = True
    Font.Charset = ANSI_CHARSET
    Font.Color = clWindowText
    Font.Height = -21
    Font.Name = 'Arial'
    Font.Style = [fsBold]
    ModalResult = 1
    ParentFont = False
    TabOrder = 5
  end
  object Edit5: TEdit
    Left = 216
    Top = 72
    Width = 33
    Height = 21
    TabOrder = 6
    Text = '10'
  end
  object OpenDialog1: TOpenDialog
    Filter = 'M3U|*.m3u'
    Left = 152
    Top = 136
  end
end

Ссылка на архив с исходниками, скомпилированной программой и примером плейлиста http://www2.ibz.ru/Soft/MozaIC.zip

tags: