Index: libs/libmythtv/avformatdecoder.cpp =================================================================== --- libs/libmythtv/avformatdecoder.cpp (revision 26445) +++ libs/libmythtv/avformatdecoder.cpp (working copy) @@ -126,6 +126,8 @@ void render_slice_vdpau(struct AVCodecContext *s, const AVFrame *src, int offset[4], int y, int type, int height); +AVStream *videostream = NULL; + static AVCodec *find_vdpau_decoder(AVCodec *c, enum CodecID id) { AVCodec *codec = c; @@ -2969,6 +2971,8 @@ AVFrame mpa_pic; avcodec_get_frame_defaults(&mpa_pic); + videostream = curstream; + avcodeclock->lock(); if (private_dec) { @@ -3006,9 +3010,11 @@ if (!gotpicture) { + gotvideo = 1; return true; } +//START // Decode CEA-608 and CEA-708 captions for (uint i = 0; i < (uint)mpa_pic.atsc_cc_len; i += ((mpa_pic.atsc_cc_buf[i] & 0x1f) * 3) + 2) @@ -3163,6 +3169,110 @@ return true; } +bool AvFormatDecoder::ProcessVideoFrame(AVStream *curstream, AVFrame *mpa_pic) +{ + int ret = 0, gotpicture = 0; + long long pts = 0; + AVCodecContext *context = curstream->codec; + + // Decode CEA-608 and CEA-708 captions + for (uint i = 0; i < (uint)mpa_pic->atsc_cc_len; + i += ((mpa_pic->atsc_cc_buf[i] & 0x1f) * 3) + 2) + { + DecodeDTVCC(mpa_pic->atsc_cc_buf + i, + mpa_pic->atsc_cc_len - i); + } + + VideoFrame *picframe = (VideoFrame *)(mpa_pic->opaque); + + if (!directrendering) + { + AVPicture tmppicture; + + VideoFrame *xf = picframe; + picframe = GetPlayer()->GetNextVideoFrame(false); + + unsigned char *buf = picframe->buf; + avpicture_fill(&tmppicture, buf, PIX_FMT_YUV420P, context->width, + context->height); + tmppicture.data[0] = buf + picframe->offsets[0]; + tmppicture.data[1] = buf + picframe->offsets[1]; + tmppicture.data[2] = buf + picframe->offsets[2]; + tmppicture.linesize[0] = picframe->pitches[0]; + tmppicture.linesize[1] = picframe->pitches[1]; + tmppicture.linesize[2] = picframe->pitches[2]; + + QSize dim = get_video_dim(*context); + sws_ctx = sws_getCachedContext(sws_ctx, context->width, + context->height, context->pix_fmt, + context->width, context->height, + PIX_FMT_YUV420P, SWS_FAST_BILINEAR, + NULL, NULL, NULL); + if (!sws_ctx) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate sws context"); + return false; + } + sws_scale(sws_ctx, mpa_pic->data, mpa_pic->linesize, 0, dim.height(), + tmppicture.data, tmppicture.linesize); + + + if (xf) + { + // Set the frame flags, but then discard it + // since we are not using it for display. + xf->interlaced_frame = mpa_pic->interlaced_frame; + xf->top_field_first = mpa_pic->top_field_first; + xf->frameNumber = framesPlayed; + GetPlayer()->DiscardVideoFrame(xf); + } + } + else if (!picframe) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "NULL videoframe - direct rendering not" + "correctly initialized."); + return false; + } + + pts = (long long)(av_q2d(curstream->time_base) * mpa_pic->reordered_opaque * 1000); + + long long temppts = pts; + + // Validate the video pts against the last pts. If it's + // a little bit smaller, equal or missing, compute + // it from the last. Otherwise assume a wraparound. + if (!ringBuffer->isDVD() && + temppts <= lastvpts && + (temppts + 10000 > lastvpts || temppts <= 0)) + { + temppts = lastvpts; + temppts += (long long)(1000 / fps); + // MPEG2/H264 frames can be repeated, update pts accordingly + temppts += (long long)(mpa_pic->repeat_pict * 500 / fps); + } + + VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + + QString("ProcessVideoFrame timecode %1 %2 %3").arg(mpa_pic->reordered_opaque).arg(temppts).arg(lastvpts)); + + picframe->interlaced_frame = mpa_pic->interlaced_frame; + picframe->top_field_first = mpa_pic->top_field_first; + picframe->repeat_pict = mpa_pic->repeat_pict; + + picframe->frameNumber = framesPlayed; + GetPlayer()->ReleaseNextVideoFrame(picframe, temppts); + if (private_dec && mpa_pic->data[3]) + context->release_buffer(context, mpa_pic); + + decoded_video_frame = picframe; + gotvideo = 1; + framesPlayed++; + + lastvpts = temppts; + + return true; +} + + /** \fn AvFormatDecoder::ProcessVBIDataPacket(const AVStream*, const AVPacket*) * \brief Process ivtv proprietary embedded vertical blanking * interval captions. @@ -4075,8 +4185,21 @@ { othresh = ((ototal>>1) + (ototal>>2)); allowedquit = ofill > othresh; + VERBOSE(VB_PLAYBACK, + LOC + QString("AudioBuffer Full ofill %1 ototal %2 othresh %3") + .arg(ofill).arg(ototal).arg(othresh)); } + VERBOSE(VB_PLAYBACK, LOC + "GetFrame -- start"); + if (private_dec && private_dec->HasBufferedFrames()) + { + VERBOSE(VB_PLAYBACK, LOC + "Private Decoder has buffered frames available!"); + AVFrame mpa_pic; + avcodec_get_frame_defaults(&mpa_pic); + private_dec->GetBufferedFrame(videostream, &mpa_pic); + ProcessVideoFrame(videostream, &mpa_pic); + } +// allowedquit = false; while (!allowedquit) { if ((decodetype & kDecodeAudio) && Index: libs/libmythtv/privatedecoder_crystalhd.h =================================================================== --- libs/libmythtv/privatedecoder_crystalhd.h (revision 26445) +++ libs/libmythtv/privatedecoder_crystalhd.h (working copy) @@ -17,6 +17,29 @@ #include #include "privatedecoder.h" +class PrivateDecoderCrystalHD; +class RetrievalThread : public QThread +{ + Q_OBJECT + + public: + RetrievalThread(PrivateDecoderCrystalHD *dec) + : QThread(NULL), m_dec(dec) { } + + protected: + virtual void run(void); + + private: + PrivateDecoderCrystalHD *m_dec; +}; + +typedef struct PacketBuffer_ +{ + uint8_t *buf; + int size; + int64_t pts; +} PacketBuffer; + enum BC_DEVICE_TYPE { BC_70012 = 0, @@ -34,13 +57,17 @@ bool no_hardware_decode, AVCodecContext *avctx); virtual bool Reset(void); + virtual bool HasBufferedFrames(void); + virtual int GetBufferedFrame(AVStream *stream, + AVFrame *picture); virtual int GetFrame(AVStream *stream, AVFrame *picture, int *got_picture_ptr, AVPacket *pkt); - void RetrieveFrame(void); + void RetrieveFrames(void); private: + bool StartRetrievalThread(void); bool CreateFilter(AVCodecContext *avctx); void FillFrame(BC_DTS_PROC_OUT *out); @@ -53,7 +80,13 @@ BC_DEVICE_TYPE m_device_type; BC_OUTPUT_FORMAT m_pix_fmt; QList m_decoded_frames; + QList m_packet_buffers; + QMutex m_decoded_frames_lock; + RetrievalThread *m_retrieval_thread; + bool m_pause_retrieval; + bool m_kill_retrieval; VideoFrame *m_frame; + PacketBuffer *m_buffer; AVBitStreamFilterContext *m_filter; }; Index: libs/libmythtv/privatedecoder_crystalhd.cpp =================================================================== --- libs/libmythtv/privatedecoder_crystalhd.cpp (revision 26445) +++ libs/libmythtv/privatedecoder_crystalhd.cpp (working copy) @@ -5,6 +5,16 @@ #define ERR QString("CrystalHD Err: ") #define WARN QString("CrystalHD Warn: ") +void RetrievalThread::run(void) +{ + if (!m_dec) + return; + + VERBOSE(VB_PLAYBACK, LOC + QString("Starting retrieval thread.")); + m_dec->RetrieveFrames(); + VERBOSE(VB_PLAYBACK, LOC + QString("Stopping retrieval thread.")); +} + PixelFormat bcmpixfmt_to_pixfmt(BC_OUTPUT_FORMAT fmt); QString device_to_string(BC_DEVICE_TYPE device); QString bcmerr_to_string(BC_STATUS err); @@ -31,12 +41,30 @@ PrivateDecoderCrystalHD::PrivateDecoderCrystalHD() : m_device(NULL), m_device_type(BC_70012), - m_pix_fmt(OUTPUT_MODE_INVALID), m_frame(NULL), m_filter(NULL) + m_pix_fmt(OUTPUT_MODE_INVALID), m_decoded_frames_lock(QMutex::Recursive), + m_retrieval_thread(NULL), m_pause_retrieval(true), m_kill_retrieval(false), + m_frame(NULL), m_filter(NULL) + { } PrivateDecoderCrystalHD::~PrivateDecoderCrystalHD() { + if (m_retrieval_thread) + { + m_pause_retrieval = true; + m_kill_retrieval = true; + int tries = 0; + while (!m_retrieval_thread->wait(100) && (tries++ < 50)) + VERBOSE(VB_PLAYBACK, WARN + "Waited 100ms for retrieval to stop"); + + if (m_retrieval_thread->isRunning()) + VERBOSE(VB_IMPORTANT, ERR + "Failed to stop retrieval."); + else + VERBOSE(VB_PLAYBACK, LOC + "Stopped frame retrieval."); + delete m_retrieval_thread; + } + if (m_filter) av_bitstream_filter_close(m_filter); @@ -64,7 +92,7 @@ static bool debugged = false; uint32_t well_documented = DTS_PLAYBACK_MODE | DTS_LOAD_FILE_PLAY_FW | - DTS_SINGLE_THREADED_MODE | DTS_SKIP_TX_CHK_CPB | + DTS_SKIP_TX_CHK_CPB | DTS_PLAYBACK_DROP_RPT_MODE | DTS_DFLT_RESOLUTION(vdecRESOLUTION_CUSTOM); INIT_ST @@ -225,7 +253,7 @@ BC_INPUT_FORMAT fmt; memset(&fmt, 0, sizeof(BC_INPUT_FORMAT)); - fmt.OptFlags = 0x80000000 | vdecFrameRateUnknown | 0x80; + fmt.OptFlags = 0x80000000 | vdecFrameRateUnknown;// | 0x80; fmt.width = avctx->coded_width; fmt.height = avctx->coded_height; fmt.Progressive = 1; @@ -313,8 +341,22 @@ } } +void inline free_buffer(PacketBuffer* buffer) +{ + if (buffer) + { + if (buffer->buf) + delete [] buffer->buf; + delete buffer; + } +} + bool PrivateDecoderCrystalHD::Reset(void) { + // FIXME actually need to make sure this has stopped before continuing + m_pause_retrieval = true; + + QMutexLocker lock(&m_decoded_frames_lock); free_frame(m_frame); m_frame = NULL; @@ -322,6 +364,10 @@ free_frame(m_decoded_frames[i]); m_decoded_frames.clear(); + for (int i = 0; i < m_packet_buffers.size(); i++) + free_buffer(m_packet_buffers[i]); + m_packet_buffers.clear(); + if (!m_device) return true; @@ -331,6 +377,42 @@ return true;; } +bool PrivateDecoderCrystalHD::HasBufferedFrames() +{ + return m_decoded_frames.size() > 0; +} + +int PrivateDecoderCrystalHD::GetBufferedFrame(AVStream *stream, + AVFrame *picture) +{ + AVCodecContext *avctx = stream->codec; + if (avctx->get_buffer(avctx, picture) < 0) + { + VERBOSE(VB_IMPORTANT, ERR + + QString("decoded frames available but no video buffers.")); + return -1; + } + + m_decoded_frames_lock.lock(); + VideoFrame *frame = m_decoded_frames.takeLast(); + m_decoded_frames_lock.unlock(); + + VERBOSE(VB_PLAYBACK, LOC + + QString("Output PTS timecode (%1) external").arg(frame->timecode * av_q2d(stream->time_base))); + picture->reordered_opaque = (int64_t)(frame->timecode / 1000); + picture->interlaced_frame = frame->interlaced_frame; + picture->top_field_first = frame->top_field_first; + picture->repeat_pict = frame->repeat_pict; + copy((VideoFrame*)picture->opaque, frame); + if (frame->priv[0] && frame->qstride) + { + memcpy(picture->atsc_cc_buf, frame->priv[0], frame->qstride); + picture->atsc_cc_len = frame->qstride; + } + free_frame(frame); + return 0; +} + int PrivateDecoderCrystalHD::GetFrame(AVStream *stream, AVFrame *picture, int *got_picture_ptr, @@ -344,8 +426,52 @@ if (!avctx) return result; - uint8_t* buf = pkt->data; - int size = pkt->size; + if (!StartRetrievalThread()) + return result; + + m_buffer = new PacketBuffer(); + m_buffer->buf = new unsigned char[pkt->size]; + m_buffer->size = pkt->size; + m_buffer->pts = pkt->pts; + memcpy(m_buffer->buf, pkt->data, pkt->size); + + if (!m_buffer) + return result; + + m_packet_buffers.insert(0, m_buffer); + VERBOSE(VB_PLAYBACK, LOC + + QString("%1 packet buffers queued up").arg(m_packet_buffers.size())); + while (m_packet_buffers.size() > 0) + { + + BC_DTS_STATUS drv_status; + INIT_ST + st = DtsGetDriverStatus(m_device, &drv_status); + CHECK_ST + + if (drv_status.cpbEmptySize < 100000) + { + usleep(10000); + return 0; + } + + PacketBuffer *buffer = m_packet_buffers.takeLast(); + +// while (m_packet_buffers.size() > 30 && DtsTxFreeSize(m_device) < buffer->size) +// usleep(10000); + + +// if (DtsTxFreeSize(m_device) < buffer->size) +// { +// m_packet_buffers.append(buffer); +// VERBOSE(VB_PLAYBACK, LOC + +// QString("Buffer full %1 packet buffers queued up").arg(m_packet_buffers.size())); +// usleep(10000); +// return 0; +// } + + uint8_t* buf = buffer->buf; + int size = buffer->size; bool free_buf = false; int outbuf_size = 0; uint8_t *outbuf = NULL; @@ -374,43 +500,53 @@ } + usleep(5000); + int64_t timecode = (int64_t)(av_q2d(stream->time_base) * buffer->pts * 1000); uint64_t chd_timestamp = 0; // msec units - if (pkt->pts != (int64_t)AV_NOPTS_VALUE) - chd_timestamp = (uint64_t)(av_q2d(stream->time_base) * pkt->pts * 1000); - + if (buffer->pts != (int64_t)AV_NOPTS_VALUE) + chd_timestamp = (uint64_t)(buffer->pts * 1000); + VERBOSE(VB_PLAYBACK, LOC + + QString("Input PTS timecode (%1)").arg(timecode)); // TODO check for busy state and available buffer size - INIT_ST + //INIT_ST st = DtsProcInput(m_device, buf, size, chd_timestamp, false); CHECK_ST + //CheckStatus(); - // TODO why is this needed - possibly overruning CrystalHD decoder or too - // many buffered packets causing mythplayer problems? - usleep(10000); - if (free_buf) delete buf; + free_buffer(buffer); if (!ok) VERBOSE(VB_IMPORTANT, ERR + "Failed to send packet to decoder."); - result = pkt->size; + result = buffer->size; - RetrieveFrame(); + } - if (!m_decoded_frames.size()) + m_decoded_frames_lock.lock(); + int available = m_decoded_frames.size(); + m_decoded_frames_lock.unlock(); + if (!available) + { return result; + } if (avctx->get_buffer(avctx, picture) < 0) { VERBOSE(VB_IMPORTANT, ERR + QString("%1 decoded frames available but no video buffers.") - .arg(m_decoded_frames.size())); + .arg(available)); return -1; } + m_decoded_frames_lock.lock(); VideoFrame *frame = m_decoded_frames.takeLast(); + m_decoded_frames_lock.unlock(); + + VERBOSE(VB_PLAYBACK, LOC + + QString("Output PTS timecode (%1)").arg(frame->timecode * av_q2d(stream->time_base))); *got_picture_ptr = 1; - picture->reordered_opaque = (int64_t)(frame->timecode / av_q2d(stream->time_base) - / 1000); + picture->reordered_opaque = (int64_t)(frame->timecode / 1000); picture->interlaced_frame = frame->interlaced_frame; picture->top_field_first = frame->top_field_first; picture->repeat_pict = frame->repeat_pict; @@ -424,40 +560,66 @@ return result; } -void PrivateDecoderCrystalHD::RetrieveFrame(void) +void PrivateDecoderCrystalHD::RetrieveFrames(void) { - usleep(1000); INIT_ST - BC_DTS_STATUS status; - st = DtsGetDriverStatus(m_device, &status); - CHECK_ST + bool valid = false; + while (!m_kill_retrieval) + { + usleep(1000); + if (m_pause_retrieval) + continue; - if (!status.ReadyListCount) - return; + BC_DTS_STATUS status; + st = DtsGetDriverStatus(m_device, &status); + CHECK_ST - BC_DTS_PROC_OUT out; - memset(&out, 0, sizeof(BC_DTS_PROC_OUT)); - st = DtsProcOutputNoCopy(m_device, 1, &out); - if (BC_STS_FMT_CHANGE == st) - { - VERBOSE(VB_IMPORTANT, LOC + "Decoder reported format change."); - CheckProcOutput(&out); - return; - } - CHECK_ST + if (!status.ReadyListCount) + continue; - if (!ok) - { - VERBOSE(VB_IMPORTANT, ERR + "Failed to retrieve decoded frame"); - return; + BC_DTS_PROC_OUT out; + memset(&out, 0, sizeof(BC_DTS_PROC_OUT)); + st = DtsProcOutputNoCopy(m_device, valid ? 2000 : 20, &out); + + if (BC_STS_FMT_CHANGE == st) + { + VERBOSE(VB_IMPORTANT, LOC + "Decoder reported format change."); + CheckProcOutput(&out); + valid = true; + continue; + } + CHECK_ST + + if (!ok) + { + VERBOSE(VB_IMPORTANT, ERR + "Failed to retrieve decoded frame"); + continue; + } + + //if (ok && valid && (out.PoutFlags & BC_POUT_FLAGS_PIB_VALID)&& out.PicInfo.timeStamp) + //if (ok && valid && (out.PoutFlags & BC_POUT_FLAGS_PIB_VALID)) + if (ok && valid) + FillFrame(&out); + st = DtsReleaseOutputBuffs(m_device, NULL, false); + CHECK_ST } +} - FillFrame(&out); - st = DtsReleaseOutputBuffs(m_device, NULL, false); - CHECK_ST - RetrieveFrame(); +bool PrivateDecoderCrystalHD::StartRetrievalThread(void) +{ + m_pause_retrieval = false; + if (m_retrieval_thread) + return true; + + m_retrieval_thread = new RetrievalThread(this); + if (!m_retrieval_thread) + return false; + + m_retrieval_thread->start(); + return true; } + void PrivateDecoderCrystalHD::FillFrame(BC_DTS_PROC_OUT *out) { bool second_field = false; @@ -541,6 +703,7 @@ void PrivateDecoderCrystalHD::AddFrameToQueue(void) { int i = 0; + m_decoded_frames_lock.lock(); // TODO is the following code needed #if 0 bool found = false; @@ -555,7 +718,12 @@ if (!found) i = m_decoded_frames.size(); #endif - m_decoded_frames.insert(i, m_frame); + m_decoded_frames.insert(0, m_frame); + VERBOSE(VB_PLAYBACK, LOC + QString("Decoded frame queue size %1") + .arg(m_decoded_frames.size())); + VERBOSE(VB_PLAYBACK, LOC + QString("Inserting frame with timecode %1") + .arg(m_frame->timecode)); + m_decoded_frames_lock.unlock(); m_frame = NULL; } Index: libs/libmythtv/privatedecoder.h =================================================================== --- libs/libmythtv/privatedecoder.h (revision 26445) +++ libs/libmythtv/privatedecoder.h (working copy) @@ -17,6 +17,8 @@ static PrivateDecoder* Create(const QString &decoder, bool no_hardware_decode, AVCodecContext *avctx); + virtual bool HasBufferedFrames(void) { return false; } + virtual int GetBufferedFrame(AVStream *stream, AVFrame *picture) { return 0; } PrivateDecoder() { } virtual ~PrivateDecoder() { } virtual QString GetName(void) = 0; Index: libs/libmythtv/avformatdecoder.h =================================================================== --- libs/libmythtv/avformatdecoder.h (revision 26445) +++ libs/libmythtv/avformatdecoder.h (working copy) @@ -203,6 +203,7 @@ bool H264PreProcessPkt(AVStream *stream, AVPacket *pkt); bool PreProcessVideoPacket(AVStream *stream, AVPacket *pkt); bool ProcessVideoPacket(AVStream *stream, AVPacket *pkt); + bool ProcessVideoFrame(AVStream *stream, AVFrame *frame); bool ProcessAudioPacket(AVStream *stream, AVPacket *pkt, DecodeType decodetype); bool ProcessSubtitlePacket(AVStream *stream, AVPacket *pkt); Index: libs/libmyth/audiooutputalsa.cpp =================================================================== --- libs/libmyth/audiooutputalsa.cpp (revision 26420) +++ libs/libmyth/audiooutputalsa.cpp (working copy) @@ -431,7 +431,9 @@ while (frames > 0) { + VERBOSE(VB_AUDIO, QString("pcm_write_func -- start")); lw = pcm_write_func(pcm_handle, tmpbuf, frames); + VERBOSE(VB_AUDIO, QString("pcm_write_func -- end")); if (lw >= 0) { Index: libs/libmyth/audiooutputbase.cpp =================================================================== --- libs/libmyth/audiooutputbase.cpp (revision 26420) +++ libs/libmyth/audiooutputbase.cpp (working copy) @@ -21,6 +21,7 @@ #define LOC QString("AO: ") #define LOC_ERR QString("AO, ERROR: ") +#define AUDIOTSTESTING 1 #define WPOS audiobuffer + org_waud #define RPOS audiobuffer + raud #define ABUF audiobuffer @@ -1259,7 +1260,9 @@ // only send zeros if card doesn't already have at least one // fragment of zeros -dag + VBAUDIOTS("WriteAudio Zeros Start"); WriteAudio(zeros, fragment_size); + VBAUDIOTS("WriteAudio Zeros Done"); continue; } else