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