diff -urN mythtv-0.22.orig/libs/libmythtv/avformatdecoder.cpp mythtv-0.22/libs/libmythtv/avformatdecoder.cpp --- mythtv-0.22.orig/libs/libmythtv/avformatdecoder.cpp 2009-02-17 19:57:44.000000000 +0100 +++ mythtv-0.22/libs/libmythtv/avformatdecoder.cpp 2009-03-02 20:04:25.000000000 +0100 @@ -2,12 +2,16 @@ #include #include #include +#include // C++ headers #include #include using namespace std; +// QT headers +#include "qdir.h" + // MythTV headers #include "mythconfig.h" #include "avformatdecoder.h" @@ -53,6 +57,7 @@ #include "avio.h" #include "../libmythmpeg2/mpeg2.h" #include "ivtv_myth.h" +#include "libavformat/riff.h" } #define LOC QString("AFD: ") @@ -149,21 +154,56 @@ typedef MythDeque avframe_q; +struct vd_struct { + union { + uint32_t ret; + uint32_t cmd; + }; + uint32_t buflen; + uint64_t pts; + uint32_t unused[8]; +} __attribute__((__packed__)); + +enum { + VD_END = 1, + VD_DECODE = 2, + VD_SEEK = 3, + VD_HAS_BIH = 0x10000, + VD_VERSION_MASK = 0xFFFF, +}; + +#include +#include +typedef struct { + int fd; + void *mem; + char *data; + char *picture; + int picsize; + sem_t *sem_rd; + sem_t *sem_wr; + struct vd_struct *vd; +} ds_mpi_t; + /** * Management of libmpeg2 decoding */ class AvFormatDecoderPrivate { public: - AvFormatDecoderPrivate(bool allow_libmpeg2) - : mpeg2dec(NULL), dvdvdec(NULL), allow_mpeg2dec(allow_libmpeg2) { ; } - ~AvFormatDecoderPrivate() { DestroyMPEG2(); } + AvFormatDecoderPrivate(bool allow_libmpeg2, bool allow_directshow) + : mpeg2dec(NULL), dvdvdec(NULL), allow_mpeg2dec(allow_libmpeg2), + ds_mpi(NULL), allow_dshow(allow_directshow) { ; } + ~AvFormatDecoderPrivate() { DestroyMPEG2(); DestroyDirectShow();} bool InitMPEG2(const QString &dec); bool HasMPEG2Dec(void) const { return (bool)(mpeg2dec); } bool HasDVDVDec(void) const { return (bool)(dvdvdec); } bool HasDecoder(void) const { return HasMPEG2Dec() || HasDVDVDec(); } + bool InitDirectShow(AVCodecContext *enc); + bool HasDirectShow() const { return (bool)(ds_mpi); } + void DestroyMPEG2(); void ResetMPEG2(); int DecodeMPEG2Video(AVCodecContext *avctx, AVFrame *picture, @@ -173,13 +213,259 @@ bool SetVideoSize(const QSize &video_dim); DVDV *GetDVDVDecoder(void) { return dvdvdec; } + void DestroyDirectShow(); + void ResetDirectShow(); + int DecodeDirectShowVideo(AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size, + long long *pts); + private: mpeg2dec_t *mpeg2dec; DVDV *dvdvdec; bool allow_mpeg2dec; + ds_mpi_t *ds_mpi; + bool allow_dshow; avframe_q partialFrames; }; +static int sem_twait(sem_t *sem, int t) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += t; + return(sem_timedwait(sem, &ts)); +} +bool AvFormatDecoderPrivate::InitDirectShow(AVCodecContext *enc) +{ + typedef struct { + uint32_t f1; + uint16_t f2; + uint16_t f3; + uint8_t f4[8]; + } GUID; + + const struct AVCodecTag *bmp_taglists[] = {codec_bmp_tags, 0}; + + if(enc->codec_tag == 0) + enc->codec_tag = av_codec_get_tag(bmp_taglists, enc->codec_id); + VERBOSE(VB_IMPORTANT, LOC + QString("Trying DirectShow for FOURCC 0x%1") + .arg(enc->codec_tag, 8, 16)); + if (!allow_dshow) + return false; + DestroyDirectShow(); + if (enc->codec_tag == 0) { + allow_dshow = false; + return false; + } +// QString dec = gContext->GetSetting("UseDirectShowVideoDecoder", "no"); + QString dec = "yes"; + + if (dec == "yes") + { + bool found = false; + QString codec; + GUID guid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; + QString codec_file = QDir::homePath() + + QString("/.mythtv/dshowcodecs"); + if (! QFileInfo(codec_file).isFile()) { + allow_dshow = false; + return false; + } + QString videotype; + AVCodec *avc = avcodec_find_decoder(enc->codec_id); + if (! avc) { + allow_dshow = false; + return false; + } + videotype = avc->name; + QFile fh (codec_file); + QString line; + fh.open(QIODevice::ReadOnly); + while (! fh.atEnd() && ! found) { + QStringList fourc, guidlist; + line = fh.readLine(1024); + codec = line.section(':', 0, 0).simplified(); + fourc = line.section(':', 1, 1).split(","); + guidlist = line.section(':', 2, 2).split(","); + if (guidlist.count() != 11) + continue; + for (QStringList::Iterator it = fourc.begin(); + it != fourc.end(); it++) + { + if ((*it).simplified() == videotype) + { + guid.f1 = guidlist[0].toUInt(0, 0); + guid.f2 = guidlist[1].toUShort(0, 0); + guid.f3 = guidlist[2].toUShort(0, 0); + for (int i = 0; i < 8; i++) + guid.f4[i] = guidlist[i + 3].toUShort(0, 0); + found = true; + } + } + if (found) + break; + } + fh.close(); + if (found) { + int ret; + char cmd[255], shm[80], sem1[80], sem2[80]; + uint32_t out_fmt; + int bpp; + //out_fmt = 0x30323449; bpp = 12; //I420 12bpp + out_fmt = 0x32595559; bpp = 16; //YUY2 16bpp + snprintf(cmd, 255, "dshowserver -c %s -s %dx%d " + "-g %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x " + "-f 0x%08x -b %d -o 0x%08x -p %d -i %x %s&", + codec.toAscii().constData(), enc->width, enc->height, + guid.f1, guid.f2, guid.f3, + guid.f4[0], guid.f4[1], guid.f4[2], guid.f4[3], + guid.f4[4], guid.f4[5], guid.f4[6], guid.f4[7], + enc->codec_tag, bpp, out_fmt, getpid(), *(int *)pthread_self(), + ((print_verbose_messages & VB_PLAYBACK) == VB_PLAYBACK ? + "-d" : "")); + ds_mpi = new ds_mpi_t; + snprintf(shm, 80, "/dshow_shm.%x", *(int *)pthread_self()); + snprintf(sem1, 80, "/dshow_sem1.%x", *(int *)pthread_self()); + snprintf(sem2, 80, "/dshow_sem2.%x", *(int *)pthread_self()); + ds_mpi->fd = shm_open(shm, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + ds_mpi->picsize = enc->width * enc->height * bpp / 8; + int extra = 0; + if (enc->height % 16) + extra = (16 - enc->height % 16) * bpp / 8; + int memsize = sizeof(struct vd_struct) + enc->width * enc->height + + ds_mpi->picsize + extra; + ftruncate(ds_mpi->fd, memsize); + ds_mpi->mem = mmap(NULL, memsize, PROT_READ | PROT_WRITE, + MAP_SHARED, ds_mpi->fd, 0); + if(ds_mpi->mem == MAP_FAILED) { + perror("mmap"); + allow_dshow = false; + return false; + } + memset((char *)ds_mpi->mem, 0, sizeof(struct vd_struct)); + + if (extra) + memset((char *)ds_mpi->mem + (memsize - extra), 0, extra); + ds_mpi->vd = (struct vd_struct *)ds_mpi->mem; + ds_mpi->data = ((char *)ds_mpi->mem) + sizeof(struct vd_struct); + ds_mpi->picture = ds_mpi->data + enc->width * enc->height; + //Create read/write semaphores in locked state + ds_mpi->sem_wr = sem_open(sem1, O_CREAT, 0644, 0); + ds_mpi->sem_rd = sem_open(sem2, O_CREAT, 0644, 0); + myth_system(cmd); + ret = sem_twait(ds_mpi->sem_rd, 10); + shm_unlink(shm); + sem_unlink(sem1); + sem_unlink(sem2); + if(ret != 0) { + VERBOSE(VB_IMPORTANT, LOC + "DirectShow filter failed"); + } else { + VERBOSE(VB_IMPORTANT, LOC + "Found DirectShow filter"); + return true; + } + } + } + allow_dshow = false; + return false; +} + +void AvFormatDecoderPrivate::DestroyDirectShow() +{ + if (ds_mpi) + { + VERBOSE(VB_PLAYBACK, LOC + "Destroying filter"); + ds_mpi->vd->cmd = VD_END; //'1' is cmd for terminating + sem_post(ds_mpi->sem_wr); + close(ds_mpi->fd); + sem_close(ds_mpi->sem_wr); + sem_close(ds_mpi->sem_rd); + delete ds_mpi; + ds_mpi = NULL; + } +} + +void AvFormatDecoderPrivate::ResetDirectShow() +{ + if (ds_mpi) { + ds_mpi->vd->cmd = VD_SEEK; //'3' is cmd for seek + sem_post(ds_mpi->sem_wr); + sem_twait(ds_mpi->sem_rd, 10); + } +} + +void yuy2i420(AVFrame *dst, char *src, int w, int l) +{ + uint8_t *y, *u, *v; + y = dst->data[0]; + u = dst->data[1]; + v = dst->data[2]; + int i,j; + for(i=0; i < l; i++) { + for(j=0; j < w; j+=2) { + *(y++) = *(src++); + *(u++) = *(src++); + *(y++) = *(src++); + *(v++) = *(src++); + } + i++; + for(j=0; j < w; j+=2) { + *(y++) = *src; + src+=2; + *(y++) = *src; + src+=2; + } + } +} + +int AvFormatDecoderPrivate::DecodeDirectShowVideo(AVCodecContext *avctx, + AVFrame *picture, + int *got_picture_ptr, + uint8_t *buf, int buf_size, + long long *pts) +{ + int ret; + ds_mpi->vd->cmd = VD_DECODE; //'1' is cmd for decoding + memcpy(ds_mpi->data, buf, buf_size); + ds_mpi->vd->buflen = buf_size; + ds_mpi->vd->pts = (uint64_t)*pts; + sem_post(ds_mpi->sem_wr); + ret = sem_twait(ds_mpi->sem_rd, 10); + if (ret == 0 && ds_mpi->vd->ret && ! (ds_mpi->vd->ret & (1<<31))) { + *got_picture_ptr = 1; + if (ds_mpi->vd->pts) { + *pts = (long long)ds_mpi->vd->pts; + } + picture->interlaced_frame = (ds_mpi->vd->ret & 10) ? true : false; + avctx->get_buffer(avctx, picture); +#if 0 //Using YV12 + if(avctx->height & 0x0f) { + unsigned long pos, pos1, siz = avctx->height * avctx->width; + memcpy(picture->data[0], ds_mpi->picture, siz); + pos = siz; + pos1 = siz + avctx->width * (16 - avctx->height % 16); + siz /= 4; + memcpy(picture->data[0]+pos1, ds_mpi->picture+pos, siz); + pos+=siz; + pos1+=siz + avctx->width * ( 16 - avctx->height % 16) / 4; + memcpy(picture->data[0]+pos1, ds_mpi->picture+pos, siz); + } else { + memcpy(picture->data[0], ds_mpi->picture, ds_mpi->picsize); + } +#else //Using YUY2 + //YUY2 is a packed format so padding is easier + //int extra = 0; + //if(avctx->height % 16) + //extra = (16 - avctx->height % 16); + yuy2i420(picture, ds_mpi->picture, + avctx->width, avctx->height); +#endif + } else { + *got_picture_ptr = 0; + } + return buf_size; +} + +/*************************************************/ + /** * \brief Initialise either libmpeg2, or DVDV (Mac HW accel), to do decoding * @@ -401,7 +687,7 @@ bool use_null_videoout, bool allow_libmpeg2) : DecoderBase(parent, pginfo), - d(new AvFormatDecoderPrivate(allow_libmpeg2)), + d(new AvFormatDecoderPrivate(allow_libmpeg2, true)), is_db_ignored(gContext->IsDatabaseIgnored()), m_h264_parser(new H264Parser()), ic(NULL), @@ -684,6 +970,7 @@ avcodec_flush_buffers(enc); } d->ResetMPEG2(); + d->ResetDirectShow(); } // Discard all the queued up decoded frames @@ -1584,6 +1871,9 @@ } } + if (CODEC_ID_H264 == enc->codec_id) + force_xv = true; + MythCodecID mcid; mcid = VideoOutputXv::GetBestSupportedCodec( /* disp dim */ width, height, @@ -1593,6 +1883,7 @@ /* test surface */ kCodec_NORMAL_END > video_codec_id, /* force_xv */ force_xv); bool vcd, idct, mc, vdpau; + enc->codec_id = (CodecID) myth2av_codecid(mcid, vcd, idct, mc, vdpau); @@ -1668,6 +1959,7 @@ } // Initialize alternate decoders when needed... + if (! d->InitDirectShow(enc)) if (((dec == "libmpeg2") && (CODEC_ID_MPEG1VIDEO == enc->codec_id || CODEC_ID_MPEG2VIDEO == enc->codec_id)) || @@ -3756,7 +4048,21 @@ int gotpicture = 0; avcodeclock.lock(); - if (d->HasDecoder()) +/* printf("Trying: %d\n",len); + if (0) { + static int fnum = 0; + char str[80]; + int fh; + sprintf(str,"enc%d", fnum++);; + fh = open(str, 01101,00777); + write(fh, ptr, len); + close(fh); + } +*/ + if (d->HasDirectShow()) + ret = d->DecodeDirectShowVideo(context, &mpa_pic, + &gotpicture, ptr, len, &pts); + else if (d->HasDecoder()) { if (decodeStillFrame) { diff -urN mythtv-0.22.orig/libs/libmythtv/libmythtv.pro mythtv-0.22/libs/libmythtv/libmythtv.pro --- mythtv-0.22.orig/libs/libmythtv/libmythtv.pro 2009-02-17 19:57:44.000000000 +0100 +++ mythtv-0.22/libs/libmythtv/libmythtv.pro 2009-03-02 19:54:23.000000000 +0100 @@ -57,6 +57,7 @@ } using_backend: LIBS += -lmp3lame LIBS += -lz $$EXTRA_LIBS $$QMAKE_LIBS_DYNLOAD +LIBS += -lrt TARGETDEPS += ../libmyth/libmyth-$${MYTH_SHLIB_EXT} TARGETDEPS += ../libavutil/libmythavutil-$${MYTH_SHLIB_EXT}