vdr-plugin-softhddevice-drm-gles 1.4.0
softhddevice.cpp
Go to the documentation of this file.
1
26#ifndef __USE_GNU
27#define __USE_GNU
28#endif
29
30#include <functional>
31#include <mutex>
32#include <variant>
33#include <algorithm>
34
35#include <assert.h>
36#include <unistd.h>
37
38#include <libintl.h>
39
40#include <pthread.h>
41#include <sys/types.h>
42#include <sys/wait.h>
43
45#include "softhdosd.h"
46
47#include "softhddevice.h"
48#include "logger.h"
49
50extern "C" {
51#include <libavcodec/avcodec.h>
52#include <libavformat/avformat.h>
53#include <libavutil/timestamp.h>
54
55}
56#include "buf2rgb.h"
57
58#include "iatomic.h"
59#include "videostream.h"
60#include "audio.h"
61#include "videorender.h"
62#include "codec_audio.h"
63#include "codec_video.h"
64#include "pes.h"
65#include "misc.h"
66#include "pipreceiver.h"
67
68#define _(str) gettext(str)
69#define _N(str) str
70
74extern "C" uint8_t * CreateJpeg(uint8_t * image, int *size, int quality,
75 int width, int height)
76{
77 return (uint8_t *) RgbToJpeg((uchar *) image, width, height, *size, quality);
78}
79
80#if defined(USE_JPEG) && JPEG_LIB_VERSION >= 80
93uint8_t *CreateJpeg(uint8_t * image, int raw_size, int *size, int quality,
94 int width, int height)
95{
96 struct jpeg_compress_struct cinfo;
97 struct jpeg_error_mgr jerr;
98 JSAMPROW row_ptr[1];
99 int rowStride;
100 uint8_t *outbuf;
101 long unsigned int outsize;
102
103 outbuf = NULL;
104 outsize = 0;
105 cinfo.err = jpeg_std_error(&jerr);
106 jpeg_create_compress(&cinfo);
107 jpeg_mem_dest(&cinfo, &outbuf, &outsize);
108
109 cinfo.image_width = width;
110 cinfo.image_height = height;
111 cinfo.input_components = raw_size / height / width;
112 cinfo.in_color_space = JCS_RGB;
113
114 jpeg_set_defaults(&cinfo);
115 jpeg_set_quality(&cinfo, quality, TRUE);
116 jpeg_start_compress(&cinfo, TRUE);
117
118 rowStride = width * 3;
119 while (cinfo.next_scanline < cinfo.image_height) {
120 row_ptr[0] = &image[cinfo.next_scanline * rowStride];
121 jpeg_write_scanlines(&cinfo, row_ptr, 1);
122 }
123
124 jpeg_finish_compress(&cinfo);
125 jpeg_destroy_compress(&cinfo);
126 *size = outsize;
127
128 return outbuf;
129}
130#endif
131
132/*****************************************************************************
133 * cSoftHdDevice class
134 ****************************************************************************/
135
144{
145// LOGDEBUG("device: %s:", __FUNCTION__);
146
147 m_pSpuDecoder = new cDvbSpuDecoder();
148 m_pConfig = config;
149 m_pAudioDecoder = nullptr;
150 m_audioChannelID = -1;
151 m_pOsdProvider = nullptr;
152 m_pipActive = false;
154}
155
162{
163 LOGDEBUG("device: %s:", __FUNCTION__);
164 delete m_pSpuDecoder;
165}
166
171{
172 LOGDEBUG("device: %s", __FUNCTION__);
174
175 return true;
176}
177
182{
183 LOGDEBUG("device: %s", __FUNCTION__);
185}
186
191{
192 LOGDEBUG("device: %s:", __FUNCTION__);
196}
197
204{
205 LOGDEBUG("device: %s: %d", __FUNCTION__, on);
206
207 if (on)
208 m_pOsdProvider = new cSoftOsdProvider(this); // no need to delete it, VDR does it
209
210 cDevice::MakePrimaryDevice(on);
211}
212
220{
221 LOGDEBUG("device: %s:", __FUNCTION__);
222 if (!IsPrimaryDevice())
223 return NULL;
224
225 return m_pSpuDecoder;
226}
227
232{
233 bool hasDecoder = !IsDetached();
234
235// LOGDEBUG("device: %s: %d", __FUNCTION__, hasDecoder);
236
237 return hasDecoder;
238}
239
244{
245 bool canReplay = !IsDetached();
246
247 LOGDEBUG("device: %s: %d", __FUNCTION__, canReplay);
248
249 return canReplay;
250}
251
263{
264 uint64_t startStateChange = cTimeMs::Now();
265 std::lock_guard<std::mutex> lock(m_mutex);
266
267 LOGDEBUG("device: received %s", EventToString(event));
268
269 if (m_state != DETACHED) {
272 }
273
274 bool needsResume = true;
275
276 auto invalid = [this, &event]() {
277 LOGWARNING("device: Invalid event '%s' in state '%s' received", EventToString(event), StateToString(m_state));
278 };
279
280 switch (m_state) {
281 case State::DETACHED:
282 std::visit(overload{
283 [&invalid](const PlayEvent&) { invalid(); },
284 [&invalid](const PauseEvent&) { invalid(); },
285 [&invalid](const StopEvent&) { invalid(); },
286 [&invalid](const TrickSpeedEvent&) { invalid(); },
287 [&invalid](const StillPictureEvent&) { invalid(); },
288 [](const DetachEvent&) { /* ignore */ },
289 [this](const AttachEvent&) {
290 SetState(STOP);
291 },
292 [&invalid](const BufferUnderrunEvent&) { invalid(); },
293 [&invalid](const BufferingThresholdReachedEvent&) { invalid(); },
294 [&invalid](const PipEvent&) { invalid(); },
295 }, event);
296 needsResume = false;
297 break;
298 case State::STOP:
299 std::visit(overload{
300 [this](const PlayEvent&) {
305 },
306 [&invalid](const PauseEvent&) { invalid(); },
307 [&invalid](const StopEvent&) { invalid(); },
308 [&invalid](const TrickSpeedEvent&) { invalid(); },
309 [&invalid](const StillPictureEvent&) { invalid(); },
310 [this, &needsResume](const DetachEvent&) {
313 needsResume = false;
314 },
315 [&invalid](const AttachEvent&) { invalid(); },
316 [&invalid](const BufferUnderrunEvent&) { invalid(); },
317 [&invalid](const BufferingThresholdReachedEvent&) { invalid(); },
318 [this](const PipEvent& p) {
319 HandlePip(p.state);
320 },
321 }, event);
322 break;
323 case State::BUFFERING:
324 std::visit(overload{
325 [this](const PlayEvent&) {
326 // ignore
327 },
328 [this](const PauseEvent&) {
329 // ignore
330 },
331 [this](const StopEvent&) {
332 SetState(STOP);
333 },
334 [this](const TrickSpeedEvent& t) {
335 // abort buffering and proceed with trick speed immediately, because trick speed shall be as fast and as demanded as possible
336 SetState(PLAY);
337 m_pRender->SetTrickSpeed(t.speed, t.forward);
339 },
340 [this](const StillPictureEvent& s) {
341 HandleStillPicture(s.data, s.size);
342 },
343 [this, &needsResume](const DetachEvent&) {
345 needsResume = false;
346 },
347 [&invalid](const AttachEvent&) { invalid(); },
348 [&invalid](const BufferUnderrunEvent&) { invalid(); },
349 [this](const BufferingThresholdReachedEvent&) {
350 bool receivedAudio = m_pAudio->HasPts();
351 bool receivedVideo = m_pVideoStream->HasInputPts();
352
353 if (receivedAudio && receivedVideo) {
355 int64_t firstAudioPtsMs = GetFirstAudioPtsMsToPlay();
356 int64_t firstVideoPtsMs = GetFirstVideoPtsMsToPlay();
357 // store the first PTSes beforehand, because dropping samples/frames will change the output of GetFirst*PtsMsToPlay()
358 m_pAudio->DropSamplesOlderThanPtsMs(firstAudioPtsMs);
359 m_pRender->SchedulePlaybackStartAtPtsMs(firstVideoPtsMs);
360 } else if (receivedAudio) {
361 LOGDEBUG("device: audio only detected");
364 } else if (receivedVideo) {
365 LOGDEBUG("device: video only detected");
368 } else
369 LOGFATAL("device: buffering threshold reached and no a/v available. This is a bug.");
370
371 SetState(PLAY);
372 },
373 [this](const PipEvent& p) {
374 HandlePip(p.state);
375 },
376 }, event);
377 break;
378 case State::PLAY:
379 std::visit(overload{
380 [this](const PlayEvent&) {
381 // resume from pause
382 int audioBehindVideoByMs;
383 switch (m_playbackMode) {
384 case AUDIO_ONLY:
385 m_pAudio->SetPaused(false);
386 break;
387 case VIDEO_ONLY:
389 break;
390 case AUDIO_AND_VIDEO:
392 if (audioBehindVideoByMs > 0) {
393 m_pAudio->DropSamplesOlderThanPtsMs(m_pAudio->GetOutputPtsMs() + audioBehindVideoByMs);
394 m_pAudio->SetPaused(false);
395 } else
397
399 break;
400 case NONE:
401 LOGFATAL("device: play event in PLAY state with NONE playback mode. This is a bug.");
402 break;
403 }
404 },
405 [this](const PauseEvent&) {
407 m_pAudio->SetPaused(true);
408 },
409 [this](const StopEvent&) {
410 SetState(STOP);
411 },
412 [this](const TrickSpeedEvent& t) {
413 m_pRender->SetTrickSpeed(t.speed, t.forward);
415 },
416 [this](const StillPictureEvent& s) {
417 HandleStillPicture(s.data, s.size);
418 },
419 [this, &needsResume](const DetachEvent&) {
422 needsResume = false;
423 },
424 [&invalid](const AttachEvent&) { invalid(); },
425 [this](const BufferUnderrunEvent&) {
427 },
428 [&invalid](const BufferingThresholdReachedEvent&) {
429 // ignore
430 },
431 [this](const PipEvent& p) {
432 HandlePip(p.state);
433 },
434 }, event);
435 break;
437 std::visit(overload{
438 [this](const PlayEvent&) {
439 SetState(PLAY);
440 },
441 [this](const PauseEvent&) {
443 m_pAudio->SetPaused(true);
444 },
445 [this](const StopEvent&) {
446 SetState(STOP);
447 },
448 [this](const TrickSpeedEvent& t) {
449 // resume from pause, or change trick speed direction/speed
450 m_pRender->SetTrickSpeed(t.speed, t.forward);
452 },
453 [this](const StillPictureEvent& s) {
454 HandleStillPicture(s.data, s.size);
455 },
456 [this, &needsResume](const DetachEvent&) {
459 needsResume = false;
460 },
461 [&invalid](const AttachEvent&) { invalid(); },
462 [this](const BufferUnderrunEvent&) {
463 // ignore during trick speed. Fast forward/reverse as fast and as demanded as possible
464 },
465 [&invalid](const BufferingThresholdReachedEvent&) { invalid(); },
466 [this](const PipEvent& p) {
467 HandlePip(p.state);
468 },
469 }, event);
470 break;
472 std::visit(overload{
473 [this](const PlayEvent&) {
474 SetState(PLAY);
475 },
476 [&invalid](const PauseEvent&) { invalid(); },
477 [this](const StopEvent&) {
478 SetState(STOP);
479 },
480 [this](const TrickSpeedEvent& t) {
481 m_pRender->SetTrickSpeed(t.speed, t.forward);
483 },
484 [this](const StillPictureEvent& s) {
485 HandleStillPicture(s.data, s.size);
486 },
487 [this, &needsResume](const DetachEvent&) {
490 needsResume = false;
491 },
492 [&invalid](const AttachEvent&) { invalid(); },
493 [&invalid](const BufferUnderrunEvent&) { invalid(); },
494 [&invalid](const BufferingThresholdReachedEvent&) { invalid(); },
495 [this](const PipEvent& p) {
496 HandlePip(p.state);
497 },
498 }, event);
499 break;
500 }
501
502 if (needsResume) {
505 }
506
507 uint64_t stopStateChange = cTimeMs::Now();
508 LOGDEBUG("device: state change done in %d ms", (int)(stopStateChange - startStateChange));
509}
510
520 switch (state) {
521 case BUFFERING:
522 // nothing
523 break;
524 case PLAY:
526 m_pAudio->SetPaused(false);
527 else {
528 m_pAudio->SetPaused(false);
530 }
531 break;
532 case TRICK_SPEED:
533 // The filter thread needs to be restarted for interlaced streams to be rendered without deinterlacer in trick speed mode. It is started lazily.
538 break;
539 case STOP:
540 ClearAudio();
541
544 m_pRender->Reset();
546
553
554 break;
555 case STILL_PICTURE:
558 break;
559 case DETACHED:
560 // resume the previously stopped threads
563
564 // now do the detach
566 delete m_pPipStream;
567
568 m_pRender->Exit(); // render must be stopped before videostream!
570 m_pAudio->Exit(); // audio must be stopped after renderer!
571#ifdef USE_GLES
572 m_pOsdProvider->StopOpenGlThread();
573#endif
574 delete m_pAudioDecoder; // includes a Close()
575 delete m_pVideoStream;
576 delete m_pRender;
577 delete m_pAudio;
578
579 break;
580 }
581}
582
592 switch (state) {
593 case PLAY:
596 m_pAudio->SetPaused(true);
597 break;
598 case BUFFERING:
600 break;
601 case TRICK_SPEED:
602 // The filter thread needs to be restarted for interlaced streams to be rendered with deinterlacer again. It is started lazily.
611 break;
612 case STOP:
613 m_receivedAudio = false;
614 m_receivedVideo = false;
615 break;
616 case STILL_PICTURE:
619 break;
620 case DETACHED:
621 m_pAudio = new cSoftHdAudio(this);
622 m_pRender = new cVideoRender(this);
625 m_pRender->Init(); // starts display thread
626 m_pVideoStream->StartDecoder(); // starts decoding thread
628 m_pPipStream->StartDecoder(); // starts decoding thread
629 // Audio is init lazily (includes starting thread)
630
631 break;
632 }
633}
634
641{
642 if (m_state != newState) {
643 LOGDEBUG("device: Preparing to leave state %s", StateToString(m_state));
645 LOGDEBUG("device: Changing state %s -> %s", StateToString(m_state), StateToString(newState));
646 m_state = newState;
648 LOGDEBUG("device: State changed to %s", StateToString(m_state));
649 }
650}
651
657bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode)
658{
659 LOGDEBUG("device: %s: %d", __FUNCTION__, play_mode);
660
661 switch (play_mode) {
662 case pmNone:
664 break;
665 case pmAudioVideo:
666 case pmAudioOnly:
667 case pmAudioOnlyBlack:
668 case pmVideoOnly:
670 break;
671 default:
672 LOGERROR("device: %s: playmode not supported %d", play_mode);
673 return 0;
674 break;
675 }
676
677 return 1;
678}
679
685{
686 switch (m_playbackMode) {
687 case NONE:
688 return AV_NOPTS_VALUE;
689 case AUDIO_AND_VIDEO:
690 case VIDEO_ONLY:
691 return m_pRender->GetVideoClock();
692 case AUDIO_ONLY:
694 }
695
696 abort();
697}
698
708void cSoftHdDevice::TrickSpeed(int speed, bool forward)
709{
710 LOGDEBUG("device: %s: %d %s", __FUNCTION__, speed, forward ? "forward" : "backward");
711
712 OnEventReceived(TrickSpeedEvent{speed, forward});
713}
714
724{
725 LOGDEBUG("device: %s:", __FUNCTION__);
726 cDevice::Clear();
727
728 if (IsDetached())
729 return;
730
733
736
740
743
744 m_pRender->Reset();
745
746 m_pAudio->SetPaused(true);
747 ClearAudio();
748
750
753}
754
762{
763 cDevice::Play();
764
766}
767
772{
773 LOGDEBUG("device: %s:", __FUNCTION__);
774 cDevice::Freeze();
775
777}
778
785void cSoftHdDevice::StillPicture(const uchar *data, int size)
786{
787 LOGDEBUG("device: %s: %s %p %d", __FUNCTION__, data[0] == 0x47 ? "ts" : "pes", data, size);
788
789 if (data[0] == 0x47) { // ts sync byte
790 cDevice::StillPicture(data, size);
791 return;
792 }
793
795}
796
804void cSoftHdDevice::HandleStillPicture(const uchar *data, int size)
805{
807
808 const uchar *currentPacketStart = data;
809 while (currentPacketStart < data + size) {
810 cPesVideo pesPacket((const uint8_t*)currentPacketStart, size - (currentPacketStart - data));
811
812 if (pesPacket.IsValid())
814 else {
815 LOGWARNING("device: %s: invalid PES packet", __FUNCTION__);
816 break;
817 }
818
819 currentPacketStart += pesPacket.GetPacketLength();
820 }
821
824}
825
838bool cSoftHdDevice::Poll(__attribute__ ((unused)) cPoller & poller, int timeoutMs)
839{
840// LOGDEBUG("device: %s: timeout %d", __FUNCTION__, timeout_ms);
841
843 return true;
844
845 usleep(timeoutMs * 1000);
846
847 return false;
848}
849
855bool cSoftHdDevice::Flush(int timeout)
856{
857 if (IsDetached())
858 return true;
859
860 LOGDEBUG("device: %s: timeout %d ms", __FUNCTION__, timeout);
862 if (timeout) { // let display thread work
863 usleep(timeout * 1000);
864 }
866 }
867
868 return true;
869}
870
877void cSoftHdDevice::SetVideoDisplayFormat(eVideoDisplayFormat videoDisplayFormat)
878{
879 LOGDEBUG("device: %s: %d", __FUNCTION__, videoDisplayFormat);
880
881 cDevice::SetVideoDisplayFormat(videoDisplayFormat);
882}
883
894void cSoftHdDevice::SetVideoFormat(bool videoFormat16_9)
895{
896 LOGDEBUG("device: %s: %d", __FUNCTION__, videoFormat16_9);
897
898 // FIXME: 4:3 / 16:9 video format not supported.
899 SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
900}
901
910void cSoftHdDevice::GetVideoSize(int &width, int &height, double &aspectRatio)
911{
912// LOGDEBUG("device: %s: %d x %d @ %f", __FUNCTION__, *width, *height, *aspectRatio);
913
914 if (IsDetached()) { // return default values according to vdr docs
915 width = 0;
916 height = 0;
917 aspectRatio = 1.0;
918 return;
919 }
920
921 m_pVideoStream->GetVideoSize(&width, &height, &aspectRatio);
922}
923
929void cSoftHdDevice::GetOsdSize(int &width, int &height, double &aspectRatio)
930{
931 if (IsDetached()) { // hardcode to 1920x1080 in detached state
932 width = 1920;
933 height = 1080;
934 aspectRatio = (double)width / (double)height;
935 return;
936 }
937
938 std::lock_guard<std::mutex> lock(m_sizeMutex);
939 width = m_screenWidth;
940 height = m_screenHeight;
941 aspectRatio = (double)width / (double)height;
942}
943
951void cSoftHdDevice::SetScreenSize(int width, int height, uint32_t refreshRate)
952{
953 std::lock_guard<std::mutex> lock(m_sizeMutex);
954 m_screenWidth = width;
955 m_screenHeight = height;
956 m_screenRefreshRate = refreshRate;
957}
958
965static void PrintStreamData(const uchar *payload)
966{
967 LOGDEBUG2(L_CODEC, "Stream: %02X%02X%02X | %02X | %02X%02X | %02X%02X%02X | %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
968 payload[0],
969 payload[1],
970 payload[2],
971 payload[3],
972 payload[4],
973 payload[5],
974 payload[6],
975 payload[7],
976 payload[8],
977 payload[9],
978 payload[10],
979 payload[11],
980 payload[12],
981 payload[13],
982 payload[14],
983 payload[15],
984 payload[16],
985 payload[17],
986 payload[18],
987 payload[19],
988 payload[20],
989 payload[21],
990 payload[22],
991 payload[23],
992 payload[24]
993 );
994}
995
1007int cSoftHdDevice::PlayAudio(const uchar *data, int size, uchar id)
1008{
1009// LOGDEBUG("device: %s: %p %p %d %d", __FUNCTION__, this, data, size, id);
1010
1011 m_receivedAudio = true;
1012
1013 if (m_pAudio->IsBufferFull())
1014 return 0;
1015
1016 cPesAudio pesPacket((const uint8_t*)data, size);
1017
1018 if (!pesPacket.IsValid()) {
1020
1021 return size;
1022 }
1023
1024 // static int64_t lastPts = AV_NOPTS_VALUE;
1025 // LOGINFO("PlayAudio: PTS %s diff %s", Timestamp2String(pesPacket.GetPts(), 90), lastPts != AV_NOPTS_VALUE && pesPacket.HasPts() ? Timestamp2String(pesPacket.GetPts() - lastPts, 90) : "N/A");
1026 // lastPts = pesPacket.GetPts();
1027
1028 if (m_audioChannelID != id) {
1029 m_audioChannelID = id;
1032 LOGDEBUG("device: %s: new channel id 0x%02X", __FUNCTION__, m_audioChannelID);
1033 }
1034
1035 m_audioReassemblyBuffer.Push(pesPacket.GetPayload(), pesPacket.GetPayloadSize(), pesPacket.GetPts());
1036
1039
1040 AVPacket *avpkt;
1041 do {
1043
1044 if (avpkt) {
1045 if (m_pAudioDecoder->GetCodecId() == AV_CODEC_ID_NONE && m_audioReassemblyBuffer.GetCodec() != AV_CODEC_ID_NONE) {
1046 // The playback has just started
1049 }
1050
1051 m_pAudioDecoder->Decode(avpkt);
1052 AVPacket *copy = avpkt;
1053 av_packet_free(&copy);
1054 }
1055 } while (avpkt != nullptr);
1056
1057 return size;
1058}
1059
1060void cSoftHdDevice::SetAudioTrackDevice( __attribute__ ((unused)) eTrackType type)
1061{
1062 //LOGDEBUG("device: %s:", __FUNCTION__);
1063}
1064
1065void cSoftHdDevice::SetDigitalAudioDevice( __attribute__ ((unused)) bool on)
1066{
1067 //LOGDEBUG("device: %s: %s", __FUNCTION__, on ? "true" : "false");
1068}
1069
1070void cSoftHdDevice::SetAudioChannelDevice( __attribute__ ((unused))
1071 int audio_channel)
1072{
1073 //LOGDEBUG("device: %s: %d", __FUNCTION__, audio_channel);
1074}
1075
1077{
1078 //LOGDEBUG("device: %s:", __FUNCTION__);
1079 return 0;
1080}
1081
1088{
1089 if (IsDetached())
1090 return;
1091
1092 LOGDEBUG("device: %s: %d", __FUNCTION__, volume);
1093 m_pAudio->SetVolume((volume * 1000) / 255);
1094}
1095
1108int cSoftHdDevice::PlayVideo(const uchar *data, int size)
1109{
1110// LOGDEBUG("device: %s: %p %d", __FUNCTION__, data, size);
1112}
1113
1124int cSoftHdDevice::PlayPipVideo(const uchar *data, int size)
1125{
1126// LOGDEBUG("device: %s: %p %d", __FUNCTION__, data, size);
1128}
1129
1136int cSoftHdDevice::PlayVideoInternal(cVideoStream *stream, cReassemblyBufferVideo *buffer, const uchar *data, int size)
1137{
1138 // LOGDEBUG("device: %s: %p %d", __FUNCTION__, data, size);
1139
1140 m_receivedVideo = true;
1141
1142 if (stream->IsInputBufferFull())
1143 return 0;
1144
1145 cPesVideo pesPacket((const uint8_t*)data, size);
1146
1147 if (!pesPacket.IsValid()) {
1148 buffer->Reset();
1149
1150 return size;
1151 }
1152
1153 if (stream->GetCodecId() == AV_CODEC_ID_NONE) {
1154 // The playback has just started
1155 if (!pesPacket.HasPts() || !buffer->ParseCodecHeader(pesPacket.GetPayload(), pesPacket.GetPayloadSize())) {
1156 // received the middle of fragmented data, wait for the next PES packets with the start of a new frame
1157 return size;
1158 }
1159
1160 PrintStreamData(data);
1161 buffer->Push(pesPacket.GetPayload(), pesPacket.GetPayloadSize(), pesPacket.GetPts());
1162
1163 stream->Open(buffer->GetCodec());
1164 } else {
1165 int payloadOffset = 0;
1166 if (pesPacket.HasPts() && !buffer->IsEmpty()) {
1167 // received the first fragment of a new frame, finish the current reassembly buffer into an AVPacket
1168 stream->PushAvPacket(buffer->PopAvPacket());
1169
1170 // populate the cleared buffer with the next frame
1171 if (buffer->HasLeadingZero(pesPacket.GetPayload(), pesPacket.GetPayloadSize()))
1172 payloadOffset = 1; // H.264/HEVC streams may have a leading zero byte before the start code
1173 }
1174
1175 buffer->Push(pesPacket.GetPayload() + payloadOffset, pesPacket.GetPayloadSize() - payloadOffset, pesPacket.GetPts());
1176 }
1177
1178 return size;
1179}
1180
1190{
1191 if (m_state != BUFFERING)
1192 return false;
1193
1194 bool audioHasPts = m_pAudio->HasPts();
1195 bool videoHasInputPts = m_pVideoStream->HasInputPts();
1196 bool videoHasOutputPts = m_pRender->GetOutputPtsMs() != AV_NOPTS_VALUE;
1197
1198 // Assume audio only or video only if no PES fragment from the other stream has been received, while the buffering threshold of the other stream is reached.
1199 // Check for buffer fill level only if at least one PES packet was reassembled and pushed to the respective decoder.
1200 bool audioOnly = audioHasPts && !videoHasInputPts && m_receivedAudio && !m_receivedVideo;
1201 bool videoOnly = !audioHasPts && videoHasInputPts && !m_receivedAudio && m_receivedVideo;
1202
1204 (videoOnly && videoHasOutputPts && m_pVideoStream->GetInputPtsMs() - m_pRender->GetOutputPtsMs() > GetBufferFillLevelThresholdMs())) {
1205 LOGDEBUG("device: %s: Detected audio or video only", __FUNCTION__);
1206 return true;
1207 } else if (!audioHasPts || !videoHasInputPts || !videoHasOutputPts)
1208 return false; // Either no video or no audio received, yet. Or, video didn't make it to the output buffer, yet.
1209
1210 int64_t syncedAudioBufferFillLevelMs = m_pAudio->GetInputPtsMs() - GetFirstAudioPtsMsToPlay();
1211 int64_t syncedVideoBufferFillLevelMs = m_pVideoStream->GetInputPtsMs() - GetFirstVideoPtsMsToPlay();
1212
1213 bool reached = m_pRender->IsOutputBufferFull() && // video decoder output buffer (audio hardware output buffer is negligible)
1214 syncedVideoBufferFillLevelMs > GetBufferFillLevelThresholdMs() && // video decoder input buffer
1215 syncedAudioBufferFillLevelMs > GetBufferFillLevelThresholdMs(); // audio decoder output buffer
1216
1217 if (reached) {
1218 LOGDEBUG2(L_AV_SYNC, "First received PTS: %s (audio), %s (video) buffer fill levels: %ldms (audio) %ldms (video)",
1221 syncedAudioBufferFillLevelMs,
1222 syncedVideoBufferFillLevelMs);
1223 }
1224
1225 return reached;
1226}
1227
1241{
1242 int64_t ret = std::max(m_pRender->GetOutputPtsMs(), m_pAudio->GetOutputPtsMs());
1243
1246
1247 return ret;
1248}
1249
1254{
1255 int64_t ret = std::max(m_pRender->GetOutputPtsMs(), m_pAudio->GetOutputPtsMs());
1256
1259
1260 return ret;
1261}
1262
1282uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int width, int height)
1283{
1284 if (IsDetached())
1285 return NULL;
1286
1287 if (m_grabActive) {
1288 LOGWARNING("device: %s: wait for the last grab to be finished - skip!", __FUNCTION__);
1289 return NULL;
1290 }
1291
1292 if (!width || !height) {
1293 LOGERROR("device: %s: Width or height must be not 0!", __FUNCTION__);
1294 return NULL;
1295 }
1296
1297 if (quality < 0) { // caller should care, but fix it
1298 quality = 95;
1299 }
1300
1301 LOGDEBUG2(L_GRAB, "device: %s: %d, %d, %d, %dx%d", __FUNCTION__, size, jpeg, quality, width, height);
1302
1303 // 1. Trigger grab in render thread and wait for the buffers to be cloned
1304 m_grabActive = true;
1305 // TriggerGrab does wait and return 0, if buffers are available,
1306 // otherwise it returns != 0, if we ran into a timeout
1307 if (m_pRender->TriggerGrab()) {
1309 m_grabActive = false;
1310 return NULL;
1311 }
1312
1313 // 2. Convert the buffers to rgb and free the cloned buffers afterwards
1317
1318 // 3. get screen dimensions
1319 int screenWidth = 0;
1320 int screenHeight = 0;
1321 double aspectRatio = 0.0f;
1322 GetOsdSize(screenWidth, screenHeight, aspectRatio);
1323
1324 int screenSize = screenWidth * screenHeight * 3; // we want a RGB24
1325
1326 // 4. set grab dimensions
1327 int grabWidth = width > 0 ? width : screenWidth;
1328 int grabHeight = height > 0 ? height : screenHeight;
1329
1330 int videoSize = 0; // data size of the grabbed video
1331 int videoWidth = screenWidth; // width of the grabbed video
1332 int videoHeight = screenHeight; // height of the grabbed video
1333 int videoX = 0, videoY = 0; // x, y of the grabbed video
1334
1335 // 5. fetch video data
1336 // Video comes as RGB, width and height is original screen dimension (video is maybe scaled)
1337 cSoftHdGrab *videoGrab = m_pRender->GetGrab(&videoSize, &videoWidth, &videoHeight, &videoX, &videoY, 0);
1338 uint8_t *video = NULL;
1339 if (videoGrab->GetSize())
1340 video = videoGrab->GetData();
1341 if (!video) {
1342 LOGDEBUG2(L_GRAB, "device: %s: video is NULL, create black screen!", __FUNCTION__);
1343 video = (uint8_t *)calloc(1, screenSize);
1344 }
1345
1346 int pipSize = 0; // data size of the grabbed pip video
1347 int pipWidth = screenWidth; // width of the grabbed pip video
1348 int pipHeight = screenHeight; // height of the grabbed pip video
1349 int pipX = 0, pipY = 0; // x, y of the grabbed pip video
1350
1351 // 6. fetch pip data
1352 // Pip video comes as RGB, width and height is original screen dimension (video is maybe scaled)
1353 cSoftHdGrab *pipGrab = m_pRender->GetGrab(&pipSize, &pipWidth, &pipHeight, &pipX, &pipY, 2);
1354 uint8_t *pip = NULL;
1355 if (pipGrab->GetSize())
1356 pip = pipGrab->GetData();
1357 if (!pip)
1358 LOGDEBUG2(L_GRAB, "device: %s: pip is NULL, skip it", __FUNCTION__);
1359
1360 // 7. fetch osd data
1361 // OSD comes as ARGB, width and height is original screen dimension (osd is always fullscreen)
1362 cSoftHdGrab *osdGrab = m_pRender->GetGrab(NULL, NULL, NULL, NULL, NULL, 1);
1363 uint8_t *osd = NULL;
1364 if (osdGrab->GetSize())
1365 osd = osdGrab->GetData();;
1366 if (!osd)
1367 LOGDEBUG2(L_GRAB, "device: %s: osd is NULL, skip it", __FUNCTION__);
1368
1369 int ret;
1370 uint8_t *videoResult = NULL;
1371 // 8. blit the video into a full black screen if scaled
1372 if (videoWidth != screenWidth || videoHeight != screenHeight || videoX != 0 || videoY != 0) {
1373 videoResult = (uint8_t *)calloc(1, screenSize);
1374 ret = BlitVideo(videoResult, video, screenWidth, screenHeight, videoX, videoY, videoWidth, videoHeight);
1375 if (ret) {
1376 free(videoResult);
1377 free(video);
1378 return NULL;
1379 }
1380 free(video);
1381 } else {
1382 videoResult = video;
1383 }
1384
1385 // 9. blit the pip video into the main video if available
1386 if (pip) {
1387 ret = BlitVideo(videoResult, pip, screenWidth, screenHeight, pipX, pipY, pipWidth, pipHeight);
1388 if (ret) {
1389 free(videoResult);
1390 free(pip);
1391 return NULL;
1392 }
1393 free(pip);
1394 }
1395
1396 // 10. alphablend fullscreen video with osd if available
1397 uint8_t *result;
1398 if (!osd) {
1399 result = videoResult;
1400 } else {
1401 result = (uint8_t *)malloc(screenSize);
1402 AlphaBlend(result, osd, videoResult, screenWidth, screenHeight);
1403 free(videoResult);
1404 free(osd);
1405 }
1406
1407 // 11. scale result to requested size width + height, if it differs from fullscreen
1408 int scaledSize = screenSize;
1409 uint8_t *scaledResult;
1410 if (screenWidth != grabWidth || screenHeight != grabHeight) {
1411 scaledResult = ScaleRgb24(result, &scaledSize, screenWidth, screenHeight, grabWidth, grabHeight);
1412 free(result);
1413 } else {
1414 scaledResult = result;
1415 }
1416
1417 // 12. make jpeg or pnm
1418 uint8_t *grabbedImage;
1419 if (jpeg) {
1420 grabbedImage = CreateJpeg(scaledResult, &size, quality, grabWidth, grabHeight);
1421 } else { // add header to raw data
1422 char buf[64];
1423 int n = snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", grabWidth, grabHeight);
1424 grabbedImage = (uint8_t *)malloc(scaledSize + n);
1425 memcpy(grabbedImage, buf, n);
1426 memcpy(grabbedImage + n, scaledResult, scaledSize);
1427 size = scaledSize + n;
1428 }
1429 free(scaledResult);
1430 LOGDEBUG2(L_GRAB, "device: %s: finished %s image (%dx%d, quality %d) at %p (size %d)", __FUNCTION__, jpeg ? "jpg" : "pnm", grabWidth, grabHeight, jpeg ? quality : 0, grabbedImage, size);
1431
1432 m_grabActive = false;
1433 return grabbedImage;
1434}
1435
1443cRect cSoftHdDevice::CanScaleVideo(const cRect & rect, __attribute__ ((unused)) int alignment)
1444{
1445 return rect;
1446}
1447
1456void cSoftHdDevice::ScaleVideo(const cRect & rect)
1457{
1458 if (IsDetached())
1459 return;
1460
1461 LOGDEBUG2(L_OSD, "device: %s: %dx%d%+d%+d",
1462 __FUNCTION__, rect.Width(), rect.Height(), rect.X(), rect.Y());
1463
1464 if (m_pRender)
1466}
1467
1472{
1473 return " -a device\taudio device (fe. alsa: hw:0,0)\n"
1474 " -p device\taudio device for pass-through (hw:0,1)\n"
1475 " -c channel\taudio mixer channel name (fe. PCM)\n"
1476 " -d resolution\tdisplay resolution (fe. 1920x1080@50)\n"
1477#ifdef USE_GLES
1478 " -w workaround\tenable/disable workarounds\n"
1479 "\tdisable-ogl-osd disable openGL osd\n"
1480#endif
1481 "\n";
1482}
1483
1490int cSoftHdDevice::ProcessArgs(int argc, char *argv[])
1491{
1492 //
1493 // Parse arguments.
1494 //
1495
1496 for (;;) {
1497#ifdef USE_GLES
1498 switch (getopt(argc, argv, "-a:c:p:d:w:")) {
1499#else
1500 switch (getopt(argc, argv, "-a:c:p:d:")) {
1501#endif
1502 case 'a': // audio device for pcm
1504 continue;
1505 case 'c': // channel of audio mixer
1507 continue;
1508 case 'p': // pass-through audio device
1510 continue;
1511 case 'd': // set display output
1513 continue;
1514#ifdef USE_GLES
1515 case 'w': // workarounds
1516 if (!strcasecmp("disable-ogl-osd", optarg)) {
1517 SetDisableOglOsd();
1518 } else {
1519 fprintf(stderr, _("Workaround '%s' unsupported\n"),
1520 optarg);
1521 return 0;
1522 }
1523 continue;
1524#endif
1525 case EOF:
1526 break;
1527 case '-':
1528 fprintf(stderr, _("We need no long options\n"));
1529 return 0;
1530 case ':':
1531 fprintf(stderr, _("Missing argument for option '%c'\n"), optopt);
1532 return 0;
1533 default:
1534 fprintf(stderr, _("Unknown option '%c'\n"), optopt);
1535 return 0;
1536 }
1537 break;
1538 }
1539
1540 while (optind < argc) {
1541 fprintf(stderr, _("Unhandled argument '%s'\n"), argv[optind++]);
1542 }
1543
1544 return 1;
1545}
1546
1551{
1552 if (IsDetached())
1553 return;
1554
1556}
1557
1570void cSoftHdDevice::OsdDrawARGB(int xi, int yi, int height, int width, int pitch,
1571 const uint8_t * argb, int x, int y)
1572{
1573 if (IsDetached())
1574 return;
1575
1576 m_pRender->OsdDrawARGB(xi, yi, height, width, pitch, argb, x, y);
1577}
1578
1579#ifdef USE_GLES
1580#ifdef WRITE_PNG
1584char cSoftHdDevice::WritePngs(void)
1585{
1586 return m_pConfig->ConfigWritePngs;
1587};
1588#endif
1592int cSoftHdDevice::MaxSizeGPUImageCache(void)
1593{
1594 return m_pConfig->ConfigMaxSizeGPUImageCache;
1595};
1596
1600int cSoftHdDevice::OglOsdIsDisabled(void)
1601{
1602 return m_pConfig->ConfigDisableOglOsd;
1603};
1604
1608void cSoftHdDevice::SetDisableOglOsd(void)
1609{
1610 m_pConfig->ConfigDisableOglOsd = 1;
1611 if (m_pRender)
1612 m_pRender->DisableOglOsd();
1613}
1614
1618void cSoftHdDevice::SetEnableOglOsd(void)
1619{
1620 m_pConfig->ConfigDisableOglOsd = 0;
1621 if (m_pRender)
1622 m_pRender->EnableOglOsd();
1623}
1624
1625#endif
1626
1631{
1632 if (m_pVideoStream)
1634}
1635
1640{
1641 m_pAudio->SetPassthrough(mask);
1642 if (m_pAudioDecoder)
1644}
1645
1650{
1651 LOGDEBUG("%s:", __FUNCTION__);
1652 m_audioChannelID = -1;
1653}
1654
1662void cSoftHdDevice::GetStats(int *duped, int *dropped, int *counter)
1663{
1664 *duped = 0;
1665 *dropped = 0;
1666 *counter = 0;
1667 if (m_pRender) {
1668 m_pRender->GetStats(duped, dropped, counter);
1669 }
1670}
1671
1672/*****************************************************************************
1673 * media player functions
1674 ****************************************************************************/
1675
1683void cSoftHdDevice::SetAudioCodec(enum AVCodecID codecId, AVCodecParameters * par, AVRational timebase)
1684{
1685 m_pAudioDecoder->Open(codecId, par, timebase);
1686}
1687
1695void cSoftHdDevice::SetVideoCodec(enum AVCodecID codecId, AVCodecParameters * par, AVRational timebase)
1696{
1697 m_pVideoStream->Open(codecId, par, timebase);
1698}
1699
1709{
1710 m_pAudio->LazyInit();
1711
1712 if (m_pAudio->IsBufferFull()) {
1713// LOGERROR("device: %s: m_pAudio->GetFreeBytes() < AUDIO_MIN_BUFFER_FREE!", __FUNCTION__);
1714 return 0;
1715 }
1716 m_pAudioDecoder->Decode(pkt);
1717 return 1;
1718}
1719
1729{
1730 m_pAudio->LazyInit();
1731
1733 return 0;
1734 }
1735
1737
1738 return 1;
1739}
1740
1748{
1749 if (Replaying()) {
1750 LOGDEBUG("device: %s: Device is replaying, stop replay first", __FUNCTION__);
1751 StopReplay();
1752 }
1753
1754 if (IsPrimaryDevice(false)) {
1755 m_needsMakePrimary = true;
1756 MakePrimaryDevice(false);
1757 }
1758
1760}
1761
1769{
1770 if (m_needsMakePrimary) {
1771 MakePrimaryDevice(true);
1772 m_needsMakePrimary = false;
1773 }
1774
1776}
1777
1782{
1783 std::lock_guard<std::mutex> lock(m_mutex);
1784 return m_state == State::DETACHED;
1785}
1786
1793}
1794
1799{
1801}
1802
1807{
1809}
1810
1815{
1817}
1818
1823{
1824 std::lock_guard<std::mutex> lock(m_mutex);
1825 return m_pipActive;
1826}
1827
1834{
1835 if (direction > 0)
1837 else
1839}
1840
1848{
1849 const cChannel *channel = m_pPipChannel;
1850 if (!channel)
1851 return;
1852
1853 OnEventReceived(PipEvent{PIPCHANSWAP}); // resets the pip channel to the current channel
1854
1855 if (channel) {
1856 LOCK_CHANNELS_READ;
1857 LOGDEBUG("pip: %s: switch main stream to %d", __FUNCTION__, channel->Number());
1858 Channels->SwitchTo(channel->Number());
1859 }
1860}
1861
1866{
1868}
1869
1874{
1876}
1877
1882{
1883 switch (event) {
1884 case PIPSTART:
1885 SetEnablePip(true);
1886 break;
1887 case PIPSTOP:
1888 SetEnablePip(false);
1889 break;
1890 case PIPTOGGLE:
1891 TogglePip();
1892 break;
1893 case PIPCHANUP:
1895 break;
1896 case PIPCHANDOWN:
1897 ChangePipChannel(-1);
1898 break;
1899 case PIPCHANSWAP:
1901 break;
1902 case PIPSIZECHANGE:
1903 SetPipSize();
1904 break;
1905 case PIPSWAPPOSITION:
1907 break;
1908 default:
1909 break;
1910 }
1911}
1912
1921{
1922 if (m_pipActive && on) {
1923 LOGDEBUG("device: %s: pip is already enabled", __FUNCTION__);
1924 return;
1925 }
1926
1927 if (!m_pipActive && !on) {
1928 LOGDEBUG("device: %s: pip is already disabled", __FUNCTION__);
1929 return;
1930 }
1931
1932 if (!m_pipActive) {
1933 LOGDEBUG("device: %s: enabling pip (channel %d)", __FUNCTION__, m_pipChannelNum);
1935 m_pRender->SetPipActive(true);
1936 } else {
1937 LOGDEBUG("device: %s: disabling pip", __FUNCTION__);
1938 m_pRender->SetPipActive(false);
1939 DelPip();
1940 }
1941
1942 m_pipActive = on;
1943}
1944
1951{
1953}
1954
1963{
1964 if (!m_pipActive)
1965 return;
1966
1967 const cChannel *channel;
1968 const cChannel *first;
1969
1970 channel = m_pPipChannel;
1971 first = channel;
1972
1973 DelPip();
1974
1975 LOCK_CHANNELS_READ;
1976 while (channel) {
1977 bool ndr;
1978 cDevice *device;
1979
1980 channel = direction > 0 ? Channels->Next(channel) : Channels->Prev(channel);
1981 if (!channel && Setup.ChannelsWrap)
1982 channel = direction > 0 ? Channels->First() : Channels->Last();
1983
1984 if (channel && !channel->GroupSep() && (device = cDevice::GetDevice(channel, 0, false, true)) &&
1985 device->ProvidesChannel(channel, 0, &ndr) && !ndr) {
1986 NewPip(channel->Number());
1987 return;
1988 }
1989
1990 if (channel == first) {
1991 Skins.Message(mtError, tr("Channel not available!"));
1992 break;
1993 }
1994 }
1995}
1996
2003{
2004 if (!m_pipActive)
2005 return;
2006
2007 DelPip();
2008 NewPip(0);
2009}
2010
2017{
2019}
2020
2027{
2030}
2031
2042{
2043 if (!m_pPipReceiver)
2044 return;
2045
2046 LOGDEBUG("pip: %s: deleting receiver for channel (%d) %s", __FUNCTION__, m_pPipChannel->Number(), m_pPipChannel->Name());
2047
2055
2056 delete m_pPipReceiver;
2057 m_pPipReceiver = nullptr;
2058 m_pPipChannel = nullptr;
2059}
2060
2067void cSoftHdDevice::NewPip(int channelNum)
2068{
2069 if (!channelNum)
2070 channelNum = CurrentChannel();
2071
2072 LOCK_CHANNELS_READ;
2073 const cChannel *channel;
2074 cDevice *device;
2075 cPipReceiver *receiver;
2076
2077 if (channelNum && (channel = Channels->GetByNumber(channelNum)) &&
2078 (device = GetDevice(channel, 0, false, false))) {
2079 DelPip();
2080 device->SwitchChannel(channel, false);
2081 receiver = new cPipReceiver(channel, this);
2082 device->AttachReceiver(receiver);
2083 m_pPipReceiver = receiver;
2084 m_pPipChannel = channel;
2085 m_pipChannelNum = channelNum;
2086
2087 LOGDEBUG("pip: %s: New receiver for channel (%d) %s", __FUNCTION__, channel->Number(), channel->Name());
2088 }
2089}
Audio and alsa module header file.
int BlitVideo(uint8_t *dst, uint8_t *src, int dstW, int dstH, int dstX, int dstY, int srcW, int srcH)
Blit the video on black background.
Definition: buf2rgb.cpp:264
void AlphaBlend(uint8_t *result, uint8_t *front, uint8_t *back, const unsigned int width, const unsigned int height)
Blend two images.
Definition: buf2rgb.cpp:203
uint8_t * ScaleRgb24(uint8_t *src, int *size, int srcW, int srcH, int dstW, int dstH)
Scale an image.
Definition: buf2rgb.cpp:152
Some helper functions header file.
cAudioDecoder - Audio decoder class
Definition: codec_audio.h:74
void FlushBuffers(void)
Flush the audio decoder.
void Decode(const AVPacket *)
Decode an audio packet.
AVCodecID GetCodecId() const
Definition: codec_audio.h:83
void SetPassthrough(int)
Set audio pass-through mask.
void Open(AVCodecID, AVCodecParameters *=nullptr, AVRational={ .num=1,.den=90000 })
Open and initiate the audio decoder.
Definition: codec_audio.cpp:81
void Close(void)
Close the audio decoder.
Audio PES packet parser.
Definition: pes.h:83
Video PES packet parser.
Definition: pes.h:70
bool IsValid()
Check if the PES packet is valid.
Definition: pes.cpp:256
int GetPayloadSize()
Get the size of the PES payload.
Definition: pes.cpp:324
int GetPacketLength()
Get the total length of the PES packet.
Definition: pes.cpp:346
const uint8_t * GetPayload()
Get a pointer to the PES payload data.
Definition: pes.cpp:311
bool HasPts()
Check if the PES packet contains a Presentation Time Stamp (PTS)
Definition: pes.cpp:282
int64_t GetPts()
Get the Presentation Time Stamp (PTS) from the PES header.
Definition: pes.cpp:295
cPipReceiver - receiver class for pip
Definition: pipreceiver.h:31
AVPacket * PopAvPacket() override
Pop an audio AVPacket from the reassembly buffer.
Definition: pes.cpp:461
Video stream reassembly buffer.
Definition: pes.h:143
bool HasLeadingZero(const uint8_t *, int)
Check if video data has a leading zero byte before the start code.
Definition: pes.cpp:444
bool ParseCodecHeader(const uint8_t *, int)
Parse video codec header to detect codec type.
Definition: pes.cpp:411
AVPacket * PopAvPacket() override
Definition: pes.h:146
virtual void Push(const uint8_t *data, int size, int64_t pts)
Definition: pes.h:123
AVCodecID GetCodec()
Definition: pes.h:128
void Reset()
Reset the reassembly buffer.
Definition: pes.cpp:629
bool IsEmpty()
Definition: pes.h:125
cSoftHdAudio - Audio class
Definition: audio.h:45
void LazyInit(void)
Initialize audio output module.
Definition: audio.cpp:1025
int64_t GetInputPtsMs(void)
Definition: audio.h:66
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition: audio.cpp:908
bool HasPts(void)
Definition: audio.h:65
void SetVolume(int)
Set mixer volume (0-1000)
Definition: audio.cpp:921
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition: audio.cpp:599
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition: audio.cpp:863
void Exit(void)
Cleanup audio output module.
Definition: audio.cpp:1036
void SetPaused(bool)
Set audio playback paused state.
Definition: audio.cpp:944
void SetPassthrough(int)
Set audio passthrough mask.
Definition: audio.cpp:1016
bool IsBufferFull(void)
Definition: audio.h:57
void FlushBuffers(void)
Flush audio buffers.
Definition: audio.cpp:815
int ConfigVideoAudioDelayMs
config audio delay
Definition: config.h:43
const char * ConfigDisplayResolution
display resolution (syntax: "1920x1080@50")
Definition: config.h:78
const char * ConfigAudioPCMDevice
audio PCM device
Definition: config.h:75
bool ConfigDisableDeint
disable deinterlacer
Definition: config.h:61
int ConfigAdditionalBufferLengthMs
config size ms of a/v buffer
Definition: config.h:42
const char * ConfigAudioMixerChannel
audio mixer channel name
Definition: config.h:77
int ConfigPipUseAlt
Definition: config.h:68
const char * ConfigAudioPassthroughDevice
audio passthrough device
Definition: config.h:76
void SetState(State)
Sets the device into the given state.
void OsdDrawARGB(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD pixmap.
cReassemblyBufferVideo m_videoReassemblyBuffer
video pes reassembly buffer
Definition: softhddevice.h:241
void Stop(void)
Called by VDR when the plugin is stopped.
bool m_pipUseAlt
use alternative pip position
Definition: softhddevice.h:259
cReassemblyBufferVideo m_pipReassemblyBuffer
pip pes reassembly buffer
Definition: softhddevice.h:254
cVideoStream * m_pPipStream
pointer to pip video stream
Definition: softhddevice.h:253
void PipSwapPosition(void)
Swap pip between normal and alternative position.
static constexpr int MIN_BUFFER_FILL_LEVEL_THRESHOLD_MS
min buffering threshold in ms
Definition: softhddevice.h:229
virtual void StillPicture(const uchar *, int)
Display the given I-frame as a still picture.
void PipSetSize(void)
Set size and position for the pip window.
cSoftOsdProvider * m_pOsdProvider
pointer to cSoftOsdProvider object
Definition: softhddevice.h:240
cVideoStream * m_pVideoStream
pointer to main video stream
Definition: softhddevice.h:237
cReassemblyBufferAudio m_audioReassemblyBuffer
audio pes reassembly buffer
Definition: softhddevice.h:242
std::atomic< bool > m_receivedAudio
flag if audio packets have been received
Definition: softhddevice.h:257
void HandlePip(enum PipState)
Handle the pip event.
cDvbSpuDecoder * m_pSpuDecoder
pointer to spu decoder
Definition: softhddevice.h:234
void OnEnteringState(State)
Actions to be performed when entering a state.
int Start(void)
Called by VDR when the plugin is started.
virtual void GetVideoSize(int &, int &, double &)
Get the video size.
int PlayAudioPkts(AVPacket *)
Play an audio packet.
virtual void GetOsdSize(int &, int &, double &)
Returns the width, height and aspect ratio the OSD.
void GetStats(int *, int *, int *)
Get statistics from the renderer.
void ClearAudio(void)
Clear all audio data from the decoder and ringbuffer.
void ResetChannelId(void)
Reset the channel ID (restarts audio)
virtual bool CanReplay(void) const
Returns true if this device can currently start a replay session.
virtual void SetDigitalAudioDevice(bool)
void SwapPipPosition(void)
Swap pip between normal and alternative position.
std::mutex m_mutex
mutex to lock the state machine
Definition: softhddevice.h:255
virtual bool HasDecoder(void) const
Tells whether this device has an MPEG decoder.
virtual void Clear(void)
Clears all video and audio data from the device.
void OnEventReceived(const Event &)
Event handler for playback state transitions.
void ResetPipChannel(void)
Resets the pip channel to the current live stream channel.
void Detach(void)
Detach the device.
virtual int PlayVideo(const uchar *, int)
Play a video packet of the main videostream.
void TogglePip(void)
Toggle picture-in-picture.
uint32_t m_screenRefreshRate
Definition: softhddevice.h:263
std::atomic< State > m_state
current plugin state, normal plugin start sets detached state
Definition: softhddevice.h:231
void OnLeavingState(State)
Actions to be performed when leaving a state.
virtual bool SetPlayMode(ePlayMode)
Sets the device into the given play mode.
cAudioDecoder * m_pAudioDecoder
pointer to cAudioDecoder object
Definition: softhddevice.h:239
void SetAudioCodec(enum AVCodecID, AVCodecParameters *, AVRational)
Open an audio codec.
cPipReceiver * m_pPipReceiver
cReceiver for pip stream
Definition: softhddevice.h:252
virtual void Play(void)
Sets the device into play mode (after a previous trick mode, or pause)
bool IsBufferingThresholdReached(void)
Check if the buffering threshold has been reached.
bool m_grabActive
simple lock variable skips a new grab request if the last one is still active
Definition: softhddevice.h:246
void DelPip(void)
Delete the pip receiver, clear decoder and display buffers and disable rendering the pip window.
cVideoRender * m_pRender
pointer to cVideoRender object
Definition: softhddevice.h:236
virtual void SetAudioChannelDevice(int)
const char * CommandLineHelp(void)
Return command line help string.
cSoftHdDevice(cSoftHdConfig *)
cSoftHdDevice constructor
int PlayPipVideo(const uchar *, int)
Play a video packet of the pip videostream.
bool m_needsMakePrimary
Definition: softhddevice.h:233
cSoftHdAudio * m_pAudio
pointer to cSoftHdAudio object
Definition: softhddevice.h:238
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig object
Definition: softhddevice.h:235
virtual cRect CanScaleVideo(const cRect &, int taCenter)
Ask the output, if it can scale video.
virtual void SetVolumeDevice(int)
Sets the audio volume on this device (Volume = 0...255).
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles.
virtual bool Flush(int=0)
Flush the device output buffers.
bool IsDetached(void) const
Returns true, if the device is detached.
virtual void TrickSpeed(int, bool)
Set trick play speed.
virtual int PlayAudio(const uchar *, int, uchar)
Play an audio packet.
void SetPipSize(void)
Set size and position for the pip window.
void PipToggle(void)
Toggle picture-in-picture.
bool PipIsEnabled(void)
Returns true, if picture-in-picture is running.
std::mutex m_sizeMutex
mutex to lock screen size (which is accessed by different threads)
Definition: softhddevice.h:256
std::atomic< bool > m_receivedVideo
flag if video packets have been received
Definition: softhddevice.h:258
virtual int GetAudioChannelDevice(void)
int PlayVideoPkts(AVPacket *)
Play a video packet.
int m_pipChannelNum
current pip channel number
Definition: softhddevice.h:250
int GetBufferFillLevelThresholdMs()
Returns the buffer fill level threshold in milliseconds.
void SetPassthrough(int)
Set the passthrough mask (called from setup menu or conf)
int PlayVideoInternal(cVideoStream *, cReassemblyBufferVideo *, const uchar *, int)
Play a video packet.
const cChannel * m_pPipChannel
current pip channel
Definition: softhddevice.h:251
void HandleStillPicture(const uchar *data, int size)
The still picture data received from VDR can contain multiple PES packets.
virtual void SetVideoDisplayFormat(eVideoDisplayFormat)
Sets the video display format.
virtual void Freeze(void)
Puts the device into "freeze frame" mode.
void ChangePipChannel(int)
Change the pip channel.
virtual ~cSoftHdDevice(void)
cSoftHdDevice destructor
int m_audioChannelID
current audio channel ID
Definition: softhddevice.h:245
void PipDisable(void)
Stop picture-in-picture.
virtual void SetVideoFormat(bool)
Set the video format.
void SetScreenSize(int, int, uint32_t)
Set the screen size.
virtual void MakePrimaryDevice(bool)
Informs a device that it will be the primary device.
void SetVideoCodec(enum AVCodecID, AVCodecParameters *, AVRational)
Open a video codec.
bool m_pipActive
true, if pip is active
Definition: softhddevice.h:249
void PipEnable(void)
Start picture-in-picture.
void SetDisableDeint(void)
Disables deinterlacer (called from setup menu or conf)
virtual uchar * GrabImage(int &, bool, int, int, int)
Grabs the currently visible screen image.
virtual cSpuDecoder * GetSpuDecoder(void)
Get the device SPU decoder.
virtual void SetAudioTrackDevice(eTrackType)
void PipChannelSwap(void)
Swap the pip channel with main live channel.
std::atomic< PlaybackMode > m_playbackMode
current playback mode
Definition: softhddevice.h:244
void OsdClose(void)
Close the OSD.
int64_t GetFirstAudioPtsMsToPlay()
Calculate the first audio PTS that should be played during synchronized playback.
virtual void ScaleVideo(const cRect &=cRect::Null)
Scale the currently shown video.
int64_t GetFirstVideoPtsMsToPlay()
int ProcessArgs(int, char *[])
Process the command line arguments.
void SetEnablePip(bool)
Enable/ disable picture-in-picture.
void PipChannelChange(int)
Change the pip channel.
virtual bool Poll(cPoller &, int=0)
Returns true if the device itself or any of the file handles in Poller is ready for further action.
void Attach(void)
Attach the device again.
void NewPip(int)
Create a new pip receiver and render the pip stream.
cSoftHdGrab - Grabber class
Definition: grab.h:35
uint8_t * GetData(void)
Definition: grab.h:51
int GetSize(void)
Definition: grab.h:52
cSoftOsdProvider - SoftHdDevice plugin OSD provider class
Definition: softhdosd.h:65
cVideoRender - Video render class
Definition: videorender.h:122
void PushPipFrame(AVFrame *)
bool IsOutputBufferFull(void)
Check, if the main render output buffer is full.
void ConvertPipBufToRgb(void)
Convert the pip drm buffer to an rgb image.
void ConvertVideoBufToRgb(void)
Convert the video drm buffer to an rgb image.
void ResetBufferReuseStrategy()
Definition: videorender.h:140
void OsdClear(void)
Clear the OSD (draw an empty/ transparent OSD)
cSoftHdGrab * GetGrab(int *, int *, int *, int *, int *, int)
Get the grabbed image.
void ClearDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
void Init(void)
Initialize the renderer.
void ResetDecodingStrategy()
Definition: videorender.h:141
void Exit(void)
Exit and cleanup the renderer.
void GetStats(int *, int *, int *)
Get some rendering statistics.
void SetTrickSpeed(int, int)
Set the trickspeed parameters.
void DisplayBlackFrame(void)
void PushMainFrame(AVFrame *)
void SetVideoOutputPosition(const cRect &)
Set size and position of the video on the screen.
int64_t GetOutputPtsMs(void)
Get the output PTS in milliseconds.
void SetPipActive(bool on)
Definition: videorender.h:194
void DisplayThreadHalt(void)
Definition: videorender.h:162
void ClearPipDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
void ClearGrab(void)
Clear the grab drm buffers.
int TriggerGrab(void)
Trigger a screen grab.
cQueue< cDrmBuffer > * GetPipOutputBuffer(void)
Definition: videorender.h:180
void ConvertOsdBufToRgb(void)
Convert the osd drm buffer to an rgb image.
void SchedulePlaybackStartAtPtsMs(int64_t ptsMs)
Definition: videorender.h:178
int64_t GetVideoClock(void)
Wrapper to get the video clock (m_pts)
void OsdDrawARGB(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD ARGB image.
void SetScheduleAudioResume(bool resume)
Definition: videorender.h:137
void SetDisplayOneFrameThenPause(bool pause)
Definition: videorender.h:177
void SetPipSize(bool)
void SetPlaybackPaused(bool pause)
Definition: videorender.h:136
void DisplayThreadResume(void)
Definition: videorender.h:163
void ResetFrameCounter(void)
Send start condition to video thread.
cQueue< cDrmBuffer > * GetMainOutputBuffer(void)
Definition: videorender.h:179
cVideoStream - Video stream class
Definition: videostream.h:51
void GetVideoSize(int *, int *, double *)
Get video size and aspect ratio.
void SetDeinterlacerDeactivated(bool deactivate)
Definition: videostream.h:88
void Flush(void)
Flushes the video stream by finalizing any pending data.
void StartDecoder()
Start the decoder.
void DecodingThreadResume(void)
Definition: videostream.h:82
void ClearVdrCoreToDecoderQueue(void)
Clears all video stream data, which is buffered to be decoded.
size_t GetAvPacketsFilled(void)
Definition: videostream.h:70
void DisableDeint(bool disable)
Definition: videostream.h:91
void CloseDecoder(void)
Close the decoder.
void Exit(void)
Exit video stream.
void CancelFilterThread(void)
Stop filter thread.
void DecodingThreadHalt(void)
Definition: videostream.h:81
void FlushDecoder(void)
Flush the decoder.
void Open(AVCodecID, AVCodecParameters *=nullptr, AVRational={ .num=1,.den=90000 })
Open a video codec.
bool PushAvPacket(AVPacket *avpkt)
Pushes a pre-assembled AVPacket directly to the processing queue.
bool IsInputBufferFull(void)
Definition: videostream.h:71
void ResetFilterThreadNeededCheck()
Definition: videostream.h:86
int64_t GetInputPtsMs(void)
void ResetTrickSpeedFramesSentCounter(void)
Definition: videostream.h:73
enum AVCodecID GetCodecId(void)
Definition: videostream.h:72
bool HasInputPts(void)
Definition: videostream.h:74
Audio decoder header file.
Video decoder header file.
PipState
Definition: event.h:29
@ PIPSTOP
Definition: event.h:31
@ PIPSWAPPOSITION
Definition: event.h:37
@ PIPCHANSWAP
Definition: event.h:35
@ PIPTOGGLE
Definition: event.h:32
@ PIPCHANUP
Definition: event.h:33
@ PIPSTART
Definition: event.h:30
@ PIPSIZECHANGE
Definition: event.h:36
@ PIPCHANDOWN
Definition: event.h:34
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent > Event
Definition: event.h:72
Atomic wrapper macros function header file.
Logger class header file.
#define LOGDEBUG2
Definition: logger.h:50
#define LOGDEBUG
Definition: logger.h:49
#define LOGERROR
Definition: logger.h:46
#define L_GRAB
Definition: logger.h:71
#define L_AV_SYNC
Definition: logger.h:59
#define L_CODEC
Definition: logger.h:63
#define LOGWARNING
Definition: logger.h:47
#define LOGFATAL
Logger macros.
Definition: logger.h:45
#define L_OSD
Definition: logger.h:61
Misc function header file.
#define AV_NOPTS_VALUE
Definition: misc.h:35
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition: misc.h:81
PES packet parser header.
Pip receiver header file.
Main plugin class header file.
#define _(str)
gettext shortcut
uint8_t * CreateJpeg(uint8_t *image, int *size, int quality, int width, int height)
Call rgb to jpeg for C Plugin.
static void PrintStreamData(const uchar *payload)
Print the start code, stream id, length, first three bytes (start code) of the payload,...
Device class header file.
@ AUDIO_AND_VIDEO
Definition: softhddevice.h:92
@ VIDEO_ONLY
Definition: softhddevice.h:94
@ AUDIO_ONLY
Definition: softhddevice.h:93
@ NONE
Definition: softhddevice.h:91
const char * EventToString(const Event &e)
Definition: softhddevice.h:63
State
Definition: softhddevice.h:54
@ PLAY
Definition: softhddevice.h:57
@ STOP
Definition: softhddevice.h:55
@ STILL_PICTURE
Definition: softhddevice.h:59
@ BUFFERING
Definition: softhddevice.h:56
@ TRICK_SPEED
Definition: softhddevice.h:58
@ DETACHED
Definition: softhddevice.h:60
const char * StateToString(State s)
Definition: softhddevice.h:78
Softhddevice osd header file.
Definition: event.h:57
Rendering class header file.
Videostream class header file.
#define VIDEO_PACKET_MAX
max number of video packets held in the buffer
Definition: videostream.h:42