From d42475f2dc42650e38069a22f020635132dd5341 Mon Sep 17 00:00:00 2001 From: Johns Date: Thu, 3 Jan 2013 18:52:34 +0100 Subject: [PATCH] Prepared reentrant video stream. --- Makefile | 5 +- audio.c | 3 +- softhddev.c | 475 +++++++++++++++++++++++++++-------------------- softhddev.h | 2 + softhddevice.cpp | 225 +++++++++++++++++++++- video.c | 66 ++++--- video.h | 16 +- 7 files changed, 550 insertions(+), 242 deletions(-) diff --git a/Makefile b/Makefile index ddbdbee..a36891a 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ GIT_REV = $(shell git describe --always 2>/dev/null) CONFIG := #-DDEBUG #-DOSD_DEBUG CONFIG += -DAV_INFO -DAV_INFO_TIME=3000 # debug a/v sync +#CONFIG += -DUSE_PIP # too experimental PIP support #CONFIG += -DHAVE_PTHREAD_NAME # supports new pthread_setname_np #CONFIG += -DNO_TS_AUDIO # disable ts audio parser #CONFIG += -DUSE_TS_VIDEO # build new ts video parser @@ -34,8 +35,8 @@ CC ?= gcc CXX ?= g++ CFLAGS ?= -g -O2 -W -Wall -Wextra -Winit-self \ -Wdeclaration-after-statement \ - -ftree-vectorize -msse3 -flax-vector-conversions -CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Werror=overloaded-virtual + -ftree-vectorize -msse3 -flax-vector-conversions #-fPIC +CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Werror=overloaded-virtual #-fPIC ### The directory environment: diff --git a/audio.c b/audio.c index 644c18a..6948785 100644 --- a/audio.c +++ b/audio.c @@ -1,7 +1,7 @@ /// /// @file audio.c @brief Audio module /// -/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. +/// Copyright (c) 2009 - 2013 by Johns. All Rights Reserved. /// /// Contributor(s): /// @@ -163,7 +163,6 @@ static int AudioStereoDescent; ///< volume descent for stereo static int AudioVolume; ///< current volume (0 .. 1000) extern int VideoAudioDelay; ///< import audio/video delay -extern int VideoGetBuffers(void); ///< Get number of input buffers. /// default ring buffer size ~2s 8ch 16bit (3 * 5 * 7 * 8) static const unsigned AudioRingBufferSize = 3 * 5 * 7 * 8 * 2 * 1000; diff --git a/softhddev.c b/softhddev.c index 4c99b7d..5d0f8fb 100644 --- a/softhddev.c +++ b/softhddev.c @@ -84,6 +84,7 @@ static volatile char SkipAudio; ///< skip audio stream static AudioDecoder *MyAudioDecoder; ///< audio decoder static enum CodecID AudioCodecID; ///< current codec id static int AudioChannelID; ///< current audio channel id +static VideoStream *AudioSyncStream; ///< video stream for audio/video sync /// Minimum free space in audio buffer 8 packets for 8 channels #define AUDIO_MIN_BUFFER_FREE (3072 * 8 * 8) @@ -987,7 +988,8 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id) return 0; } // soft limit buffer full - if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && VideoGetBuffers() > 3) { + if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && (!AudioSyncStream + || VideoGetBuffers(AudioSyncStream) > 3)) { return 0; } // PES header 0x00 0x00 0x01 ID @@ -1210,7 +1212,8 @@ int PlayTsAudio(const uint8_t * data, int size) return 0; } // soft limit buffer full - if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && VideoGetBuffers() > 3) { + if (AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE && (!AudioSyncStream + || VideoGetBuffers(AudioSyncStream) > 3)) { return 0; } @@ -1235,89 +1238,104 @@ void SetVolumeDevice(int volume) #include // portable atomic_t +#define VIDEO_BUFFER_SIZE (512 * 1024) ///< video PES buffer default size +#define VIDEO_PACKET_MAX 192 ///< max number of video packets + +/** +** Video output stream device structure. Parser, decoder, display. +*/ +struct __video_stream__ +{ + VideoHwDecoder *HwDecoder; ///< video hardware decoder + VideoDecoder *Decoder; ///< video decoder + + enum CodecID CodecID; ///< current codec id + enum CodecID LastCodecID; ///< last codec id + + volatile char NewStream; ///< flag new video stream + volatile char ClosingStream; ///< flag closing video stream + volatile char SkipStream; ///< skip video stream + volatile char Freezed; ///< stream freezed + + volatile char TrickSpeed; ///< current trick speed + volatile char ClearBuffers; ///< clear video buffers + volatile char ClearClose; ///< clear video buffers for close + + AVPacket PacketRb[VIDEO_PACKET_MAX]; ///< video PES packet ring buffer + int PacketWrite; ///< ring buffer write pointer + int PacketRead; ///< ring buffer read pointer + atomic_t PacketsFilled; ///< how many of the ring buffer is used +}; + +static VideoStream MyVideoStream[1]; ///< normal video stream + #ifdef DEBUG uint32_t VideoSwitch; ///< debug video switch ticks +static int VideoMaxPacketSize; ///< biggest used packet buffer #endif //#define STILL_DEBUG 2 #ifdef STILL_DEBUG static char InStillPicture; ///< flag still picture #endif -static volatile char NewVideoStream; ///< flag new video stream -static volatile char ClosingVideoStream; ///< flag closing video stream -static VideoHwDecoder *MyHwDecoder; ///< video hw decoder -static VideoDecoder *MyVideoDecoder; ///< video decoder -static enum CodecID VideoCodecID; ///< current codec id const char *X11DisplayName; ///< x11 display name static volatile char Usr1Signal; ///< true got usr1 signal -#define VIDEO_BUFFER_SIZE (512 * 1024) ///< video PES buffer default size -#define VIDEO_PACKET_MAX 192 ///< max number of video packets - /// video PES packet ring buffer -static AVPacket VideoPacketRb[VIDEO_PACKET_MAX]; -static int VideoPacketWrite; ///< write pointer -static int VideoPacketRead; ///< read pointer -static atomic_t VideoPacketsFilled; ///< how many of the buffer is used - -static volatile char VideoClearBuffers; ///< clear video buffers -static volatile char VideoClearClose; ///< clear video buffers upto close -static volatile char SkipVideo; ///< skip video -static volatile char CurrentTrickSpeed; ///< current trick speed - -#ifdef DEBUG -static int VideoMaxPacketSize; ///< biggest used packet buffer -#endif - /** ** Initialize video packet ringbuffer. +** +** @param stream video stream */ -static void VideoPacketInit(void) +static void VideoPacketInit(VideoStream * stream) { int i; for (i = 0; i < VIDEO_PACKET_MAX; ++i) { AVPacket *avpkt; - avpkt = &VideoPacketRb[i]; + avpkt = &stream->PacketRb[i]; // build a clean ffmpeg av packet if (av_new_packet(avpkt, VIDEO_BUFFER_SIZE)) { Fatal(_("[softhddev] out of memory\n")); } - avpkt->priv = NULL; } - atomic_set(&VideoPacketsFilled, 0); - VideoPacketRead = VideoPacketWrite = 0; + atomic_set(&stream->PacketsFilled, 0); + stream->PacketRead = stream->PacketWrite = 0; } /** ** Cleanup video packet ringbuffer. +** +** @param stream video stream */ -static void VideoPacketExit(void) +static void VideoPacketExit(VideoStream * stream) { int i; - atomic_set(&VideoPacketsFilled, 0); + atomic_set(&stream->PacketsFilled, 0); for (i = 0; i < VIDEO_PACKET_MAX; ++i) { - av_free_packet(&VideoPacketRb[i]); + av_free_packet(&stream->PacketRb[i]); } } /** ** Place video data in packet ringbuffer. ** +** @param stream video stream ** @param pts presentation timestamp of pes packet ** @param data data of pes packet ** @param data size of pes packet */ -static void VideoEnqueue(int64_t pts, const void *data, int size) +static void VideoEnqueue(VideoStream * stream, int64_t pts, const void *data, + int size) { AVPacket *avpkt; // Debug(3, "video: enqueue %d\n", size); - avpkt = &VideoPacketRb[VideoPacketWrite]; + avpkt = &stream->PacketRb[stream->PacketWrite]; if (!avpkt->stream_index) { // add pts only for first added avpkt->pts = pts; } @@ -1352,13 +1370,16 @@ static void VideoEnqueue(int64_t pts, const void *data, int size) /** ** Reset current packet. +** +** @param stream video stream */ -static void VideoResetPacket(void) +static void VideoResetPacket(VideoStream * stream) { AVPacket *avpkt; - avpkt = &VideoPacketRb[VideoPacketWrite]; + avpkt = &stream->PacketRb[stream->PacketWrite]; avpkt->stream_index = 0; + avpkt->priv = NULL; avpkt->pts = AV_NOPTS_VALUE; avpkt->dts = AV_NOPTS_VALUE; } @@ -1366,13 +1387,14 @@ static void VideoResetPacket(void) /** ** Finish current packet advance to next. ** +** @param stream video stream ** @param codec_id codec id of packet (MPEG/H264) */ -static void VideoNextPacket(int codec_id) +static void VideoNextPacket(VideoStream * stream, int codec_id) { AVPacket *avpkt; - avpkt = &VideoPacketRb[VideoPacketWrite]; + avpkt = &stream->PacketRb[stream->PacketWrite]; if (!avpkt->stream_index) { // ignore empty packets if (codec_id != CODEC_ID_NONE) { return; @@ -1380,7 +1402,7 @@ static void VideoNextPacket(int codec_id) Debug(3, "video: possible stream change loss\n"); } - if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) { + if (atomic_read(&stream->PacketsFilled) >= VIDEO_PACKET_MAX - 1) { // no free slot available drop last packet Error(_("video: no empty slot in packet ringbuffer\n")); avpkt->stream_index = 0; @@ -1396,13 +1418,13 @@ static void VideoNextPacket(int codec_id) //H264Dump(avpkt->data, avpkt->stream_index); // advance packet write - VideoPacketWrite = (VideoPacketWrite + 1) % VIDEO_PACKET_MAX; - atomic_inc(&VideoPacketsFilled); + stream->PacketWrite = (stream->PacketWrite + 1) % VIDEO_PACKET_MAX; + atomic_inc(&stream->PacketsFilled); VideoDisplayWakeup(); // intialize next package to use - VideoResetPacket(); + VideoResetPacket(stream); } /** @@ -1414,8 +1436,13 @@ static void VideoNextPacket(int codec_id) ** ** FIXME: there are stations which have multiple pictures and ** the last picture incomplete in the PES packet. +** +** FIXME: move function call into PlayVideo, than the hardware +** decoder didn't need to support multiple frames decoding. +** +** @param avpkt ffmpeg a/v packet */ -void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt) +static void FixPacketForFFMpeg(VideoDecoder * vdecoder, AVPacket * avpkt) { uint8_t *p; int n; @@ -1455,7 +1482,7 @@ void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt) tmp->data[0], tmp->data[1], tmp->data[2], tmp->data[3]); } #endif - CodecVideoDecode(MyVideoDecoder, tmp); + CodecVideoDecode(vdecoder, tmp); // time-stamp only valid for first packet tmp->pts = AV_NOPTS_VALUE; tmp->dts = AV_NOPTS_VALUE; @@ -1472,27 +1499,32 @@ void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt) tmp->data[0], tmp->data[1], tmp->data[2], tmp->data[3]); } #endif - CodecVideoDecode(MyVideoDecoder, tmp); + CodecVideoDecode(vdecoder, tmp); } /** ** Poll PES packet ringbuffer. ** ** Called if video frame buffers are full. +** +** @param stream video stream +** +** @retval 1 something todo +** @retval -1 empty stream */ -int VideoPollInput(void) +int VideoPollInput(VideoStream * stream) { - if (VideoClearBuffers) { - atomic_set(&VideoPacketsFilled, 0); - VideoPacketRead = VideoPacketWrite; - if (MyVideoDecoder) { - CodecVideoFlushBuffers(MyVideoDecoder); - VideoResetStart(MyHwDecoder); + if (stream->ClearBuffers) { + atomic_set(&stream->PacketsFilled, 0); + stream->PacketRead = stream->PacketWrite; + if (stream->Decoder) { + CodecVideoFlushBuffers(stream->Decoder); + VideoResetStart(stream->HwDecoder); } - VideoClearBuffers = 0; + stream->ClearBuffers = 0; return 1; } - if (!atomic_read(&VideoPacketsFilled)) { + if (!atomic_read(&stream->PacketsFilled)) { return -1; } return 1; @@ -1501,83 +1533,85 @@ int VideoPollInput(void) /** ** Decode from PES packet ringbuffer. ** +** @param stream video stream +** ** @retval 0 packet decoded ** @retval 1 stream paused ** @retval -1 empty stream */ -int VideoDecodeInput(void) +int VideoDecodeInput(VideoStream * stream) { int filled; AVPacket *avpkt; int saved_size; - static int last_codec_id = CODEC_ID_NONE; - if (VideoClearBuffers) { - atomic_set(&VideoPacketsFilled, 0); - VideoPacketRead = VideoPacketWrite; - if (MyVideoDecoder) { - CodecVideoFlushBuffers(MyVideoDecoder); - VideoResetStart(MyHwDecoder); + if (stream->ClearBuffers) { // clear buffer request + atomic_set(&stream->PacketsFilled, 0); + stream->PacketRead = stream->PacketWrite; + if (stream->Decoder) { + CodecVideoFlushBuffers(stream->Decoder); + VideoResetStart(stream->HwDecoder); } - VideoClearBuffers = 0; + stream->ClearBuffers = 0; return 1; } - if (StreamFreezed) { // stream freezed + if (stream->Freezed) { // stream freezed // clear is called during freezed return 1; } - filled = atomic_read(&VideoPacketsFilled); + filled = atomic_read(&stream->PacketsFilled); if (!filled) { return -1; } // clearing for normal channel switch has no advantage - if (VideoClearClose /*|| ClosingVideoStream */ ) { + if (stream->ClearClose /*|| stream->ClosingStream */ ) { int f; // flush buffers, if close is in the queue for (f = 0; f < filled; ++f) { - avpkt = &VideoPacketRb[(VideoPacketRead + f) % VIDEO_PACKET_MAX]; + avpkt = + &stream->PacketRb[(stream->PacketRead + f) % VIDEO_PACKET_MAX]; if ((int)(size_t) avpkt->priv == CODEC_ID_NONE) { if (f) { Debug(3, "video: cleared upto close\n"); - atomic_sub(f, &VideoPacketsFilled); - VideoPacketRead = (VideoPacketRead + f) % VIDEO_PACKET_MAX; - VideoClearClose = 0; + atomic_sub(f, &stream->PacketsFilled); + stream->PacketRead = + (stream->PacketRead + f) % VIDEO_PACKET_MAX; + stream->ClearClose = 0; } break; } } - ClosingVideoStream = 0; + stream->ClosingStream = 0; } - avpkt = &VideoPacketRb[VideoPacketRead]; - // // handle queued commands // + avpkt = &stream->PacketRb[stream->PacketRead]; switch ((int)(size_t) avpkt->priv) { case CODEC_ID_NONE: - ClosingVideoStream = 0; - if (last_codec_id != CODEC_ID_NONE) { - last_codec_id = CODEC_ID_NONE; - CodecVideoClose(MyVideoDecoder); + stream->ClosingStream = 0; + if (stream->LastCodecID != CODEC_ID_NONE) { + stream->LastCodecID = CODEC_ID_NONE; + CodecVideoClose(stream->Decoder); goto skip; } // FIXME: look if more close are in the queue // size can be zero goto skip; case CODEC_ID_MPEG2VIDEO: - if (last_codec_id != CODEC_ID_MPEG2VIDEO) { - last_codec_id = CODEC_ID_MPEG2VIDEO; - CodecVideoOpen(MyVideoDecoder, VideoHardwareDecoder < 0 + if (stream->LastCodecID != CODEC_ID_MPEG2VIDEO) { + stream->LastCodecID = CODEC_ID_MPEG2VIDEO; + CodecVideoOpen(stream->Decoder, VideoHardwareDecoder < 0 && VdpauDecoder ? "mpegvideo_vdpau" : NULL, CODEC_ID_MPEG2VIDEO); } break; case CODEC_ID_H264: - if (last_codec_id != CODEC_ID_H264) { - last_codec_id = CODEC_ID_H264; - CodecVideoOpen(MyVideoDecoder, VideoHardwareDecoder + if (stream->LastCodecID != CODEC_ID_H264) { + stream->LastCodecID = CODEC_ID_H264; + CodecVideoOpen(stream->Decoder, VideoHardwareDecoder && VdpauDecoder ? "h264_vdpau" : NULL, CODEC_ID_H264); } break; @@ -1590,28 +1624,30 @@ int VideoDecodeInput(void) avpkt->size = avpkt->stream_index; avpkt->stream_index = 0; - if (last_codec_id == CODEC_ID_MPEG2VIDEO) { - FixPacketForFFMpeg(MyVideoDecoder, avpkt); + if (stream->LastCodecID == CODEC_ID_MPEG2VIDEO) { + FixPacketForFFMpeg(stream->Decoder, avpkt); } else { - CodecVideoDecode(MyVideoDecoder, avpkt); + CodecVideoDecode(stream->Decoder, avpkt); } avpkt->size = saved_size; skip: // advance packet read - VideoPacketRead = (VideoPacketRead + 1) % VIDEO_PACKET_MAX; - atomic_dec(&VideoPacketsFilled); + stream->PacketRead = (stream->PacketRead + 1) % VIDEO_PACKET_MAX; + atomic_dec(&stream->PacketsFilled); return 0; } /** ** Get number of video buffers. +** +** @param stream video stream */ -int VideoGetBuffers(void) +int VideoGetBuffers(const VideoStream * stream) { - return atomic_read(&VideoPacketsFilled); + return atomic_read(&stream->PacketsFilled); } /** @@ -1631,13 +1667,17 @@ static void StartVideo(void) VideoSetFullscreen(1); } VideoOsdInit(); - if (!MyVideoDecoder) { - if ((MyHwDecoder = VideoNewHwDecoder())) { - MyVideoDecoder = CodecVideoNewDecoder(MyHwDecoder); + if (!MyVideoStream->Decoder) { + if ((MyVideoStream->HwDecoder = VideoNewHwDecoder(MyVideoStream))) { + MyVideoStream->Decoder = + CodecVideoNewDecoder(MyVideoStream->HwDecoder); + MyVideoStream->SkipStream = 0; + AudioSyncStream = MyVideoStream; } - VideoCodecID = CODEC_ID_NONE; + MyVideoStream->CodecID = CODEC_ID_NONE; + MyVideoStream->LastCodecID = CODEC_ID_NONE; } - VideoPacketInit(); + VideoPacketInit(MyVideoStream); } /** @@ -1647,19 +1687,21 @@ static void StopVideo(void) { VideoOsdExit(); VideoExit(); - if (MyVideoDecoder) { + if (MyVideoStream->Decoder) { + MyVideoStream->SkipStream = 1; // FIXME: this can crash, hw decoder released by video exit - CodecVideoClose(MyVideoDecoder); - CodecVideoDelDecoder(MyVideoDecoder); - MyVideoDecoder = NULL; + CodecVideoClose(MyVideoStream->Decoder); + CodecVideoDelDecoder(MyVideoStream->Decoder); + MyVideoStream->Decoder = NULL; + AudioSyncStream = NULL; } - if (MyHwDecoder) { - // done by exit: VideoDelHwDecoder(MyHwDecoder); - MyHwDecoder = NULL; + if (MyVideoStream->HwDecoder) { + // done by exit: VideoDelHwDecoder(MyVideoStream->HwDecoder); + MyVideoStream->HwDecoder = NULL; } - VideoPacketExit(); + VideoPacketExit(MyVideoStream); - NewVideoStream = 1; + MyVideoStream->NewStream = 1; } #ifdef DEBUG @@ -1727,20 +1769,14 @@ static int ValidateMpeg(const uint8_t * data, int size) /** ** Play video packet. ** +** @param stream video stream ** @param data data of exactly one complete PES packet ** @param size size of PES packet ** ** @return number of bytes used, 0 if internal buffer are full. ** -** @note vdr sends incomplete packets, va-api h264 decoder only -** supports complete packets. -** We buffer here until we receive an complete PES Packet, which -** is no problem, the audio is always far behind us. -** cTsToPes::GetPes splits the packets. -** -** @todo FIXME: combine the 5 ifs at start of the function */ -int PlayVideo(const uint8_t * data, int size) +int PlayVideo3(VideoStream * stream, const uint8_t * data, int size) { const uint8_t *check; int64_t pts; @@ -1748,27 +1784,25 @@ int PlayVideo(const uint8_t * data, int size) int z; int l; - if (!MyVideoDecoder) { // no x11 video started + if (!stream->Decoder) { // no x11 video started return size; } - if (SkipVideo) { // skip video + if (stream->SkipStream) { // skip video stream return size; } - if (StreamFreezed) { // stream freezed + if (stream->Freezed) { // stream freezed return 0; } - if (NewVideoStream) { // channel switched + if (stream->NewStream) { // channel switched Debug(3, "video: new stream %dms\n", GetMsTicks() - VideoSwitch); - // FIXME: hack to test results - if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) { + if (atomic_read(&stream->PacketsFilled) >= VIDEO_PACKET_MAX - 1) { Debug(3, "video: new video stream lost\n"); - NewVideoStream = 0; return 0; } - VideoNextPacket(CODEC_ID_NONE); - VideoCodecID = CODEC_ID_NONE; - ClosingVideoStream = 1; - NewVideoStream = 0; + VideoNextPacket(stream, CODEC_ID_NONE); + stream->CodecID = CODEC_ID_NONE; + stream->ClosingStream = 1; + stream->NewStream = 0; } // must be a PES start code if (size < 9 || !data || data[0] || data[1] || data[2] != 0x01) { @@ -1791,12 +1825,13 @@ int PlayVideo(const uint8_t * data, int size) return size; } // hard limit buffer full: needed for replay - if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 3) { + if (atomic_read(&stream->PacketsFilled) >= VIDEO_PACKET_MAX - 3) { return 0; } // soft limit buffer full - if (atomic_read(&VideoPacketsFilled) > 3 + if (atomic_read(&stream->PacketsFilled) > 3 && AudioUsedBytes() > AUDIO_MIN_BUFFER_FREE) { + // FIXME: audio only for main video stream return 0; } // get pts/dts @@ -1825,11 +1860,11 @@ int PlayVideo(const uint8_t * data, int size) if ((data[6] & 0xC0) == 0x80 && z >= 2 && check[0] == 0x01 && check[1] == 0x09) { // old PES HDTV recording z == 2 - if (VideoCodecID == CODEC_ID_H264) { + if (stream->CodecID == CODEC_ID_H264) { #if 0 // this should improve ffwd+frew, but produce crash in ffmpeg // with some streams - if (CurrentTrickSpeed && pts != (int64_t) AV_NOPTS_VALUE) { + if (stream->TrickSpeed && pts != (int64_t) AV_NOPTS_VALUE) { // H264 NAL End of Sequence static uint8_t seq_end_h264[] = { 0x00, 0x00, 0x00, 0x01, 0x0A }; @@ -1843,22 +1878,22 @@ int PlayVideo(const uint8_t * data, int size) } } #endif - VideoNextPacket(CODEC_ID_H264); + VideoNextPacket(stream, CODEC_ID_H264); } else { Debug(3, "video: h264 detected\n"); - VideoCodecID = CODEC_ID_H264; + stream->CodecID = CODEC_ID_H264; } // SKIP PES header (ffmpeg supports short start code) - VideoEnqueue(pts, check - 2, l + 2); + VideoEnqueue(stream, pts, check - 2, l + 2); return size; } // PES start code 0x00 0x00 0x01 if (z > 1 && check[0] == 0x01) { - if (VideoCodecID == CODEC_ID_MPEG2VIDEO) { - VideoNextPacket(CODEC_ID_MPEG2VIDEO); + if (stream->CodecID == CODEC_ID_MPEG2VIDEO) { + VideoNextPacket(stream, CODEC_ID_MPEG2VIDEO); } else { Debug(3, "video: mpeg2 detected ID %02x\n", check[3]); - VideoCodecID = CODEC_ID_MPEG2VIDEO; + stream->CodecID = CODEC_ID_MPEG2VIDEO; } #ifdef DEBUG if (ValidateMpeg(data, size)) { @@ -1866,29 +1901,70 @@ int PlayVideo(const uint8_t * data, int size) } #endif // SKIP PES header, begin of start code - VideoEnqueue(pts, check - z, l + z); + VideoEnqueue(stream, pts, check - z, l + z); return size; } // this happens when vdr sends incomplete packets - if (VideoCodecID == CODEC_ID_NONE) { + if (stream->CodecID == CODEC_ID_NONE) { Debug(3, "video: not detected\n"); return size; } // SKIP PES header - VideoEnqueue(pts, data + 9 + n, size - 9 - n); + VideoEnqueue(stream, pts, data + 9 + n, size - 9 - n); // incomplete packets produce artefacts after channel switch // packet < 65526 is the last split packet, detect it here for // better latency - if (size < 65526 && VideoCodecID == CODEC_ID_MPEG2VIDEO) { + if (size < 65526 && stream->CodecID == CODEC_ID_MPEG2VIDEO) { // mpeg codec supports incomplete packets // waiting for a full complete packages, increases needed delays - VideoNextPacket(CODEC_ID_MPEG2VIDEO); + VideoNextPacket(stream, CODEC_ID_MPEG2VIDEO); } return size; } +/** +** Play video packet. +** +** @param data data of exactly one complete PES packet +** @param size size of PES packet +** +** @return number of bytes used, 0 if internal buffer are full. +** +*/ +int PlayVideo2(const uint8_t * data, int size) +{ + static VideoStream *stream; + + if (!stream) { // test hack v1 + stream = MyVideoStream; + } + + return PlayVideo3(stream, data, size); +} + +/** +** Play video packet. +** +** @param data data of exactly one complete PES packet +** @param size size of PES packet +** +** @return number of bytes used, 0 if internal buffer are full. +** +** @note vdr sends incomplete packets, va-api h264 decoder only +** supports complete packets. +** We buffer here until we receive an complete PES Packet, which +** is no problem, the audio is always far behind us. +** cTsToPes::GetPes splits the packets. +** +** @todo FIXME: combine the 5 ifs at start of the function +*/ +int PlayVideo(const uint8_t * data, int size) +{ + return PlayVideo3(MyVideoStream, data, size); +} + /// call VDR support function extern uint8_t *CreateJpeg(uint8_t *, int *, int, int, int); @@ -1986,12 +2062,12 @@ uint8_t *GrabImage(int *size, int jpeg, int quality, int width, int height) int SetPlayMode(int play_mode) { VideoDisplayWakeup(); - if (MyVideoDecoder) { // tell video parser we have new stream - if (VideoCodecID != CODEC_ID_NONE) { - NewVideoStream = 1; + if (MyVideoStream->Decoder) { // tell video parser we have new stream + if (MyVideoStream->CodecID != CODEC_ID_NONE) { + MyVideoStream->NewStream = 1; // tell hw decoder we are closing stream - VideoSetClosing(MyHwDecoder); - VideoResetStart(MyHwDecoder); + VideoSetClosing(MyVideoStream->HwDecoder); + VideoResetStart(MyVideoStream->HwDecoder); #ifdef DEBUG VideoSwitch = GetMsTicks(); #endif @@ -2026,8 +2102,8 @@ int SetPlayMode(int play_mode) */ int64_t GetSTC(void) { - if (MyHwDecoder) { - return VideoGetClock(MyHwDecoder); + if (MyVideoStream->HwDecoder) { + return VideoGetClock(MyVideoStream->HwDecoder); } // could happen during dettached Warning(_("softhddev: %s called without hw decoder\n"), __FUNCTION__); @@ -2050,8 +2126,8 @@ void GetVideoSize(int *width, int *height, double *aspect) int aspect_num; int aspect_den; - if (MyHwDecoder) { - VideoGetVideoSize(MyHwDecoder, width, height, &aspect_num, + if (MyVideoStream->HwDecoder) { + VideoGetVideoSize(MyVideoStream->HwDecoder, width, height, &aspect_num, &aspect_den); *aspect = (double)aspect_num / (double)aspect_den; } else { @@ -2080,14 +2156,15 @@ void GetVideoSize(int *width, int *height, double *aspect) */ void TrickSpeed(int speed) { - CurrentTrickSpeed = speed; - if (MyHwDecoder) { - VideoSetTrickSpeed(MyHwDecoder, speed); + MyVideoStream->TrickSpeed = speed; + if (MyVideoStream->HwDecoder) { + VideoSetTrickSpeed(MyVideoStream->HwDecoder, speed); } else { // can happen, during startup Debug(3, "softhddev: %s called without hw decoder\n", __FUNCTION__); } StreamFreezed = 0; + MyVideoStream->Freezed = 0; } /** @@ -2097,19 +2174,19 @@ void Clear(void) { int i; - VideoResetPacket(); // terminate work - VideoClearBuffers = 1; + VideoResetPacket(MyVideoStream); // terminate work + MyVideoStream->ClearBuffers = 1; AudioFlushBuffers(); //NewAudioStream = 1; // FIXME: audio avcodec_flush_buffers, video is done by VideoClearBuffers // wait for empty buffers // FIXME: without softstart sync VideoDecode isn't called. - for (i = 0; VideoClearBuffers && i < 20; ++i) { + for (i = 0; MyVideoStream->ClearBuffers && i < 20; ++i) { usleep(1 * 1000); } Debug(3, "[softhddev]%s: %dms buffers %d\n", __FUNCTION__, i, - VideoGetBuffers()); + VideoGetBuffers(MyVideoStream)); } /** @@ -2128,6 +2205,7 @@ void Play(void) void Freeze(void) { StreamFreezed = 1; + MyVideoStream->Freezed = 1; AudioPause(); } @@ -2163,14 +2241,14 @@ void StillPicture(const uint8_t * data, int size) #ifdef STILL_DEBUG InStillPicture = 1; #endif - VideoSetTrickSpeed(MyHwDecoder, 1); - VideoResetPacket(); + VideoSetTrickSpeed(MyVideoStream->HwDecoder, 1); + VideoResetPacket(MyVideoStream); old_video_hardware_decoder = VideoHardwareDecoder; // enable/disable hardware decoder for still picture VideoHardwareDecoder = ConfigStillDecoder; - VideoNextPacket(CODEC_ID_NONE); // close last stream + VideoNextPacket(MyVideoStream, CODEC_ID_NONE); // close last stream - if (VideoCodecID == CODEC_ID_NONE) { + if (MyVideoStream->CodecID == CODEC_ID_NONE) { // FIXME: should detect codec, see PlayVideo Error(_("[softhddev] no codec known for still picture\n")); } @@ -2179,7 +2257,8 @@ void StillPicture(const uint8_t * data, int size) #ifdef STILL_DEBUG fprintf(stderr, "still-picture\n"); #endif - for (i = 0; i < (VideoCodecID == CODEC_ID_MPEG2VIDEO ? 4 : 4); ++i) { + for (i = 0; i < (MyVideoStream->CodecID == CODEC_ID_MPEG2VIDEO ? 4 : 4); + ++i) { const uint8_t *split; int n; @@ -2202,47 +2281,49 @@ void StillPicture(const uint8_t * data, int size) if (!len || len + 6 > n) { if ((split[3] & 0xF0) == 0xE0) { // video only - while (!PlayVideo(split, n)) { // feed remaining bytes + while (!PlayVideo3(MyVideoStream, split, n)) { // feed remaining bytes } } break; } if ((split[3] & 0xF0) == 0xE0) { // video only - while (!PlayVideo(split, len + 6)) { // feed it + while (!PlayVideo3(MyVideoStream, split, len + 6)) { // feed it } } split += 6 + len; n -= 6 + len; } while (n > 6); - VideoNextPacket(VideoCodecID); // terminate last packet + VideoNextPacket(MyVideoStream, MyVideoStream->CodecID); // terminate last packet } else { // ES packet - if (VideoCodecID != CODEC_ID_MPEG2VIDEO) { - VideoNextPacket(CODEC_ID_NONE); // close last stream - VideoCodecID = CODEC_ID_MPEG2VIDEO; + if (MyVideoStream->CodecID != CODEC_ID_MPEG2VIDEO) { + VideoNextPacket(MyVideoStream, CODEC_ID_NONE); // close last stream + MyVideoStream->CodecID = CODEC_ID_MPEG2VIDEO; } - VideoEnqueue(AV_NOPTS_VALUE, data, size); + VideoEnqueue(MyVideoStream, AV_NOPTS_VALUE, data, size); } - if (VideoCodecID == CODEC_ID_H264) { - VideoEnqueue(AV_NOPTS_VALUE, seq_end_h264, sizeof(seq_end_h264)); + if (MyVideoStream->CodecID == CODEC_ID_H264) { + VideoEnqueue(MyVideoStream, AV_NOPTS_VALUE, seq_end_h264, + sizeof(seq_end_h264)); } else { - VideoEnqueue(AV_NOPTS_VALUE, seq_end_mpeg, sizeof(seq_end_mpeg)); + VideoEnqueue(MyVideoStream, AV_NOPTS_VALUE, seq_end_mpeg, + sizeof(seq_end_mpeg)); } - VideoNextPacket(VideoCodecID); // terminate last packet + VideoNextPacket(MyVideoStream, MyVideoStream->CodecID); // terminate last packet } // wait for empty buffers - for (i = 0; VideoGetBuffers() && i < 30; ++i) { + for (i = 0; VideoGetBuffers(MyVideoStream) && i < 30; ++i) { usleep(10 * 1000); } Debug(3, "[softhddev]%s: buffers %d %dms\n", __FUNCTION__, - VideoGetBuffers(), i * 10); + VideoGetBuffers(MyVideoStream), i * 10); #ifdef STILL_DEBUG InStillPicture = 0; #endif - VideoNextPacket(CODEC_ID_NONE); // close last stream - VideoSetTrickSpeed(MyHwDecoder, 0); + VideoNextPacket(MyVideoStream, CODEC_ID_NONE); // close last stream + VideoSetTrickSpeed(MyVideoStream->HwDecoder, 0); VideoHardwareDecoder = old_video_hardware_decoder; } @@ -2262,26 +2343,15 @@ void StillPicture(const uint8_t * data, int size) int Poll(int timeout) { // poll is only called during replay, flush buffers after replay - VideoClearClose = 1; + MyVideoStream->ClearClose = 1; for (;;) { -#if 0 - int empty; - int t; - - // buffers are too full - empty = atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX * 1 / 4 - || AudioUsedBytes() < AUDIO_MIN_BUFFER_FREE * 2; - if (empty || !timeout) { - return empty; - } -#else int full; int t; int used; int filled; used = AudioUsedBytes(); - filled = atomic_read(&VideoPacketsFilled); + filled = atomic_read(&MyVideoStream->PacketsFilled); // soft limit + hard limit full = (used > AUDIO_MIN_BUFFER_FREE && filled > 3) || AudioFreeBytes() < AUDIO_MIN_BUFFER_FREE @@ -2290,7 +2360,7 @@ int Poll(int timeout) if (!full || !timeout) { return !full; } -#endif + t = 15; if (timeout < t) { t = timeout; @@ -2307,11 +2377,11 @@ int Poll(int timeout) */ int Flush(int timeout) { - if (atomic_read(&VideoPacketsFilled)) { + if (atomic_read(&MyVideoStream->PacketsFilled)) { if (timeout) { // let display thread work usleep(timeout * 1000); } - return !atomic_read(&VideoPacketsFilled); + return !atomic_read(&MyVideoStream->PacketsFilled); } return 1; } @@ -2384,7 +2454,7 @@ const char *CommandLineHelp(void) " -d display\tdisplay of x11 server (fe. :0.0)\n" " -f\t\tstart with fullscreen window (only with window manager)\n" " -g geometry\tx11 window geometry wxh+x+y\n" - " -v device\tvideo device (va-api, vdpau, noop)\n" + " -v device\tvideo driver device (va-api, vdpau, noop)\n" " -s\t\tstart in suspended mode\n" " -x\t\tstart x11 server\n" " -X args\tX11 server arguments (f.e. -nocursor)\n" " -w workaround\tenable/disable workarounds\n" @@ -2693,7 +2763,7 @@ int Start(void) StartVideo(); } } else { - SkipVideo = 1; + MyVideoStream->SkipStream = 1; SkipAudio = 1; } pthread_mutex_init(&SuspendLockMutex, NULL); @@ -2755,14 +2825,14 @@ void MainThreadHook(void) void Suspend(int video, int audio, int dox11) { pthread_mutex_lock(&SuspendLockMutex); - if (SkipVideo && SkipAudio) { // already suspended + if (MyVideoStream->SkipStream && SkipAudio) { // already suspended pthread_mutex_unlock(&SuspendLockMutex); return; } Debug(3, "[softhddev]%s:\n", __FUNCTION__); - SkipVideo = 1; + MyVideoStream->SkipStream = 1; SkipAudio = 1; if (audio) { @@ -2791,7 +2861,7 @@ void Suspend(int video, int audio, int dox11) */ void Resume(void) { - if (!SkipVideo && !SkipAudio) { // we are not suspended + if (!MyVideoStream->SkipStream && !SkipAudio) { // we are not suspended return; } @@ -2800,7 +2870,7 @@ void Resume(void) pthread_mutex_lock(&SuspendLockMutex); // FIXME: start x11 - if (!MyHwDecoder) { // video not running + if (!MyVideoStream->HwDecoder) { // video not running StartVideo(); } if (!MyAudioDecoder) { // audio not running @@ -2812,7 +2882,9 @@ void Resume(void) AudioChannelID = -1; } - SkipVideo = 0; + if (MyVideoStream->Decoder) { + MyVideoStream->SkipStream = 0; + } SkipAudio = 0; pthread_mutex_unlock(&SuspendLockMutex); @@ -2832,8 +2904,9 @@ void GetStats(int *missed, int *duped, int *dropped, int *counter) *duped = 0; *dropped = 0; *counter = 0; - if (MyHwDecoder) { - VideoGetStats(MyHwDecoder, missed, duped, dropped, counter); + if (MyVideoStream->HwDecoder) { + VideoGetStats(MyVideoStream->HwDecoder, missed, duped, dropped, + counter); } } @@ -2847,7 +2920,7 @@ void GetStats(int *missed, int *duped, int *dropped, int *counter) */ void ScaleVideo(int x, int y, int width, int height) { - if (MyHwDecoder) { - VideoSetOutputPosition(MyHwDecoder, x, y, width, height); + if (MyVideoStream->HwDecoder) { + VideoSetOutputPosition(MyVideoStream->HwDecoder, x, y, width, height); } } diff --git a/softhddev.h b/softhddev.h index cf8bf2b..564dcba 100644 --- a/softhddev.h +++ b/softhddev.h @@ -44,6 +44,8 @@ extern "C" /// C plugin play video packet extern int PlayVideo(const uint8_t *, int); + /// C plugin play video packet (next version) + extern int PlayVideo2(const uint8_t *, int); /// C plugin play TS video packet extern void PlayTsVideo(const uint8_t *, int); /// C plugin grab an image diff --git a/softhddevice.cpp b/softhddevice.cpp index f0f8c38..722bad9 100644 --- a/softhddevice.cpp +++ b/softhddevice.cpp @@ -50,7 +50,7 @@ extern "C" /// vdr-plugin version number. /// Makefile extracts the version number for generating the file name /// for the distribution archive. -static const char *const VERSION = "0.5.3" +static const char *const VERSION = "0.6.0" #ifdef GIT_REV "-GIT" GIT_REV #endif @@ -1178,6 +1178,201 @@ cSoftHdControl::~cSoftHdControl() dsyslog("[softhddev]%s: dummy player stopped\n", __FUNCTION__); } +////////////////////////////////////////////////////////////////////////////// +// PIP +////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_PIP + +static int OsdPipTest; ///< OSD pip test flag + +////////////////////////////////////////////////////////////////////////////// +// cReceiver +////////////////////////////////////////////////////////////////////////////// + +#include + +/** +** Receiver class for PIP mode. +*/ +class cSoftReceiver:public cReceiver +{ + protected: + virtual void Activate(bool); + virtual void Receive(uchar *, int); + public: + cSoftReceiver(const cChannel *); ///< receiver constructor + virtual ~ cSoftReceiver(); ///< receiver destructor +}; + +/** +** Receiver constructor. +** +** @param channel channel to receive. +*/ +cSoftReceiver::cSoftReceiver(const cChannel * channel):cReceiver(channel) +{ + fprintf(stderr, "pip: v-pid: %04x\n", channel->Vpid()); + SetPids(NULL); // clear all pids, we want video only + AddPid(channel->Vpid()); +} + +/** +** Receiver destructor. +*/ +cSoftReceiver::~cSoftReceiver() +{ +} + +/** +** Called before the receiver gets attached or detached. +** +** @param on flag attached, detached +*/ +void cSoftReceiver::Activate(bool on) +{ + fprintf(stderr, "pip: activate %d\n", on); + + OsdPipTest = on; +} + +/// +/// Parse packetized elementary stream. +/// +/// @param data payload data of transport stream +/// @param size number of payload data bytes +/// @param is_start flag, start of pes packet +/// +static void PipPesParse(const uint8_t * data, int size, int is_start) +{ + static uint8_t *pes_buf; + static int pes_size; + static int pes_index; + + // FIXME: quick&dirty + + if (!pes_buf) { + pes_size = 500 * 1024 * 1024; + pes_buf = (uint8_t *) malloc(pes_size); + pes_index = 0; + } + if (is_start) { // start of pes packet + if (pes_index) { + fprintf(stderr, "pip: pes packet %8d %02x%02x\n", pes_index, + pes_buf[2], pes_buf[3]); + if (pes_buf[0] || pes_buf[1] || pes_buf[2] != 0x01) { + fprintf(stderr, "pip: invalid pes packet %d\n", pes_index); + } else { + PlayVideo2(pes_buf, pes_index); + // FIXME: buffer full: pes packet is dropped + } + pes_index = 0; + } + } + + if (pes_index + size > pes_size) { + fprintf(stderr, "pip: pes buffer too small\n"); + // FIXME: error state + return; + } + memcpy(pes_buf + pes_index, data, size); + pes_index += size; +} + + /// Transport stream packet size +#define TS_PACKET_SIZE 188 + /// Transport stream packet sync byte +#define TS_PACKET_SYNC 0x47 + +/** +** Receive TS packet from device. +** +** @param data ts packet +** @param size size (#TS_PACKET_SIZE=188) of tes packet +*/ +void cSoftReceiver::Receive(uchar * data, int size) +{ + static int x; + const uint8_t *p; + + if (!x) { + fprintf(stderr, "pip: receive %p(%d)\n", data, size); + x++; + } + + p = data; + while (size >= TS_PACKET_SIZE) { + int payload; + + if (p[0] != TS_PACKET_SYNC) { + esyslog(tr("tsdemux: transport stream out of sync\n")); + // FIXME: kill all buffers + return; + } + if (p[1] & 0x80) { // error indicatord + dsyslog("tsdemux: transport error\n"); + // FIXME: kill all buffers + goto next_packet; + } + if (0) { + int pid; + + pid = (p[1] & 0x1F) << 8 | p[2]; + fprintf(stderr, "tsdemux: PID: %#04x%s%s\n", pid, + p[1] & 0x40 ? " start" : "", p[3] & 0x10 ? " payload" : ""); + } + // skip adaptation field + switch (p[3] & 0x30) { // adaption field + case 0x00: // reserved + case 0x20: // adaptation field only + default: + goto next_packet; + case 0x10: // only payload + payload = 4; + break; + case 0x30: // skip adapation field + payload = 5 + p[4]; + // illegal length, ignore packet + if (payload >= TS_PACKET_SIZE) { + dsyslog("tsdemux: illegal adaption field length\n"); + goto next_packet; + } + break; + } + + PipPesParse(p + payload, TS_PACKET_SIZE - payload, p[1] & 0x40); + + next_packet: + p += TS_PACKET_SIZE; + size -= TS_PACKET_SIZE; + } +} + +////////////////////////////////////////////////////////////////////////////// + +/** +** Prepare new PIP. +*/ +static void NewPip(void) +{ + int channel_nr; + const cChannel *channel; + cDevice *device; + cSoftReceiver *receiver; + + if ((channel_nr = cDevice::CurrentChannel()) + && (channel = Channels.GetByNumber(cDevice::CurrentChannel())) + && (device = cDevice::GetDevice(channel, 1, false))) { + fprintf(stderr, "pip: %d %p %p\n", channel_nr, channel, device); + device->SwitchChannel(channel, false); + receiver = new cSoftReceiver(channel); + device->AttachReceiver(receiver); + fprintf(stderr, "pip: attached\n"); + } +} + +#endif + ////////////////////////////////////////////////////////////////////////////// // cOsdMenu ////////////////////////////////////////////////////////////////////////////// @@ -1192,8 +1387,8 @@ class cSoftHdMenu:public cOsdMenu int HotkeyCode; ///< current hot-key code void Create(void); ///< create plugin main menu public: - cSoftHdMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0); - virtual ~ cSoftHdMenu(); + cSoftHdMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0); + virtual ~ cSoftHdMenu(); virtual eOSState ProcessKey(eKeys); }; @@ -1213,12 +1408,16 @@ void cSoftHdMenu::Create(void) SetHasHotkeys(); Add(new cOsdItem(hk(tr("Suspend SoftHdDevice")), osUser1)); +#ifdef USE_PIP + Add(new cOsdItem(hk(tr("PIP")), osUser2)); +#endif Add(new cOsdItem(NULL, osUnknown, false)); Add(new cOsdItem(NULL, osUnknown, false)); GetStats(&missed, &duped, &dropped, &counter); - Add(new cOsdItem(cString:: - sprintf(tr(" Frames missed(%d) duped(%d) dropped(%d) total(%d)"), - missed, duped, dropped, counter), osUnknown, false)); + Add(new + cOsdItem(cString::sprintf(tr + (" Frames missed(%d) duped(%d) dropped(%d) total(%d)"), missed, + duped, dropped, counter), osUnknown, false)); SetCurrent(Get(current)); // restore selected menu entry Display(); // display build menu @@ -1408,6 +1607,11 @@ eOSState cSoftHdMenu::ProcessKey(eKeys key) } } return osEnd; +#ifdef USE_PIP + case osUser2: + NewPip(); + return osEnd; +#endif default: Create(); break; @@ -1707,8 +1911,8 @@ bool cSoftHdDevice::Flush(int timeout_ms) ** Sets the video display format to the given one (only useful if this ** device has an MPEG decoder). */ -void cSoftHdDevice:: -SetVideoDisplayFormat(eVideoDisplayFormat video_display_format) +void cSoftHdDevice:: SetVideoDisplayFormat(eVideoDisplayFormat + video_display_format) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, video_display_format); @@ -1826,6 +2030,11 @@ void cSoftHdDevice::SetVolumeDevice(int volume) int cSoftHdDevice::PlayVideo(const uchar * data, int length) { //dsyslog("[softhddev]%s: %p %d\n", __FUNCTION__, data, length); +#ifdef USE_PIP + if (OsdPipTest) { + return length; + } +#endif return::PlayVideo(data, length); } diff --git a/video.c b/video.c index d72c3f6..46c6cf2 100644 --- a/video.c +++ b/video.c @@ -226,7 +226,7 @@ typedef struct _video_module_ char Enabled; ///< flag output module enabled /// allocate new video hw decoder - VideoHwDecoder *(*const NewHwDecoder)(void); + VideoHwDecoder *(*const NewHwDecoder)(VideoStream *); void (*const DelHwDecoder) (VideoHwDecoder *); unsigned (*const GetSurface) (VideoHwDecoder *); void (*const ReleaseSurface) (VideoHwDecoder *, unsigned); @@ -279,7 +279,7 @@ typedef struct _video_module_ char VideoIgnoreRepeatPict; ///< disable repeat pict warning -static const char *VideoDevice; ///< video output device +static const char *VideoDriverName; ///< video output device static Display *XlibDisplay; ///< Xlib X11 display static xcb_connection_t *Connection; ///< xcb connection static xcb_colormap_t VideoColormap; ///< video colormap @@ -1380,6 +1380,7 @@ struct _vaapi_decoder_ int TrickSpeed; ///< current trick speed int TrickCounter; ///< current trick speed counter struct timespec FrameTime; ///< time of last display + VideoStream *Stream; ///< video stream int Closing; ///< flag about closing current stream int64_t PTS; ///< video PTS clock @@ -1762,7 +1763,7 @@ static void VaapiInitSurfaceFlags(VaapiDecoder * decoder) /// /// @returns a new prepared VA-API hardware decoder. /// -static VaapiDecoder *VaapiNewHwDecoder(void) +static VaapiDecoder *VaapiNewHwDecoder(VideoStream * stream) { VaapiDecoder *decoder; int i; @@ -1826,6 +1827,7 @@ static VaapiDecoder *VaapiNewHwDecoder(void) decoder->OutputWidth = VideoWindowWidth; decoder->OutputHeight = VideoWindowHeight; + decoder->Stream = stream; decoder->Closing = -300 - 1; decoder->PTS = AV_NOPTS_VALUE; @@ -4394,7 +4396,7 @@ static void VaapiAdvanceDecoderFrame(VaapiDecoder * decoder) // FIXME: don't warn after stream start, don't warn during pause Error(_("video: display buffer empty, duping frame (%d/%d) %d\n"), decoder->FramesDuped, decoder->FrameCounter, - VideoGetBuffers()); + VideoGetBuffers(decoder->Stream)); return; } // wait for rendering finished @@ -4681,7 +4683,7 @@ static void VaapiSyncDecoder(VaapiDecoder * decoder) VaapiMessage(1, _("video: decoder buffer empty, " "duping frame (%d/%d) %d v-buf\n"), decoder->FramesDuped, - decoder->FrameCounter, VideoGetBuffers()); + decoder->FrameCounter, VideoGetBuffers(decoder->Stream)); if (decoder->Closing < -300) { atomic_set(&decoder->SurfacesFilled, 0); } @@ -4701,7 +4703,8 @@ static void VaapiSyncDecoder(VaapiDecoder * decoder) Timestamp2String(video_clock), abs((video_clock - audio_clock) / 90) < 8888 ? ((video_clock - audio_clock) / 90) : 8888, - AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers(), + AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, + VideoGetBuffers(decoder->Stream), (1 + decoder->Interlaced) * atomic_read(&decoder->SurfacesFilled) - decoder->SurfaceField); if (!(decoder->FramesDisplayed % (5 * 60 * 60))) { @@ -4844,10 +4847,10 @@ static void VaapiDisplayHandlerThread(void) // FIXME: hot polling pthread_mutex_lock(&VideoLockMutex); // fetch+decode or reopen - err = VideoDecodeInput(); + err = VideoDecodeInput(decoder->Stream); pthread_mutex_unlock(&VideoLockMutex); } else { - err = VideoPollInput(); + err = VideoPollInput(decoder->Stream); } if (err) { // FIXME: sleep on wakeup @@ -5111,7 +5114,8 @@ static void VaapiOsdExit(void) static const VideoModule VaapiModule = { .Name = "va-api", .Enabled = 1, - .NewHwDecoder = (VideoHwDecoder * (*const)(void))VaapiNewHwDecoder, + .NewHwDecoder = + (VideoHwDecoder * (*const)(VideoStream *)) VaapiNewHwDecoder, .DelHwDecoder = (void (*const) (VideoHwDecoder *))VaapiDelHwDecoder, .GetSurface = (unsigned (*const) (VideoHwDecoder *))VaapiGetSurface, .ReleaseSurface = @@ -5211,6 +5215,7 @@ typedef struct _vdpau_decoder_ int TrickSpeed; ///< current trick speed int TrickCounter; ///< current trick speed counter struct timespec FrameTime; ///< time of last display + VideoStream *Stream; ///< video stream int Closing; ///< flag about closing current stream int64_t PTS; ///< video PTS clock @@ -5826,9 +5831,11 @@ static void VdpauMixerCreate(VdpauDecoder * decoder) /// /// Allocate new VDPAU decoder. /// +/// @param stream video stream +/// /// @returns a new prepared vdpau hardware decoder. /// -static VdpauDecoder *VdpauNewHwDecoder(void) +static VdpauDecoder *VdpauNewHwDecoder(VideoStream * stream) { VdpauDecoder *decoder; int i; @@ -5884,6 +5891,7 @@ static VdpauDecoder *VdpauNewHwDecoder(void) decoder->OutputWidth = VideoWindowWidth; decoder->OutputHeight = VideoWindowHeight; + decoder->Stream = stream; decoder->Closing = -300 - 1; decoder->PTS = AV_NOPTS_VALUE; @@ -7830,7 +7838,7 @@ static void VdpauAdvanceDecoderFrame(VdpauDecoder * decoder) // FIXME: don't warn after stream start, don't warn during pause Error(_("video: display buffer empty, duping frame (%d/%d) %d\n"), decoder->FramesDuped, decoder->FrameCounter, - VideoGetBuffers()); + VideoGetBuffers(decoder->Stream)); return; } decoder->SurfaceRead = (decoder->SurfaceRead + 1) % VIDEO_SURFACES_MAX; @@ -8094,7 +8102,7 @@ static void VdpauSyncDecoder(VdpauDecoder * decoder) VdpauMessage(1, _("video: decoder buffer empty, " "duping frame (%d/%d) %d v-buf\n"), decoder->FramesDuped, - decoder->FrameCounter, VideoGetBuffers()); + decoder->FrameCounter, VideoGetBuffers(decoder->Stream)); if (decoder->Closing < -300) { atomic_set(&decoder->SurfacesFilled, 0); } @@ -8114,7 +8122,8 @@ static void VdpauSyncDecoder(VdpauDecoder * decoder) Timestamp2String(video_clock), abs((video_clock - audio_clock) / 90) < 8888 ? ((video_clock - audio_clock) / 90) : 8888, - AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers(), + AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, + VideoGetBuffers(decoder->Stream), (1 + decoder->Interlaced) * atomic_read(&decoder->SurfacesFilled) - decoder->SurfaceField); if (!(decoder->FramesDisplayed % (5 * 60 * 60))) { @@ -8335,10 +8344,10 @@ static void VdpauDisplayHandlerThread(void) // FIXME: hot polling pthread_mutex_lock(&VideoLockMutex); // fetch+decode or reopen - err = VideoDecodeInput(); + err = VideoDecodeInput(decoder->Stream); pthread_mutex_unlock(&VideoLockMutex); } else { - err = VideoPollInput(); + err = VideoPollInput(decoder->Stream); } if (err) { // FIXME: sleep on wakeup @@ -8638,7 +8647,8 @@ static void VdpauOsdExit(void) static const VideoModule VdpauModule = { .Name = "vdpau", .Enabled = 1, - .NewHwDecoder = (VideoHwDecoder * (*const)(void))VdpauNewHwDecoder, + .NewHwDecoder = + (VideoHwDecoder * (*const)(VideoStream *)) VdpauNewHwDecoder, .DelHwDecoder = (void (*const) (VideoHwDecoder *))VdpauDelHwDecoder, .GetSurface = (unsigned (*const) (VideoHwDecoder *))VdpauGetSurface, .ReleaseSurface = @@ -8673,9 +8683,12 @@ static const VideoModule VdpauModule = { /// /// Allocate new noop decoder. /// +/// @param stream video stream +/// /// @returns always NULL. /// -static VideoHwDecoder *NoopNewHwDecoder(void) +static VideoHwDecoder *NoopNewHwDecoder( + __attribute__ ((unused)) VideoStream * stream) { return NULL; } @@ -9384,11 +9397,13 @@ struct _video_hw_decoder_ /// /// Allocate new video hw decoder. /// +/// @param stream video stream +/// /// @returns a new initialized video hardware decoder. /// -VideoHwDecoder *VideoNewHwDecoder(void) +VideoHwDecoder *VideoNewHwDecoder(VideoStream * stream) { - return VideoUsedModule->NewHwDecoder(); + return VideoUsedModule->NewHwDecoder(stream); } /// @@ -10107,7 +10122,7 @@ static void VideoCreateWindow(xcb_window_t parent, xcb_visualid_t visual, /// void VideoSetDevice(const char *device) { - VideoDevice = device; + VideoDriverName = device; } /// @@ -10696,15 +10711,16 @@ void VideoInit(const char *display_name) for (i = 0; i < (int)(sizeof(VideoModules) / sizeof(*VideoModules)); ++i) { // FIXME: support list of drivers and include display name // use user device or first working enabled device driver - if ((VideoDevice && !strcasecmp(VideoDevice, VideoModules[i]->Name)) - || (!VideoDevice && VideoModules[i]->Enabled)) { + if ((VideoDriverName + && !strcasecmp(VideoDriverName, VideoModules[i]->Name)) + || (!VideoDriverName && VideoModules[i]->Enabled)) { if (VideoModules[i]->Init(display_name)) { VideoUsedModule = VideoModules[i]; goto found; } } } - Error(_("video: '%s' output module isn't supported\n"), VideoDevice); + Error(_("video: '%s' output module isn't supported\n"), VideoDriverName); VideoUsedModule = &NoopModule; found: @@ -10822,7 +10838,7 @@ void FeedKeyPress( __attribute__ ((unused)) { } -int VideoDecodeInput(void) +int VideoDecodeInput( __attribute__ ((unused)) VideoStream * stream) { return -1; } @@ -10923,7 +10939,7 @@ int main(int argc, char *const argv[]) // VideoInit(NULL); VideoOsdInit(); - video_hw_decoder = VideoNewHwDecoder(); + video_hw_decoder = VideoNewHwDecoder(NULL); start_tick = GetMsTicks(); n = 0; for (;;) { diff --git a/video.h b/video.h index 1385122..28a26ec 100644 --- a/video.h +++ b/video.h @@ -30,6 +30,9 @@ /// Video hardware decoder typedef typedef struct _video_hw_decoder_ VideoHwDecoder; + /// Video output stream typedef +typedef struct __video_stream__ VideoStream; + //---------------------------------------------------------------------------- // Variables //---------------------------------------------------------------------------- @@ -43,7 +46,7 @@ extern int VideoAudioDelay; ///< audio/video delay //---------------------------------------------------------------------------- /// Allocate new video hardware decoder. -extern VideoHwDecoder *VideoNewHwDecoder(void); +extern VideoHwDecoder *VideoNewHwDecoder(VideoStream *); /// Deallocate video hardware decoder. extern void VideoDelHwDecoder(VideoHwDecoder *); @@ -208,8 +211,13 @@ extern void VideoOsdExit(void); ///< Cleanup osd. extern void VideoInit(const char *); ///< Setup video module. extern void VideoExit(void); ///< Cleanup and exit video module. -extern int VideoPollInput(void); ///< Poll video input buffers. -extern int VideoDecodeInput(void); ///< Decode video input buffers. -extern int VideoGetBuffers(void); ///< Get number of input buffers. + /// Poll video input buffers. +extern int VideoPollInput(VideoStream *); + + /// Decode video input buffers. +extern int VideoDecodeInput(VideoStream *); + + /// Get number of input buffers. +extern int VideoGetBuffers(const VideoStream *); /// @}