From 9541c0ac1ea415705dd1d53bb70578ab2e827822 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 4 May 2013 12:04:53 +0200 Subject: [PATCH] generic-pc: add somewhat working video decoder --- generic-pc/video.cpp | 269 ++++++++++++++++++++++++++++++++++++----- generic-pc/video_lib.h | 32 ++++- 2 files changed, 267 insertions(+), 34 deletions(-) diff --git a/generic-pc/video.cpp b/generic-pc/video.cpp index 4ca5f66..3940686 100644 --- a/generic-pc/video.cpp +++ b/generic-pc/video.cpp @@ -15,57 +15,62 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA + * + * cVideo implementation with decoder. + * uses ffmpeg for demuxing / decoding + * decoded frames are stored in SWFramebuffer class + * + * TODO: buffer handling surely needs some locking... */ -/* this is a dummy implementation with no functionality at all */ - -#include -#include -#include -#include #include -#include -#include -#include - #include #include #include -#include +extern "C" { +#include +#include +} + +/* ffmpeg buf 32k */ +#define INBUF_SIZE 0x8000 +/* my own buf 256k */ +#define DMX_BUF_SZ 0x20000 -#include -#include #include "video_lib.h" -#define VIDEO_DEVICE "/dev/dvb/adapter0/video0" +#include "dmx_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) -#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args) -#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args) -#define fop(cmd, args...) ({ \ - int _r; \ - if (fd >= 0) { \ - if ((_r = ::cmd(fd, args)) < 0) \ - lt_info(#cmd"(fd, "#args")\n"); \ - else \ - lt_debug(#cmd"(fd, "#args")\n");\ - } \ - else { _r = fd; } \ - _r; \ -}) - -cVideo * videoDecoder = NULL; +cVideo *videoDecoder = NULL; +extern cDemux *videoDemux; int system_rev = 0; +static uint8_t *dmxbuf; +static int bufpos; + cVideo::cVideo(int, void *, void *) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s\n", __func__); + av_register_all(); + dmxbuf = (uint8_t *)malloc(DMX_BUF_SZ); + bufpos = 0; + thread_running = false; + w_h_changed = false; + dec_w = dec_h = 0; + buf_num = 0; + buf_in = 0; + buf_out = 0; + firstpts = AV_NOPTS_VALUE; } cVideo::~cVideo(void) { + Stop(); + /* ouch :-( */ + videoDecoder = NULL; } int cVideo::setAspectRatio(int, int) @@ -85,11 +90,21 @@ int cVideo::setCroppingMode(int) int cVideo::Start(void *, unsigned short, unsigned short, void *) { + lt_info("%s running %d >\n", __func__, thread_running); + if (!thread_running) + OpenThreads::Thread::start(); + lt_info("%s running %d <\n", __func__, thread_running); return 0; } int cVideo::Stop(bool) { + lt_info("%s running %d >\n", __func__, thread_running); + if (thread_running) { + thread_running = false; + OpenThreads::Thread::join(); + } + lt_info("%s running %d <\n", __func__, thread_running); return 0; } @@ -135,9 +150,9 @@ void cVideo::Pig(int, int, int, int, int, int) void cVideo::getPictureInfo(int &width, int &height, int &rate) { - width = 720; - height = 576; - rate = 50; + width = dec_w; + height = dec_h; + rate = dec_r; } void cVideo::SetSyncMode(AVSYNC_TYPE) @@ -148,3 +163,191 @@ int cVideo::SetStreamType(VIDEO_FORMAT) { return 0; } + +cVideo::SWFramebuffer *cVideo::getDecBuf(void) +{ + if (buf_num == 0) + return NULL; + SWFramebuffer *p = &buffers[buf_out]; + buf_out++; + buf_num--; + buf_out %= VDEC_MAXBUFS; + return p; +} + +static int my_read(void *, uint8_t *buf, int buf_size) +{ + int tmp = 0; + if (videoDecoder && bufpos < DMX_BUF_SZ - 4096) { + while (bufpos < buf_size && ++tmp < 20) { /* retry max 20 times */ + int ret = videoDemux->Read(dmxbuf + bufpos, DMX_BUF_SZ - bufpos, 20); + if (ret > 0) + bufpos += ret; + } + } + if (bufpos == 0) + return 0; + if (bufpos > buf_size) { + memcpy(buf, dmxbuf, buf_size); + memmove(dmxbuf, dmxbuf + buf_size, bufpos - buf_size); + bufpos -= buf_size; + return buf_size; + } + memcpy(buf, dmxbuf, bufpos); + tmp = bufpos; + bufpos = 0; + return tmp; +} + +void cVideo::run(void) +{ + lt_info("====================== start decoder thread ================================\n"); + AVCodec *codec; + AVCodecContext *c= NULL; + AVFormatContext *avfc = NULL; + AVInputFormat *inp; + AVFrame *frame, *rgbframe; + uint8_t *inbuf = (uint8_t *)av_malloc(INBUF_SIZE); + AVPacket avpkt; + + time_t warn_r = 0; /* last read error */ + time_t warn_d = 0; /* last decode error */ + + bufpos = 0; + buf_num = 0; + buf_in = 0; + buf_out = 0; + + firstpts = AV_NOPTS_VALUE; + framecount = 0; + av_init_packet(&avpkt); + inp = av_find_input_format("mpegts"); + AVIOContext *pIOCtx = avio_alloc_context(inbuf, INBUF_SIZE, // internal Buffer and its size + 0, // bWriteable (1=true,0=false) + NULL, // user data; will be passed to our callback functions + my_read, // read callback + NULL, // write callback + NULL); // seek callback + avfc = avformat_alloc_context(); + avfc->pb = pIOCtx; + avfc->iformat = inp; + avfc->probesize = 188*5; + + thread_running = true; + if (avformat_open_input(&avfc, NULL, inp, NULL) < 0) { + lt_info("%s: Could not open input\n", __func__); + goto out; + } + while (avfc->nb_streams < 1) + { + lt_info("%s: nb_streams %d, should be 1 => retry\n", __func__, avfc->nb_streams); + if (av_read_frame(avfc, &avpkt) < 0) + lt_info("%s: av_read_frame < 0\n", __func__); + av_free_packet(&avpkt); + if (! thread_running) + goto out; + } + lt_info("%s: nb_streams %d\n", __func__, avfc->nb_streams); + + if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + lt_info("%s: no video codec? 0x%x\n", __func__, avfc->streams[0]->codec->codec_type); + + c = avfc->streams[0]->codec; + codec = avcodec_find_decoder(c->codec_id); + if (!codec) { + lt_info("%s: Codec not found\n", __func__); + goto out; + } + if (avcodec_open2(c, codec, NULL) < 0) { + lt_info("%s: Could not open codec\n", __func__); + goto out; + } + frame = avcodec_alloc_frame(); + rgbframe = avcodec_alloc_frame(); + if (!frame || !rgbframe) { + lt_info("%s: Could not allocate video frame\n", __func__); + goto out2; + } + while (thread_running) { + if (av_read_frame(avfc, &avpkt) < 0) { + if (warn_r - time(NULL) > 4) { + lt_info("%s: av_read_frame < 0\n", __func__); + warn_r = time(NULL); + } + usleep(10000); + continue; + } + int got_frame = 0; + int len = avcodec_decode_video2(c, frame, &got_frame, &avpkt); + if (len < 0) { + if (warn_d - time(NULL) > 4) { + lt_info("%s: avcodec_decode_video2 %d\n", __func__, len); + warn_d = time(NULL); + } + av_free_packet(&avpkt); + continue; + } + if (avpkt.size > len) + lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len); + if (got_frame) { + unsigned int need = avpicture_get_size(PIX_FMT_RGB32, c->width, c->height); + struct SwsContext *convert = sws_getContext(c->width, c->height, c->pix_fmt, + c->width, c->height, PIX_FMT_RGB32, + SWS_BICUBIC, 0, 0, 0); + if (!convert) + lt_info("%s: ERROR setting up SWS context\n", __func__); + else { + SWFramebuffer *f = &buffers[buf_in]; + if (f->size() < need) + f->resize(need); + avpicture_fill((AVPicture *)rgbframe, &(*f)[0], PIX_FMT_RGB32, + c->width, c->height); + sws_scale(convert, frame->data, frame->linesize, 0, c->height, + rgbframe->data, rgbframe->linesize); + sws_freeContext(convert); + // TODO: locking needed! + if (dec_w != c->width || dec_h != c->height) { + lt_info("%s: pic changed %dx%d -> %dx%d\n", __func__, + dec_w, dec_h, c->width, c->height); + dec_w = c->width; + dec_h = c->height; + w_h_changed = true; + } + f->width(c->width); + f->height(c->height); + f->pts(av_frame_get_best_effort_timestamp(frame)); + buf_in++; + buf_in %= VDEC_MAXBUFS; + buf_num++; + if (buf_num > (VDEC_MAXBUFS - 1)) { + lt_info("%s: buf_num overflow\n", __func__); + buf_out++; + buf_out %= VDEC_MAXBUFS; + buf_num--; + } + if (firstpts == AV_NOPTS_VALUE && f->pts() != AV_NOPTS_VALUE) + firstpts = f->pts(); + } + dec_r = c->time_base.den/(c->time_base.num * c->ticks_per_frame); + framecount++; + lt_debug("%s: time_base: %d/%d, ticks: %d rate: %d pts 0x%" PRIx64 "\n", __func__, + c->time_base.num, c->time_base.den, c->ticks_per_frame, dec_r, + av_frame_get_best_effort_timestamp(frame)); + } + av_free_packet(&avpkt); + } + out2: + avcodec_close(c); + avcodec_free_frame(&frame); + avcodec_free_frame(&rgbframe); + out: + avformat_close_input(&avfc); + av_free(pIOCtx->buffer); + av_free(pIOCtx); + /* reset output buffers */ + bufpos = 0; + buf_num = 0; + buf_in = 0; + buf_out = 0; + lt_info("======================== end decoder thread ================================\n"); +} diff --git a/generic-pc/video_lib.h b/generic-pc/video_lib.h index c3979e0..9847767 100644 --- a/generic-pc/video_lib.h +++ b/generic-pc/video_lib.h @@ -1,6 +1,8 @@ #ifndef _VIDEO_TD_H #define _VIDEO_TD_H +#include +#include #include #include "../common/cs_types.h" @@ -112,9 +114,29 @@ typedef enum } VIDEO_CONTROL; -class cVideo +#define VDEC_MAXBUFS 0x30 +class cVideo : public OpenThreads::Thread { public: + /* called from GL thread */ + class SWFramebuffer : public std::vector + { + public: + SWFramebuffer() : mWidth(0), mHeight(0) {} + void width(int w) { mWidth = w; } + void height(int h) { mHeight = h; } + void pts(uint64_t p) { mPts = p; } + int width() const { return mWidth; } + int height() const { return mHeight; } + int64_t pts() const { return mPts; } + private: + int mWidth; + int mHeight; + int64_t mPts; + }; + int64_t firstpts; + uint64_t framecount; + int buf_in, buf_out, buf_num; /* constructor & destructor */ cVideo(int mode, void *, void *); ~cVideo(void); @@ -163,6 +185,14 @@ class cVideo int CloseVBI(void) { return 0; }; int StartVBI(unsigned short) { return 0; }; int StopVBI(void) { return 0; }; + SWFramebuffer *getDecBuf(void); + private: + void run(); + SWFramebuffer buffers[VDEC_MAXBUFS]; + int dec_w, dec_h; + int dec_r; + bool w_h_changed; + bool thread_running; }; #endif