]> git.vomp.tv Git - vompclient.git/blob - vaudioselector.cc
43 CWFs
[vompclient.git] / vaudioselector.cc
1 /*
2     Copyright 2006-2020 Chris Tallon, Marten Richter
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <algorithm>
21 #include <sstream>
22 #include <iomanip>
23
24 #include "input.h"
25 #include "colour.h"
26 #include "video.h"
27 #include "audio.h"
28 #include "boxstack.h"
29 #include "i18n.h"
30 #include "message.h"
31 #include "messagequeue.h"
32 #include "recinfo.h"
33 #include "log.h"
34 #include "channel.h"
35
36 #include "vaudioselector.h"
37
38 VAudioSelector::VAudioSelector(MessageReceiver* tparent, bool* availableMpegAudioChannels,
39                                bool* availableAc3AudioChannels, int currentAudioChannel, bool* availableSubtitleChannels, int* ttxtpages,
40                                int currentSubtitleChannel, int currentSubtitleType, RecInfo* recInfo)
41 : parent(tparent), liveMode(false)
42 {
43   LogNT::getInstance()->debug("VAudioSelector", "{}", currentAudioChannel);
44
45   int i;
46
47   if (availableSubtitleChannels != NULL)
48   {
49     for (i = 0; i < PES_DVBSUBTITLE_MAXCHANNELS; i++)
50     {
51       if (availableSubtitleChannels[i])
52       {
53         AudioSubtitleChannel sc;
54         sc.type = 0x10; //dvbsubtitle
55         sc.pestype = PES_DVBSUBTITLE_START + i;
56         scl.push_back(std::move(sc));
57       }
58     }
59
60     for (i = 0; i < 10; i++)
61     {
62       if (ttxtpages[i] > 0)
63       {
64         AudioSubtitleChannel sc;
65         sc.type = 0x11; //Teletxt
66         sc.pestype = ttxtpages[i];
67         std::ostringstream oss;
68         oss << tr("TTxt:") << " " << std::hex << std::setw(3) << ttxtpages[i];
69         sc.name = std::move(oss.str());
70         scl.push_back(std::move(sc));
71       }
72     }
73   }
74
75   if (scl.size() > 0)
76   {
77     AudioSubtitleChannel sc;
78     sc.type = 0xFF; //special
79     sc.name = tr("No Subtitles");
80     sc.pestype = 0;
81     scl.insert(scl.begin(), std::move(sc));
82     subtitles = true;
83   }
84
85   if (subtitles)
86     setSize(400, 120);
87   else
88     setSize(200, 120);
89
90   createBuffer();
91
92   asl.setPosition(40, 30);
93   asl.setSize(200 - 45, area.h - 30);
94   add(&asl);
95
96   if (subtitles)
97   {
98     ssl.setPosition(200 + 40, 30);
99     ssl.setSize(200 - 45, area.h - 30);
100     add(&ssl);
101   }
102
103   // Load data from availableAudioChannels, currentAudioChannel and recInfo
104
105   for (i = 0; i < PES_AUDIO_MAXCHANNELS; i++)
106   {
107     if (availableMpegAudioChannels[i])
108     {
109       AudioSubtitleChannel ac;
110       ac.type = 0;
111       ac.pestype = PES_AUDIO_START + i;
112       acl.push_back(std::move(ac));
113     }
114   }
115
116   if (availableAc3AudioChannels != NULL)
117   {
118     for (i = 0; i < PES_AUDIO_AC3_MAXCHANNELS; i++)
119     {
120       if (availableAc3AudioChannels[i])
121       {
122         AudioSubtitleChannel ac;
123         ac.type = 1; //ac3
124         ac.pestype = PES_AUDIO_AC3_START + i;
125         acl.push_back(std::move(ac));
126       }
127     }
128   }
129
130   int numchan_recinfo = recInfo->numComponents;
131   UINT mp_audcounter = 0;
132   UINT ac3_counter = 0;
133   int ac3_offset = 0;
134   UINT dvb_subcounter = 1;
135
136   for (auto& ac : acl)
137   {
138     if (ac.type == 0) ++ac3_offset;
139   }
140
141   unsigned char type;
142   char* lang;
143   char* description;
144   int type_int;
145
146   for (i = 0; i < numchan_recinfo; i++)
147   {
148     type = recInfo->types[i];
149     lang = recInfo->languages[i];
150     description = recInfo->descriptions[i];
151     AudioSubtitleChannel* acp = NULL;
152     type_int = 0;
153
154     if (recInfo->streams[i] == 2)
155     {
156       switch (type)
157       {
158         case 1: //mpaudio mono
159         case 3: //mpaudio stereo
160           if (mp_audcounter < acl.size()) acp = &acl[mp_audcounter];
161
162           type_int = 0;
163           break;
164
165         case 5: //ac3
166           if (ac3_counter + ac3_offset < acl.size()) acp = &acl[ac3_counter + ac3_offset];
167
168           type_int = 1;
169           break;
170       }
171     }
172     else if (recInfo->streams[i] == 3)
173     {
174       /*  switch (type)
175         {
176         case 20:*/
177       if (dvb_subcounter < scl.size()) acp = &scl[dvb_subcounter];
178
179       type_int = 0x10;
180       /*     break;
181        };*/
182     }
183     else continue;   //neither audio nor subtitle
184
185     if (acp)
186     {
187       if (acp->type == type_int)
188       {
189         if (description && (strlen(description) > 0))
190         {
191           acp->name = description;
192         }
193         else if (lang && (strlen(lang) > 0))
194         {
195           acp->name = lang;
196         }
197       }
198     }
199
200     switch (type_int)
201     {
202       case 0: //mpaudio
203         mp_audcounter++;
204         break;
205
206       case 1: //ac3
207         ac3_counter++;
208         break;
209
210       case 0x10:
211         dvb_subcounter++;
212         break;
213     }
214   }
215
216   // Now do display. acl and scl are read only from now on, so pointers always remain valid
217
218   if (acl.size())
219   {
220     for (auto& ac : acl)
221     {
222       if (!ac.name.empty())
223       {
224         asl.addOption(ac.name.c_str(), &ac, (ac.pestype == currentAudioChannel));
225       }
226       else
227       {
228         std::string tempString;
229
230         if      (ac.type == 0) tempString = std::to_string(ac.pestype - PES_AUDIO_START);
231         else if (ac.type == 1) tempString = "ac3 " + std::to_string(ac.pestype - PES_AUDIO_AC3_START);
232         else                   tempString = "unknown";
233
234         asl.addOption(tempString.c_str(), &ac, (ac.pestype == currentAudioChannel));
235       }
236     }
237   }
238   else
239   {
240     asl.addOption(tr("No audio channel data available"), 0, 1);
241   }
242
243   if (subtitles)
244   {
245     ssl.setDarkSelOption(true);
246
247     for (auto& sc : scl)
248     {
249       bool selected = false;
250
251       if (sc.pestype == currentSubtitleChannel && sc.type == currentSubtitleType) selected = true;
252
253       if (!sc.name.empty())
254       {
255         ssl.addOption(sc.name.c_str(), &sc, selected);
256       }
257       else
258       {
259         std::string tempString;
260
261         if (sc.type == 0x10) tempString = std::to_string(sc.pestype - PES_DVBSUBTITLE_START);
262         else                 tempString = "unknown";
263
264         ssl.addOption(tempString.c_str(), &sc, selected);
265       }
266     }
267   }
268
269   MessageQueue::getInstance()->addReceiver(this);
270 }
271
272 VAudioSelector::VAudioSelector(MessageReceiver* tparent, Channel* channel, int currentAudioChannel, int currentSubtitletype, int currentSubtitleChannel, int* ttxtpages)
273 : parent(tparent), liveMode(true)
274 {
275   UINT i;
276
277   for (i = 0; i < channel->numSPids; i++)
278   {
279     AudioSubtitleChannel sc;
280     sc.type = 0x10;
281     sc.name = channel->spids[i].desc;
282     sc.pestype = channel->spids[i].pid;
283     scl.push_back(std::move(sc));
284   }
285
286   if (ttxtpages)
287   {
288     for (i = 0; i < 10; i++)
289     {
290       if (ttxtpages[i] > 0)
291       {
292         AudioSubtitleChannel sc;
293         sc.type = 0x11; //Teletxt
294         sc.pestype = ttxtpages[i];
295         std::ostringstream oss;
296         oss << tr("TTxt:") << " " << std::hex << std::setw(3) << ttxtpages[i];
297         sc.name = std::move(oss.str());
298         scl.push_back(std::move(sc));
299       }
300     }
301   }
302
303   if (scl.size() > 0)
304   {
305     AudioSubtitleChannel sc;
306     sc.type = 0xFF; //special
307     sc.name = tr("No Subtitles");
308     sc.pestype = 0;
309     scl.insert(scl.begin(), std::move(sc));
310     subtitles = true;
311   }
312
313   if (subtitles)
314     setSize(400, 120);
315   else
316     setSize(200, 120);
317
318   createBuffer();
319
320   asl.setPosition(40, 30);
321   asl.setSize(200 - 45, area.h - 30);
322   add(&asl);
323
324   if (subtitles)
325   {
326     ssl.setPosition(200 + 40, 30);
327     ssl.setSize(200 - 45, area.h - 30);
328     add(&ssl);
329   }
330
331   // Load data from availableAudioChannels, currentAudioChannel and recInfo
332
333   for (i = 0; i < channel->numAPids; i++)
334   {
335     if (Audio::getInstance()->streamTypeSupported(channel->apids[i].type))
336     {
337       AudioSubtitleChannel ac;
338       ac.type = 0;
339       ac.name = channel->apids[i].desc;
340       ac.pestype = channel->apids[i].pid;
341       ac.streamtype = channel->apids[i].type;
342       acl.push_back(std::move(ac));
343     }
344   }
345
346   if (Audio::getInstance()->supportsAc3())
347   {
348     for (i = 0; i < channel->numDPids; i++)
349     {
350       if (Audio::getInstance()->streamTypeSupported(channel->dpids[i].type))
351       {
352         AudioSubtitleChannel ac;
353         ac.type = 1;
354         ac.name = channel->dpids[i].desc;
355         ac.pestype = channel->dpids[i].pid;
356         ac.streamtype = channel->dpids[i].type;
357         acl.push_back(std::move(ac));
358       }
359     }
360   }
361
362   // acl and scl now read only so pointers are always valid
363
364   if (acl.size())
365   {
366     for (auto& ac : acl)
367     {
368       asl.addOption(ac.name.c_str(), &ac, (ac.pestype == currentAudioChannel));
369     }
370   }
371   else
372   {
373     asl.addOption(tr("No audio channel data available"), 0, 1);
374   }
375
376   if (subtitles)
377   {
378     ssl.setDarkSelOption(true);
379
380     for (auto& sc : scl)
381     {
382       bool selected = false;
383       if ((sc.type == currentSubtitletype) && (sc.pestype == currentSubtitleChannel)) selected = true;
384       ssl.addOption(sc.name.c_str(), &sc, selected);
385     }
386   }
387
388   MessageQueue::getInstance()->addReceiver(this);
389 }
390
391 VAudioSelector::~VAudioSelector()
392 {
393   MessageQueue::getInstance()->removeReceiver(this);
394
395   Message* m = new Message();
396   m->from = this;
397   m->to = parent;
398   m->message = Message::CHILD_CLOSE;
399   MessageQueue::getInstance()->postMessage(m);
400 }
401
402 void VAudioSelector::draw()
403 {
404   TBBoxx::draw();
405
406   // FIXME bad drawing
407
408   rectangle(0, 0, area.w, 30, DrawStyle::TITLEBARBACKGROUND);
409   drawText(tr("Audio"), 45, 5, DrawStyle::LIGHTTEXT);
410
411   if (subtitles)
412   {
413     drawText(tr("Subtitles"), 45 + 200, 5, DrawStyle::LIGHTTEXT);
414
415     ssl.setBackgroundColour(backgroundColour);
416     ssl.draw();
417   }
418
419   asl.setBackgroundColour(backgroundColour);
420   asl.draw();
421 }
422
423 int VAudioSelector::handleCommand(int command)
424 {
425   switch (command)
426   {
427     case Input::BACK:
428     case Input::OK:
429     case Input::GREEN:
430     {
431       return BoxStack::DELETE_ME;
432     }
433
434     case Input::UP:
435     {
436       if (editsubtitles)
437       {
438         ssl.up();
439         ssl.draw();
440         BoxStack::getInstance()->update(this);
441         Message* m = new Message();
442         m->from = this;
443         m->to = parent;
444         m->message = Message::SUBTITLE_CHANGE_CHANNEL;
445         AudioSubtitleChannel* asc = static_cast<AudioSubtitleChannel*>(ssl.getCurrentOptionData());
446         m->parameter = (asc->pestype & 0xFFFF) | (asc->type & 0xFF) << 16;
447         MessageQueue::getInstance()->postMessage(m);
448       }
449       else
450       {
451         asl.up();
452         asl.draw();
453         BoxStack::getInstance()->update(this);
454         Message* m = new Message();
455         m->from = this;
456         m->to = parent;
457         m->message = Message::AUDIO_CHANGE_CHANNEL;
458         AudioSubtitleChannel* asc = static_cast<AudioSubtitleChannel*>(asl.getCurrentOptionData());
459         m->parameter = (asc->pestype & 0xFFFF) | (asc->type & 0xFF) << 16;
460         MessageQueue::getInstance()->postMessage(m);
461       }
462
463       return BoxStack::COMMAND_HANDLED;
464     }
465
466     case Input::DOWN:
467     {
468       if (editsubtitles)
469       {
470         ssl.down();
471         ssl.draw();
472         BoxStack::getInstance()->update(this);
473         Message* m = new Message();
474         m->from = this;
475         m->to = parent;
476         m->message = Message::SUBTITLE_CHANGE_CHANNEL;
477         AudioSubtitleChannel* subchan = static_cast<AudioSubtitleChannel*>(ssl.getCurrentOptionData());
478         AudioSubtitleChannel* audchan = static_cast<AudioSubtitleChannel*>(asl.getCurrentOptionData());
479         m->parameter =   (subchan->pestype & 0xFFFF)
480                        | (subchan->type & 0xFF) << 16
481                        | (audchan->streamtype & 0xFF) << 24; // FIXME: Is this really supposed to be audio channel here? Old code did audio, looks wrong
482         MessageQueue::getInstance()->postMessage(m);
483       }
484       else
485       {
486         asl.down();
487         asl.draw();
488         BoxStack::getInstance()->update(this);
489         Message* m = new Message();
490         m->from = this;
491         m->to = parent;
492         m->message = Message::AUDIO_CHANGE_CHANNEL;
493         AudioSubtitleChannel* asc = static_cast<AudioSubtitleChannel*>(asl.getCurrentOptionData());
494         m->parameter =   (asc->pestype & 0xFFFF)
495                        | (asc->type & 0xFF) << 16
496                        | (asc->streamtype & 0xFF) << 24;
497         MessageQueue::getInstance()->postMessage(m);
498       }
499
500       return BoxStack::COMMAND_HANDLED;
501     }
502
503     case Input::LEFT:
504     {
505       if (editsubtitles && subtitles)
506       {
507         ssl.setDarkSelOption(true);
508         asl.setDarkSelOption(false);
509         editsubtitles = false;
510         asl.draw();
511         ssl.draw();
512         BoxStack::getInstance()->update(this);
513       }
514
515       return BoxStack::COMMAND_HANDLED;
516     }
517
518     case Input::RIGHT:
519     {
520       if (!editsubtitles && subtitles)
521       {
522         ssl.setDarkSelOption(false);
523         asl.setDarkSelOption(true);
524         editsubtitles = true;
525         asl.draw();
526         ssl.draw();
527         BoxStack::getInstance()->update(this);
528       }
529
530       return BoxStack::COMMAND_HANDLED;
531     }
532   }
533
534   return BoxStack::ABANDON_COMMAND;
535 }
536
537 void VAudioSelector::processMessage(Message* m)
538 {
539   if (m->message == Message::MOUSE_MOVE)
540   {
541     int lastsel = asl.getCurrentOption();
542
543     if ((m->parameter - getScreenX()) < 200 && asl.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
544     {
545       editsubtitles = false;
546       ssl.setDarkSelOption(true);
547       asl.setDarkSelOption(false);
548       asl.draw();
549       ssl.draw();
550       BoxStack::getInstance()->update(this);
551
552       if (lastsel != asl.getCurrentOption())
553       {
554         Message* m2 = new Message();
555         m2->from = this;
556         m2->to = parent;
557         m2->message = Message::AUDIO_CHANGE_CHANNEL;
558         AudioSubtitleChannel* asc = static_cast<AudioSubtitleChannel*>(asl.getCurrentOptionData());
559         m2->parameter = (asc->pestype & 0xFFFF) | (asc->type & 0xFF) << 16 ;
560         MessageQueue::getInstance()->postMessage(m2);
561       }
562
563       return;
564     }
565
566     lastsel = ssl.getCurrentOption();
567
568     if (ssl.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
569     {
570       editsubtitles = true;
571       ssl.setDarkSelOption(false);
572       asl.setDarkSelOption(true);
573       asl.draw();
574       ssl.draw();
575       BoxStack::getInstance()->update(this);
576
577       if (lastsel != ssl.getCurrentOption())
578       {
579         Message* m2 = new Message();
580         m2->from = this;
581         m2->to = parent;
582         m2->message = Message::SUBTITLE_CHANGE_CHANNEL;
583         AudioSubtitleChannel* asc = static_cast<AudioSubtitleChannel*>(ssl.getCurrentOptionData());
584         m2->parameter = (asc->pestype & 0xFFFF) | (asc->type & 0xFF) << 16 ;
585         MessageQueue::getInstance()->postMessage(m2);
586       }
587
588       return;
589     }
590   }
591   else if (m->message == Message::MOUSE_LBDOWN)
592   {
593     if (asl.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
594     {
595       editsubtitles = false;
596       ssl.setDarkSelOption(true);
597       asl.setDarkSelOption(false);
598       asl.draw();
599       ssl.draw();
600       Input::sendInputKey(Input::OK);
601     }
602     else if (ssl.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
603     {
604       editsubtitles = true;
605       ssl.setDarkSelOption(false);
606       asl.setDarkSelOption(true);
607       asl.draw();
608       ssl.draw();
609       Input::sendInputKey(Input::OK);
610     }
611     else
612     {
613       //check if press is outside this view! then simulate cancel
614       int x = m->parameter - getScreenX();
615       int y = m->tag - getScreenY();
616
617       if (x < 0 || y < 0 || x > static_cast<int>(getWidth()) || y > static_cast<int>(getHeight()))
618       {
619         Input::sendInputKey(Input::BACK);
620       }
621     }
622   }
623 }