#include "xineCommon.h" #include #include #if APIVERSNUM >= 10703 #include "vdr172remux.h" #define VDR172 ::vdr172 #else #define VDR172 #endif #include "xineDevice.h" #include "xineOsd.h" #include "xineSettings.h" //#define LOG_ME(x) x #define LOG_ME(x) namespace PluginXine { void cXineSpuDecoder::ptsAdjust(uint32_t &pts) { if (0 == pts || -1 == (int32_t)pts) { return; } const int64_t ptsXine = m_xineDevice->GetSTC(); if (-1 == ptsXine) return; // ::fprintf(stderr, "ptsAdjust: %ld, %lld, %lld\n", pts, ptsXine, pts - ptsXine); pts = (uint32_t)ptsXine; } int cXineSpuDecoder::setTime(uint32_t pts) { ptsAdjust(pts); return cDvbSpuDecoder::setTime(pts); } static cXineDevice *theXineDevice = 0; bool cXineDevice::HasDecoder(void) const { return true; } cSpuDecoder *cXineDevice::GetSpuDecoder(void) { if (!m_spuDecoder && IsPrimaryDevice()) { m_spuDecoder = new cXineSpuDecoder(this); } return m_spuDecoder; } bool cXineDevice::CanReplay(void) const { return true; } static bool findVideo = false; static bool foundVideo = false; static int ts = 0; static bool f = false; static bool np = false; static bool muted = false; static int jumboPESsize = 0; static int jumboPEStail = 0; static ePlayMode pm = pmNone; static bool audioSeen = false; static int64_t ptsV = -1, ptsA = -1, ptsP = -1, ptsD = -1; static cMutex softStartMutex; static enum { sstNone = 0, sstNormal, sstNoMetronom } softStartTrigger = sstNone; static enum { sIdle , sInitiateSequence , sStartSequence , sContinueSequence } softStartState = sIdle; double tNow() { timeval tv; ::gettimeofday(&tv, 0); return tv.tv_sec + tv.tv_usec / 1.0e+6; } static double ttt0 = tNow(), ttt1 = tNow(), ttt2 = 0, ttt3 = 0, ttt4 = 0, ttt5 = 0, ttt6 = 0; // static int streams[ 256 ]; static bool doClear = false; bool cXineDevice::SetPlayMode(ePlayMode PlayMode) { if (pmNone == PlayMode) { doClear = true; ttt0 = tNow(); } else ttt2 = tNow(); /* timeval tv0; ::gettimeofday(&tv0, 0); */ if (0) { time_t t1 = time(0); static time_t t0 = t1; if (0 == PlayMode && (t1 - t0) > (30 * 60)) *(char *)0 = 0; t0 = t1; } bool playModeSupported = false; switch (PlayMode) { case pmNone: case pmAudioVideo: case pmAudioOnlyBlack: case pmExtern_THIS_SHOULD_BE_AVOIDED: playModeSupported = true; break; case pmAudioOnly: #if APIVERSNUM >= 10308 case pmVideoOnly: #endif break; } ptsV = ptsA = ptsP = ptsD = -1; ts = 0; np = 0; f = 0; xfprintf(stderr, "SetPlayMode: %d\n", PlayMode); if (pmExtern_THIS_SHOULD_BE_AVOIDED == pm && pmNone == PlayMode) { m_xineLib.enableExternal(false); } m_xineLib.pause(false); m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); if (muted) { muted = false; m_xineLib.execFuncMute(false); } if (pmNone == PlayMode) { pm = PlayMode; jumboPESsize = 0; jumboPEStail = 0; /* for (unsigned int i = 0; i < sizeof (streams) / sizeof (*streams); i++) { if (streams[ i ]) fprintf(stderr, "stream: 0x%02x\n", i); } */ m_xineLib.ignore(); // m_xineLib.execFuncMute(); // m_xineLib.execFuncSetPrebuffer(0); m_xineLib.execFuncClear(-2); // m_xineLib.execFuncStart(); // m_xineLib.execFuncMetronom(0); m_xineLib.execFuncStillFrame(); m_xineLib.execFuncWait(); // for (int i = 0; i < 2; i++) m_xineLib.showNoSignal(); PushOut(); m_xineLib.execFuncFlush(); { cMutexLock lock(&softStartMutex); softStartTrigger = sstNone; softStartState = sIdle; } foundVideo = false; findVideo = false; } else { audioSeen = false; { cMutexLock lock(&softStartMutex); softStartTrigger = sstNone; softStartState = sIdle; } // ::memset(&streams, 0, sizeof (streams)); m_xineLib.freeze(); m_xineLib.ignore(false); m_xineLib.freeze(false); /* PushOut(); m_xineLib.execFuncFlush(); m_xineLib.execFuncWait(); */ // m_xineLib.execFuncSetPrebuffer(m_settings.GetModeParams()->m_prebufferFrames); // m_xineLib.execFuncSetPrebuffer(0); m_xineLib.execFuncClear(-4); // m_xineLib.execFuncStart(); m_xineLib.execFuncWait(); #if APIVERSNUM < 10342 m_settings.SelectReplayPrebufferMode(0 == cTransferControl::ReceiverDevice()); #else m_settings.SelectReplayPrebufferMode(!Transferring()); #endif if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); // ::fprintf(stderr, "LiveTV\n"); softStartTrigger = sstNormal; } else np = true; foundVideo = false; findVideo = true; cMutexLock pmMutexLock(&m_pmMutex); pm = PlayMode; m_pmCondVar.Broadcast(); } if (pmExtern_THIS_SHOULD_BE_AVOIDED == PlayMode) m_xineLib.enableExternal(); /* timeval tv1; ::gettimeofday(&tv1, 0); fprintf(stderr, "spm: %.3lf ms\n", 1000 * ((tv1.tv_sec + tv1.tv_usec / 1.0e+6) - (tv0.tv_sec + tv0.tv_usec / 1.0e+6))); */ if (pmNone == PlayMode) { ttt1 = tNow(); ttt4 = 0; ttt5 = 0; } else ttt3 = tNow(); return playModeSupported; } void cXineDevice::DiscontinuityDetected() { if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); if (softStartState == sIdle && softStartTrigger == sstNone && pm != pmNone) { softStartTrigger = sstNormal; xfprintf(stderr, "DiscontinuityDetected: triggering soft start\n"); } } } static bool lastCmdWasClear = false; bool cXineDevice::HasIBPTrickSpeed(void) { /* #if APIVERSNUM >= 10706 return true; #else */ return false; //#endif } void cXineDevice::TrickSpeed(int Speed, bool Forward) { f = false; ts = Speed; xfprintf(stderr, "TrickSpeed: %d\n", Speed); m_xineLib.execFuncTrickSpeedMode(lastCmdWasClear); m_xineLib.execFuncSetSpeed(100.0 / Speed); m_xineLib.execFuncWait(); m_xineLib.freeze(false); m_xineLib.pause(false); } void cXineDevice::Clear(void) { lastCmdWasClear = true; doClear = true; ptsV = ptsA = ptsP = ptsD = -1; static int cntClear = 0; xfprintf(stderr, "Clear(%d)", cntClear); m_xineLib.pause(); jumboPESsize = 0; jumboPEStail = 0; if (f) m_xineLib.execFuncSetSpeed(100.0); m_xineLib.execFuncClear(cntClear++); // m_xineLib.execFuncStart(); np = true; if (f) m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); m_xineLib.pause(false); xfprintf(stderr, "!\n"); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNoMetronom; } cDevice::Clear(); } void cXineDevice::Play(void) { lastCmdWasClear = false; f = false; ts = 0; xfprintf(stderr, "Play\n"); m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); if (muted) { muted = false; m_xineLib.execFuncMute(false); } m_xineLib.execFuncWait(); m_xineLib.freeze(false); m_xineLib.pause(false); LOG_ME(::fprintf(stderr, "----\n");) } void cXineDevice::Freeze(void) { lastCmdWasClear = false; f = true; xfprintf(stderr, "Freeze\n"); m_xineLib.freeze(); m_xineLib.pause(); m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); LOG_ME(::fprintf(stderr, "------\n");) } void cXineDevice::Mute(void) { xfprintf(stderr, "Mute\n"); m_xineLib.execFuncMute(true); muted = true; } static void store_frame(const unsigned char *buf, int len, int line) { if (0) { static int cnt = 0; char name[ 100 ]; ::sprintf(name, "/tmp/frame_%05d_%05d", line, cnt++); FILE *f = fopen(name, "wb"); size_t r = fwrite(buf, 1, len, f); (void)r; fclose(f); } } #define VERBOSE_NOP() do{ xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); } while (0) #define VERBOSE_NOP1() do{ store_frame(Data, Length, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); } while (0) #define VERBOSE_RETURN0(x) do{ xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN1(x) do{ store_frame(buf0, len0, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN2(x) do{ store_frame(buf, len, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN3(x) do{ store_frame(Data, Length, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #if APIVERSNUM < 10331 enum ePesHeader { phNeedMoreData = -1, phInvalid = 0, phMPEG1 = 1, phMPEG2 = 2 }; static ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL) { if (Count < 7) return phNeedMoreData; // too short if ((Data[6] & 0xC0) == 0x80) { // MPEG 2 if (Count < 9) return phNeedMoreData; // too short PesPayloadOffset = 6 + 3 + Data[8]; if (Count < PesPayloadOffset) return phNeedMoreData; // too short if (ContinuationHeader) *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]); return phMPEG2; // MPEG 2 } // check for MPEG 1 ... PesPayloadOffset = 6; // skip up to 16 stuffing bytes for (int i = 0; i < 16; i++) { if (Data[PesPayloadOffset] != 0xFF) break; if (Count <= ++PesPayloadOffset) return phNeedMoreData; // too short } // skip STD_buffer_scale/size if ((Data[PesPayloadOffset] & 0xC0) == 0x40) { PesPayloadOffset += 2; if (Count <= PesPayloadOffset) return phNeedMoreData; // too short } if (ContinuationHeader) *ContinuationHeader = false; if ((Data[PesPayloadOffset] & 0xF0) == 0x20) { // skip PTS only PesPayloadOffset += 5; } else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) { // skip PTS and DTS PesPayloadOffset += 10; } else if (Data[PesPayloadOffset] == 0x0F) { // continuation header PesPayloadOffset++; if (ContinuationHeader) *ContinuationHeader = true; } else return phInvalid; // unknown if (Count < PesPayloadOffset) return phNeedMoreData; // too short return phMPEG1; // MPEG 1 } #endif //#if APIVERSNUM < 10345 namespace cRemux { // Start codes: #define SC_SEQUENCE 0xB3 // "sequence header code" #define SC_GROUP 0xB8 // "group start code" #define SC_PICTURE 0x00 // "picture start code" int GetPacketLength(const uchar *Data, int Count, int Offset) { // Returns the length of the packet starting at Offset, or -1 if Count is // too small to contain the entire packet. int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1; if (Length > 0 && Offset + Length <= Count) return Length; return -1; } bool IsFrameH264(const uchar *Data, int Length) { int PesPayloadOffset; const uchar *limit = Data + Length; if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid) return false; // neither MPEG1 nor MPEG2 Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01 if (Data < limit) { // cVideoRepacker ensures that in case of H264 we will see an access unit delimiter here if (0x01 == Data[-1] && 9 == Data[0] && 0x00 == Data[-2] && 0x00 == Data[-3]) return true; } return false; } int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType) { // Scans the video packet starting at Offset and returns its length. // If the return value is -1 the packet was not completely in the buffer. int Length = GetPacketLength(Data, Count, Offset); if (Length > 0) { const bool h264 = IsFrameH264(Data + Offset, Length); int PesPayloadOffset = 0; if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) { const uchar *p = Data + Offset + PesPayloadOffset + 2; const uchar *pLimit = Data + Offset + Length - 3; #if APIVERSNUM >= 10326 // cVideoRepacker ensures that a new PES packet is started for a new sequence, // group or picture which allows us to easily skip scanning through a huge // amount of video data. if (p < pLimit) { if (p[-2] || p[-1] || p[0] != 0x01) pLimit = 0; // skip scanning: packet doesn't start with 0x000001 else { if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: // access unit delimiter // when the MSB in p[1] is set (which violates H.264) then this is a hint // from cVideoRepacker::HandleNalUnit() that this bottom field shall not // be reported as picture. if (p[1] & 0x80) ((uchar *)p)[1] &= ~0x80; // revert the hint and fall through else break; default: // skip scanning: packet doesn't start a new picture pLimit = 0; } } else { switch (p[1]) { case SC_SEQUENCE: case SC_GROUP: case SC_PICTURE: break; default: // skip scanning: packet doesn't start a new sequence, group or picture pLimit = 0; } } } } #endif while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) { if (!p[-2] && !p[-1]) { // found 0x000001 if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: { // access unit delimiter int primary_pic_type = p[2] >> 5; switch (primary_pic_type) { case 0: // I case 3: // SI case 5: // I, SI PictureType = I_FRAME; break; case 1: // I, P case 4: // SI, SP case 6: // I, SI, P, SP PictureType = P_FRAME; break; case 2: // I, P, B case 7: // I, SI, P, SP, B PictureType = B_FRAME; break; } return Length; } } } else { switch (p[1]) { case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07; return Length; } } p += 4; // continue scanning after 0x01ssxxyy } else p += 3; // continue scanning after 0x01xxyy } } PictureType = NO_PICTURE; return Length; } return -1; } } //#endif /* static bool IsNotVideoIorPframe(const uchar *buf, int len) { if (0xe0 != (0xf0 & buf[ 3 ])) // not video return true; uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(buf, len, 0, pt); return (I_FRAME == pt || P_FRAME == pt); } */ /* static char *getFrameType(const uchar *buf, int len) { if (0xe0 != (0xf0 & buf[ 3 ])) // not video return ""; static char *frameTypes[ 8 ] = { "", "i", "p", "b", "4", "5", "6", "7" }; uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(buf, len, 0, pt); return frameTypes[ pt ]; } */ static bool getPTS(const unsigned char *buf0, int len0, int64_t &pts) { while (len0 > 0) { while (len0 > 3 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { buf0++; len0--; } if (3 == len0 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { break; } if (len0 < 6) VERBOSE_RETURN1(false); if (0x00 != buf0[ 0 ] || 0x00 != buf0[ 1 ] || 0x01 != buf0[ 2 ]) { VERBOSE_RETURN1(false); } if (0xe0 != (0xf0 & buf0[ 3 ]) // video && 0xc0 != (0xe0 & buf0[ 3 ]) // audio && 0xbd != (0xff & buf0[ 3 ]) // dolby && 0xbe != (0xff & buf0[ 3 ])) // padding (DVD) { VERBOSE_RETURN1(false); } int len = (6 + buf0[ 4 ] * 0x100 + buf0[ 5 ]); if (len > len0) VERBOSE_RETURN1(false); const unsigned char *buf = buf0; buf0 += len; len0 -= len; // if (len0 != 0) // VERBOSE_NOP(); if (0xbe == (0xff & buf[ 3 ])) // padding (DVD) continue; if (len < (6 + 3)) VERBOSE_RETURN2(false); if (0x80 != (0xc0 & buf[ 6 ])) // MPEG1 { do // ... while (false); { int o = 0; for (int i = 0; i < 16; i++) { if (buf[ 6 + o ] != 0xff) break; if (len < (6 + ++o)) VERBOSE_RETURN2(false); } if (0x40 == (0xc0 & buf[ 6 + o ])) o += 2; if (len < (6 + o)) VERBOSE_RETURN2(false); if (0x31 == (0xf1 & buf[ 6 + o + 0 ])) { if (len < (6 + o + 5 + 5)) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); if (0x11 != (0xf1 & buf[ 6 + o + 5 + 0 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 4 ])) VERBOSE_RETURN2(false); int64_t _pts = ((int64_t)(0x0e & buf[ 6 + o + 0 ])) << 29 | (0xff & buf[ 6 + o + 1 ]) << 22 | (0xfe & buf[ 6 + o + 2 ]) << 14 | (0xff & buf[ 6 + o + 3 ]) << 7 | (0xfe & buf[ 6 + o + 4 ]) >> 1; // ::fprintf(stderr, "pts: %lld\n", _pts); if (0 == _pts) break; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // break; pts = _pts; return true; } else if (0x21 == (0xf1 & buf[ 6 + o + 0 ])) { if (len < (6 + o + 5)) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); int64_t _pts = ((int64_t)(0x0e & buf[ 6 + o + 0 ])) << 29 | (0xff & buf[ 6 + o + 1 ]) << 22 | (0xfe & buf[ 6 + o + 2 ]) << 14 | (0xff & buf[ 6 + o + 3 ]) << 7 | (0xfe & buf[ 6 + o + 4 ]) >> 1; // ::fprintf(stderr, "pts: %lld\n", _pts); if (0 == _pts) break; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // break; pts = _pts; return true; } else if (0x0f == (0xff & buf[ 6 + o + 0 ])) { break; } for (int i = 0; i < 30; i++) xfprintf(stderr, "%02x ", buf[ i ]); xfprintf(stderr, "\n"); VERBOSE_RETURN2(false); } while (false); continue; } if (0x40 == (0xc0 & buf[ 7 ])) VERBOSE_RETURN2(false); if (0x00 == (0xc0 & buf[ 7 ])) continue; // ignore // if (0x00 != (0x3f & buf[ 7 ])) // VERBOSE_RETURN2(false); bool hasPTS = (0 != (0x80 & buf[ 7 ])); bool hasDTS = (0 != (0x40 & buf[ 7 ])); unsigned char hdl = buf[ 8 ]; if (hdl < ((hasPTS + hasDTS) * 5)) VERBOSE_RETURN2(false); if (len < (6 + 3 + hdl)) VERBOSE_RETURN2(false); if ((0x20 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { if ((0x20 * hasPTS + 0x00 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '00X0' instead of '00X1'. } else if ((0x00 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '000X' instead of '001X'. } else { fprintf(stderr, "buf:"); for (int i = 0; i < 6 + 3 + hdl; i++) fprintf(stderr, " %02x", buf[i]); fprintf(stderr, "\n"); VERBOSE_RETURN2(false); } } if (0x01 != (0x01 & buf[ 11 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 13 ])) VERBOSE_RETURN2(false); if (hasDTS) { if (0x11 != (0xf1 & buf[ 14 ])) { if (0x21 == (0xf1 & buf[ 14 ])) { // accept streams, that start with '0010' instead of '0001'. } else if (0xa1 == (0xf1 & buf[ 14 ])) { // accept streams, that start with '1010' instead of '0001'. } else { fprintf(stderr, "buf:"); for (int i = 0; i < 6 + 3 + hdl; i++) fprintf(stderr, " %02x", buf[i]); fprintf(stderr, "\n"); VERBOSE_RETURN2(false); } } // accept streams that have no marker bits set // if (0x01 != (0x01 & buf[ 16 ])) // VERBOSE_RETURN2(false); // if (0x01 != (0x01 & buf[ 18 ])) // VERBOSE_RETURN2(false); } /* fprintf(stderr, " %02x %02x %02x %02x %02x\n" , buf[ 9 ] , buf[ 10 ] , buf[ 11 ] , buf[ 12 ] , buf[ 13 ]); */ int64_t _pts = ((int64_t)(0x0e & buf[ 9 ])) << 29 | (0xff & buf[ 10 ]) << 22 | (0xfe & buf[ 11 ]) << 14 | (0xff & buf[ 12 ]) << 7 | (0xfe & buf[ 13 ]) >> 1; if (0 == _pts) return false; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // return false; pts = _pts; return true; } // VERBOSE_RETURN2(false); return false; } /* static bool stripPTSandDTS(unsigned char *buf0, int len0) { while (len0 > 0) { while (len0 > 3 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { buf0++; len0--; } if (3 == len0 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { break; } if (len0 < 6) VERBOSE_RETURN1(false); if (0x00 != buf0[ 0 ] || 0x00 != buf0[ 1 ] || 0x01 != buf0[ 2 ]) { VERBOSE_RETURN1(false); } if (0xe0 != (0xf0 & buf0[ 3 ]) // video && 0xc0 != (0xe0 & buf0[ 3 ]) // audio && 0xbd != (0xff & buf0[ 3 ]) // dolby && 0xbe != (0xff & buf0[ 3 ]) // padding (DVD) && 0xba != (0xff & buf0[ 3 ]) // system layer: pack header && 0xbb != (0xff & buf0[ 3 ]) // system layer: system header && 0xb9 != (0xff & buf0[ 3 ])) // system layer: stream end { //fprintf(stderr, "buf0[ 3 ]: %02x\n", buf0[ 3 ]); VERBOSE_RETURN1(false); } int len = (6 + buf0[ 4 ] * 0x100 + buf0[ 5 ]); if (0xba == buf0[ 3 ]) // pack header has fixed length { if (0x00 == (0xc0 & buf0[ 4 ])) // MPEG 1 len = 12; else // MPEG 2 len = 14 + (buf0[ 13 ] & 0x07); } else if (0xb9 == buf0[ 3 ]) // stream end has fixed length { len = 4; } if (len > len0) VERBOSE_RETURN1(false); unsigned char *buf = buf0; buf0 += len; len0 -= len; // if (len0 != 0) // VERBOSE_NOP(); if (0xbe == (0xff & buf[ 3 ]) // padding (DVD) || 0xba == (0xff & buf[ 3 ]) // system layer: pack header || 0xbb == (0xff & buf[ 3 ]) // system layer: system header || 0xb9 == (0xff & buf[ 3 ])) // system layer: stream end { continue; } if (len < (6 + 3)) VERBOSE_RETURN2(false); if (0x80 != (0xc0 & buf[ 6 ])) // MPEG1 { bool ok = false; do // ... while (false); { int o = 0; for (int i = 0; i < 16; i++) { if (buf[ 6 + o ] != 0xff) break; if (len < (6 + ++o)) VERBOSE_RETURN2(false); } if (0x40 == (0xc0 & buf[ 6 + o ])) o += 2; if (len < (6 + o)) VERBOSE_RETURN2(false); if (0x31 == (0xf1 & buf[ 6 + o + 0 ])) { if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); if (0x11 != (0xf1 & buf[ 6 + o + 5 + 0 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 4 ])) VERBOSE_RETURN2(false); buf[ 6 + o + 0 ] = 0xff; buf[ 6 + o + 1 ] = 0xff; buf[ 6 + o + 2 ] = 0xff; buf[ 6 + o + 3 ] = 0xff; buf[ 6 + o + 4 ] = 0xff; buf[ 6 + o + 5 + 0 ] = 0xff; buf[ 6 + o + 5 + 1 ] = 0xff; buf[ 6 + o + 5 + 2 ] = 0xff; buf[ 6 + o + 5 + 3 ] = 0xff; buf[ 6 + o + 5 + 4 ] = 0x0f; ok = true; } else if (0x21 == (0xf1 & buf[ 6 + o + 0 ])) { if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); buf[ 6 + o + 0 ] = 0xff; buf[ 6 + o + 1 ] = 0xff; buf[ 6 + o + 2 ] = 0xff; buf[ 6 + o + 3 ] = 0xff; buf[ 6 + o + 4 ] = 0x0f; ok = true; } else if (0x0f == (0xff & buf[ 6 + o + 0 ])) { ok = true; } if (ok) break; for (int i = 0; i < 30; i++) xfprintf(stderr, "%02x ", buf[ i ]); xfprintf(stderr, "\n"); VERBOSE_RETURN2(false); } while (false); if (ok) continue; } if (0x40 == (0xc0 & buf[ 7 ])) VERBOSE_RETURN2(false); if (0x00 == (0xc0 & buf[ 7 ])) continue; // ignore // if (0x00 != (0x3f & buf[ 7 ])) // VERBOSE_RETURN2(false); bool hasPTS = (0 != (0x80 & buf[ 7 ])); bool hasDTS = (0 != (0x40 & buf[ 7 ])); unsigned char hdl = buf[ 8 ]; if (hdl < ((hasPTS + hasDTS) * 5)) VERBOSE_RETURN2(false); if (len < (6 + 3 + hdl)) VERBOSE_RETURN2(false); if ((0x20 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { if ((0x20 * hasPTS + 0x00 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '00X0' instead of '00X1'. } else if ((0x00 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '000X' instead of '001X'. } else { VERBOSE_RETURN2(false); } } if (0x01 != (0x01 & buf[ 11 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 13 ])) VERBOSE_RETURN2(false); if (hasDTS) { if (0x11 != (0xf1 & buf[ 14 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 16 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 18 ])) VERBOSE_RETURN2(false); } buf[ 7 ] &= 0x3f; for (int i = 9; i < (9 + ((hasPTS + hasDTS) * 5)); i++) buf[ i ] = 0xff; } return true; } */ static uchar padding[ 6 + 0xffff ] = { 0x00 , 0x00 , 0x01 , 0xbe , 0xff , 0xff }; int cXineDevice::PushOut() { uchar *Data = padding; int Length = sizeof (padding); return PlayCommon3(Data, Length, -1); } //static bool blahblah = false; void cXineDevice::StillPicture(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (Length > 0 && Data[0] == 0x47) { const uchar *tsData = Data; const int tsLength = Length; uchar *pesData = (uchar *)malloc(Length); int pesLength = 0; uchar tsHead[4] = { 0xff, 0xff, 0xff, 0xff }; VDR172::cRemux *vRemux = 0; cPatPmtParser patPmtParser; while (Length >= TS_SIZE) { if (TsHasPayload(Data)) { int plo = TsPayloadOffset(Data); if (plo < Length) { int pid = TsPid(Data); if (pid == 0) patPmtParser.ParsePat(Data, TS_SIZE); #if VDRVERSNUM < 10733 else if (pid == patPmtParser.PmtPid()) #else else if (patPmtParser.IsPmtPid(pid)) #endif patPmtParser.ParsePmt(Data, TS_SIZE); else if (pid == patPmtParser.Vpid()) { if (!vRemux) { vRemux = new VDR172::cRemux(patPmtParser.Vpid(), 0, 0, 0); vRemux->SetTimeouts(0, 0); } memcpy(tsHead, Data, sizeof (tsHead)); vRemux->Put(Data, TS_SIZE); int n = 0; uchar *p = vRemux->Get(n); if (p) { memcpy(pesData + pesLength, p, n); pesLength += n; vRemux->Del(n); } } } } Data += TS_SIZE; Length -= TS_SIZE; } if (!vRemux) { free(pesData); return; } uchar tsTail[TS_SIZE]; memset(tsTail + sizeof (tsHead), 0xff, sizeof (tsTail) - sizeof (tsHead)); memcpy(tsTail, tsHead, sizeof (tsHead)); tsTail[1] &= ~TS_ERROR; tsTail[1] |= TS_PAYLOAD_START; tsTail[3] &= ~TS_SCRAMBLING_CONTROL; tsTail[3] |= TS_ADAPT_FIELD_EXISTS; tsTail[3] |= TS_PAYLOAD_EXISTS; tsTail[3] = (tsTail[3] & 0xf0) | (((tsTail[3] & TS_CONT_CNT_MASK) + 1) & TS_CONT_CNT_MASK); tsTail[4] = TS_SIZE - 5 - 13; tsTail[5] = 0x00; tsTail[TS_SIZE - 13] = 0x00; tsTail[TS_SIZE - 12] = 0x00; tsTail[TS_SIZE - 11] = 0x01; tsTail[TS_SIZE - 10] = pesData[3]; tsTail[TS_SIZE - 9] = 0x00; tsTail[TS_SIZE - 8] = 0x07; tsTail[TS_SIZE - 7] = 0x80; tsTail[TS_SIZE - 6] = 0x00; tsTail[TS_SIZE - 5] = 0x00; tsTail[TS_SIZE - 4] = 0x00; tsTail[TS_SIZE - 3] = 0x00; tsTail[TS_SIZE - 2] = 0x01; tsTail[TS_SIZE - 1] = VDR172::cRemux::IsFrameH264(pesData, pesLength) ? 10 : 0xb7; if (0) { FILE *ff = fopen("/tmp/still.ts", "wb"); fwrite(tsData, 1, tsLength, ff); fwrite(tsTail, 1, TS_SIZE, ff); fclose(ff); } vRemux->Put(tsTail, TS_SIZE); int n = 0; uchar *p = vRemux->Get(n); if (p) { memcpy(pesData + pesLength, p, n); pesLength += n; vRemux->Del(n); } if (0) { FILE *ff = fopen("/tmp/still.pes", "wb"); fwrite(pesData, 1, pesLength, ff); fclose(ff); } delete vRemux; StillPicture(pesData, pesLength); free(pesData); return; } #endif xfprintf(stderr, "StillPicture: %p, %d\n", Data, Length); if (0) { for (int i = 0; i < Length - 3; i++) { if (i != 0 && Data[ i + 0 ] == 0x00 && Data[ i + 1 ] == 0x00 && Data[ i + 2 ] == 0x01) { xfprintf(stderr, "\n"); } xfprintf(stderr, "%02x ", Data[ i ]); } for (int i = Length - 3; i < Length; i++) { xfprintf(stderr, "%02x ", Data[ i ]); } xfprintf(stderr, "\n"); } const int maxPackets = 3; uchar pes[ maxPackets * (6 + 0xffff) ]; static const uchar header[ 6 + 3 ] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; do // ... while (false); { if (Length < 6) { VERBOSE_NOP(); break; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP(); break; } if (0xe0 != (0xf0 & Data[ 3 ]) // video && 0xc0 != (0xe0 & Data[ 3 ]) // audio && 0xbd != (0xff & Data[ 3 ]) // dolby && 0xbe != (0xff & Data[ 3 ])) // padding (DVD) { if (Length > maxPackets * (0xffff - 3)) { VERBOSE_NOP(); break; } int todo = Length; const uchar *src = Data; uchar *dst = pes; while (todo > 0) { ::memcpy(dst, header, sizeof (header)); int bite = todo; if (bite > 0xffff - 3) bite = 0xffff - 3; todo -= bite; dst[ 4 ] = 0xff & ((bite + 3) >> 8); dst[ 5 ] = 0xff & (bite + 3); ::memcpy(dst + sizeof (header), src, bite); Length += sizeof (header); dst += sizeof (header) + bite; src += bite; } Data = pes; } } while (false); //NNN stripPTSandDTS((uchar *)Data, Length); ts = 0; m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); m_xineLib.execFuncStillFrame(); m_xineLib.execFuncWait(); f = 0; m_xineLib.pause(false); // PushOut(); //blahblah = true; #if APIVERSNUM >= 10704 uchar pesTail[] = { 0x00, 0x00, 0x01, Data[3], 0x00, 3 + 4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, VDR172::cRemux::IsFrameH264(Data, Length) ? 10 : 0xb7 }; #endif for (int i = 0; i < 1 /* 4 */; i++) { //fprintf(stderr, " (%d) ", i); int r = PlayVideo1(Data, Length, true); if (r < 0) return; #if APIVERSNUM >= 10704 r = PlayVideo1(pesTail, sizeof (pesTail), true); if (r < 0) return; #endif } /* FILE *ff = fopen("/tmp/still.pes", "wb"); fwrite(Data, 1, Length, ff); fclose(ff); */ PushOut(); // m_xineLib.execFuncFlush(0); m_xineLib.execFuncFlush(); // ::fprintf(stderr, "------------\n"); LOG_ME(::fprintf(stderr, "------------\n");) } static bool softStartPoll(cXineLib &xineLib, cPoller &poller, const int timeout, const bool result); bool cXineDevice::Poll(cPoller &Poller, int TimeoutMs /* = 0 */) { cMutexLock lock(&softStartMutex); if (softStartState != sIdle) return true; if (m_xineLib.Poll(Poller, TimeoutMs)) return softStartPoll(m_xineLib, Poller, TimeoutMs, true); return softStartPoll(m_xineLib, Poller, TimeoutMs, false); } static bool jw = false; bool cXineDevice::Flush(int TimeoutMs /* = 0 */) { const bool jw0 = jw; m_xineLib.pause(false); if (!jw0) { int r = PushOut(); if (r < 0) return true; } bool retVal = m_xineLib.execFuncFlush(TimeoutMs, jw0); if (!retVal) xfprintf(stderr, jw0 ? "f" : "F"); jw = true; return retVal; } static bool dumpAudio(const char *proc, const uchar *Data, int Length) { return false; nextPacket: if (Length == 0) return true; /* fprintf(stderr , "%s: " , proc); */ if (Length < 6) VERBOSE_RETURN0(false); if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_RETURN3(false); } int l = Data[ 4 ] * 0x0100 + Data[ 5 ]; if (Length < (6 + l)) VERBOSE_RETURN0(false); if (0xe0 == (Data[ 3 ] & 0xf0) //video || 0xc0 == (Data[ 3 ] & 0xe0) //audio || 0xbe == Data[ 3 ]) //padding { Data += (6 + l); Length -= (6 + l); goto nextPacket; } // if (0xbd != Data[ 3 ]) //private (dolby, pcm) // VERBOSE_RETURN0(false); // fprintf(stderr, "private "); if (l < (3 + 0 + 2)) VERBOSE_RETURN0(false); int h = Data[ 8 ]; if (l < (3 + h + 2)) VERBOSE_RETURN0(false); xfprintf(stderr , "%s: " , proc); xfprintf(stderr , "0x%02x 0x%02x\n" , Data[ 6 + 3 + h + 0 ] , Data[ 6 + 3 + h + 1 ]); Data += (6 + l); Length -= (6 + l); goto nextPacket; } static bool IsVideo(const uchar *Data, int Length) { return (Length >= 4 && 0x00 == Data[ 0 ] && 0x00 == Data[ 1 ] && 0x01 == Data[ 2 ] && 0xe0 == (0xf0 & Data[ 3 ])); } #if APIVERSNUM >= 10701 static int m_tsVideoPid = 0; #endif int cXineDevice::PlayTsVideo(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (Length >= TS_SIZE) { #if APIVERSNUM >= 10712 m_tsVideoPid = PatPmtParser()->Vpid(); #else m_tsVideoPid = TsPid(Data); #endif } #endif return Length; } int cXineDevice::PlayTsAudio(const uchar *Data, int Length) { return Length; } #if APIVERSNUM >= 10701 static bool m_trampoline = false; struct sTrampoline { uchar Header[6]; const uchar *Data; int Length; bool VideoOnly; }; static VDR172::cRemux *m_tsVideoRemux = 0; static VDR172::cRemux *m_tsAudioRemux = 0; #endif int cXineDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 cMutexLock lock(&m_playTsMutex); int ret = PlayTsImpl(Data, Length, VideoOnly); // if (Length > 0 && ret <= 0) { char cmd[500]; sprintf(cmd, "ddd /soft/vdr-" APIVERSION "/bin/vdr %d", getpid()); system(cmd); sleep(10); } //should never happen! return ret; #endif return -1; } int cXineDevice::PlayTsImpl(const uchar *Data, int Length, bool VideoOnly /* = false */) { if (!Data) return PlaySingleTs(Data, Length, VideoOnly); int ret = 0; while (Length >= TS_SIZE) { int r = PlaySingleTs(Data, TS_SIZE, VideoOnly); if (r < 0) { if (!ret) return r; return ret; } ret += r; Length -= r; Data += r; } if (ret) return ret; if (Length > 0) return PlaySingleTs(Data, Length, VideoOnly); return ret; } int cXineDevice::PlaySingleTs(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 if (!Data) { m_tsVideoPid = 0; delete m_tsVideoRemux; delete m_tsAudioRemux; m_tsVideoRemux = 0; m_tsAudioRemux = 0; } int ret = cDevice::PlayTs(Data, Length, VideoOnly); if (ret <= 0) return ret; if (!TsHasPayload(Data)) return ret; // silently ignore TS packets w/o payload if (TsIsScrambled(Data)) fprintf(stderr, "TS is scrambled **************\n"); int payloadOffset = TsPayloadOffset(Data); if (payloadOffset >= Length) return ret; int pid = TsPid(Data); if (pid == 0) { m_tsVideoPid = 0; return ret; } m_trampoline = true; sTrampoline trampoline = { { 0x00, 0x00, 0x01, 0xe0, 0x00, sizeof (trampoline) - 6 }, Data, Length, VideoOnly }; PlayPesPacket(trampoline.Header, sizeof (trampoline)); return ret; #endif return -1; } //static int aaz = 0; void cXineDevice::PlayTsTrampoline(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 m_trampoline = false; const tTrackId *track = 0; int pid = TsPid(Data); if (pid == m_tsVideoPid) goto remux; else if ((track = GetTrack(GetCurrentAudioTrack())) && pid == track->id) { if (!VideoOnly || HasIBPTrickSpeed()) goto remux; } return; remux: static int vpid = 0; static int apid[2] = { 0, 0 }; static int dpid[2] = { 0, 0 }; int v = 0; int a = 0; int d = 0; v = m_tsVideoPid; if (!m_tsVideoRemux || vpid != v) { //fprintf(stderr, "vpid: %d, v: %d\n", vpid, v); delete m_tsVideoRemux; vpid = v; m_tsVideoRemux = new VDR172::cRemux(vpid, 0, 0, 0); m_tsVideoRemux->SetTimeouts(0, 0); } if (ttAudioFirst <= GetCurrentAudioTrack() && GetCurrentAudioTrack() <= ttAudioLast) a = (track = GetTrack(GetCurrentAudioTrack())) ? track->id : 0; else d = (track = GetTrack(GetCurrentAudioTrack())) ? track->id : 0; /* if (a || d) { if (aaz) if (aaz != (d ? 1 : 2)) *(char *)0 =0 ; } */ if (!m_tsAudioRemux || *apid != a || *dpid != d) { //fprintf(stderr, "a: %d, d: %d\n", a, d); delete m_tsAudioRemux; *apid = a; *dpid = d; m_tsAudioRemux = new VDR172::cRemux(0, apid, dpid, 0); m_tsAudioRemux->SetTimeouts(0, 0); } if (pid == vpid) { m_tsVideoRemux->Put(Data, Length); int n = 0; uchar pt; uchar *p = m_tsVideoRemux->Get(n, &pt); if (p) { if (m_xineLib.isTrickSpeedMode()) { if (pt == I_FRAME && VDR172::cRemux::IsFrameH264(p, n)) { fprintf(stderr, "TrickSpeedMode: push H.264 SequenceEndCode\n"); uchar sequenceEndCode[] = { 0x00, 0x00, 0x01, p[3], 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 10 }; PlayVideo(sequenceEndCode, sizeof (sequenceEndCode)); } } int w = PlayVideo(p, n); m_tsVideoRemux->Del(w); } } else { m_tsAudioRemux->Put(Data, Length); int n = 0; uchar *p = m_tsAudioRemux->Get(n); if (p) { int w = PlayAudio(p, n, 0); m_tsAudioRemux->Del(w); } } #endif } int cXineDevice::PlayVideo(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (m_trampoline) { sTrampoline *t = (sTrampoline *)Data; PlayTsTrampoline(t->Data, t->Length, t->VideoOnly); return Length; } #endif if (ttt4 == 0) ttt4 = tNow(); // static FILE *f = fopen("/tmp/egon1", "wb"); // fwrite(Data, Length, 1, f); return PlayVideo1(Data, Length, false); } int cXineDevice::PlayVideo1(const uchar *Data, int Length, const bool stillImageData) { LOG_ME(::fprintf(stderr, "V");) if (f) { LOG_ME(::fprintf(stderr, "<");) return 0; } if (pmNone == pm) { cMutexLock pmMutexLock(&m_pmMutex); if (pmNone == pm) m_pmCondVar.Wait(m_pmMutex); } int retVal = PlayVideo2(Data, Length, stillImageData); LOG_ME(::fprintf(stderr, "v");) return retVal; } int cXineDevice::PlayCommon2(const uchar *Data, int Length, int64_t ptsForce) { /* if (blahblah) { fprintf(stderr, "blahblah C2"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ do // ... while (false); { if (Length < 6) { VERBOSE_NOP(); break; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP(); break; } int l = 6 + Data[ 4 ] * 0x0100 + Data[ 5 ]; if (Length < l) { VERBOSE_NOP(); break; } if (0xe0 != (Data[ 3 ] & 0xf0) //video && 0xc0 != (Data[ 3 ] & 0xe0) //audio && 0xbd != Data[ 3 ]) //private (dolby, pcm) { VERBOSE_NOP(); break; } int payloadOffset = 0; if (AnalyzePesHeader(Data, Length, payloadOffset) <= phInvalid) { VERBOSE_NOP(); break; } if (l <= payloadOffset) { // drop short frames // ::fprintf(stderr, "i"); return Length; } // if (0xc0 == (Data[ 3 ] & 0xe0)) //audio // return Length; } while (false); return PlayCommon3(Data, Length, ptsForce); } typedef long long int lld_t; static int xzabc = 0; int cXineDevice::PlayCommon3(const uchar *Data, int Length, int64_t ptsForce) { // if (!m_settings.LiveTV()) // { // VERBOSE_NOP1(); // return Length; // } // if (xzabc) if (0) { if (0xe0 == (Data[ 3 ] & 0xf0)) //video { uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(Data, Length, 0, pt); if (pt != NO_PICTURE) { static int64_t last = -1; int64_t pts = -1; getPTS(Data, Length, pts); fprintf(stderr, "** %c ** %lld ** %lld ** %lld **\n", "0IPB4567"[pt], (lld_t)ptsForce, (lld_t)(ptsForce - last), (lld_t)pts); last = ptsForce; } } if (0xc0 == (Data[ 3 ] & 0xe0)) //audio { { static int64_t last = -1; int64_t pts = -1; getPTS(Data, Length, pts); fprintf(stderr, "\t\t\t\t\t\t\t** %c ** %lld ** %lld ** %lld **\n", 'A', (lld_t)ptsForce, (lld_t)(ptsForce - last), (lld_t)pts); last = ptsForce; } } } if (0) { int64_t pts = ptsForce; if (ptsForce > -1 || getPTS(Data, Length, pts)) { xzabc = 0; int64_t *pPTS = 0; if (0xe0 == (Data[ 3 ] & 0xf0)) //video { pPTS = &ptsV; } else if (0xc0 == (Data[ 3 ] & 0xe0)) //audio { pPTS = &ptsA; } else if (0xbd == Data[ 3 ]) //private (dolby, pcm) { int h = Data[ 6 + 2 ]; if (0xa0 == (0xf0 & Data[ 6 + 3 + h + 0 ])) // pcm? pPTS = &ptsP; else pPTS = &ptsD; } else { xfprintf(stderr, "0x%02x\t", Data[ 3 ]); VERBOSE_NOP(); } if (pPTS && *pPTS != pts) { *pPTS = pts; int64_t ptsX = -1; m_xineLib.execFuncGetPTS(ptsX); int64_t dV = (ptsV != -1 && ptsX != -1) ? ptsV - ptsX : 0; int64_t dA = (ptsA != -1 && ptsX != -1) ? ptsA - ptsX : 0; int64_t dP = (ptsP != -1 && ptsX != -1) ? ptsP - ptsX : 0; int64_t dD = (ptsD != -1 && ptsX != -1) ? ptsD - ptsX : 0; int64_t dVA = (ptsV != -1 && ptsA != -1) ? ptsA - ptsV : 0; int64_t dVD = (ptsV != -1 && ptsD != -1) ? ptsD - ptsV : 0; fprintf(stderr, "ptsVideo: %lld, ptsAudio: %lld, ptsPCM: %lld, ptsDolby: %lld, ptsXine: %lld, dVA: %lld, dVD: %lld, dV: %lld, dA: %lld, dP: %lld, dD: %lld\n", (lld_t)ptsV, (lld_t)ptsA, (lld_t)ptsP, (lld_t)ptsD, (lld_t)ptsX, (lld_t)dVA, (lld_t)dVD, (lld_t)dV, (lld_t)dA, (lld_t)dP, (lld_t)dD); } } } /* if (blahblah) { blahblah = false; fprintf(stderr, "blahblah C3"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ /* if (aaz) { if (Data[3] == 0xbd || (Data[3] & 0xe0) == 0xc0) { fprintf(stderr, "===== %c =====\n", Data[3] == 0xbd ? 'D' : 'A'); if (aaz != (Data[3] == 0xbd ? 1 : 2)) *(char *)0 = 0; aaz = 0; } } */ int done = 0; while (done < Length) { int r = m_xineLib.execFuncStream1(Data + done, Length - done); if (r < 0) return r; done += r; } return done; } int cXineDevice::PlayVideo2(const uchar *Data, int Length, const bool stillImageData) { // fprintf(stderr, "D"); int done = 0; while (done < Length) { char ch = 'X'; int todo = Length - done; if (todo >= 6) { if (0x00 == Data[ done + 0 ] && 0x00 == Data[ done + 1 ] && 0x01 == Data[ done + 2 ]) { int id = Data[ done + 3 ]; int len = 6 + Data[ done + 4 ] * 0x0100 + Data[ done + 5 ]; if (0xba == id) // pack header has fixed length { if (0x00 == (0xc0 & Data[ done + 4 ])) // MPEG 1 len = 12; else // MPEG 2 len = 14 + (Data[ done + 13 ] & 0x07); } else if (0xb9 == id) // stream end has fixed length { len = 4; } if (todo >= len) { todo = len; if (0xbe == id // padding || 0xba == id // system layer: pack header || 0xbb == id // system layer: system header || 0xb9 == id) // system layer: stream end { done += todo; // fprintf(stderr, "x"); continue; } ch = '.'; } else { // ::fprintf(stderr, "todo: %d, len: %d\t", todo, len); VERBOSE_NOP(); ch = '3'; // break; } } else { VERBOSE_NOP(); ch = '2'; } } else { VERBOSE_NOP(); ch = '1'; } // fprintf(stderr, "%c", ch); int r = PlayVideo3(Data + done, todo, stillImageData); if (r < 0) return r; done += r; } return done; } static void resetScramblingControl(const uchar *Data, int Length) { if (Length < 6) { VERBOSE_NOP(); return; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP1(); return; } if (0xe0 != (0xf0 & Data[ 3 ]) // video && 0xc0 != (0xe0 & Data[ 3 ]) // audio && 0xbd != (0xff & Data[ 3 ]) // dolby && 0xbe != (0xff & Data[ 3 ]) // padding (DVD) && 0xba != (0xff & Data[ 3 ]) // system layer: pack header && 0xbb != (0xff & Data[ 3 ]) // system layer: system header && 0xb9 != (0xff & Data[ 3 ])) // system layer: stream end { VERBOSE_NOP(); return; } if (0xbe == (0xff & Data[ 3 ]) // padding (DVD) || 0xba == (0xff & Data[ 3 ]) // system layer: pack header || 0xbb == (0xff & Data[ 3 ]) // system layer: system header || 0xb9 == (0xff & Data[ 3 ])) // system layer: stream end { return; } int len = (6 + Data[ 4 ] * 0x100 + Data[ 5 ]); if (len < 6 + 1 || Length < 6 + 1) { VERBOSE_NOP(); return; } if (0x00 != (0x30 & Data[ 6 ])) { if (0x80 == (0xc0 & Data[ 6 ])) // only touch MPEG2 { xfprintf(stderr, "reseting PES_scrambling_control: 0x%02x\n", Data[ 6 ]); ((uchar *)Data)[ 6 ] &= ~0x30; } } } int cXineDevice::PlayVideo3(const uchar *Data, int Length, const bool stillImageData) { /* if (blahblah) { fprintf(stderr, "blahblah V3"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ resetScramblingControl(Data, Length); dumpAudio("Video", Data, Length); return PlayCommon(Data, Length, stillImageData); } static double videoFrameDuration(const uchar *Data, int Length) { int PesPayloadOffset = 0; ePesHeader ph = AnalyzePesHeader(Data, Length, PesPayloadOffset); if (ph < phMPEG1) return -1; if ((Data[ 3 ] & 0xf0) != 0xe0) return -1; if (Length < PesPayloadOffset + 8) return -1; const uchar *p = Data + PesPayloadOffset; if (*p++ != 0x00) return -1; if (*p++ != 0x00) return -1; if (*p++ != 0x01) return -1; if (*p++ != 0xb3) return -1; p += 3; static const double frameRates[ 16 ] = { -1, 24000/1001.0, 24, 25, 30000/1001.0, 30, 50, 60000/1001.0, 60, -1, -1, -1, -1, -1, -1, -1, }; int frameRateIndex = *p & 0x0f; if (frameRates[ frameRateIndex ] < 0) return -1; long n = 0, d = 0; if (ph == phMPEG2) { const uchar *const limit = Data + Length - 10 + 3; while (p < limit) { if (p[ 0 ] != 0x01 || p[ -1 ] != 0x00 || p[ -2 ] != 0x00) { p++; continue; } if (p[ 1 ] != 0xb5) // extension start code break; if ((p[ 2 ] & 0xf0) != 0x10) // sequence extension break; p += 7; n = (*p & 0x60) >> 5; d = (*p & 0x1f); break; } } return 90000 / (frameRates[ frameRateIndex ] * (n + 1) / (d + 1)); } static int frameSizes[ 256 ]; static double audioFrameDuration(const uchar *Data, int Length) { int PesPayloadOffset = 0; ePesHeader ph = AnalyzePesHeader(Data, Length, PesPayloadOffset); if (ph < phMPEG1) return -1; if ((Data[ 3 ] & 0xff) == 0xbd) { static int samplingFrequencies[ 4 ] = // all values are specified in Hz { 48000, 44100, 32000, -1 }; if (PesPayloadOffset + 5 <= Length && Data[ PesPayloadOffset + 0 ] == 0x0b && Data[ PesPayloadOffset + 1 ] == 0x77 && frameSizes[ Data[ PesPayloadOffset + 4 ] ] > 0) { if (samplingFrequencies[ Data[ PesPayloadOffset + 4 ] >> 6 ] < 0) return -1; return 90000.0 * 1536 / samplingFrequencies[ Data[ PesPayloadOffset + 4 ] >> 6 ]; } else if (PesPayloadOffset + 4 + 5 <= Length && Data[ PesPayloadOffset + 4 + 0 ] == 0x0b && Data[ PesPayloadOffset + 4 + 1 ] == 0x77 && frameSizes[ Data[ PesPayloadOffset + 4 + 4 ] ] > 0) { if (samplingFrequencies[ Data[ PesPayloadOffset + 4 + 4 ] >> 6 ] < 0) return -1; return 90000.0 * 1536 / samplingFrequencies[ Data[ PesPayloadOffset + 4 + 4 ] >> 6 ]; } else return -1; } if ((Data[ 3 ] & 0xe0) != 0xc0) return -1; if (Length < PesPayloadOffset + 4) return -1; ulong Header = 0; Header |= Data[ PesPayloadOffset + 0 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 1 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 2 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 3 ]; bool Mpeg2 = (ph == phMPEG2); int syncword = (Header & 0xFFF00000) >> 20; int id = (Header & 0x00080000) >> 19; int layer = (Header & 0x00060000) >> 17; // int protection_bit = (Header & 0x00010000) >> 16; int bitrate_index = (Header & 0x0000F000) >> 12; int sampling_frequency = (Header & 0x00000C00) >> 10; // int padding_bit = (Header & 0x00000200) >> 9; // int private_bit = (Header & 0x00000100) >> 8; // int mode = (Header & 0x000000C0) >> 6; // int mode_extension = (Header & 0x00000030) >> 4; // int copyright = (Header & 0x00000008) >> 3; // int orignal_copy = (Header & 0x00000004) >> 2; int emphasis = (Header & 0x00000003); if (syncword != 0xFFF) return -1; if (id == 0 && !Mpeg2) // reserved in MPEG 1 return -1; if (layer == 0) // reserved return -1; if (bitrate_index == 0xF) // forbidden return -1; if (sampling_frequency == 3) // reserved return -1; if (emphasis == 2) // reserved return -1; static int samplingFrequencies[ 2 ][ 4 ] = // all values are specified in Hz { { 44100, 48000, 32000, -1 }, // MPEG 1 { 22050, 24000, 16000, -1 } // MPEG 2 }; static int slots_per_frame[ 2 ][ 3 ] = { { 12, 144, 144 }, // MPEG 1, Layer I, II, III { 12, 144, 72 } // MPEG 2, Layer I, II, III }; int mpegIndex = 1 - id; int layerIndex = 3 - layer; // Layer I (i. e., layerIndex == 0) has a larger slot size int slotSize = (layerIndex == 0) ? 4 : 1; // bytes int sf = samplingFrequencies[ mpegIndex ][ sampling_frequency ]; //fprintf(stderr, "afd: %.3f ms, PES-Length: %d\n", 1000.0 * 8 * slotSize * slots_per_frame[ mpegIndex ][ layerIndex ] / sf, Length); return 90000.0 * 8 * slotSize * slots_per_frame[ mpegIndex ][ layerIndex ] / sf; } static double softStartTime = 0; static const double softStartSpeedStart = 0.75; static const double softStartSpeedMin = 0.15; static double softStartDurationVideoSD = 31 / 25.0; static double softStartDurationVideoHD = 31 / 25.0; static double softStartDurationAudio = 31 / 25.0; static const int softStartMaxSpeedChanges = 20; static double softStartLastSpeed = -1; static double softStartSpeedChangeTime = -1; static int64_t softStartPtsVdr = -1; static const int64_t softStartLogPtsDelta = 4 * 90000; static int softStartHitPoll = 0; static bool softStartNoMetronom = false; static const double pi = 4 * ::atan(1); static bool softStartHasVideo = true; static bool softStartHasAudio = true; static bool softStartIsVideoHD = false; inline double getSoftStartDuration(const bool forVideo) { if (forVideo) return softStartHasVideo ? (softStartIsVideoHD ? softStartDurationVideoHD : softStartDurationVideoSD) : 0; return softStartHasAudio ? softStartDurationAudio : 0; } inline double getSoftStartDuration() { double dV = getSoftStartDuration(true); double dA = getSoftStartDuration(false); return (dV > dA) ? dV : dA; } static double softStartCalcSpeed0(const double /* t */) { return 0; } static double softStartCalcSpeed1(const double t) { const double p = (1 + ::cos(2 * pi * t)) / 2; return softStartSpeedMin + p * ((softStartSpeedStart * (1 - t) + 1.0 * t) - softStartSpeedMin); } static double softStartCalcSpeed2(const double t) { double p = 2 * t - 1; if (p < 0) p = -p; // p = p * p * p; p = p * p; return softStartSpeedMin + p * ((softStartSpeedStart * (1 - t) + 1.0 * t) - softStartSpeedMin); } static double softStartCalcSpeed3(const double t) { if (t < 0.25) return softStartSpeedStart; if (t < 0.50) return softStartSpeedMin + (1 - softStartSpeedMin) * 0 / 3; if (t < 0.75) return softStartSpeedMin + (1 - softStartSpeedMin) * 1 / 3; return softStartSpeedMin + (1 - softStartSpeedMin) * 2 / 3; } static double softStartCalcSpeed4(const double t) { const double p = (1 + ::cos(pi * (1 + t))) / 2; return softStartSpeedMin + (1 - softStartSpeedMin) * p; } static double softStartCalcSpeed(const double t) { if (t >= 1) return 1; return softStartCalcSpeed0(t); // choose a method (void)softStartCalcSpeed0; (void)softStartCalcSpeed1; (void)softStartCalcSpeed2; (void)softStartCalcSpeed3; (void)softStartCalcSpeed4; } bool softStartPoll(cXineLib &xineLib, cPoller &poller, const int timeout, bool result) { if (softStartState > sIdle) { if (result) { softStartHitPoll = 0; } else if (++softStartHitPoll >= 2) { do { softStartState = sIdle; xineLib.execFuncFirstFrame(); xineLib.execFuncSetSpeed(100.0); xineLib.execFuncWait(); } while (!xineLib.Poll(poller, timeout, true)); softStartHitPoll = 0; result = true; } // ::fprintf(stderr, "softStartHitPoll: %d, %d\n", result, softStartHitPoll); } return result; } static int64_t vpts, apts, extra0, extra; static bool useApts; static bool seenAudio = false; static bool seenVideo = false; static bool seenApts = false; static bool seenVpts = false; static const int64_t extra_max = 50 * 90000 / 25; static bool oldMode = false; static inline int64_t calcPtsDelta(int64_t lhs, int64_t rhs) { int64_t delta = lhs - rhs; if (delta < -(1ll << 32)) delta += 1ll << 33; else if (delta > +(1ll << 32)) delta -= 1ll << 33; return delta; } static inline double softStartCalcQ0(int64_t vdr, int64_t xine, bool queued, bool forVideo, int64_t hyst) { if (vdr < 0 || xine < 0) return 0; double delta = calcPtsDelta(vdr, xine) / 90000.0; if (delta <= 0) return 0; if (delta >= softStartLogPtsDelta / 90000.0) return queued ? 0 : 1; if (delta >= (getSoftStartDuration(forVideo) + hyst / 90000.0)) return 1; return delta / (getSoftStartDuration(forVideo) + hyst / 90000.0); } static bool gotQ1 = false; static int64_t hystQ = 0; static int64_t hystQ1 = 0; static bool vdrTime100reload = false; static inline double softStartCalcQ(int64_t vdr, int64_t xine, bool queued, bool forVideo) { return softStartCalcQ0(vdr, xine, queued, forVideo, hystQ); } static inline double softStartCalcQ(int64_t vdrVideo, int64_t vdrAudio, int64_t xine, bool queued) { double qV = softStartHasVideo ? softStartCalcQ(vdrVideo, xine, queued, true) : 1; double qA = softStartHasAudio ? softStartCalcQ(vdrAudio, xine, queued, false) : 1; double q = (qV < qA) ? qV : qA; bool gQ1 = (q >= 1); if (gQ1 != gotQ1) { gotQ1 = gQ1; if (gotQ1) { hystQ = 0; } else { vdrTime100reload = true; hystQ = (hystQ1 += 90000 / 25); } } //fprintf(stderr, "q: %.3lf, gq1: %d, hystQ: %lld, hystQ1: %lld, vdr: %lld, xine: %lld, d: %lld\n", q, gotQ1, hystQ, hystQ1, vdr, xine, vdr - xine); return q; } static int64_t vdrPTSLast = -2; static int64_t vdrAptsLast = -1; static int64_t vdrVptsLast = -1; static double vdrVptsCalced = -1; static double vdrAptsCalced = -1; static double vdrVduration = -1; static double vdrAduration = -1; static double vdrVptsBuffered[ 2 ] = { -1, -1 }; static bool getPTS(const uchar *Data, int Length, int64_t &pts, bool isVideo, bool isAudio, double &vptsCalced, double &aptsCalced, double &vDuration, double &aDuration, double vptsBuffered[2]) { uchar pt = NO_PICTURE; if (isVideo) { cRemux::ScanVideoPacket(Data, Length, 0, pt); if (pt != NO_PICTURE && vDuration > 0 && vptsBuffered[ 1 ] > 0) vptsBuffered[ 1 ] += vDuration; } bool retVal = false; if (getPTS(Data, Length, pts)) { if (isAudio) { aptsCalced = pts; double duration = audioFrameDuration(Data, Length); if (duration >= 0) aDuration = duration; } if (isVideo) { if (pt == B_FRAME) vptsCalced = pts; else if (pt != NO_PICTURE) vptsBuffered[ 1 ] = pts; double duration = videoFrameDuration(Data, Length); if (duration >= 0) vDuration = duration; if (pt != B_FRAME) goto other; } return true; } else { if (isAudio) { double duration = audioFrameDuration(Data, Length); bool frameStart = (duration > -1); if (aptsCalced > -1 && aDuration > -1 && frameStart) { aptsCalced += aDuration; while (aptsCalced >= 0x200000000ull) aptsCalced -= 0x200000000ull; pts = (int64_t)aptsCalced; retVal = true; } if (duration >= 0) aDuration = duration; } if (isVideo) { other: double dur = vDuration; if (pt != B_FRAME && pt != NO_PICTURE) { vptsCalced = vptsBuffered[ 0 ]; vptsBuffered[ 0 ] = vptsBuffered[ 1 ]; dur = 0; } bool frameStart = (pt != NO_PICTURE); if (vptsCalced > -1 && dur > -1 && frameStart) { vptsCalced += dur; while (vptsCalced >= 0x200000000ull) vptsCalced -= 0x200000000ull; pts = (int64_t)vptsCalced; retVal = true; } double duration = videoFrameDuration(Data, Length); if (duration >= 0) vDuration = duration; } return retVal; } } static double vdrTime100 = -1; int cXineDevice::PlayCommon(const uchar *Data, int Length, const bool stillImageData) { const bool isAudio = !IsVideo(Data, Length); const bool isVideo = !isAudio && !stillImageData; //if (isAudio && ts) fprintf(stderr, "A"); if ((stillImageData || m_xineLib.isTrickSpeedMode()) && isAudio) { // ::fprintf(stderr, "x"); return Length; } /* if (stillImageData) fprintf(stderr, "writing: %d\n", Length - 6); */ if (findVideo && (isVideo || isAudio)) { findVideo = false; foundVideo = isVideo; //xfprintf(stderr, "foundVideo: %d\n", foundVideo); } if (0 && isAudio) { static int64_t opts = -1; static int64_t xpts = -1; getPTS(Data, Length, xpts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered); //fprintf(stderr, "audio: %.3lf, %d, %lld, %lld\n", audioFrameDuration(Data, Length), Length, xpts, xpts - opts); opts = xpts; } if (0 && isVideo) { static int64_t opts = -1; static int64_t xpts = -1; getPTS(Data, Length, xpts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered); //fprintf(stderr, "video: %.3lf, %d, %lld, %lld\n", videoFrameDuration(Data, Length), Length, xpts, xpts - opts); opts = xpts; } int64_t ptsForce = -1; { cMutexLock lock(&softStartMutex); if (softStartTrigger && !stillImageData) { softStartNoMetronom = (sstNoMetronom == softStartTrigger); softStartTrigger = sstNone; softStartState = sInitiateSequence; //fprintf(stderr, "/////////////////////////////\n"); // ::fprintf(stderr, "#(%d,%d)", ::getpid(), pthread_self()); } if (stillImageData) { softStartLastSpeed = -1; } else if (softStartState > sIdle) { timeval tv; ::gettimeofday(&tv, 0); const double now = (tv.tv_sec + tv.tv_usec / 1.0e+6); if (softStartState == sInitiateSequence) { xfprintf(stderr, "["); softStartDurationVideoSD = m_settings.GetModeParams()->m_prebufferFramesVideoSD / 25.0; softStartDurationVideoHD = m_settings.GetModeParams()->m_prebufferFramesVideoHD / 25.0; softStartDurationAudio = m_settings.GetModeParams()->m_prebufferFramesAudio / 25.0; softStartHasVideo = false; softStartHasAudio = false; softStartIsVideoHD = false; softStartTime = now; softStartSpeedChangeTime = -1; softStartLastSpeed = -1; softStartPtsVdr = -1; softStartHitPoll = 0; softStartState = sStartSequence; vpts = -1; apts = -1; extra0 = 0; extra = 0; useApts = 1; seenAudio = false; seenVideo = false; seenApts = false; seenVpts = false; vdrPTSLast = -2; vdrAptsLast = -1; vdrVptsLast = -1; vdrAptsCalced = -1; vdrVptsCalced = -1; vdrAduration = -1; vdrVduration = -1; vdrVptsBuffered[ 0 ] = -1; vdrVptsBuffered[ 1 ] = -1; vdrTime100 = -1; vdrTime100reload = false; gotQ1 = false; hystQ = hystQ1 = 90000 * m_settings.GetModeParams()->m_prebufferHysteresis / 25; } { char packetType = 0; char packetTypeOnce = 0; if (isVideo) { if (!seenVideo) { softStartHasVideo = true; softStartIsVideoHD = cRemux::IsFrameH264(Data, Length); //fprintf(stderr, "\nH264: %d\n", softStartIsVideoHD); seenVideo = true; // ::fprintf(stderr, "seen video\n"); // xfprintf(stderr, "v"); packetTypeOnce = 'v'; } packetType = 'v'; } else if (isAudio) { if (!seenAudio) { softStartHasAudio = true; audioSeen = true; seenAudio = true; // ::fprintf(stderr, "seen audio\n"); // xfprintf(stderr, "a"); packetTypeOnce = 'a'; } packetType = 'a'; } int64_t pts = 0; if (getPTS(Data, Length, pts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered)) { ptsForce = pts; if (isVideo) { if (!seenVpts) { seenVpts = true; // ::fprintf(stderr, "seen video pts\n"); // xfprintf(stderr, "V"); packetTypeOnce = 'V'; } packetType = 'V'; vpts = pts; if (apts > -1) { int64_t delta = calcPtsDelta(vpts, apts); if (delta < 0) delta = - delta; if (extra0 < delta) { extra0 = delta; // ::fprintf(stderr, "max. A/V delta: %lld pts => total extra buffering: %d frames", extra0, (int)(extra0 * 25 / 90000)); extra = extra0; if (extra > extra_max) { extra = extra_max; // ::fprintf(stderr, ", limited to %d frames", (int)(extra * 25 / 90000)); } // ::fprintf(stderr, "\n"); if (oldMode) xfprintf(stderr, "+%d", (int)(extra * 25 / 90000)); } } // ::fprintf(stderr, "video: v: %lld, a: %lld, d: %lld, e: %lld\n", vpts, apts, vpts - apts, extra); } else if (isAudio) { if (!seenApts) { seenApts = true; // ::fprintf(stderr, "seen audio pts\n"); // xfprintf(stderr, "A"); packetTypeOnce = 'A'; } packetType = 'A'; apts = pts; if (vpts > -1) { int64_t delta = calcPtsDelta(vpts, apts); if (delta < 0) delta = - delta; if (extra0 < delta) { extra0 = delta; // ::fprintf(stderr, "max. A/V delta: %lld pts => total extra buffering: %d frames", extra0, (int)(extra0 * 25 / 90000)); extra = extra0; if (extra > extra_max) { extra = extra_max; // ::fprintf(stderr, ", limited to %d frames", (int)(extra * 25 / 90000)); } // ::fprintf(stderr, "\n"); if (oldMode) xfprintf(stderr, "+%d", (int)(extra * 25 / 90000)); } } // ::fprintf(stderr, "audio: v: %lld, a: %lld, d: %lld, e: %lld\n", vpts, apts, vpts - apts, extra); } } //xfprintf(stderr, "%s%c", getFrameType(Data, Length), packetType); packetTypeOnce = 0; //ZZZ // if (packetTypeOnce) xfprintf(stderr, "%c", packetTypeOnce); } if ((seenVideo && !seenVpts) || (seenAudio && !seenApts)) { softStartTime = now; } if (softStartState >= sStartSequence) { if (isVideo) useApts = false; if (useApts || isVideo) { softStartPtsVdr = ptsForce; // getPTS(Data, Length, softStartPtsVdr); if (softStartPtsVdr != -1) { bool queued = false; int64_t ptsXine = -1; m_xineLib.execFuncGetPTS(ptsXine, 0, &queued); const int64_t delta = (ptsXine >= 0) ? calcPtsDelta(softStartPtsVdr, ptsXine) : 0; if (softStartState == sStartSequence || delta < -softStartLogPtsDelta || (delta > +softStartLogPtsDelta && queued)) { // if (softStartState != sStartSequence) // ::fprintf(stderr, "SoftStart: ptsVdr: %12"PRId64", ptsXine: %12"PRId64", delta: %12"PRId64", queued: %d\n", softStartPtsVdr, ptsXine, delta, queued); //AAA m_xineLib.execFuncStart(); //AAA m_xineLib.execFuncWait(); if (!softStartNoMetronom) { xfprintf(stderr, "M"); // xfprintf(stderr, " %12" PRId64 ", %12" PRId64 ", %12" PRId64 "\n", delta, softStartPtsVdr, ptsXine); //ZZZ m_xineLib.execFuncMetronom(softStartPtsVdr); //ZZZ m_xineLib.execFuncWait(); } softStartTime = now; softStartState = sContinueSequence; } } } } // if (softStartState <= sStartSequence) // stripPTSandDTS((uchar *)Data, Length); m_xineLib.execFuncFirstFrame(); int64_t vdrPTS = -1; if ((seenVideo && vpts <= -1) || (seenAudio && apts <= -1)) { } else if (vpts > -1) { if (apts > -1 && vpts > apts) vdrPTS = apts; else vdrPTS = vpts; } else if (apts > -1) vdrPTS = apts; bool queued = false; int64_t xinePTS = -1; m_xineLib.execFuncGetPTS(xinePTS, 0, &queued); const double totalDuration = (getSoftStartDuration() + extra / 90000.0); const double q = oldMode ? ((now - softStartTime) / totalDuration) : softStartCalcQ(vpts, apts, xinePTS, queued); double p = softStartCalcSpeed(q); if (!oldMode) { if (vdrPTSLast == vdrPTS || vdrVptsLast > vpts || vdrAptsLast > apts) p = softStartLastSpeed / 100; else vdrPTSLast = vdrPTS; if (apts != vdrAptsLast || vpts != vdrVptsLast) { // xfprintf(stderr, "p: %.3lf, DA: %lld, DV: %lld, DC: %lld\n", p, (apts > -1) ? (apts - xinePTS) : -1, (vpts > -1) ? (vpts - xinePTS) : -1, (vdrPTS > - 1) ? (vdrPTS - xinePTS) : -1); // xfprintf(stderr, "(%.1lf|%.1lf)", 25 * ((apts > -1) ? ((apts - xinePTS) / 90000.0) : 0.0), 25 * ((vpts > -1) ? ((vpts - xinePTS) / 90000.0) : 0.0)); } vdrVptsLast = vpts; vdrAptsLast = apts; } double speed = (p > 0) ? (100.0 * p) : 12.5; if (speed >= 100.0 || xinePTS == -1) { bool newlineRequired = false; if (vdrTime100 < 0) { ttt6 = tNow(); /* fprintf(stderr, "+++++ %.3lf ms, %.3lf ms, %.3lf ms, (%.3lf ms, %.3lf ms, %.3lf ms) +++++\n" , (ttt1 - ttt0) * 1000.0 , (ttt2 - ttt1) * 1000.0 , (ttt3 - ttt2) * 1000.0 , (ttt4 - ttt3) * 1000.0 , (ttt5 - ttt3) * 1000.0 , (ttt6 - ttt3) * 1000.0 ); */ vdrTime100 = now; xfprintf(stderr, "]"); newlineRequired = true; } speed = 100.0; if (vdrTime100reload) { vdrTime100reload = false; vdrTime100 = now; } if (xinePTS < 0) softStartState = sIdle; else if ((now - vdrTime100) >= m_settings.GetModeParams()->m_monitoringDuration) { if (m_settings.GetModeParams()->MonitoringContinuous()) hystQ1 = 90000 * (m_settings.GetModeParams()->m_prebufferHysteresis - 1) / 25; else softStartState = sIdle; } if ((softStartState == sIdle || speed != softStartLastSpeed) && vdrPTS > -1 && xinePTS > -1) { xfprintf(stderr, "buffered %.1lf frames (v:%.1lf, a:%.1lf)", calcPtsDelta(vdrPTS, xinePTS) / 90000.0 * 25, (vpts > -1) ? (calcPtsDelta(vpts, xinePTS) / 90000.0 * 25) : 0.0, (apts > -1) ? (calcPtsDelta(apts, xinePTS) / 90000.0 * 25) : 0.0); if (softStartState == sIdle) xfprintf(stderr, " <<<<<"); newlineRequired = true; } if (newlineRequired) xfprintf(stderr, "\n"); } if (queued) speed = 100; if (100.0 == speed || (speed != softStartLastSpeed && (!oldMode || (now - softStartSpeedChangeTime) > (totalDuration / softStartMaxSpeedChanges)))) { softStartSpeedChangeTime = now; // fprintf(stderr, "slowstart: %lg, %lg\n", speed, p); m_xineLib.execFuncSetSpeed(speed); m_xineLib.execFuncWait(); // if (100.0 == speed) // m_xineLib.execFuncSetPrebuffer(m_settings.GetModeParams()->m_prebufferFrames); softStartLastSpeed = speed; } } } //if (jw) *(char *)0 = 0; jw = false; // fprintf(stderr, "v"); if (ts) { //NNN stripPTSandDTS((uchar *)Data, Length); } else if (0) //ZZZ { int64_t pts = 0; if (np && getPTS(Data, Length, pts)) { np = false; // fprintf(stderr, "M %lld %llx\n", pts); m_xineLib.execFuncMetronom(pts); m_xineLib.execFuncWait(); } } int r = PlayCommon1(Data, Length, ptsForce); // fprintf(stderr, "V"); return r; } int cXineDevice::PlayCommon1(const uchar *Data, int Length, int64_t ptsForce) { /* if (blahblah) { fprintf(stderr, "blahblah C1"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ if (oldMode) return PlayCommon2(Data, Length, ptsForce); struct sBuffer { sBuffer *next; int length; int64_t ptsForce; uchar data[1]; }; static sBuffer *pHead = 0; static sBuffer *pTail = 0; static int bufCount = 0; while (!doClear && pHead) { if (sIdle != softStartState) { cPoller p; if (!m_xineLib.Poll(p, -1)) break; } int r = PlayCommon2(pHead->data, pHead->length, pHead->ptsForce); if (r > 0) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); //fprintf(stderr, "bufCount: %d\n", --bufCount); } else if (r < 0) { while (pHead) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); } bufCount = 0; pTail = 0; //fprintf(stderr, "--- bufCount: %d\n", bufCount); return r; } else break; } if (doClear) { //fprintf(stderr, "----- clearing -----\n"); while (pHead) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); } bufCount = 0; pTail = 0; doClear = false; //fprintf(stderr, "=== bufCount: %d\n", bufCount); // return Length; } if (!pHead) { pTail = 0; do // ... while (false); { if (sIdle != softStartState) { cPoller p; if (!m_xineLib.Poll(p, -1)) break; //fprintf(stderr, "### bufCount: %d\n", bufCount); } return PlayCommon2(Data, Length, ptsForce); } while (false); } sBuffer *p = (sBuffer *)::malloc(sizeof (sBuffer) - sizeof (p->data) + Length); if (!p) return 0; p->next = 0; p->length = Length; p->ptsForce = ptsForce; memcpy(&p->data, Data, Length); if (!pTail) pHead = pTail = p; else { pTail->next = p; pTail = p; } //fprintf(stderr, "*** bufCount: %d\n", ++bufCount); return Length; } static uchar jumboPESdata[ 6 + 0xffff ]; static uchar *jumboPEStailData = 0; static bool mkJumboPES(const uchar *Data, int Length) { int origJumboPESsize = jumboPESsize; jumboPESsize = 0; if (Length < 9) VERBOSE_RETURN0(false); if (0x00 != Data[ 0 ]) VERBOSE_RETURN0(false); if (0x00 != Data[ 1 ]) VERBOSE_RETURN0(false); if (0x01 != Data[ 2 ]) VERBOSE_RETURN0(false); if (0xbd != Data[ 3 ]) VERBOSE_RETURN0(false); int l = Data[ 4 ] * 256 + Data[ 5 ]; if ((6 + l) != Length) { const uchar *data = Data + (6 + l); int length = Length - (6 + l); if (length < 6) VERBOSE_RETURN3(false); if (0x00 != data[ 0 ]) VERBOSE_RETURN3(false); if (0x00 != data[ 1 ]) VERBOSE_RETURN3(false); if (0x01 != data[ 2 ]) VERBOSE_RETURN3(false); if (0xbe != data[ 3 ]) VERBOSE_RETURN3(false); int L = data[ 4 ] * 256 + data[ 5 ]; if ((6 + L) != length) VERBOSE_RETURN3(false); // ignore padding Length -= length; } /* for (int i = 0; i < 20; i++) fprintf(stderr, "%02x ", Data[ i ]); fprintf(stderr, "\n"); */ bool cont = (0x80 == Data[ 6 ] && 0x00 == Data[ 7 ] && 0x00 == Data[ 8 ]); if (cont && Length >= 6 + 3 + 5 && Data[ 9 ] == 0x0b && Data[ 10 ] == 0x77 && frameSizes[ Data[ 13 ] ] > 0) { cont = false; } if (!cont || 0 == origJumboPESsize) { if (0 != origJumboPESsize) VERBOSE_RETURN0(false); if ((origJumboPESsize + Length - 0) > (6 + 0xffff)) VERBOSE_RETURN0(false); if (jumboPEStail > 0) { int headerSize = 6 + 3 + Data[ 8 ]; ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 0 ], headerSize); ::memmove(&jumboPESdata[ origJumboPESsize + headerSize ], jumboPEStailData, jumboPEStail); ::memcpy(&jumboPESdata[ origJumboPESsize + headerSize + jumboPEStail ], &Data[ headerSize ], Length - headerSize); origJumboPESsize += headerSize + jumboPEStail + Length - headerSize; jumboPEStail = 0; jumboPEStailData = 0; //FIXME: PTS should be adjusted to take care of jumboPEStail's duration. // Otherwise there is a certain jitter on audio duration <=> PTS. } else { ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 0 ], Length - 0); origJumboPESsize += Length - 0; } } else { if (0 == origJumboPESsize) VERBOSE_RETURN0(false); if ((origJumboPESsize + Length - 9) > (6 + 0xffff)) VERBOSE_RETURN0(false); ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 9 ], Length - 9); origJumboPESsize += Length - 9; } if (0 == origJumboPESsize) VERBOSE_RETURN0(false); jumboPESsize = origJumboPESsize; if (2048 == Length) { // fprintf(stderr, " b %d", jumboPESsize); return false; } jumboPESdata[ 4 ] = (jumboPESsize - 6) >> 8; jumboPESdata[ 5 ] = (jumboPESsize - 6) & 0xff; // fprintf(stderr, " B %d", jumboPESsize); return true; } static int initFrameSizes() { ::memset(frameSizes, 0, sizeof (frameSizes)); // fs = 48 kHz frameSizes[ 0x00 ] = 64; frameSizes[ 0x01 ] = 64; frameSizes[ 0x02 ] = 80; frameSizes[ 0x03 ] = 80; frameSizes[ 0x04 ] = 96; frameSizes[ 0x05 ] = 96; frameSizes[ 0x06 ] = 112; frameSizes[ 0x07 ] = 112; frameSizes[ 0x08 ] = 128; frameSizes[ 0x09 ] = 128; frameSizes[ 0x0a ] = 160; frameSizes[ 0x0b ] = 160; frameSizes[ 0x0c ] = 192; frameSizes[ 0x0d ] = 192; frameSizes[ 0x0e ] = 224; frameSizes[ 0x0f ] = 224; frameSizes[ 0x10 ] = 256; frameSizes[ 0x11 ] = 256; frameSizes[ 0x12 ] = 320; frameSizes[ 0x13 ] = 320; frameSizes[ 0x14 ] = 384; frameSizes[ 0x15 ] = 384; frameSizes[ 0x16 ] = 448; frameSizes[ 0x17 ] = 448; frameSizes[ 0x18 ] = 512; frameSizes[ 0x19 ] = 512; frameSizes[ 0x1a ] = 640; frameSizes[ 0x1b ] = 640; frameSizes[ 0x1c ] = 768; frameSizes[ 0x1d ] = 768; frameSizes[ 0x1e ] = 896; frameSizes[ 0x1f ] = 896; frameSizes[ 0x20 ] = 1024; frameSizes[ 0x21 ] = 1024; frameSizes[ 0x22 ] = 1152; frameSizes[ 0x23 ] = 1152; frameSizes[ 0x24 ] = 1280; frameSizes[ 0x25 ] = 1280; // fs = 44.1 kHz frameSizes[ 0x40 ] = 69; frameSizes[ 0x41 ] = 70; frameSizes[ 0x42 ] = 87; frameSizes[ 0x43 ] = 88; frameSizes[ 0x44 ] = 104; frameSizes[ 0x45 ] = 105; frameSizes[ 0x46 ] = 121; frameSizes[ 0x47 ] = 122; frameSizes[ 0x48 ] = 139; frameSizes[ 0x49 ] = 140; frameSizes[ 0x4a ] = 174; frameSizes[ 0x4b ] = 175; frameSizes[ 0x4c ] = 208; frameSizes[ 0x4d ] = 209; frameSizes[ 0x4e ] = 243; frameSizes[ 0x4f ] = 244; frameSizes[ 0x50 ] = 278; frameSizes[ 0x51 ] = 279; frameSizes[ 0x52 ] = 348; frameSizes[ 0x53 ] = 349; frameSizes[ 0x54 ] = 417; frameSizes[ 0x55 ] = 418; frameSizes[ 0x56 ] = 487; frameSizes[ 0x57 ] = 488; frameSizes[ 0x58 ] = 557; frameSizes[ 0x59 ] = 558; frameSizes[ 0x5a ] = 696; frameSizes[ 0x5b ] = 697; frameSizes[ 0x5c ] = 835; frameSizes[ 0x5d ] = 836; frameSizes[ 0x5e ] = 975; frameSizes[ 0x5f ] = 976; frameSizes[ 0x60 ] = 1114; frameSizes[ 0x61 ] = 1115; frameSizes[ 0x62 ] = 1253; frameSizes[ 0x63 ] = 1254; frameSizes[ 0x64 ] = 1393; frameSizes[ 0x65 ] = 1394; // fs = 32 kHz frameSizes[ 0x80 ] = 96; frameSizes[ 0x81 ] = 96; frameSizes[ 0x82 ] = 120; frameSizes[ 0x83 ] = 120; frameSizes[ 0x84 ] = 144; frameSizes[ 0x85 ] = 144; frameSizes[ 0x86 ] = 168; frameSizes[ 0x87 ] = 168; frameSizes[ 0x88 ] = 192; frameSizes[ 0x89 ] = 192; frameSizes[ 0x8a ] = 240; frameSizes[ 0x8b ] = 240; frameSizes[ 0x8c ] = 288; frameSizes[ 0x8d ] = 288; frameSizes[ 0x8e ] = 336; frameSizes[ 0x8f ] = 336; frameSizes[ 0x90 ] = 384; frameSizes[ 0x91 ] = 384; frameSizes[ 0x92 ] = 480; frameSizes[ 0x93 ] = 480; frameSizes[ 0x94 ] = 576; frameSizes[ 0x95 ] = 576; frameSizes[ 0x96 ] = 672; frameSizes[ 0x97 ] = 672; frameSizes[ 0x98 ] = 768; frameSizes[ 0x99 ] = 768; frameSizes[ 0x9a ] = 960; frameSizes[ 0x9b ] = 960; frameSizes[ 0x9c ] = 1152; frameSizes[ 0x9d ] = 1152; frameSizes[ 0x9e ] = 1344; frameSizes[ 0x9f ] = 1344; frameSizes[ 0xa0 ] = 1536; frameSizes[ 0xa1 ] = 1536; frameSizes[ 0xa2 ] = 1728; frameSizes[ 0xa3 ] = 1728; frameSizes[ 0xa4 ] = 1920; frameSizes[ 0xa5 ] = 1920; return 0; }; #if APIVERSNUM < 10318 void cXineDevice::PlayAudio(const uchar *Data, int Length) { cDevice::PlayAudio(Data, Length); PlayAudioCommon(Data, Length); } #else int cXineDevice::GetAudioChannelDevice(void) { return m_audioChannel; } static int m_acd = -1; void cXineDevice::SetAudioChannelDevice(int AudioChannel) { #if APIVERSNUM >= 10701 if (m_acd == AudioChannel) return; #endif m_acd = AudioChannel; xfprintf(stderr, "SetAudioChannelDevice: %d\n", AudioChannel); m_audioChannel = AudioChannel; m_xineLib.execFuncSelectAudio(m_audioChannel); } static int m_dad = -1; void cXineDevice::SetDigitalAudioDevice(bool On) { #if APIVERSNUM >= 10701 if (m_dad == On) return; #endif m_dad = On; xfprintf(stderr, "SetDigitalAudioDevice: %d\n", On); //aaz = On ? 1 : 2; m_xineLib.execFuncSelectAudio(On ? -1 : m_audioChannel); if (pmNone == pm) return; doClear = true; if (m_settings.LiveTV() && !audioSeen) { cMutexLock lock(&softStartMutex); if (softStartState == sIdle) softStartTrigger = sstNoMetronom; return; } m_xineLib.pause(); //xzabc = 1; jumboPESsize = 0; jumboPEStail = 0; if (f) m_xineLib.execFuncSetSpeed(100.0); //double t0 = tNow(); if (m_settings.LiveTV() || !foundVideo) // radio recording: audio channels are not related { ptsV = ptsA = ptsP = ptsD = -1; m_xineLib.execFuncClear(-3); // if (!foundVideo) // m_xineLib.execFuncStart(); // np = true; } m_xineLib.execFuncResetAudio(); if (f) m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); //xzabc = 2; m_xineLib.pause(false); //double t1 = tNow(); fprintf(stderr, "!!!!!!! %.3lf ms\n", (t1 - t0) * 1000); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNoMetronom; } //fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); } #if APIVERSNUM < 10342 int cXineDevice::PlayAudio(const uchar *Data, int Length) { // fprintf(stderr, " 0x%02x ", Data[ 3 ]); return PlayAudioCommon(Data, Length); } #else int cXineDevice::PlayAudio(const uchar *Data, int Length, uchar /* Id */) { // fprintf(stderr, " 0x%02x ", Data[ 3 ]); return PlayAudioCommon(Data, Length); } #endif #endif int cXineDevice::PlayAudioCommon(const uchar *Data, int Length) { if (ttt5 == 0) ttt5 = tNow(); // fprintf(stderr, " 0x%02x: %d ", Data[ 3 ], Data[4] * 256 + Data[5]); /* if (Data[ 3 ] == 0xd0) { FILE *f = fopen("/tmp/d0", "ab"); fwrite(Data, Length, 1, f); fclose(f); } */ { static int i = initFrameSizes(); (void)i; } store_frame(Data, Length, __LINE__); LOG_ME(::fprintf(stderr, "A");) if (f) { LOG_ME(::fprintf(stderr, "<");) return Length; } if (pmNone == pm) { cMutexLock pmMutexLock(&m_pmMutex); if (pmNone == pm) m_pmCondVar.Wait(m_pmMutex); } int retVal = PlayAudio2(Data, Length); LOG_ME(::fprintf(stderr, "a")); return retVal; } int cXineDevice::PlayAudio2(const uchar *Data, int Length) { // fprintf(stderr, "D"); int done = 0; while (done < Length) { char ch = 'X'; int id = 0x00; int todo = Length - done; if (todo >= 6) { if (0x00 == Data[ done + 0 ] && 0x00 == Data[ done + 1 ] && 0x01 == Data[ done + 2 ]) { id = Data[ done + 3 ]; int len = Data[ done + 4 ] * 0x0100 + Data[ done + 5 ]; if (todo >= (6 + len)) { todo = (6 + len); if (0xbe == id) { done += todo; // ::fprintf(stderr, "x"); continue; } ch = '.'; } else { VERBOSE_NOP(); ch = '3'; } } else { VERBOSE_NOP(); ch = '2'; } } else { VERBOSE_NOP(); ch = '1'; } // ::fprintf(stderr, "%c", ch); int r; if (0xbd == id) r = PlayAudio3(Data + done, todo); else r = PlayVideo3(Data + done, todo, false); if (r < 0) return r; if (r != todo) VERBOSE_NOP(); done += r; } return done; } int cXineDevice::PlayAudio3(const uchar *Data, int Length) { resetScramblingControl(Data, Length); store_frame(Data, Length, __LINE__); /* ::fprintf(stderr, "l: %d\t", Length); for (int i = 0; i < 20; i++) ::fprintf(stderr, "%02x ", Data[ i ]); ::fprintf(stderr, "\n"); */ // fprintf(stderr, "A"); if (mkJumboPES(Data, Length)) { int todo = jumboPESsize; jumboPESsize = 0; dumpAudio("Audio", jumboPESdata, todo); bool dolby = false; bool pcm = false; do { if (todo < (6 + 3 + 0 + 2)) break; if (0x00 != jumboPESdata[ 0 ] || 0x00 != jumboPESdata[ 1 ] || 0x01 != jumboPESdata[ 2 ] || 0xbd != jumboPESdata[ 3 ]) { break; } int l = jumboPESdata[ 4 ] * 0x0100 + jumboPESdata[ 5 ]; if (l < (3 + 0 + 2)) break; if (todo < (6 + l)) break; int h = jumboPESdata[ 8 ]; if (l < (3 + h + 2)) break; if (0x0b == jumboPESdata[ 6 + 3 + h + 0 ] && 0x77 == jumboPESdata[ 6 + 3 + h + 1 ]) { if (l < (3 + h + 2 + 2 + 1)) { VERBOSE_NOP(); break; } int frameStart = 6 + 3 + h; bool failed = false; while (true) { int frameSize = 2 * frameSizes[ jumboPESdata[ frameStart + 4 ] ]; if (frameSize <= 0) { failed = true; xfprintf(stderr, "frame_size_code: 0x%02x\n", jumboPESdata[ frameStart + 4 ]); VERBOSE_NOP(); break; } if (frameStart + frameSize > todo) break; frameStart += frameSize; if (frameStart + 2 + 2 + 1 > todo) break; if (0x0b != jumboPESdata[ frameStart + 0 ] || 0x77 != jumboPESdata[ frameStart + 1 ]) { failed = true; VERBOSE_NOP(); break; } } if (failed) break; jumboPEStail = todo - frameStart; jumboPEStailData = jumboPESdata + frameStart; todo = frameStart; jumboPESdata[ 4 + 0 ] = (todo - 6) >> 8; jumboPESdata[ 4 + 1 ] = (todo - 6) & 0xff; dolby = true; store_frame(jumboPESdata, todo, __LINE__); break; } if (0x80 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { dolby = true; break; } if (0xa0 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { pcm = true; break; } for (int i = 6 + 3 + h; i < todo - 2 - 2 - 1; i++) { if (0x0b == jumboPESdata[ i + 0 ] && 0x77 == jumboPESdata[ i + 1 ] && 0 != frameSizes[ jumboPESdata[ i + 4 ] ]) { jumboPEStail = todo - i; jumboPEStailData = jumboPESdata + i; } } } while (false); if (pcm || (dolby && m_settings.AudioDolbyOn())) { int done = 0; while (done < todo) { int r = PlayCommon(jumboPESdata + done, todo - done, false); if (r < 0) return r; // fprintf(stderr, "."); done += r; } // Don't return done here as the remaining bytes were buffered elsewhere! // return done; } } else if (jumboPESsize == Length) { int todo = jumboPESsize; bool dolby = false; bool pcm = false; do { if (todo < (6 + 3 + 0 + 2)) break; if (0x00 != jumboPESdata[ 0 ] || 0x00 != jumboPESdata[ 1 ] || 0x01 != jumboPESdata[ 2 ] || 0xbd != jumboPESdata[ 3 ]) { break; } int l = jumboPESdata[ 4 ] * 0x0100 + jumboPESdata[ 5 ]; if (l < (3 + 0 + 2)) break; if (todo < (6 + l)) break; int h = jumboPESdata[ 8 ]; if (l < (3 + h + 2)) break; if (0x80 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { dolby = true; break; } if (0xa0 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { pcm = true; break; } } while (false); if (dolby || pcm) { dumpAudio("Audio", jumboPESdata, todo); jumboPESsize = 0; } if (pcm || (dolby && m_settings.AudioDolbyOn())) { int done = 0; while (done < todo) { int r = PlayCommon(jumboPESdata + done, todo - done, false); if (r < 0) return r; // fprintf(stderr, "."); done += r; } // Don't return done here as the remaining bytes were buffered elsewhere! // return done; } } // fprintf(stderr, "\n"); return Length; } #if APIVERSNUM >= 10338 uchar *cXineDevice::GrabImage(int &Size, bool Jpeg /* = true */, int Quality /* = -1 */, int SizeX /* = -1 */, int SizeY /* = -1 */) { const char *const FileName = 0; #else bool cXineDevice::GrabImage(const char *FileName, bool Jpeg /* = true */, int Quality /* = -1 */, int SizeX /* = -1 */, int SizeY /* = -1 */) { int Size = 0; #endif xfprintf(stderr, "GrabImage ...\n\n"); if (-1 == Quality) Quality = 100; if (-1 == SizeX) SizeX = m_settings.DefaultGrabSizeX(); if (-1 == SizeY) SizeY = m_settings.DefaultGrabSizeY(); uchar *result = m_xineLib.execFuncGrabImage(FileName, Size, Jpeg, Quality, SizeX, SizeY); xfprintf(stderr, result ? "\nGrabImage succeeded.\n" : "\nGrabImage failed.\n"); return result; } int64_t cXineDevice::GetSTC(void) { // ::fprintf(stderr, "GetSTC: "); int64_t pts = -1; if (!m_xineLib.execFuncGetPTS(pts, 100) || pts < 0) pts = cDevice::GetSTC(); // ::fprintf(stderr, "%lld\n", pts); return pts; } void cXineDevice::SetVideoFormat(bool VideoFormat16_9) { xfprintf(stderr, "SetVideoFormat: %d\n", VideoFormat16_9); cDevice::SetVideoFormat(VideoFormat16_9); m_is16_9 = VideoFormat16_9; m_xineLib.selectNoSignalStream(VideoFormat16_9); } #if APIVERSNUM >= 10707 #if APIVERSNUM >= 10708 void cXineDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) { int x, y, zx, zy; m_xineLib.execFuncVideoSize(x, y, Width, Height, zx, zy, &VideoAspect); } void cXineDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) #else void cXineDevice::GetVideoSize(int &Width, int &Height, eVideoAspect &Aspect) // function name misleading -- max osd extent shall be returned #endif { Width = m_settings.OsdExtentParams().GetOsdExtentWidth(); Height = m_settings.OsdExtentParams().GetOsdExtentHeight(); #if APIVERSNUM >= 10708 PixelAspect = (m_is16_9 ? 16.0 : 4.0) * Height / ((m_is16_9 ? 9.0 : 3.0) * Width); //fprintf(stderr, "GetOsdSize(%d, %d, %lg)\n", Width, Height, PixelAspect); #else Aspect = m_is16_9 ? va16_9 : va4_3; #endif } #endif static bool firstCallToSetVolume = true; static void switchSkin(const bool restore); void cXineDevice::SetVolumeDevice(int Volume) { if (firstCallToSetVolume) { firstCallToSetVolume = false; if (m_settings.ShallSwitchSkin()) switchSkin(false); } xfprintf(stderr, "SetVolumeDevice: %d\n", Volume); m_xineLib.execFuncSetVolume(Volume); } #if APIVERSNUM < 10307 cOsdBase *cXineDevice::NewOsd(int w, int h, int x, int y) #elif APIVERSNUM < 10509 cOsd *cXineDevice::NewOsd(int w, int h, int x, int y) #else cOsd *cXineDevice::NewOsd(int w, int h, int x, int y, uint Level, bool TrueColor /* = false */) #endif { // ::fprintf(stderr, "NewOsd ---: %s\n", ::ctime(&(const time_t &)::time(0))); cXineOsdMutexLock osdLock(&m_osdMutex); // ::fprintf(stderr, "NesOsd +++: %s\n", ::ctime(&(const time_t &)::time(0))); #if APIVERSNUM < 10509 if (m_currentOsd) { esyslog("vdr-xine: new OSD(%d, %d) requested while another OSD is active", x, y); xfprintf(stderr, "vdr-xine: new OSD(%d, %d) requested while another OSD is active", x, y); return 0; } #endif if (x < 0 || y < 0) { esyslog("vdr-xine: new OSD(%d, %d) requested with coordinates out of range", x, y); xfprintf(stderr, "vdr-xine: new OSD(%d, %d) requested with coordinates out of range", x, y); } if (w <= 0 || h <= 0) { w = m_settings.OsdExtentParams().GetOsdExtentWidth(); h = m_settings.OsdExtentParams().GetOsdExtentHeight(); } #if APIVERSNUM < 10509 m_currentOsd = new cXineOsd(*this, w, h, x, y); #else return new cXineOsd(*this, w, h, x, y, Level); #endif return m_currentOsd; } #if APIVERSNUM < 10307 void cXineDevice::OnFreeOsd(cOsdBase *const osd) #else void cXineDevice::OnFreeOsd(cOsd *const osd) #endif { #if APIVERSNUM < 10509 cXineOsdMutexLock osdLock(&m_osdMutex); assert(osd == m_currentOsd); m_currentOsd = 0; #endif } bool cXineDevice::ChangeCurrentOsd(cXineOsd *const osd, bool on) { cXineOsdMutexLock osdLock(&m_osdMutex); if (m_currentOsd) { if (on) { esyslog("vdr-xine: OSD activation requested while another OSD is active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD activation requested while another OSD is active -- ignoring request"); return false; } else if (m_currentOsd != osd) { esyslog("vdr-xine: OSD deactivation requested for an OSD which is not active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD deactivation requested for an OSD which is not active -- ignoring request"); return false; } else { m_currentOsd = 0; } } else { if (!on) { esyslog("vdr-xine: OSD deactivation requested while no OSD is active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD deactivation requested while no OSD is active -- ignoring request"); return false; } else { m_currentOsd = osd; } } return true; } static cDevice *originalPrimaryDevice = 0; #if APIVERSNUM >= 10307 void cXineDevice::MakePrimaryDevice(bool On) { cDevice::MakePrimaryDevice(On); xfprintf(stderr, "-------------------------\n"); xfprintf(stderr, "MakePrimaryDevice: %d\n", On); xfprintf(stderr, "=========================\n"); if (On) new cXineOsdProvider(*this); else cOsdProvider::Shutdown(); originalPrimaryDevice = 0; } #endif #if APIVERSNUM >= 10320 static cSkin *origSkin = 0; #endif static void switchSkin(const bool restore) { #if APIVERSNUM >= 10320 if (restore) { Skins.SetCurrent(origSkin->Name()); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); } else { origSkin = Skins.Current(); Skins.SetCurrent("curses"); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); } #else #warning vdr-xine: switching skins is only available for VDR versions >= 1.3.20 isyslog("vdr-xine: switching skins is only available for VDR versions >= 1.3.20"); #endif } void cXineDevice::reshowCurrentOsd(const bool dontOptimize /* = true */, const int frameLeft /* = -1 */, const int frameTop /* = -1 */, const int frameWidth /* = -1 */, const int frameHeight /* = -1 */, const int frameZoomX /* = -1 */, const int frameZoomY /* = -1 */, const bool unlockOsdUpdate /* = false */) { cXineOsdMutexLock osdLock(&m_osdMutex); if (unlockOsdUpdate) m_osdUpdateLocked = false; if (m_currentOsd) m_currentOsd->ReshowCurrentOsd(dontOptimize, frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY); } void cXineDevice::LockOsdUpdate() { cXineOsdMutexLock osdLock(&m_osdMutex); m_osdUpdateLocked = true; } bool cXineDevice::OsdUpdateLocked() { cXineOsdMutexLock osdLock(&m_osdMutex); return m_osdUpdateLocked; } void cXineDevice::mainMenuTrampoline() { #if APIVERSNUM >= 10332 cMutexLock switchPrimaryDeviceLock(&m_switchPrimaryDeviceMutex); if (m_switchPrimaryDeviceDeviceNo < 0) return; cControl::Shutdown(); if (m_switchPrimaryDeviceDeviceNo == (1 + DeviceNumber())) { char *msg = 0; ::asprintf(&msg, tr("Switching primary DVB to %s..."), m_plugin->Name()); Skins.Message(mtInfo, msg); ::free(msg); } SetPrimaryDevice(m_switchPrimaryDeviceDeviceNo); if (m_switchPrimaryDeviceDeviceNo != (1 + DeviceNumber())) { char *msg = 0; ::asprintf(&msg, tr("Switched primary DVB back from %s"), m_plugin->Name()); Skins.Message(mtInfo, msg); ::free(msg); } m_switchPrimaryDeviceDeviceNo = -1; m_switchPrimaryDeviceCond.Broadcast(); #endif } void cXineDevice::switchPrimaryDevice(const int deviceNo, const bool waitForExecution) { #if APIVERSNUM >= 10332 #if APIVERSNUM < 10347 while (cRemote::HasKeys()) cCondWait::SleepMs(10); #endif cMutexLock switchPrimaryDeviceLock(&m_switchPrimaryDeviceMutex); m_switchPrimaryDeviceDeviceNo = deviceNo; #if APIVERSNUM < 10347 cRemote::CallPlugin(m_plugin->Name()); #endif if (waitForExecution) m_switchPrimaryDeviceCond.Wait(m_switchPrimaryDeviceMutex); #else #warning vdr-xine: switching primary device is no longer supported for VDR versions < 1.3.32 isyslog("vdr-xine: switching primary device is no longer supported for VDR versions < 1.3.32"); #endif } void cXineDevice::OnClientConnect() { reshowCurrentOsd(); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNormal; } if (m_settings.AutoPrimaryDevice()) { cDevice *primaryDevice = cDevice::PrimaryDevice(); if (this != primaryDevice) switchPrimaryDevice(1 + DeviceNumber(), true); originalPrimaryDevice = primaryDevice; } if (m_settings.ShallSwitchSkin()) switchSkin(true); } void cXineDevice::OnClientDisconnect() { if (m_settings.ShallSwitchSkin()) switchSkin(false); if (m_settings.AutoPrimaryDevice() && originalPrimaryDevice) { if (this != originalPrimaryDevice) switchPrimaryDevice(1 + originalPrimaryDevice->DeviceNumber(), false); } } void cXineDevice::ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate) { // ::fprintf(stderr, ">>> cXineDevice::ReshowCurrentOSD()\n"); reshowCurrentOsd(false, frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY, unlockOsdUpdate); // ::fprintf(stderr, "<<< cXineDevice::ReshowCurrentOSD()\n"); } bool cXineDevice::DeviceReplayingOrTransferring() { #if APIVERSNUM >= 10341 return Replaying() || Transferring(); #else return Replaying() /* || Transferring() */; #endif } bool cXineDevice::open() { if (!m_xineLib.Open()) return false; if (m_settings.ShallSwitchSkin()) { #if APIVERSNUM >= 10320 Skins.SetCurrent(Setup.OSDSkin); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); #endif switchSkin(false); } return true; } void cXineDevice::close() { m_xineLib.Close(); if (m_settings.ShallSwitchSkin()) switchSkin(true); } void cXineDevice::Stop() { if (!theXineDevice) return; theXineDevice->close(); } cXineDevice::cXineDevice(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote) : cDevice() , m_settings(settings) , m_osdUpdateLocked(false) , m_currentOsd(0) , m_spuDecoder(0) , m_is16_9(true) , m_audioChannel(0) , m_plugin(plugin) , m_switchPrimaryDeviceDeviceNo(-1) , m_xineLib(plugin, settings, m_osdMutex, remote) { m_xineLib.SetEventSink(this); } cXineDevice::~cXineDevice() { #if APIVERSNUM < 10320 close(); #endif if (m_spuDecoder) delete m_spuDecoder; } bool cXineDevice::Create(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote) { if (theXineDevice) return false; theXineDevice = new cXineDevice(plugin, settings, remote); return 0 != theXineDevice && theXineDevice->hasNoSignalStream(); } bool cXineDevice::Open() { if (!theXineDevice) return false; return theXineDevice->open(); } cXineDevice *cXineDevice::GetDevice() { return theXineDevice; } #if APIVERSNUM >= 10733 ///< Asks the output device whether it can scale the currently shown video in ///< such a way that it fits into the given Rect, while retaining its proper ///< aspect ratio. If the scaled video doesn't exactly fit into Rect, Alignment ///< is used to determine how to align the actual rectangle with the requested ///< one. The actual rectangle can be smaller, larger or the same size as the ///< given Rect, and its location may differ, depending on the capabilities of ///< the output device, which may not be able to display a scaled video at ///< arbitrary sizes and locations. The device shall, however, do its best to ///< match the requested Rect as closely as possible, preferring a size and ///< location that fits completely into the requested Rect if possible. ///< Returns the rectangle that can actually be used when scaling the video. ///< A skin plugin using this function should rearrange its content according ///< to the rectangle returned from calling this function, and should especially ///< be prepared for cases where the returned rectangle is way off the requested ///< Rect, or even Null. In such cases, the skin may want to fall back to ///< working with full screen video. ///< If this device can't scale the video, a Null rectangle is returned (this ///< is also the default implementation). cRect cXineDevice::CanScaleVideo(const cRect &Rect, int Alignment/* = taCenter*/) { // first implementation: we can always scale, we're a soft device ;-), ignore alignment for now // we need to store the value for the case we have to call ScaleVideo ourselves in vdr-xine vidWinRect = Rect; return vidWinRect; } ///< Scales the currently shown video in such a way that it fits into the given ///< Rect. Rect should be one retrieved through a previous call to ///< CanScaleVideo() (otherwise results may be undefined). ///< Even if video output is scaled, the functions GetVideoSize() and ///< GetOsdSize() must still return the same values as if in full screen mode! ///< If this device can't scale the video, nothing happens. ///< To restore full screen video, call this function with a Null rectangle. void cXineDevice::ScaleVideo(const cRect &Rect/* = cRect::Null*/) { // refresh stored value vidWinRect = Rect; // let our specialized code do the actual resizing / repositioning, get accurate parameters first int videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY, osdWidth, osdHeight; double videoAspect, pixelAspect; m_xineLib.execFuncVideoSize(videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY, &videoAspect); GetOsdSize(osdWidth, osdHeight, pixelAspect); tArea vidWinArea; vidWinArea.x1 = vidWinRect.X(); vidWinArea.y1 = vidWinRect.Y(); vidWinArea.x2 = vidWinRect.X() + vidWinRect.Width(); vidWinArea.y2 = vidWinRect.Y() + vidWinRect.Height(); if (vidWinRect == cRect::Null) { // will just resize to full size vidWinArea.bpp = 0; } else { vidWinArea.bpp = 12; // make corrections double aspectFactor = (double(osdWidth) / double(osdHeight)) / videoAspect; int output_width = vidWinRect.Height() * (videoAspect * aspectFactor); int output_height = vidWinRect.Width() / (videoAspect * aspectFactor); if (double(vidWinRect.Width())/double(vidWinRect.Height()) > videoAspect * aspectFactor) { output_height = vidWinRect.Height(); vidWinArea.x1 += (vidWinRect.Width() - output_width) / 2; } else if (double(vidWinRect.Width())/double(vidWinRect.Height()) < videoAspect * aspectFactor) { output_width = vidWinRect.Width(); vidWinArea.y1 += (vidWinRect.Height() - output_height) / 2; } vidWinArea.x2 = vidWinArea.x1 + output_width; vidWinArea.y2 = vidWinArea.y1 + output_height; } m_xineLib.SetVideoWindow(videoWidth, videoHeight, vidWinArea); } const cRect & cXineDevice::GetScaleRect() { // just return the stored value return vidWinRect; } #endif // APIVERSNUM >= 10733 };