summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog2
-rw-r--r--softhddev.c97
-rw-r--r--softhddev.h2
-rw-r--r--softhddevice.cpp2
-rw-r--r--video.c887
-rw-r--r--video.h9
6 files changed, 647 insertions, 352 deletions
diff --git a/ChangeLog b/ChangeLog
index 8e8b245..2d92071 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,7 @@ User johns
Date:
Release Version 0.5.0
+ Audio/Video sync rewrite, trick-speed support moved to video.
Faster VdpauBlackSurface version.
Fix bug: VideoSetPts wrong position for multi frame packets.
@@ -13,7 +14,6 @@ Date: Mon Mar 26 20:45:54 CEST 2012
User johns
Date: Fri Mar 23 18:43:20 CET 2012
- Audio/Video sync rewrite, trick-speed support moved to video.
Add optional argument (display) to ATTA svdrp commmand.
Wakeup display to show OSD for remote learning mode.
Support switching the primary device with svdrp.
diff --git a/softhddev.c b/softhddev.c
index c0ca0d6..3dec80f 100644
--- a/softhddev.c
+++ b/softhddev.c
@@ -1130,8 +1130,11 @@ void SetVolumeDevice(int volume)
#include <alsa/iatomic.h> // portable atomic_t
+#ifdef DEBUG
uint32_t VideoSwitch; ///< debug video switch ticks
+#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
@@ -1150,8 +1153,7 @@ 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 VideoTrickSpeed; ///< current trick speed
-static volatile char VideoTrickCounter; ///< current trick speed counter
+static volatile char CurrentTrickSpeed; ///< current trick speed
#ifdef DEBUG
static int VideoMaxPacketSize; ///< biggest used packet buffer
@@ -1340,6 +1342,10 @@ void FixPacketForFFMpeg(VideoDecoder * MyVideoDecoder, AVPacket * avpkt)
/**
** Decode from PES packet ringbuffer.
+**
+** @retval 0 packet decoded
+** @retval 1 stream paused
+** @retval -1 empty stream
*/
int VideoDecode(void)
{
@@ -1360,19 +1366,13 @@ int VideoDecode(void)
VideoClearBuffers = 0;
return 1;
}
- if (VideoTrickSpeed) {
- if (VideoTrickCounter++ < VideoTrickSpeed * 2) {
- usleep(5 * 1000);
- return 1;
- }
- VideoTrickCounter = 0;
- }
filled = atomic_read(&VideoPacketsFilled);
if (!filled) {
return -1;
}
- if (VideoClearClose) {
+ // clearing for normal channel switch has no advantage
+ if (VideoClearClose /*|| ClosingVideoStream */ ) {
int f;
// flush buffers, if close is in the queue
@@ -1388,6 +1388,7 @@ int VideoDecode(void)
break;
}
}
+ ClosingVideoStream = 0;
}
avpkt = &VideoPacketRb[VideoPacketRead];
@@ -1396,11 +1397,13 @@ int VideoDecode(void)
//
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);
goto skip;
}
+ // FIXME: look if more close are in the queue
// size can be zero
goto skip;
case CODEC_ID_MPEG2VIDEO:
@@ -1450,6 +1453,11 @@ int VideoDecode(void)
}
}
+ if (ClosingVideoStream) { // closing don't sync
+ avpkt->pts = AV_NOPTS_VALUE;
+ avpkt->dts = AV_NOPTS_VALUE;
+ }
+
if (last_codec_id == CODEC_ID_MPEG2VIDEO) {
FixPacketForFFMpeg(MyVideoDecoder, avpkt);
} else {
@@ -1618,7 +1626,7 @@ int PlayVideo(const uint8_t * data, int size)
return 0;
}
if (NewVideoStream) { // channel switched
- Debug(3, "video: new stream %d\n", GetMsTicks() - VideoSwitch);
+ Debug(3, "video: new stream %dms\n", GetMsTicks() - VideoSwitch);
// FIXME: hack to test results
if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX - 1) {
Debug(3, "video: new video stream lost\n");
@@ -1627,6 +1635,9 @@ int PlayVideo(const uint8_t * data, int size)
}
VideoNextPacket(CODEC_ID_NONE);
VideoCodecID = CODEC_ID_NONE;
+ // clear clock until new stream starts
+ VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
+ ClosingVideoStream = 1;
NewVideoStream = 0;
}
// must be a PES start code
@@ -1638,13 +1649,14 @@ int PlayVideo(const uint8_t * data, int size)
if (data[3] == PES_PADDING_STREAM) { // from DVD plugin
return size;
}
- n = data[8]; // header size
- if (size < 9 + n + 4) { // wrong size
+ n = data[8]; // header size
+ if (size <= 9 + n) { // wrong size
if (size == 9 + n) {
Warning(_("[softhddev] empty video packet\n"));
} else {
- Error(_("[softhddev] invalid video packet %d bytes\n"), size);
+ Error(_("[softhddev] invalid video packet %d/%d bytes\n"), 9 + n,
+ size);
}
return size;
}
@@ -1653,7 +1665,6 @@ int PlayVideo(const uint8_t * data, int size)
return 0;
}
// get pts/dts
-
pts = AV_NOPTS_VALUE;
if (data[7] & 0x80) {
pts =
@@ -1665,10 +1676,12 @@ int PlayVideo(const uint8_t * data, int size)
l = size - 9 - n;
z = 0;
while (!*check) { // count leading zeros
- if (--l < 2) {
+ if (l < 3) {
Warning(_("[softhddev] empty video packet %d bytes\n"), size);
- return size;
+ z = 0;
+ break;
}
+ --l;
++check;
++z;
}
@@ -1677,7 +1690,7 @@ int PlayVideo(const uint8_t * data, int size)
if ((data[6] & 0xC0) == 0x80 && z > 2 && check[0] == 0x01
&& check[1] == 0x09) {
if (VideoCodecID == CODEC_ID_H264) {
- if (VideoTrickSpeed && pts != (int64_t) AV_NOPTS_VALUE) {
+ if (CurrentTrickSpeed && pts != (int64_t) AV_NOPTS_VALUE) {
// H264 NAL End of Sequence
static uint8_t seq_end_h264[] =
{ 0x00, 0x00, 0x00, 0x01, 0x0A };
@@ -1716,7 +1729,6 @@ int PlayVideo(const uint8_t * data, int size)
return size;
}
// this happens when vdr sends incomplete packets
-
if (VideoCodecID == CODEC_ID_NONE) {
Debug(3, "video: not detected\n");
return size;
@@ -1836,7 +1848,9 @@ int SetPlayMode(int play_mode)
if (MyVideoDecoder) { // tell video parser we have new stream
if (VideoCodecID != CODEC_ID_NONE) {
NewVideoStream = 1;
+#ifdef DEBUG
VideoSwitch = GetMsTicks();
+#endif
}
}
if (MyAudioDecoder) { // tell audio parser we have new stream
@@ -1844,15 +1858,40 @@ int SetPlayMode(int play_mode)
NewAudioStream = 1;
}
}
- if (play_mode == 2 || play_mode == 3) {
- Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
+ switch (play_mode) {
+ case 1: // audio/video from player
+ break;
+ case 2: // audio only
+ Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
+ VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
+ break;
+ case 3: // audio only, black screen
+ Debug(3, "softhddev: FIXME: audio only, silence video errors\n");
+ VideoSetClock(MyHwDecoder, AV_NOPTS_VALUE);
+ break;
+ case 4: // video only
+ break;
}
+
Play();
return 1;
}
/**
+** Gets the current System Time Counter, which can be used to
+** synchronize audio, video and subtitles.
+*/
+int64_t GetSTC(void)
+{
+ if (MyHwDecoder) {
+ return VideoGetClock(MyHwDecoder);
+ }
+ Error(_("softhddev: %s called without hw decoder\n"), __FUNCTION__);
+ return AV_NOPTS_VALUE;
+}
+
+/**
** Set trick play speed.
**
** Every single frame shall then be displayed the given number of
@@ -1862,8 +1901,12 @@ int SetPlayMode(int play_mode)
*/
void TrickSpeed(int speed)
{
- VideoTrickSpeed = speed;
- VideoTrickCounter = 0;
+ CurrentTrickSpeed = speed;
+ if (MyHwDecoder) {
+ VideoSetTrickSpeed(MyHwDecoder, speed);
+ } else {
+ Error(_("softhddev: %s called without hw decoder\n"), __FUNCTION__);
+ }
StreamFreezed = 0;
}
@@ -1892,9 +1935,7 @@ void Clear(void)
*/
void Play(void)
{
- VideoTrickSpeed = 0;
- VideoTrickCounter = 0;
- StreamFreezed = 0;
+ TrickSpeed(0); // normal play
SkipAudio = 0;
AudioPlay();
}
@@ -2018,6 +2059,9 @@ void StillPicture(const uint8_t * data, int size)
** The dvd plugin is using this correct.
**
** @param timeout timeout to become ready in ms
+**
+** @retval true if ready
+** @retval false if busy
*/
int Poll(int timeout)
{
@@ -2329,7 +2373,6 @@ void SoftHdDeviceExit(void)
StopVideo();
CodecExit();
- //VideoPacketExit();
if (ConfigStartX11Server) {
Debug(3, "x-setup: Stop x11 server\n");
diff --git a/softhddev.h b/softhddev.h
index 50d7538..78862d6 100644
--- a/softhddev.h
+++ b/softhddev.h
@@ -51,6 +51,8 @@ extern "C"
/// C plugin set play mode
extern int SetPlayMode(int);
+ /// C plugin get current system time counter
+ extern int64_t GetSTC(void);
/// C plugin set trick speed
extern void TrickSpeed(int);
/// C plugin clears all video and audio data from the device
diff --git a/softhddevice.cpp b/softhddevice.cpp
index f699b47..8be2e9d 100644
--- a/softhddevice.cpp
+++ b/softhddevice.cpp
@@ -1085,7 +1085,7 @@ int64_t cSoftHdDevice::GetSTC(void)
{
//dsyslog("[softhddev]%s:\n", __FUNCTION__);
- return::VideoGetClock();
+ return::GetSTC();
}
/**
diff --git a/video.c b/video.c
index 9039098..f07282f 100644
--- a/video.c
+++ b/video.c
@@ -234,6 +234,9 @@ typedef struct _video_module_
const enum PixelFormat *);
void (*const RenderFrame) (VideoHwDecoder *, const AVCodecContext *,
const AVFrame *);
+ void (*const SetClock) (VideoHwDecoder *, int64_t);
+ int64_t(*const GetClock) (const VideoHwDecoder *);
+ void (*const SetTrickSpeed) (const VideoHwDecoder *, int);
uint8_t *(*const GrabOutput)(int *, int *, int *);
void (*const SetBackground) (uint32_t);
void (*const SetVideoMode) (void);
@@ -345,7 +348,9 @@ static xcb_atom_t WmDeleteWindowAtom; ///< WM delete message atom
static xcb_atom_t NetWmState; ///< wm-state message atom
static xcb_atom_t NetWmStateFullscreen; ///< fullscreen wm-state message atom
+#ifdef DEBUG
extern uint32_t VideoSwitch; ///< ticks for channel switch
+#endif
extern void AudioVideoReady(void); ///< tell audio video is ready
#ifdef USE_VIDEO_THREAD
@@ -391,6 +396,7 @@ static void VideoSetPts(int64_t * pts_p, int interlaced, const AVFrame * frame)
// update video clock
if (*pts_p != (int64_t) AV_NOPTS_VALUE) {
*pts_p += interlaced ? 40 * 90 : 20 * 90;
+ //Info("video: %s +pts\n", Timestamp2String(*pts_p));
}
//av_opt_ptr(avcodec_get_frame_class(), frame, "best_effort_timestamp");
//pts = frame->best_effort_timestamp;
@@ -1320,12 +1326,12 @@ struct _vaapi_decoder_
atomic_t SurfacesFilled; ///< how many of the buffer is used
int SurfaceField; ///< current displayed field
- int DropNextFrame; ///< flag drop next frame
- int DupNextFrame; ///< flag duplicate next frame
+ int TrickSpeed; ///< current trick speed
+ int TrickCounter; ///< current trick speed counter
struct timespec FrameTime; ///< time of last display
int64_t PTS; ///< video PTS clock
- int StartCounter; ///< number of start frames
+ int SyncCounter; ///< counter to sync frames
int FramesDuped; ///< number of frames duplicated
int FramesMissed; ///< number of frames missed
int FramesDropped; ///< number of frames dropped
@@ -1349,6 +1355,46 @@ static void VaapiReleaseSurface(VaapiDecoder *, VASurfaceID);
// VA-API Functions
//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+///
+/// Output video messages.
+///
+/// Reduce output.
+///
+/// @param level message level (Error, Warning, Info, Debug, ...)
+/// @param format printf format string (NULL to flush messages)
+/// @param ... printf arguments
+///
+/// @returns true, if message shown
+///
+static int VaapiMessage(int level, const char *format, ...)
+{
+ if (SysLogLevel > level || DebugLevel > level) {
+ static const char *last_format;
+ static char buf[256];
+ va_list ap;
+
+ va_start(ap, format);
+ if (format != last_format) { // don't repeat same message
+ last_format = format;
+ if (buf[0]) { // print last repeated message
+ syslog(LOG_ERR, "%s", buf);
+ buf[0] = '\0';
+ }
+
+ if (format) {
+ vsyslog(LOG_ERR, format, ap);
+ }
+ va_end(ap);
+ return 1;
+ }
+ vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ }
+ return 0;
+}
+
// Surfaces -------------------------------------------------------------
///
@@ -1577,9 +1623,9 @@ static void VaapiReleaseSurface(VaapiDecoder * decoder, VASurfaceID surface)
///
static void VaapiPrintFrames(const VaapiDecoder * decoder)
{
- Debug(3, "video/vaapi: %d missed, %d duped, %d dropped frames of %d\n",
+ Debug(3, "video/vaapi: %d missed, %d duped, %d dropped frames of %d,%d\n",
decoder->FramesMissed, decoder->FramesDuped, decoder->FramesDropped,
- decoder->FrameCounter);
+ decoder->FrameCounter, decoder->FramesDisplayed);
#ifndef DEBUG
(void)decoder;
#endif
@@ -1801,11 +1847,10 @@ static void VaapiCleanup(VaapiDecoder * decoder)
}
decoder->SurfaceRead = 0;
decoder->SurfaceWrite = 0;
+ decoder->SurfaceField = 0;
- decoder->SurfaceField = 1;
-
- //decoder->FrameCounter = 0;
- decoder->StartCounter = 0;
+ decoder->FrameCounter = 0;
+ decoder->FramesDisplayed = 0;
decoder->PTS = AV_NOPTS_VALUE;
VideoDeltaPTS = 0;
}
@@ -1868,6 +1913,8 @@ static void VaapiDelHwDecoder(VaapiDecoder * decoder)
free(decoder);
}
+#ifdef DEBUG // currently unused, keep it for later
+
static VAProfile VaapiFindProfile(const VAProfile * profiles, unsigned n,
VAProfile profile);
static VAEntrypoint VaapiFindEntrypoint(const VAEntrypoint * entrypoints,
@@ -1992,6 +2039,8 @@ static void Vaapi1080i(void)
fprintf(stderr, "done\n");
}
+#endif
+
///
/// VA-API setup.
///
@@ -2184,7 +2233,7 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
int e;
VAConfigAttrib attrib;
- Debug(3, "video: new stream format %d\n", GetMsTicks() - VideoSwitch);
+ Debug(3, "video: new stream format %dms\n", GetMsTicks() - VideoSwitch);
// create initial black surface and display
VaapiBlackSurface(decoder);
@@ -3987,7 +4036,7 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
decoder->Interlaced = interlaced;
decoder->TopFieldFirst = frame->top_field_first;
- decoder->SurfaceField = 1;
+ decoder->SurfaceField = 0;
}
// update aspect ratio changes
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,60,100)
@@ -4171,74 +4220,45 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
}
///
-/// Advance displayed frame.
+/// Advance displayed frame of decoder.
+///
+/// @param decoder VA-API hw decoder
///
-static void VaapiAdvanceFrame(void)
+static void VaapiAdvanceDecoderFrame(VaapiDecoder * decoder)
{
- int i;
-
- // show any frame as fast as possible
- // we keep always the last frame in the ring buffer
-
- for (i = 0; i < VaapiDecoderN; ++i) {
- VaapiDecoder *decoder;
+ // next surface, if complete frame is displayed (1 -> 0)
+ if (decoder->SurfaceField) {
VASurfaceID surface;
int filled;
- decoder = VaapiDecoders[i];
filled = atomic_read(&decoder->SurfacesFilled);
-
- // 0 -> 1
- // 1 -> 0 + advance
- if (VideoDeinterlace[decoder->Resolution] < VideoDeinterlaceSoftBob
- && decoder->Interlaced) {
- // FIXME: first frame is never shown
- if (decoder->SurfaceField) {
- if (filled > 1) {
- decoder->SurfaceField = 0;
- }
- } else {
- decoder->SurfaceField = 1;
- return;
- }
- }
-
- if (filled > 1) {
- decoder->SurfaceRead = (decoder->SurfaceRead + 1)
- % VIDEO_SURFACES_MAX;
- atomic_dec(&decoder->SurfacesFilled);
-
- // wait for rendering finished
- surface = decoder->SurfacesRb[decoder->SurfaceRead];
- if (vaSyncSurface(decoder->VaDisplay, surface)
- != VA_STATUS_SUCCESS) {
- Error(_("video/vaapi: vaSyncSurface failed\n"));
- }
- // debug duplicate frames
- } else if (filled == 1) {
- static int last_warned_frame;
-
+ // FIXME: this should check the caller
+ // check decoder, if new surface is available
+ if (filled <= 1) {
+ // keep use of last surface
++decoder->FramesDuped;
- decoder->DropNextFrame = 0;
// FIXME: don't warn after stream start, don't warn during pause
- if (last_warned_frame != decoder->FrameCounter) {
- Warning(_
- ("video: display buffer empty, duping frame (%d/%d) %d\n"),
- decoder->FramesDuped, decoder->FrameCounter,
- VideoGetBuffers());
- }
- last_warned_frame = decoder->FrameCounter;
- if (!(decoder->FramesDisplayed % 300)) {
- VaapiPrintFrames(decoder);
- }
- // wait for rendering finished
- surface = decoder->SurfacesRb[decoder->SurfaceRead];
- if (vaSyncSurface(decoder->VaDisplay, surface)
- != VA_STATUS_SUCCESS) {
- Error(_("video/vaapi: vaSyncSurface failed\n"));
- }
+ Error(_("video: display buffer empty, duping frame (%d/%d) %d\n"),
+ decoder->FramesDuped, decoder->FrameCounter,
+ VideoGetBuffers());
+ return;
+ }
+ // wait for rendering finished
+ surface = decoder->SurfacesRb[decoder->SurfaceRead];
+ if (vaSyncSurface(decoder->VaDisplay, surface) != VA_STATUS_SUCCESS) {
+ Error(_("video/vaapi: vaSyncSurface failed\n"));
}
+
+ decoder->SurfaceRead = (decoder->SurfaceRead + 1) % VIDEO_SURFACES_MAX;
+ atomic_dec(&decoder->SurfacesFilled);
+
+ // progressiv oder software deinterlacer
+ decoder->SurfaceField = !decoder->Interlaced
+ || VideoDeinterlace[decoder->Resolution]
+ >= VideoDeinterlaceSoftBob;
+ return;
}
+ decoder->SurfaceField = 1;
}
///
@@ -4273,6 +4293,7 @@ static void VaapiDisplayFrame(void)
// no surface availble show black with possible osd
if (!filled) {
VaapiBlackSurface(decoder);
+ VaapiMessage(3, "video/vaapi: black surface displayed\n");
continue;
}
@@ -4306,9 +4327,11 @@ static void VaapiDisplayFrame(void)
put2 = put1;
}
clock_gettime(CLOCK_REALTIME, &nowtime);
+ // FIXME: 31 only correct for 50Hz
if ((nowtime.tv_sec - decoder->FrameTime.tv_sec)
* 1000 * 1000 * 1000 + (nowtime.tv_nsec -
- decoder->FrameTime.tv_nsec) > 30 * 1000 * 1000) {
+ decoder->FrameTime.tv_nsec) > 31 * 1000 * 1000) {
+ // FIXME: ignore still-frame, trick-speed
Debug(3, "video/vaapi: time/frame too long %ldms\n",
((nowtime.tv_sec - decoder->FrameTime.tv_sec)
* 1000 * 1000 * 1000 + (nowtime.tv_nsec -
@@ -4333,11 +4356,62 @@ static void VaapiDisplayFrame(void)
}
///
-/// Sync and display surface.
+/// Set VA-API decoder video clock.
+///
+/// @param decoder VA-API hardware decoder
+/// @param pts audio presentation timestamp
+///
+void VaapiSetClock(VaapiDecoder * decoder, int64_t pts)
+{
+ decoder->PTS = pts;
+}
+
+///
+/// Get VA-API decoder video clock.
///
/// @param decoder VA-API decoder
///
-static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
+static int64_t VaapiGetClock(const VaapiDecoder * decoder)
+{
+ // pts is the timestamp of the latest decoded frame
+ if (decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
+ return AV_NOPTS_VALUE;
+ }
+ // subtract buffered decoded frames
+ if (decoder->Interlaced) {
+ return decoder->PTS -
+ 20 * 90 * (2 * atomic_read(&decoder->SurfacesFilled)
+ - decoder->SurfaceField);
+ }
+ return decoder->PTS - 20 * 90 * (atomic_read(&decoder->SurfacesFilled) +
+ 2);
+}
+
+///
+/// Set trick play speed.
+///
+/// @param decoder VA-API decoder
+/// @param speed trick speed (0 = normal)
+///
+static void VaapiSetTrickSpeed(VaapiDecoder * decoder, int speed)
+{
+ decoder->TrickSpeed = speed;
+ decoder->TrickCounter = 0;
+}
+
+///
+/// Sync decoder output to audio.
+///
+/// trick-speed show frame <n> times
+/// still-picture show frame until new frame arrives
+/// 60hz-mode repeat every 5th picture
+/// video>audio slow down video by duplicating frames
+/// video<audio speed up video by skipping frames
+/// soft-start show every second frame
+///
+/// @param decoder VDPAU hw decoder
+///
+static void VaapiSyncDecoder(VaapiDecoder * decoder)
{
int err;
int filled;
@@ -4345,70 +4419,132 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
int64_t video_clock;
err = 0;
- if (Video60HzMode && !(decoder->FramesDisplayed % 6)) {
- // FIXME: drop next frame?
- decoder->DupNextFrame++;
- } else if (!decoder->DupNextFrame) {
- VaapiAdvanceFrame();
- }
-
- VaapiDisplayFrame();
-
- //
- // audio/video sync
- //
audio_clock = AudioGetClock();
- video_clock = VideoGetClock();
+ video_clock = VaapiGetClock(decoder);
filled = atomic_read(&decoder->SurfacesFilled);
- // FIXME: audio not known assume 333ms delay
- decoder->StartCounter++;
- if (!VideoSoftStartSync && decoder->StartCounter < 60
+ // 60Hz: repeat every 5th field
+ if (Video60HzMode && !(decoder->FramesDisplayed % 6)) {
+ if (audio_clock == (int64_t) AV_NOPTS_VALUE
+ || video_clock == (int64_t) AV_NOPTS_VALUE) {
+ goto out;
+ }
+ // both clocks are known
+ if (audio_clock + VideoAudioDelay <= video_clock + 15 * 90) {
+ goto out;
+ }
+ // out of sync: audio before video
+ if (!decoder->TrickSpeed) {
+ goto skip_sync;
+ }
+ }
+ // TrickSpeed
+ if (decoder->TrickSpeed) {
+ if (decoder->TrickCounter--) {
+ goto out;
+ }
+ decoder->TrickCounter = decoder->TrickSpeed;
+ }
+ // at start of new video stream, soft or hard sync video to audio
+ if (!VideoSoftStartSync && decoder->FramesDisplayed < 60
&& (audio_clock == (int64_t) AV_NOPTS_VALUE
|| video_clock > audio_clock + VideoAudioDelay + 120 * 90)) {
- Debug(3, "video: initial slow down %d\n", decoder->StartCounter);
- decoder->DupNextFrame = 2;
- err = 1;
+ err =
+ VaapiMessage(3, "video: initial slow down video, frame %d\n",
+ decoder->FramesDisplayed);
+ goto out;
+ }
+
+ if (decoder->SyncCounter && decoder->SyncCounter--) {
+ goto skip_sync;
}
- if (decoder->DupNextFrame) {
- decoder->DupNextFrame--;
- ++decoder->FramesDuped;
- } else if (audio_clock != (int64_t) AV_NOPTS_VALUE
+ if (audio_clock != (int64_t) AV_NOPTS_VALUE
&& video_clock != (int64_t) AV_NOPTS_VALUE) {
// both clocks are known
- if (abs(video_clock - audio_clock) > 5000 * 90) {
- Debug(3, "video: pts difference too big\n");
- err = 1;
+ if (abs(video_clock - audio_clock + VideoAudioDelay) > 5000 * 90) {
+ err = VaapiMessage(3, "video: audio/video difference too big\n");
} else if (video_clock > audio_clock + VideoAudioDelay + 100 * 90) {
- Debug(3, "video: slow down video\n");
- err = 1;
- decoder->DupNextFrame += 2;
+ // FIXME: this quicker sync step, did not work with new code!
+ err = VaapiMessage(3, "video: slow down video, duping frame\n");
+ ++decoder->FramesDuped;
+ decoder->SyncCounter = 1;
+ goto out;
} else if (video_clock > audio_clock + VideoAudioDelay + 45 * 90) {
- Debug(3, "video: slow down video\n");
- err = 1;
- decoder->DupNextFrame++;
+ err = VaapiMessage(3, "video: slow down video, duping frame\n");
+ ++decoder->FramesDuped;
+ decoder->SyncCounter = 1;
+ goto out;
} else if (audio_clock + VideoAudioDelay > video_clock + 15 * 90
- && filled > 1) {
- Debug(3, "video: speed up video\n");
- err = 1;
- decoder->DropNextFrame = 1;
+ && filled > 1 + 2 * decoder->Interlaced) {
+ err = VaapiMessage(3, "video: speed up video, droping frame\n");
+ ++decoder->FramesDropped;
+ VaapiAdvanceDecoderFrame(decoder);
+ decoder->SyncCounter = 1;
}
}
+
+ skip_sync:
+ // check if next field is available
+ if (decoder->SurfaceField && filled <= 1) {
+ if (filled == 1) {
+ ++decoder->FramesDuped;
+ // FIXME: don't warn after stream start, don't warn during pause
+ err =
+ VaapiMessage(1,
+ _("video: decoder buffer empty, "
+ "duping frame (%d/%d) %d v-buf\n"), decoder->FramesDuped,
+ decoder->FrameCounter, VideoGetBuffers());
+ }
+ goto out;
+ }
+
+ VaapiAdvanceDecoderFrame(decoder);
+ out:
#if defined(DEBUG) || defined(AV_INFO)
// debug audio/video sync
if (err || !(decoder->FramesDisplayed % AV_INFO_TIME)) {
+ if (!err) {
+ VaapiMessage(0, NULL);
+ }
Info("video: %s%+5" PRId64 " %4" PRId64 " %3d/\\ms %3d v-buf\n",
Timestamp2String(video_clock),
abs((video_clock - audio_clock) / 90) <
8888 ? ((video_clock - audio_clock) / 90) : 8888,
AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers());
+ if (!(decoder->FramesDisplayed % (5 * 60 * 60))) {
+ VaapiPrintFrames(decoder);
+ }
}
#endif
}
///
+/// Sync a video frame.
+///
+static void VaapiSyncFrame(void)
+{
+ int i;
+
+ //
+ // Sync video decoder to audio
+ //
+ for (i = 0; i < VaapiDecoderN; ++i) {
+ VaapiSyncDecoder(VaapiDecoders[i]);
+ }
+}
+
+///
+/// Sync and display surface.
+///
+static void VaapiSyncDisplayFrame(void)
+{
+ VaapiDisplayFrame();
+ VaapiSyncFrame();
+}
+
+///
/// Sync and render a ffmpeg frame
///
/// @param decoder VA-API decoder
@@ -4420,22 +4556,10 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
{
#ifdef DEBUG
if (!atomic_read(&decoder->SurfacesFilled)) {
- Debug(3, "video: new stream frame %d\n", GetMsTicks() - VideoSwitch);
+ Debug(3, "video: new stream frame %dms\n", GetMsTicks() - VideoSwitch);
}
#endif
- if (decoder->DropNextFrame) { // drop frame requested
- // FIXME: interlace this drops two frames
- ++decoder->FramesDropped;
- Warning(_("video: dropping frame (%d/%d)\n"), decoder->FramesDropped,
- decoder->FrameCounter);
- if (!(decoder->FramesDisplayed % 300)) {
- VaapiPrintFrames(decoder);
- }
- decoder->DropNextFrame--;
- VideoSetPts(&decoder->PTS, decoder->Interlaced, frame);
- return;
- }
// if video output buffer is full, wait and display surface.
// loop for interlace
while (atomic_read(&decoder->SurfacesFilled) >= VIDEO_SURFACES_MAX - 1) {
@@ -4464,7 +4588,7 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
}
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
- VaapiSyncDisplayFrame(decoder);
+ VaapiSyncDisplayFrame();
}
VideoSetPts(&decoder->PTS, decoder->Interlaced, frame);
@@ -4475,27 +4599,6 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
}
///
-/// Get VA-API decoder video clock.
-///
-/// @param decoder VA-API decoder
-///
-static int64_t VaapiGetClock(const VaapiDecoder * decoder)
-{
- // pts is the timestamp of the latest decoded frame
- if (!decoder || decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
- return AV_NOPTS_VALUE;
- }
- // subtract buffered decoded frames
- if (decoder->Interlaced) {
- return decoder->PTS -
- 20 * 90 * (2 * atomic_read(&decoder->SurfacesFilled)
- - decoder->SurfaceField);
- }
- return decoder->PTS - 20 * 90 * (atomic_read(&decoder->SurfacesFilled) +
- 2);
-}
-
-///
/// Set VA-API background color.
///
/// @param rgba 32 bit RGBA color.
@@ -4560,7 +4663,7 @@ static void VaapiDisplayHandlerThread(void)
}
pthread_mutex_lock(&VideoLockMutex);
- VaapiSyncDisplayFrame(decoder);
+ VaapiSyncDisplayFrame();
pthread_mutex_unlock(&VideoLockMutex);
}
@@ -4805,6 +4908,10 @@ static const VideoModule VaapiModule = {
AVCodecContext *, const enum PixelFormat *))Vaapi_get_format,
.RenderFrame = (void (*const) (VideoHwDecoder *,
const AVCodecContext *, const AVFrame *))VaapiSyncRenderFrame,
+ .SetClock = (void (*const) (VideoHwDecoder *, int64_t))VaapiSetClock,
+ .GetClock = (int64_t(*const) (const VideoHwDecoder *))VaapiGetClock,
+ .SetTrickSpeed =
+ (void (*const) (const VideoHwDecoder *, int))VaapiSetTrickSpeed,
.GrabOutput = NULL,
.SetBackground = VaapiSetBackground,
.SetVideoMode = VaapiSetVideoMode,
@@ -4883,12 +4990,12 @@ typedef struct _vdpau_decoder_
atomic_t SurfacesFilled; ///< how many of the buffer is used
int SurfaceField; ///< current displayed field
- int DropNextFrame; ///< flag drop next frame
- int DupNextFrame; ///< flag duplicate next frame
+ int TrickSpeed; ///< current trick speed
+ int TrickCounter; ///< current trick speed counter
struct timespec FrameTime; ///< time of last display
int64_t PTS; ///< video PTS clock
- int StartCounter; ///< number of start frames
+ int SyncCounter; ///< counter to sync frames
int FramesDuped; ///< number of frames duplicated
int FramesMissed; ///< number of frames missed
int FramesDropped; ///< number of frames dropped
@@ -5014,18 +5121,40 @@ static void VdpauOsdInit(int, int); ///< forward definition
///
/// Reduce output.
///
-static void VdpauMessage(int level, const char *format, ...)
+/// @param level message level (Error, Warning, Info, Debug, ...)
+/// @param format printf format string (NULL to flush messages)
+/// @param ... printf arguments
+///
+/// @returns true, if message shown
+///
+static int VdpauMessage(int level, const char *format, ...)
{
if (SysLogLevel > level || DebugLevel > level) {
+ static const char *last_format;
+ static char buf[256];
va_list ap;
va_start(ap, format);
- vsyslog(LOG_ERR, format, ap);
+ if (format != last_format) { // don't repeat same message
+ last_format = format;
+ if (buf[0]) { // print last repeated message
+ syslog(LOG_ERR, "%s", buf);
+ buf[0] = '\0';
+ }
+
+ if (format) {
+ vsyslog(LOG_ERR, format, ap);
+ }
+ va_end(ap);
+ return 1;
+ }
+ vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
}
+ return 0;
}
-//----------------------------------------------------------------------------
+// Surfaces -------------------------------------------------------------
///
/// Create surfaces for VDPAU decoder.
@@ -5174,9 +5303,9 @@ static void VdpauReleaseSurface(VdpauDecoder * decoder, unsigned surface)
///
static void VdpauPrintFrames(const VdpauDecoder * decoder)
{
- Debug(3, "video/vdpau: %d missed, %d duped, %d dropped frames of %d\n",
+ Debug(3, "video/vdpau: %d missed, %d duped, %d dropped frames of %d,%d\n",
decoder->FramesMissed, decoder->FramesDuped, decoder->FramesDropped,
- decoder->FrameCounter);
+ decoder->FrameCounter, decoder->FramesDisplayed);
#ifndef DEBUG
(void)decoder;
#endif
@@ -5509,9 +5638,6 @@ static VdpauDecoder *VdpauNewHwDecoder(void)
for (i = 0; i < VIDEO_SURFACES_MAX; ++i) {
decoder->SurfacesRb[i] = VDP_INVALID_HANDLE;
}
- // we advance before display, to loose no surface, we set it before
- //decoder->SurfaceRead = VIDEO_SURFACES_MAX - 1;
- //decoder->SurfaceField = 1;
#ifdef DEBUG
if (VIDEO_SURFACES_MAX < 1 + 1 + 1 + 1) {
@@ -5582,11 +5708,10 @@ static void VdpauCleanup(VdpauDecoder * decoder)
}
decoder->SurfaceRead = 0;
decoder->SurfaceWrite = 0;
-
decoder->SurfaceField = 0;
- //decoder->FrameCounter = 0;
- decoder->StartCounter = 0;
+ decoder->FrameCounter = 0;
+ decoder->FramesDisplayed = 0;
decoder->PTS = AV_NOPTS_VALUE;
VideoDeltaPTS = 0;
}
@@ -5610,7 +5735,6 @@ static void VdpauDelHwDecoder(VdpauDecoder * decoder)
}
VdpauCleanup(decoder);
-
VdpauPrintFrames(decoder);
free(decoder);
@@ -6300,7 +6424,7 @@ static enum PixelFormat Vdpau_get_format(VdpauDecoder * decoder,
VdpStatus status;
int max_refs;
- Debug(3, "video: new stream format %d\n", GetMsTicks() - VideoSwitch);
+ Debug(3, "video: new stream format %dms\n", GetMsTicks() - VideoSwitch);
VdpauCleanup(decoder);
if (!VideoHardwareDecoder || (video_ctx->codec_id == CODEC_ID_MPEG2VIDEO
@@ -6444,6 +6568,8 @@ static enum PixelFormat Vdpau_get_format(VdpauDecoder * decoder,
#ifdef USE_GRAB
+#ifdef DEBUG // function not used
+
///
/// Grab video surface.
///
@@ -6509,6 +6635,8 @@ static void VdpauGrabVideoSurface(VdpauDecoder * decoder)
free(base);
}
+#endif
+
///
/// Grab output surface.
///
@@ -7261,7 +7389,7 @@ static void VdpauBlackSurface(VdpauDecoder * decoder)
source_rect.x1 = 0;
source_rect.y1 = 0;
- if ( 0 ) {
+ if (0) {
// FIXME: wrong for radio channels
output_rect.x0 = decoder->OutputX; // video output (scale)
output_rect.y0 = decoder->OutputY;
@@ -7274,8 +7402,9 @@ static void VdpauBlackSurface(VdpauDecoder * decoder)
output_rect.y1 = VideoWindowHeight;
}
- status = VdpauOutputSurfaceRenderOutputSurface(
- VdpauSurfacesRb[VdpauSurfaceIndex], &output_rect,
+ status =
+ VdpauOutputSurfaceRenderOutputSurface(VdpauSurfacesRb
+ [VdpauSurfaceIndex], &output_rect,
VdpauOsdOutputSurface[!VdpauOsdSurfaceIndex], &source_rect, NULL, NULL,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
if (status != VDP_STATUS_OK) {
@@ -7285,53 +7414,37 @@ static void VdpauBlackSurface(VdpauDecoder * decoder)
}
///
-/// Advance displayed frame.
+/// Advance displayed frame of decoder.
///
-static void VdpauAdvanceFrame(void)
+/// @param decoder VDPAU hw decoder
+///
+static void VdpauAdvanceDecoderFrame(VdpauDecoder * decoder)
{
- int i;
-
- for (i = 0; i < VdpauDecoderN; ++i) {
+ // next surface, if complete frame is displayed (1 -> 0)
+ if (decoder->SurfaceField) {
int filled;
- VdpauDecoder *decoder;
-
- decoder = VdpauDecoders[i];
- // next field
- if (decoder->Interlaced) {
- decoder->SurfaceField ^= 1;
- }
- // next surface, if complete frame is displayed
- if (!decoder->SurfaceField) {
- // check decoder, if new surface is available
- // need 2 frames for progressive
- // need 4 frames for interlaced
- filled = atomic_read(&decoder->SurfacesFilled);
- if (filled <= 1 + 2 * decoder->Interlaced) {
- static int last_warned_frame;
-
- // keep use of last surface
- ++decoder->FramesDuped;
- decoder->DropNextFrame = 0;
- // FIXME: don't warn after stream start, don't warn during pause
- if (last_warned_frame != decoder->FrameCounter) {
- Warning(_
- ("video: display buffer empty, duping frame (%d/%d) %d\n"),
- decoder->FramesDuped, decoder->FrameCounter,
- VideoGetBuffers());
- }
- last_warned_frame = decoder->FrameCounter;
- if (!(decoder->FramesDisplayed % 300)) {
- VdpauPrintFrames(decoder);
- }
- decoder->SurfaceField = decoder->Interlaced;
- } else {
- decoder->SurfaceRead = (decoder->SurfaceRead + 1)
- % VIDEO_SURFACES_MAX;
- atomic_dec(&decoder->SurfacesFilled);
- }
+ // FIXME: this should check the caller
+ // check decoder, if new surface is available
+ // need 2 frames for progressive
+ // need 4 frames for interlaced
+ filled = atomic_read(&decoder->SurfacesFilled);
+ if (filled <= 1 + 2 * decoder->Interlaced) {
+ // keep use of last surface
+ ++decoder->FramesDuped;
+ // 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());
+ return;
}
+ decoder->SurfaceRead = (decoder->SurfaceRead + 1) % VIDEO_SURFACES_MAX;
+ atomic_dec(&decoder->SurfacesFilled);
+ decoder->SurfaceField = !decoder->Interlaced;
+ return;
}
+ // next field
+ decoder->SurfaceField = 1;
}
///
@@ -7345,12 +7458,12 @@ static void VdpauDisplayFrame(void)
int i;
if (VideoSurfaceModesChanged) { // handle changed modes
+ VideoSurfaceModesChanged = 0;
for (i = 0; i < VdpauDecoderN; ++i) {
if (VdpauDecoders[i]->VideoMixer != VDP_INVALID_HANDLE) {
VdpauMixerSetup(VdpauDecoders[i]);
}
}
- VideoSurfaceModesChanged = 0;
}
//
// wait for surface visible (blocks max ~5ms)
@@ -7365,17 +7478,15 @@ static void VdpauDisplayFrame(void)
// check if surface was displayed for more than 1 frame
// FIXME: 21 only correct for 50Hz
if (last_time && first_time > last_time + 21 * 1000 * 1000) {
+ // FIXME: ignore still-frame, trick-speed
Debug(3, "video/vdpau: %ld display time %ld\n", first_time / 1000,
(first_time - last_time) / 1000);
// FIXME: can be more than 1 frame long shown
for (i = 0; i < VdpauDecoderN; ++i) {
VdpauDecoders[i]->FramesMissed++;
- Warning(_("video: missed frame (%d/%d)\n"),
+ VdpauMessage(2, _("video/vdpau: missed frame (%d/%d)\n"),
VdpauDecoders[i]->FramesMissed,
VdpauDecoders[i]->FrameCounter);
- if (!(VdpauDecoders[i]->FramesDisplayed % 300)) {
- VdpauPrintFrames(VdpauDecoders[i]);
- }
}
}
last_time = first_time;
@@ -7395,6 +7506,7 @@ static void VdpauDisplayFrame(void)
if (filled < 1 + 2 * decoder->Interlaced) {
// FIXME: rewrite MixVideo to support less surfaces
VdpauBlackSurface(decoder);
+ VdpauMessage(3, "video/vdpau: black surface displayed\n");
continue;
}
@@ -7419,6 +7531,7 @@ static void VdpauDisplayFrame(void)
}
for (i = 0; i < VdpauDecoderN; ++i) {
+ // remember time of last shown surface
clock_gettime(CLOCK_REALTIME, &VdpauDecoders[i]->FrameTime);
}
@@ -7428,11 +7541,72 @@ static void VdpauDisplayFrame(void)
}
///
-/// Sync and display surface.
+/// Set VDPAU decoder video clock.
+///
+/// @param decoder VDPAU hardware decoder
+/// @param pts audio presentation timestamp
+///
+void VdpauSetClock(VdpauDecoder * decoder, int64_t pts)
+{
+ decoder->PTS = pts;
+}
+
+///
+/// Get VDPAU decoder video clock.
///
/// @param decoder VDPAU hw decoder
///
-static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
+/// FIXME: 20 wrong for 60hz dvb streams
+///
+static int64_t VdpauGetClock(const VdpauDecoder * decoder)
+{
+ // pts is the timestamp of the latest decoded frame
+ if (decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
+ return AV_NOPTS_VALUE;
+ }
+ // subtract buffered decoded frames
+ if (decoder->Interlaced) {
+ /*
+ Info("video: %s =pts field%d #%d\n",
+ Timestamp2String(decoder->PTS),
+ decoder->SurfaceField,
+ atomic_read(&decoder->SurfacesFilled));
+ */
+ // - 2 fields are future, + 2 in driver queue
+ return decoder->PTS -
+ 20 * 90 * (2 * atomic_read(&decoder->SurfacesFilled)
+ - decoder->SurfaceField - 2 + 2);
+ }
+ // + 2 in driver queue
+ return decoder->PTS - 20 * 90 * (atomic_read(&decoder->SurfacesFilled) +
+ 2);
+}
+
+///
+/// Set trick play speed.
+///
+/// @param decoder VDPAU decoder
+/// @param speed trick speed (0 = normal)
+///
+static void VdpauSetTrickSpeed(VdpauDecoder * decoder, int speed)
+{
+ decoder->TrickSpeed = speed;
+ decoder->TrickCounter = speed;
+}
+
+///
+/// Sync decoder output to audio.
+///
+/// trick-speed show frame <n> times
+/// still-picture show frame until new frame arrives
+/// 60hz-mode repeat every 5th picture
+/// video>audio slow down video by duplicating frames
+/// video<audio speed up video by skipping frames
+/// soft-start show every second frame
+///
+/// @param decoder VDPAU hw decoder
+///
+static void VdpauSyncDecoder(VdpauDecoder * decoder)
{
int err;
int filled;
@@ -7440,70 +7614,132 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
int64_t video_clock;
err = 0;
- if (Video60HzMode && !(decoder->FramesDisplayed % 6)) {
- // FIXME: drop next frame?
- decoder->DupNextFrame++;
- } else if (!decoder->DupNextFrame) {
- VdpauAdvanceFrame();
- }
-
- VdpauDisplayFrame();
-
- //
- // audio/video sync
- //
audio_clock = AudioGetClock();
- video_clock = VideoGetClock();
+ video_clock = VdpauGetClock(decoder);
filled = atomic_read(&decoder->SurfacesFilled);
- // FIXME: audio not known assume 333ms delay
- decoder->StartCounter++;
- if (!VideoSoftStartSync && decoder->StartCounter < 60
+ // 60Hz: repeat every 5th field
+ if (Video60HzMode && !(decoder->FramesDisplayed % 6)) {
+ if (audio_clock == (int64_t) AV_NOPTS_VALUE
+ || video_clock == (int64_t) AV_NOPTS_VALUE) {
+ goto out;
+ }
+ // both clocks are known
+ if (audio_clock + VideoAudioDelay <= video_clock + 15 * 90) {
+ goto out;
+ }
+ // out of sync: audio before video
+ if (!decoder->TrickSpeed) {
+ goto skip_sync;
+ }
+ }
+ // TrickSpeed
+ if (decoder->TrickSpeed) {
+ if (decoder->TrickCounter--) {
+ goto out;
+ }
+ decoder->TrickCounter = decoder->TrickSpeed;
+ }
+ // at start of new video stream, soft or hard sync video to audio
+ if (!VideoSoftStartSync && decoder->FramesDisplayed < 60
&& (audio_clock == (int64_t) AV_NOPTS_VALUE
|| video_clock > audio_clock + VideoAudioDelay + 120 * 90)) {
- Debug(3, "video: initial slow down %d\n", decoder->StartCounter);
- err = 1;
- decoder->DupNextFrame = 2;
+ err =
+ VdpauMessage(3, "video: initial slow down video, frame %d\n",
+ decoder->FramesDisplayed);
+ goto out;
+ }
+
+ if (decoder->SyncCounter && decoder->SyncCounter--) {
+ goto skip_sync;
}
- if (decoder->DupNextFrame) {
- decoder->DupNextFrame--;
- ++decoder->FramesDuped;
- } else if (audio_clock != (int64_t) AV_NOPTS_VALUE
+ if (audio_clock != (int64_t) AV_NOPTS_VALUE
&& video_clock != (int64_t) AV_NOPTS_VALUE) {
// both clocks are known
- if (abs(video_clock - audio_clock) > 5000 * 90) {
- Debug(3, "video: pts difference too big\n");
- err = 1;
+ if (abs(video_clock - audio_clock + VideoAudioDelay) > 5000 * 90) {
+ err = VdpauMessage(3, "video: audio/video difference too big\n");
} else if (video_clock > audio_clock + VideoAudioDelay + 100 * 90) {
- Debug(3, "video: slow down video\n");
- err = 1;
- decoder->DupNextFrame += 2;
+ // FIXME: this quicker sync step, did not work with new code!
+ err = VdpauMessage(3, "video: slow down video, duping frame\n");
+ ++decoder->FramesDuped;
+ decoder->SyncCounter = 1;
+ goto out;
} else if (video_clock > audio_clock + VideoAudioDelay + 45 * 90) {
- Debug(3, "video: slow down video\n");
- err = 1;
- decoder->DupNextFrame++;
+ err = VdpauMessage(3, "video: slow down video, duping frame\n");
+ ++decoder->FramesDuped;
+ decoder->SyncCounter = 1;
+ goto out;
} else if (audio_clock + VideoAudioDelay > video_clock + 15 * 90
&& filled > 1 + 2 * decoder->Interlaced) {
- Debug(3, "video: speed up video\n");
- err = 1;
- decoder->DropNextFrame = 1;
+ err = VdpauMessage(3, "video: speed up video, droping frame\n");
+ ++decoder->FramesDropped;
+ VdpauAdvanceDecoderFrame(decoder);
+ decoder->SyncCounter = 1;
+ }
+ }
+
+ skip_sync:
+ // check if next field is available
+ if (decoder->SurfaceField && filled <= 1 + 2 * decoder->Interlaced) {
+ if (filled == 1 + 2 * decoder->Interlaced) {
+ ++decoder->FramesDuped;
+ // FIXME: don't warn after stream start, don't warn during pause
+ err =
+ VdpauMessage(1,
+ _("video: decoder buffer empty, "
+ "duping frame (%d/%d) %d v-buf\n"), decoder->FramesDuped,
+ decoder->FrameCounter, VideoGetBuffers());
}
+ goto out;
}
+
+ VdpauAdvanceDecoderFrame(decoder);
+ out:
#if defined(DEBUG) || defined(AV_INFO)
// debug audio/video sync
if (err || !(decoder->FramesDisplayed % AV_INFO_TIME)) {
+ if (!err) {
+ VdpauMessage(0, NULL);
+ }
Info("video: %s%+5" PRId64 " %4" PRId64 " %3d/\\ms %3d v-buf\n",
Timestamp2String(video_clock),
abs((video_clock - audio_clock) / 90) <
8888 ? ((video_clock - audio_clock) / 90) : 8888,
AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers());
+ if (!(decoder->FramesDisplayed % (5 * 60 * 60))) {
+ VdpauPrintFrames(decoder);
+ }
}
#endif
}
///
+/// Sync a video frame.
+///
+static void VdpauSyncFrame(void)
+{
+ int i;
+
+ //
+ // Sync video decoder to audio
+ //
+ for (i = 0; i < VdpauDecoderN; ++i) {
+ VdpauSyncDecoder(VdpauDecoders[i]);
+ }
+}
+
+///
+/// Sync and display surface.
+///
+static void VdpauSyncDisplayFrame(void)
+{
+ VdpauDisplayFrame();
+ VdpauSyncFrame();
+}
+
+///
/// Sync and render a ffmpeg frame
///
/// @param decoder VDPAU hw decoder
@@ -7520,28 +7756,17 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder,
}
#ifdef DEBUG
if (!atomic_read(&decoder->SurfacesFilled)) {
- Debug(3, "video: new stream frame %d\n", GetMsTicks() - VideoSwitch);
+ Debug(3, "video: new stream frame %dms\n", GetMsTicks() - VideoSwitch);
}
#endif
- if (decoder->DropNextFrame) { // drop frame requested
- // FIXME: interlace this drops two frames
- ++decoder->FramesDropped;
- Warning(_("video: dropping frame (%d/%d)\n"), decoder->FramesDropped,
- decoder->FrameCounter);
- if (!(decoder->FramesDisplayed % 300)) {
- VdpauPrintFrames(decoder);
- }
- decoder->DropNextFrame--;
- VideoSetPts(&decoder->PTS, decoder->Interlaced, frame);
- return;
- }
if (VdpauPreemption) { // display preempted
VideoSetPts(&decoder->PTS, decoder->Interlaced, frame);
return;
}
// if video output buffer is full, wait and display surface.
// loop for interlace
+ // FIXME: wrong for multiple streams
while (atomic_read(&decoder->SurfacesFilled) >= VIDEO_SURFACES_MAX) {
struct timespec abstime;
@@ -7572,7 +7797,7 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder,
if (VdpauPreemption) { // display become preempted
return;
}
- VdpauSyncDisplayFrame(decoder);
+ VdpauSyncDisplayFrame();
}
VideoSetPts(&decoder->PTS, decoder->Interlaced, frame);
@@ -7580,27 +7805,6 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder,
}
///
-/// Get VDPAU decoder video clock.
-///
-/// @param decoder VDPAU hw decoder
-///
-static int64_t VdpauGetClock(const VdpauDecoder * decoder)
-{
- // pts is the timestamp of the latest decoded frame
- if (!decoder || decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
- return AV_NOPTS_VALUE;
- }
- // subtract buffered decoded frames
- if (decoder->Interlaced) {
- return decoder->PTS -
- 20 * 90 * (2 * atomic_read(&decoder->SurfacesFilled)
- - decoder->SurfaceField);
- }
- return decoder->PTS - 20 * 90 * (atomic_read(&decoder->SurfacesFilled) +
- 2);
-}
-
-///
/// Recover from preemption.
///
static int VdpauPreemptionRecover(void)
@@ -7723,7 +7927,7 @@ static void VdpauDisplayHandlerThread(void)
}
pthread_mutex_lock(&VideoLockMutex);
- VdpauSyncDisplayFrame(decoder);
+ VdpauSyncDisplayFrame();
pthread_mutex_unlock(&VideoLockMutex);
}
@@ -8001,6 +8205,10 @@ static const VideoModule VdpauModule = {
AVCodecContext *, const enum PixelFormat *))Vdpau_get_format,
.RenderFrame = (void (*const) (VideoHwDecoder *,
const AVCodecContext *, const AVFrame *))VdpauSyncRenderFrame,
+ .SetClock = (void (*const) (VideoHwDecoder *, int64_t))VdpauSetClock,
+ .GetClock = (int64_t(*const) (const VideoHwDecoder *))VdpauGetClock,
+ .SetTrickSpeed =
+ (void (*const) (const VideoHwDecoder *, int))VdpauSetTrickSpeed,
.GrabOutput = VdpauGrabOutputSurface,
.SetBackground = VdpauSetBackground,
.SetVideoMode = VdpauSetVideoMode,
@@ -8148,6 +8356,10 @@ static const VideoModule NoopModule = {
AVCodecContext *, const enum PixelFormat *))Noop_get_format,
.RenderFrame = (void (*const) (VideoHwDecoder *,
const AVCodecContext *, const AVFrame *))NoopSyncRenderFrame,
+ .SetClock = (void (*const) (VideoHwDecoder *, int64_t))NoopSetClock,
+ .GetClock = (int64_t(*const) (const VideoHwDecoder *))NoopGetClock,
+ .SetTrickSpeed =
+ (void (*const) (const VideoHwDecoder *, int))NoopSetTrickSpeed,
.GrabOutput = NoopGrabOutputSurface,
#endif
.SetBackground = NoopSetBackground,
@@ -8692,84 +8904,93 @@ VideoHwDecoder *VideoNewHwDecoder(void)
///
/// Destroy a video hw decoder.
///
-/// @param decoder video hardware decoder
+/// @param hw_decoder video hardware decoder
///
-void VideoDelHwDecoder(VideoHwDecoder * decoder)
+void VideoDelHwDecoder(VideoHwDecoder * hw_decoder)
{
- if (decoder) {
- VideoUsedModule->DelHwDecoder(decoder);
+ if (hw_decoder) {
+ VideoUsedModule->DelHwDecoder(hw_decoder);
}
}
///
/// Get a free hardware decoder surface.
///
-/// @param decoder video hardware decoder
+/// @param hw_decoder video hardware decoder
///
/// @returns the oldest free surface or invalid surface
///
-unsigned VideoGetSurface(VideoHwDecoder * decoder)
+unsigned VideoGetSurface(VideoHwDecoder * hw_decoder)
{
- return VideoUsedModule->GetSurface(decoder);
+ return VideoUsedModule->GetSurface(hw_decoder);
}
///
/// Release a hardware decoder surface.
///
-/// @param decoder VDPAU video hardware decoder
-/// @param surface surface no longer used
+/// @param hw_decoder video hardware decoder
+/// @param surface surface no longer used
///
-void VideoReleaseSurface(VideoHwDecoder * decoder, unsigned surface)
+void VideoReleaseSurface(VideoHwDecoder * hw_decoder, unsigned surface)
{
// FIXME: must be guarded against calls, after VideoExit
- VideoUsedModule->ReleaseSurface(decoder, surface);
+ VideoUsedModule->ReleaseSurface(hw_decoder, surface);
}
///
/// Callback to negotiate the PixelFormat.
///
-/// @param fmt is the list of formats which are supported by the codec,
-/// it is terminated by -1 as 0 is a valid format, the
-/// formats are ordered by quality.
+/// @param hw_decoder video hardware decoder
+/// @param fmt is the list of formats which are supported by
+/// the codec, it is terminated by -1 as 0 is a
+/// valid format, the formats are ordered by
+/// quality.
///
-enum PixelFormat Video_get_format(VideoHwDecoder * decoder,
+enum PixelFormat Video_get_format(VideoHwDecoder * hw_decoder,
AVCodecContext * video_ctx, const enum PixelFormat *fmt)
{
+ int ms_delay;
+
+ // FIXME: use frame time
+ ms_delay = (1000 * video_ctx->time_base.num * video_ctx->ticks_per_frame)
+ / video_ctx->time_base.den;
+
+ Debug(3, "video: ready %s %dms/frame\n",
+ Timestamp2String(VideoGetClock(hw_decoder)), ms_delay);
AudioVideoReady();
- return VideoUsedModule->get_format(decoder, video_ctx, fmt);
- //return fmt[0];
+ return VideoUsedModule->get_format(hw_decoder, video_ctx, fmt);
}
///
/// Display a ffmpeg frame
///
-/// @param decoder VDPAU video hardware decoder
+/// @param hw_decoder video hardware decoder
/// @param video_ctx ffmpeg video codec context
/// @param frame frame to display
///
-void VideoRenderFrame(VideoHwDecoder * decoder,
+void VideoRenderFrame(VideoHwDecoder * hw_decoder,
const AVCodecContext * video_ctx, const AVFrame * frame)
{
if (frame->repeat_pict && !VideoIgnoreRepeatPict) {
Warning(_("video: repeated pict %d found, but not handled\n"),
frame->repeat_pict);
}
- VideoUsedModule->RenderFrame(decoder, video_ctx, frame);
+ VideoUsedModule->RenderFrame(hw_decoder, video_ctx, frame);
}
///
/// Get VA-API ffmpeg context
///
-/// @param decoder VA-API decoder
+/// @param hw_decoder video hardware decoder (must be VA-API)
///
-struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder * decoder)
+struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder * hw_decoder)
{
#ifdef USE_VAAPI
if (VideoUsedModule == &VaapiModule) {
- return decoder->Vaapi.VaapiContext;
+ return hw_decoder->Vaapi.VaapiContext;
}
#endif
- (void)decoder;
+ (void)hw_decoder;
Error(_("video/vaapi: get vaapi context, without vaapi enabled\n"));
return NULL;
}
@@ -8779,8 +9000,8 @@ struct vaapi_context *VideoGetVaapiContext(VideoHwDecoder * decoder)
///
/// Draw ffmpeg vdpau render state.
///
-/// @param decoder video hw decoder
-/// @param vrs vdpau render state
+/// @param hw_decoder video hardware decoder
+/// @param vrs vdpau render state
///
void VideoDrawRenderState(VideoHwDecoder * hw_decoder,
struct vdpau_render_state *vrs)
@@ -8832,28 +9053,47 @@ void VideoDrawRenderState(VideoHwDecoder * hw_decoder,
#endif
///
-/// Get video clock.
+/// Set video clock.
///
-/// @note this isn't monoton, decoding reorders frames,
-/// setter keeps it monotonic
-/// @todo we have multiple clocks, for multiple stream
+/// @param hw_decoder video hardware decoder
+/// @param pts audio presentation timestamp
///
-int64_t VideoGetClock(void)
+void VideoSetClock(VideoHwDecoder * hw_decoder, int64_t pts)
{
-#ifdef USE_VDPAU
- if (VideoUsedModule == &VdpauModule) {
- return VdpauGetClock(VdpauDecoders[0]);
+ Debug(3, "video: set clock %s\n", Timestamp2String(pts));
+ if (hw_decoder) {
+ VideoUsedModule->SetClock(hw_decoder, pts);
}
-#endif
-#ifdef USE_VAAPI
- if (VideoUsedModule == &VaapiModule) {
- return VaapiGetClock(VaapiDecoders[0]);
+}
+
+///
+/// Get video clock.
+///
+/// @param hw_decoder video hardware decoder
+///
+/// @note this isn't monoton, decoding reorders frames, setter keeps it
+/// monotonic
+///
+int64_t VideoGetClock(const VideoHwDecoder * hw_decoder)
+{
+ if (hw_decoder) {
+ return VideoUsedModule->GetClock(hw_decoder);
}
-#endif
return AV_NOPTS_VALUE;
}
///
+/// Set trick play speed.
+///
+/// @param hw_decoder video hardware decoder
+/// @param speed trick speed (0 = normal)
+///
+void VideoSetTrickSpeed(VideoHwDecoder * hw_decoder, int speed)
+{
+ return VideoUsedModule->SetTrickSpeed(hw_decoder, speed);
+}
+
+///
/// Grab full screen image.
///
/// @param size[out] size of allocated image
@@ -9647,11 +9887,14 @@ void VideoInit(const char *display_name)
|| (!VideoDevice && VideoModules[i]->Enabled)) {
if (VideoModules[i]->Init(display_name)) {
VideoUsedModule = VideoModules[i];
- break;
+ goto found;
}
}
}
+ Error(_("video: '%s' output module isn't supported\n"), VideoDevice);
+ VideoUsedModule = &NoopModule;
+ found:
// FIXME: make it configurable from gui
VideoHardwareDecoder = -1;
if (getenv("NO_MPEG_HW")) {
diff --git a/video.h b/video.h
index c370ee0..c196c64 100644
--- a/video.h
+++ b/video.h
@@ -146,7 +146,14 @@ extern void VideoOsdDrawARGB(int, int, int, int, const uint8_t *);
/// Get OSD size.
extern void VideoGetOsdSize(int *, int *);
-extern int64_t VideoGetClock(void); ///< Get video clock.
+ /// Set video clock.
+extern void VideoSetClock(VideoHwDecoder *, int64_t);
+
+ /// Get video clock.
+extern int64_t VideoGetClock(const VideoHwDecoder *);
+
+ /// Set trick play speed.
+extern void VideoSetTrickSpeed(VideoHwDecoder *, int);
/// Grab screen.
extern uint8_t *VideoGrab(int *, int *, int *, int);