diff options
author | Thomas Reufer <thomas@reufer.ch> | 2016-03-20 21:42:51 +0100 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2016-03-20 21:42:51 +0100 |
commit | b0fe61c70c862afcb5defb2cede3dac32587f475 (patch) | |
tree | cebb2b673d55f020b6143bd2c8e7fac6385a4f3a | |
parent | 10cf387b03d2431e7e152aee08fef0b91c9b7de1 (diff) | |
download | vdr-plugin-amlhddevice-b0fe61c70c862afcb5defb2cede3dac32587f475.tar.gz vdr-plugin-amlhddevice-b0fe61c70c862afcb5defb2cede3dac32587f475.tar.bz2 |
-rw-r--r-- | HISTORY | 1 | ||||
-rw-r--r-- | amldevice.c | 476 | ||||
-rw-r--r-- | amldevice.h | 67 | ||||
-rw-r--r-- | amlhddevice.c | 14 |
4 files changed, 404 insertions, 154 deletions
@@ -2,6 +2,7 @@ VDR Plugin 'amlhddevice' Revision History ----------------------------------------- - fixed: + - replay and fast trick speeds - A/V sync 2015-08-23: Version 0.0.1 diff --git a/amldevice.c b/amldevice.c index c4ab07e..0ac2ab9 100644 --- a/amldevice.c +++ b/amldevice.c @@ -5,11 +5,6 @@ */ #include <stdio.h> - -extern "C" { -#include <codec.h> -} - #include <libsi/si.h> #include "tools.h" @@ -17,142 +12,109 @@ extern "C" { #define INVALID_PTS (-1) -/* ------------------------------------------------------------------------- */ +#define TRICKMODE_NONE (0) +#define TRICKMODE_I (1) +#define TRICKMODE_FFFB (2) -class cAmlDecoder -{ -public: +#define EXTERNAL_PTS (1) +#define SYNC_OUTSIDE (2) - cAmlDecoder() : - m_initialized(false) - { - Reset(); - } +#define PTS_FREQ 90000 +#define PTS_FREQ_MS PTS_FREQ / 1000 +#define AV_SYNC_THRESH PTS_FREQ * 30 - virtual ~cAmlDecoder() - { - Stop(); - } +/* ------------------------------------------------------------------------- */ - virtual void Reset(void) - { - Stop(); - memset(&m_param, 0, sizeof(codec_para_t)); - m_param.stream_type = STREAM_TYPE_TS; - } +const int cScheduler::s_speeds[eNumSpeeds] = { + 0, PTS_FREQ_MS / 12, PTS_FREQ_MS / 8, PTS_FREQ_MS / 4, + PTS_FREQ_MS, PTS_FREQ_MS * 4, PTS_FREQ_MS * 8, PTS_FREQ_MS * 12 +}; - virtual int WriteTs(const uchar *data, int length) - { - int ret = length; +cScheduler::cScheduler() : + m_pts(INVALID_PTS), + m_timestamp(), + m_speed(eNormal), + m_forward(true) +{ } - while (length) - { - int written = codec_write(&m_param, (void *)data, length); - if (written > 0) - { - length -= written; - data += written; - } - else - { - ELOG("failed to write codec data, ret=0x%x", written); - ret = 0; - break; - } - } - return ret; - } +void cScheduler::SetSpeed(int speed, bool forward) +{ + m_speed = - void SetAudioPid(int pid, int streamType) - { - Stop(); - - m_param.has_audio = 1; - m_param.audio_pid = pid; - m_param.audio_type = - streamType == 0x03 ? AFORMAT_MPEG : - streamType == 0x04 ? AFORMAT_MPEG : - streamType == SI::AC3DescriptorTag ? AFORMAT_AC3 : - streamType == SI::EnhancedAC3DescriptorTag ? AFORMAT_EAC3 : - streamType == 0x0f ? AFORMAT_AAC : - streamType == 0x11 ? AFORMAT_AAC : AFORMAT_UNKNOWN; - - m_param.audio_channels = 0; - m_param.audio_samplerate = 0; - m_param.audio_info.channels = 0; - m_param.audio_info.sample_rate = 0; - - Start(); - } + // slow forward + speed == 8 ? eSlowest : + speed == 4 ? eSlower : + speed == 2 ? eSlow : - void SetVideoPid(int pid, int streamType) - { - Stop(); + // fast for-/backward + speed == 6 ? eFast : + speed == 3 ? eFaster : + speed == 1 ? eFastest : - m_param.has_video = 1; - m_param.video_pid = pid; - m_param.video_type = - streamType == 0x01 ? VFORMAT_MPEG12 : - streamType == 0x02 ? VFORMAT_MPEG12 : - streamType == 0x1b ? VFORMAT_H264 : VFORMAT_UNKNOWN; + // slow backward + speed == 63 ? eSlowest : + speed == 48 ? eSlower : + speed == 24 ? eSlow : eNormal; - m_param.am_sysinfo.format = m_param.video_type == VFORMAT_H264 ? - VIDEO_DEC_FORMAT_H264 : VIDEO_DEC_FORMAT_UNKNOW; + m_forward = forward; +} - Start(); - } +void cScheduler::Reset() +{ + m_pts = INVALID_PTS; + m_speed = eNormal; +} -protected: +bool cScheduler::Check(int64_t pts) +{ + // no scheduling + if (m_speed == eNormal || pts == INVALID_PTS) + return true; - virtual void Stop(void) + // first packet + if (m_pts == INVALID_PTS) { - if (!m_initialized || (!m_param.has_audio && !m_param.has_video)) - return; - - if (codec_close(&m_param) != CODEC_ERROR_NONE) - ELOG("failed to close codec!"); - else - m_initialized = false; + m_timestamp.Set(0); + m_pts = pts; + return true; } - virtual void Start(void) + if ((int64_t)m_timestamp.Elapsed() * s_speeds[m_speed] > + (m_forward ? PtsDiff(m_pts, pts) : PtsDiff(pts, m_pts))) { - if (m_initialized || (!m_param.has_audio && !m_param.has_video)) - return; - - cSysFs::Write("/sys/class/tsdemux/stb_source", 2); - cSysFs::Write("/sys/class/tsync/pts_pcrscr", "0x0"); - - if (codec_init(&m_param) != CODEC_ERROR_NONE) - ELOG("failed to init codec!"); - else - { - m_initialized = true; - cSysFs::Write("/sys/class/tsync/enable", - m_param.has_audio && m_param.has_video ? 1 : 0); - } + m_pts = pts; + m_timestamp.Set(0); + return true; } + return false; +} - codec_para_t m_param; - bool m_initialized; -}; +int64_t cScheduler::GetPts(void) +{ + return m_pts; +} /* ------------------------------------------------------------------------- */ cAmlDevice::cAmlDevice(void (*onPrimaryDevice)(void)) : cDevice(), m_onPrimaryDevice(onPrimaryDevice), - m_decoder(new cAmlDecoder()), - m_audioPid(0), - m_videoPid(0) + m_audioId(0), + m_trickMode(false), + m_scheduler() { + memset(&m_videoCodec, 0, sizeof(codec_para_t)); + memset(&m_audioCodec, 0, sizeof(codec_para_t)); + + m_videoCodec.stream_type = STREAM_TYPE_ES_VIDEO; + m_audioCodec.stream_type = STREAM_TYPE_ES_AUDIO; + + codec_audio_basic_init(); } cAmlDevice::~cAmlDevice() { DeInit(); - - delete m_decoder; } int cAmlDevice::Init(void) @@ -180,8 +142,6 @@ void cAmlDevice::MakePrimaryDevice(bool On) if (On && m_onPrimaryDevice) m_onPrimaryDevice(); cDevice::MakePrimaryDevice(On); - - codec_audio_basic_init(); } void cAmlDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) @@ -191,6 +151,12 @@ void cAmlDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) PixelAspect = (double)Width / Height; } +int64_t cAmlDevice::GetSTC(void) +{ + return m_trickMode ? m_scheduler.GetPts() : (m_audioCodec.has_audio ? + codec_get_apts(&m_audioCodec) : codec_get_vpts(&m_videoCodec)); +} + bool cAmlDevice::SetPlayMode(ePlayMode PlayMode) { DBG("%s(%s)", __FUNCTION__, @@ -203,15 +169,15 @@ bool cAmlDevice::SetPlayMode(ePlayMode PlayMode) switch (PlayMode) { case pmNone: - m_decoder->Reset(); - m_audioPid = 0; - m_videoPid = 0; + cSysFs::Write("/sys/class/video/blackout_policy", 1); + Clear(); break; case pmAudioVideo: case pmAudioOnly: case pmAudioOnlyBlack: case pmVideoOnly: + cSysFs::Write("/sys/class/video/blackout_policy", 0); break; default: @@ -221,50 +187,275 @@ bool cAmlDevice::SetPlayMode(ePlayMode PlayMode) return true; } -int cAmlDevice::PlayTsVideo(const uchar *Data, int Length) +int cAmlDevice::PlayVideo(const uchar *Data, int Length) +{ + int ret = Length; + int payloadLen = Length - PesPayloadOffset(Data); + const uchar *payload = Data + PesPayloadOffset(Data); + + if (!m_videoCodec.has_video) + { + int streamType = PatPmtParser()->Vtype(); + + m_videoCodec.has_video = 1; + m_videoCodec.am_sysinfo.param = m_trickMode ? 0 : (void *)(EXTERNAL_PTS); + m_videoCodec.video_type = + streamType == 0x01 ? VFORMAT_MPEG12 : + streamType == 0x02 ? VFORMAT_MPEG12 : + streamType == 0x1b ? VFORMAT_H264 : VFORMAT_UNKNOWN; + + m_videoCodec.am_sysinfo.format = + m_videoCodec.video_type == VFORMAT_H264 ? + VIDEO_DEC_FORMAT_H264 : VIDEO_DEC_FORMAT_UNKNOW; + + if (codec_init(&m_videoCodec) != CODEC_ERROR_NONE) + { + ELOG("failed to init video codec!"); + m_videoCodec.has_video = 0; + } + else + { + DLOG("set video codec to: %s", + m_videoCodec.video_type == VFORMAT_MPEG12 ? "MPEG2" : + m_videoCodec.video_type == VFORMAT_H264 ? "H264" : "UNKNOWN"); + + if (!m_trickMode) + { + codec_set_cntl_avthresh(&m_videoCodec, AV_SYNC_THRESH); + codec_set_cntl_syncthresh(&m_videoCodec, 0); + + cSysFs::Write("/sys/class/tsync/mode", m_audioCodec.has_audio ? 1 : 0); + cSysFs::Write("/sys/class/tsync/enable", 1); + cSysFs::Write("/sys/class/tsync/pts_pcrscr", "0x0"); + } + } + } + if (m_videoCodec.has_video) + { + uint64_t pts = PesHasPts(Data) ? PesGetPts(Data) : INVALID_PTS; + if (m_scheduler.Check(pts)) + { + if (!m_trickMode) + codec_checkin_pts(&m_videoCodec, pts); + } + else + ret = 0; + + while (ret && m_videoCodec.has_video && payloadLen > 0) + { + int written = codec_write(&m_videoCodec, (void *)payload, payloadLen); + if (written > 0) + { + payloadLen -= written; + payload += written; + } + else + ret = 0; + } + } + return ret; +} + +int cAmlDevice::PlayAudio(const uchar *Data, int Length, uchar Id) { - int pid = TsPid(Data); - if (pid != m_videoPid) + int ret = Length; + const uchar *payload = Data + PesPayloadOffset(Data); + int payloadLen = Length - PesPayloadOffset(Data); + + if (!m_audioCodec.has_audio || m_audioId != Id) { - PatPmtParser(); - if (pid == PatPmtParser()->Vpid()) + bool reset = m_audioCodec.has_audio > 0; + + m_audioId = Id; + m_audioCodec.has_audio = 1; + m_audioCodec.am_sysinfo.param = (void *)(EXTERNAL_PTS); + m_audioCodec.audio_channels = 0; + m_audioCodec.audio_samplerate = 0; + m_audioCodec.audio_info.channels = 0; + m_audioCodec.audio_info.sample_rate = 0; + m_audioCodec.audio_type = AFORMAT_UNKNOWN; + + if (Data[3] >= 0xc0 && Data[3] <= 0xdf) + m_audioCodec.audio_type = AFORMAT_MPEG; + else { - m_decoder->SetVideoPid(pid, PatPmtParser()->Vtype()); - m_videoPid = pid; + if ((payload[0] & 0xf8) == 0x88) + m_audioCodec.audio_type = AFORMAT_DTS; + else if ((payload[0] & 0xf8) == 0x80) + m_audioCodec.audio_type = AFORMAT_AC3; + } + + if ((reset ? codec_reset_audio(&m_audioCodec) : + codec_init(&m_audioCodec)) != CODEC_ERROR_NONE) + { + ELOG("failed to init audio codec!"); + m_audioCodec.has_audio = 0; + } + else + { + DLOG("set audio codec to: %s", + m_audioCodec.audio_type == AFORMAT_MPEG ? "MPEG" : + m_audioCodec.audio_type == AFORMAT_AC3 ? "AC-3" : + m_audioCodec.audio_type == AFORMAT_DTS ? "DTS" : "UNKNOWN"); + + if (m_trickMode) + codec_set_cntl_mode(&m_videoCodec, TRICKMODE_I); + else + { + cSysFs::Write("/sys/class/tsync/mode", 1); + cSysFs::Write("/sys/class/tsync/enable", 1); + cSysFs::Write("/sys/class/tsync/pts_pcrscr", "0x0"); + } } } - return m_decoder->WriteTs(Data, Length); + if (m_audioCodec.has_audio) + { + uint64_t pts = PesHasPts(Data) ? PesGetPts(Data) : INVALID_PTS; + if (m_scheduler.Check(pts)) + { + if (!m_trickMode) + codec_checkin_pts(&m_audioCodec, pts); + } + else + ret = 0; + + while (ret && m_audioCodec.has_audio && payloadLen > 0) + { + int written = codec_write(&m_audioCodec, (void *)payload, payloadLen); + if (written > 0) + { + payloadLen -= written; + payload += written; + } + else + ret = 0; + } + } + return ret; } -int cAmlDevice::PlayTsAudio(const uchar *Data, int Length) +void cAmlDevice::StillPicture(const uchar *Data, int Length) { - int pid = TsPid(Data); - if (pid != m_audioPid) + if (Data[0] == 0x47) + cDevice::StillPicture(Data, Length); + else { - int streamType = -1; - for (int i = 0; PatPmtParser()->Apid(i); i++) - if (pid == PatPmtParser()->Apid(i)) + DBG("%s", __FUNCTION__); + if (!m_videoCodec.has_video) + { + int streamType = PatPmtParser()->Vtype(); + + m_videoCodec.has_video = 1; + m_videoCodec.am_sysinfo.param = 0;//(void *)(EXTERNAL_PTS); + m_videoCodec.video_type = + streamType == 0x01 ? VFORMAT_MPEG12 : + streamType == 0x02 ? VFORMAT_MPEG12 : + streamType == 0x1b ? VFORMAT_H264 : VFORMAT_UNKNOWN; + + m_videoCodec.am_sysinfo.format = + m_videoCodec.video_type == VFORMAT_H264 ? + VIDEO_DEC_FORMAT_H264 : VIDEO_DEC_FORMAT_UNKNOW; + + if (codec_init(&m_videoCodec) != CODEC_ERROR_NONE) { - streamType = PatPmtParser()->Atype(i); - break; + ELOG("failed to init video codec!"); + m_videoCodec.has_video = 0; } - if (streamType < 0) - for (int i = 0; PatPmtParser()->Dpid(i); i++) - if (pid == PatPmtParser()->Dpid(i)) + else + { + codec_set_cntl_mode(&m_videoCodec, TRICKMODE_I); + m_trickMode = true; + } + } + if (m_videoCodec.has_video) + { + int repeat = m_videoCodec.video_type == VFORMAT_H264 ? 12 : 4; + while (repeat--) + { + int length = Length; + const uchar *data = Data; + + while (PesLongEnough(length)) { - streamType = PatPmtParser()->Dtype(i); - break; + int pktLen = PesHasLength(data) ? PesLength(data) : length; + + if ((data[3] & 0xf0) == 0xe0) + PlayVideo(data, pktLen); + + data += pktLen; + length -= pktLen; } + } + } + } +} - m_decoder->SetAudioPid(pid, streamType); - m_audioPid = pid; +void cAmlDevice::Clear(void) +{ + DBG("%s", __FUNCTION__); + + cSysFs::Write("/sys/class/tsync/enable", 0); + cSysFs::Write("/sys/class/tsync/mode", 0); + + if (m_audioCodec.has_audio) + codec_close(&m_audioCodec); + + if (m_videoCodec.has_video) + { + if (m_trickMode) + { + m_trickMode = false; + codec_resume(&m_videoCodec); + codec_set_cntl_mode(&m_videoCodec, TRICKMODE_NONE); + } + codec_close(&m_videoCodec); } - return m_decoder->WriteTs(Data, Length); + m_audioCodec.has_audio = 0; + m_videoCodec.has_video = 0; + + m_scheduler.Reset(); +} + +void cAmlDevice::Play(void) +{ + DBG("%s", __FUNCTION__); + + if (m_videoCodec.has_video) + { + codec_resume(&m_videoCodec); + codec_set_cntl_mode(&m_videoCodec, TRICKMODE_NONE); + } + if (m_audioCodec.has_audio) + codec_resume(&m_audioCodec); +} + +void cAmlDevice::Freeze(void) +{ + DBG("%s", __FUNCTION__); + + if (m_videoCodec.has_video) + codec_pause(&m_videoCodec); + + if (m_audioCodec.has_audio) + codec_pause(&m_audioCodec); +} + +void cAmlDevice::TrickSpeed(int Speed, bool Forward) +{ + DBG("%s", __FUNCTION__); + m_trickMode = true; + m_scheduler.SetSpeed(Speed, Forward); } bool cAmlDevice::Poll(cPoller &Poller, int TimeoutMs) { - return true; + if (m_videoCodec.has_video) + Poller.Add(m_videoCodec.handle, true); + + if (m_audioCodec.has_audio) + Poller.Add(m_audioCodec.handle, true); + + return Poller.Poll(TimeoutMs); } bool cAmlDevice::Flush(int TimeoutMs) @@ -272,3 +463,8 @@ bool cAmlDevice::Flush(int TimeoutMs) DBG("%s", __FUNCTION__); return true; } + +void cAmlDevice::SetVolumeDevice(int Volume) +{ + DBG("%s(%d)", __FUNCTION__, Volume); +} diff --git a/amldevice.h b/amldevice.h index 35f38dc..6554392 100644 --- a/amldevice.h +++ b/amldevice.h @@ -8,10 +8,48 @@ #define AML_DEVICE_H #include <vdr/device.h> - #include "tools.h" -class cAmlDecoder; +extern "C" { +#include <codec.h> +} + +class cScheduler +{ + +public: + + cScheduler(); + virtual ~cScheduler() { } + + void SetSpeed(int speed, bool forward); + void Reset(); + + bool Check(int64_t pts); + int64_t GetPts(void); + + enum eSpeed { + ePause, + eSlowest, + eSlower, + eSlow, + eNormal, + eFast, + eFaster, + eFastest, + eNumSpeeds + }; + +private: + + int64_t m_pts; + cTimeMs m_timestamp; + + eSpeed m_speed; + bool m_forward; + + static const int s_speeds[eNumSpeeds]; +}; class cAmlDevice : cDevice { @@ -35,21 +73,36 @@ public: virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); + virtual int64_t GetSTC(void); + protected: virtual void MakePrimaryDevice(bool On); - virtual int PlayTsVideo(const uchar *Data, int Length); - virtual int PlayTsAudio(const uchar *Data, int Length); + virtual int PlayVideo(const uchar *Data, int Length); + virtual int PlayAudio(const uchar *Data, int Length, uchar Id); + + void StillPicture(const uchar *Data, int Length); + + virtual void Clear(void); + virtual void Play(void); + virtual void Freeze(void); + + virtual void TrickSpeed(int Speed, bool Forward); + + virtual void SetVolumeDevice(int Volume); private: void (*m_onPrimaryDevice)(void); - cAmlDecoder *m_decoder; + codec_para_t m_videoCodec; + codec_para_t m_audioCodec; + + uchar m_audioId; + bool m_trickMode; - int m_audioPid; - int m_videoPid; + cScheduler m_scheduler; }; #endif diff --git a/amlhddevice.c b/amlhddevice.c index d83208a..f2e5105 100644 --- a/amlhddevice.c +++ b/amlhddevice.c @@ -17,11 +17,11 @@ class cPluginAmlHdDevice : public cPlugin { private: - class cAmlDevice *m_device; + cAmlDevice *m_device; static void OnPrimaryDevice(void) { - new cFbOsdProvider("/dev/fb0"); + new cFbOsdProvider("/dev/fb1"); } public: @@ -60,9 +60,9 @@ bool cPluginAmlHdDevice::Initialize(void) if (m_device->Init()) return false; - cSysFs::Write("/sys/class/graphics/fb0/mode", "U:1280x720p-0\n"); - cSysFs::Write("/sys/class/graphics/fb0/blank", 0); - cSysFs::Write("/sys/class/graphics/fb1/blank", 1); + cSysFs::Write("/sys/class/graphics/fb1/mode", "U:1280x720p-0\n"); + cSysFs::Write("/sys/class/graphics/fb1/blank", 0); + cSysFs::Write("/sys/class/graphics/fb0/blank", 1); } return true; } @@ -74,8 +74,8 @@ bool cPluginAmlHdDevice::Start(void) void cPluginAmlHdDevice::Stop(void) { - cSysFs::Write("/sys/class/graphics/fb0/blank", 1); - cSysFs::Write("/sys/class/graphics/fb1/blank", 0); + cSysFs::Write("/sys/class/graphics/fb1/blank", 1); + cSysFs::Write("/sys/class/graphics/fb0/blank", 0); } VDRPLUGINCREATOR(cPluginAmlHdDevice); // Don't touch this! okay. |