mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-libstb-hal.git
synced 2025-08-26 23:12:44 +02:00
Merge remote-tracking branch 'seife/master'
Origin commit data
------------------
Branch: master
Commit: ec3f82441e
Author: martii <m4rtii@gmx.de>
Date: 2013-05-04 (Sat, 04 May 2013)
------------------
No further description and justification available within origin commit message!
------------------
This commit was generated by Migit
This commit is contained in:
@@ -33,6 +33,14 @@ if BOXTYPE_GENERIC
|
|||||||
SUBDIRS += generic-pc
|
SUBDIRS += generic-pc
|
||||||
libstb_hal_la_LIBADD += \
|
libstb_hal_la_LIBADD += \
|
||||||
generic-pc/libgeneric.la
|
generic-pc/libgeneric.la
|
||||||
|
|
||||||
|
libstb_hal_test_LDADD += \
|
||||||
|
-lglut -lGL -lGLU -lGLEW -lao \
|
||||||
|
-lOpenThreads \
|
||||||
|
@AVFORMAT_LIBS@ \
|
||||||
|
@AVUTIL_LIBS@ \
|
||||||
|
@AVCODEC_LIBS@ \
|
||||||
|
@SWSCALE_LIBS@
|
||||||
endif
|
endif
|
||||||
if BOXTYPE_SPARK
|
if BOXTYPE_SPARK
|
||||||
libstb_hal_test_LDADD += -lasound
|
libstb_hal_test_LDADD += -lasound
|
||||||
|
@@ -225,6 +225,7 @@ if $PKG_CONFIG --exists "$2" ; then
|
|||||||
AC_MSG_RESULT(yes)
|
AC_MSG_RESULT(yes)
|
||||||
$1_CFLAGS=$($PKG_CONFIG --cflags "$2")
|
$1_CFLAGS=$($PKG_CONFIG --cflags "$2")
|
||||||
$1_LIBS=$($PKG_CONFIG --libs "$2")
|
$1_LIBS=$($PKG_CONFIG --libs "$2")
|
||||||
|
$1_EXISTS=yes
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
fi
|
fi
|
||||||
@@ -235,7 +236,7 @@ AC_SUBST($1_LIBS)
|
|||||||
|
|
||||||
AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG],[
|
AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG],[
|
||||||
_TUXBOX_APPS_LIB_PKGCONFIG($1,$2)
|
_TUXBOX_APPS_LIB_PKGCONFIG($1,$2)
|
||||||
if test -z "$$1_CFLAGS" ; then
|
if test x"$$1_EXISTS" != xyes; then
|
||||||
AC_MSG_ERROR([could not find package $2]);
|
AC_MSG_ERROR([could not find package $2]);
|
||||||
fi
|
fi
|
||||||
])
|
])
|
||||||
|
@@ -22,6 +22,12 @@ if test x"$BOXTYPE" = x"tripledragon"; then
|
|||||||
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
|
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test x$BOXTYPE = xgeneric; then
|
||||||
|
TUXBOX_APPS_LIB_PKGCONFIG(AVFORMAT,libavformat)
|
||||||
|
TUXBOX_APPS_LIB_PKGCONFIG(AVCODEC,libavcodec)
|
||||||
|
TUXBOX_APPS_LIB_PKGCONFIG(AVUTIL,libavutil)
|
||||||
|
TUXBOX_APPS_LIB_PKGCONFIG(SWSCALE,libswscale)
|
||||||
|
fi
|
||||||
AC_OUTPUT([
|
AC_OUTPUT([
|
||||||
Makefile
|
Makefile
|
||||||
common/Makefile
|
common/Makefile
|
||||||
|
@@ -3,14 +3,15 @@ INCLUDES = \
|
|||||||
|
|
||||||
noinst_LTLIBRARIES = libgeneric.la
|
noinst_LTLIBRARIES = libgeneric.la
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
|
||||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||||
AM_LDFLAGS = -lpthread
|
|
||||||
|
|
||||||
libgeneric_la_SOURCES = \
|
libgeneric_la_SOURCES = \
|
||||||
hardware_caps.c \
|
hardware_caps.c \
|
||||||
dmx.cpp \
|
dmx.cpp \
|
||||||
video.cpp \
|
video.cpp \
|
||||||
audio.cpp \
|
audio.cpp \
|
||||||
|
glfb.cpp \
|
||||||
init.cpp \
|
init.cpp \
|
||||||
playback.cpp \
|
playback.cpp \
|
||||||
pwrmngr.cpp \
|
pwrmngr.cpp \
|
||||||
|
@@ -1,32 +1,67 @@
|
|||||||
/* dummy cAudio implementation that does nothing for now */
|
/*
|
||||||
|
* (C) 2010-2013 Stefan Seyfried
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* cAudio implementation with decoder.
|
||||||
|
* uses libao <http://www.xiph.org/ao/> for output
|
||||||
|
* ffmpeg <http://ffmpeg.org> for demuxing / decoding
|
||||||
|
*/
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
|
||||||
#include <sys/fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <linux/dvb/audio.h>
|
|
||||||
|
|
||||||
#include "audio_lib.h"
|
#include "audio_lib.h"
|
||||||
|
#include "dmx_lib.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
|
|
||||||
#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0"
|
#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args)
|
||||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args)
|
#define lt_info(args...) _lt_info(HAL_DEBUG_AUDIO, this, args)
|
||||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args)
|
|
||||||
|
|
||||||
#include <linux/soundcard.h>
|
#include <OpenThreads/Thread>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <ao/ao.h>
|
||||||
|
}
|
||||||
|
/* ffmpeg buf 2k */
|
||||||
|
#define INBUF_SIZE 0x0800
|
||||||
|
/* my own buf 16k */
|
||||||
|
#define DMX_BUF_SZ 0x4000
|
||||||
|
|
||||||
cAudio * audioDecoder = NULL;
|
cAudio * audioDecoder = NULL;
|
||||||
|
extern cDemux *audioDemux;
|
||||||
|
static uint8_t *dmxbuf = NULL;
|
||||||
|
static int bufpos;
|
||||||
|
|
||||||
|
static cAudio *gThiz = NULL;
|
||||||
|
|
||||||
cAudio::cAudio(void *, void *, void *)
|
cAudio::cAudio(void *, void *, void *)
|
||||||
{
|
{
|
||||||
|
thread_started = false;
|
||||||
|
dmxbuf = (uint8_t *)malloc(DMX_BUF_SZ);
|
||||||
|
bufpos = 0;
|
||||||
|
curr_pts = 0;
|
||||||
|
gThiz = this;
|
||||||
|
ao_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
cAudio::~cAudio(void)
|
cAudio::~cAudio(void)
|
||||||
{
|
{
|
||||||
closeDevice();
|
closeDevice();
|
||||||
|
free(dmxbuf);
|
||||||
|
ao_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cAudio::openDevice(void)
|
void cAudio::openDevice(void)
|
||||||
@@ -53,13 +88,21 @@ int cAudio::setVolume(unsigned int left, unsigned int right)
|
|||||||
|
|
||||||
int cAudio::Start(void)
|
int cAudio::Start(void)
|
||||||
{
|
{
|
||||||
lt_debug("%s\n", __func__);
|
lt_info("%s >\n", __func__);
|
||||||
|
OpenThreads::Thread::start();
|
||||||
|
lt_info("%s <\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cAudio::Stop(void)
|
int cAudio::Stop(void)
|
||||||
{
|
{
|
||||||
lt_debug("%s\n", __func__);
|
lt_info("%s >\n", __func__);
|
||||||
|
if (thread_started)
|
||||||
|
{
|
||||||
|
thread_started = false;
|
||||||
|
OpenThreads::Thread::join();
|
||||||
|
}
|
||||||
|
lt_info("%s <\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +121,7 @@ void cAudio::SetStreamType(AUDIO_FORMAT type)
|
|||||||
lt_debug("%s %d\n", __func__, type);
|
lt_debug("%s %d\n", __func__, type);
|
||||||
};
|
};
|
||||||
|
|
||||||
int cAudio::setChannel(int channel)
|
int cAudio::setChannel(int /*channel*/)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
@@ -140,3 +183,139 @@ void cAudio::setBypassMode(bool disable)
|
|||||||
{
|
{
|
||||||
lt_debug("%s %d\n", __func__, disable);
|
lt_debug("%s %d\n", __func__, disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _my_read(void *, uint8_t *buf, int buf_size)
|
||||||
|
{
|
||||||
|
return gThiz->my_read(buf, buf_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::my_read(uint8_t *buf, int buf_size)
|
||||||
|
{
|
||||||
|
int tmp = 0;
|
||||||
|
if (audioDecoder && bufpos < DMX_BUF_SZ - 4096) {
|
||||||
|
while (bufpos < buf_size && ++tmp < 20) { /* retry max 20 times */
|
||||||
|
int ret = audioDemux->Read(dmxbuf + bufpos, DMX_BUF_SZ - bufpos, 10);
|
||||||
|
if (ret > 0)
|
||||||
|
bufpos += ret;
|
||||||
|
if (! thread_started)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bufpos == 0)
|
||||||
|
return 0;
|
||||||
|
//lt_info("%s buf_size %d bufpos %d th %d tmp %d\n", __func__, buf_size, bufpos, thread_started, tmp);
|
||||||
|
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 cAudio::run()
|
||||||
|
{
|
||||||
|
lt_info("====================== start decoder thread ================================\n");
|
||||||
|
/* libavcodec & friends */
|
||||||
|
av_register_all();
|
||||||
|
|
||||||
|
AVCodec *codec;
|
||||||
|
AVCodecContext *c= NULL;
|
||||||
|
AVFormatContext *avfc = NULL;
|
||||||
|
AVInputFormat *inp;
|
||||||
|
AVFrame *frame;
|
||||||
|
uint8_t *inbuf = (uint8_t *)av_malloc(INBUF_SIZE);
|
||||||
|
AVPacket avpkt;
|
||||||
|
int ret, driver;
|
||||||
|
/* libao */
|
||||||
|
ao_info *ai;
|
||||||
|
ao_device *adevice;
|
||||||
|
ao_sample_format sformat;
|
||||||
|
|
||||||
|
curr_pts = 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_started = true;
|
||||||
|
|
||||||
|
if (avformat_open_input(&avfc, NULL, inp, NULL) < 0) {
|
||||||
|
lt_info("%s: avformat_open_input() failed.\n", __func__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = avformat_find_stream_info(avfc, NULL);
|
||||||
|
lt_debug("%s: avformat_find_stream_info: %d\n", __func__, ret);
|
||||||
|
if (avfc->nb_streams != 1)
|
||||||
|
{
|
||||||
|
lt_info("%s: nb_streams: %d, should be 1!\n", __func__, avfc->nb_streams);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
|
||||||
|
lt_info("%s: stream 0 no audio 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: avcodec_open2() failed\n", __func__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
frame = avcodec_alloc_frame();
|
||||||
|
if (!frame) {
|
||||||
|
lt_info("%s: avcodec_alloc_frame failed\n", __func__);
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
driver = ao_default_driver_id();
|
||||||
|
sformat.bits = 16;
|
||||||
|
sformat.channels = c->channels;
|
||||||
|
sformat.rate = c->sample_rate;
|
||||||
|
sformat.byte_format = AO_FMT_NATIVE;
|
||||||
|
sformat.matrix = 0;
|
||||||
|
adevice = ao_open_live(driver, &sformat, NULL);
|
||||||
|
ai = ao_driver_info(driver);
|
||||||
|
lt_info("libao driver: %d name '%s' short '%s' author '%s'\n",
|
||||||
|
driver, ai->name, ai->short_name, ai->author);
|
||||||
|
#if 0
|
||||||
|
lt_info(" driver options:");
|
||||||
|
for (int i = 0; i < ai->option_count; ++i)
|
||||||
|
fprintf(stderr, " %s", ai->options[i]);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#endif
|
||||||
|
lt_info("codec params: sample_fmt %d sample_rate %d channels %d\n",
|
||||||
|
c->sample_fmt, c->sample_rate, c->channels);
|
||||||
|
while (thread_started) {
|
||||||
|
int gotframe = 0;
|
||||||
|
if (av_read_frame(avfc, &avpkt) < 0)
|
||||||
|
break;
|
||||||
|
avcodec_decode_audio4(c, frame, &gotframe, &avpkt);
|
||||||
|
if (gotframe && thread_started) {
|
||||||
|
curr_pts = av_frame_get_best_effort_timestamp(frame);
|
||||||
|
lt_debug("%s: pts 0x%" PRIx64 " %3f\n", __func__, curr_pts, curr_pts/90000.0);
|
||||||
|
ao_play(adevice, (char*)frame->extended_data[0], frame->linesize[0]);
|
||||||
|
}
|
||||||
|
av_free_packet(&avpkt);
|
||||||
|
}
|
||||||
|
ao_close(adevice); /* can take long :-( */
|
||||||
|
avcodec_free_frame(&frame);
|
||||||
|
out2:
|
||||||
|
avcodec_close(c);
|
||||||
|
out:
|
||||||
|
avformat_close_input(&avfc);
|
||||||
|
av_free(pIOCtx->buffer);
|
||||||
|
av_free(pIOCtx);
|
||||||
|
lt_info("======================== end decoder thread ================================\n");
|
||||||
|
}
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
#ifndef _AUDIO_LIB_H_
|
#ifndef _AUDIO_LIB_H_
|
||||||
#define _AUDIO_LIB_H_
|
#define _AUDIO_LIB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <OpenThreads/Thread>
|
||||||
#include "../common/cs_types.h"
|
#include "../common/cs_types.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@@ -36,7 +38,7 @@ typedef enum
|
|||||||
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
||||||
} AUDIO_FORMAT;
|
} AUDIO_FORMAT;
|
||||||
|
|
||||||
class cAudio
|
class cAudio : public OpenThreads::Thread
|
||||||
{
|
{
|
||||||
friend class cPlayback;
|
friend class cPlayback;
|
||||||
private:
|
private:
|
||||||
@@ -50,18 +52,22 @@ class cAudio
|
|||||||
AUDIO_FORMAT StreamType;
|
AUDIO_FORMAT StreamType;
|
||||||
AUDIO_SYNC_MODE SyncMode;
|
AUDIO_SYNC_MODE SyncMode;
|
||||||
bool started;
|
bool started;
|
||||||
|
bool thread_started;
|
||||||
|
|
||||||
int volume;
|
int volume;
|
||||||
|
int64_t curr_pts;
|
||||||
|
|
||||||
void openDevice(void);
|
void openDevice(void);
|
||||||
void closeDevice(void);
|
void closeDevice(void);
|
||||||
|
|
||||||
int do_mute(bool enable, bool remember);
|
int do_mute(bool enable, bool remember);
|
||||||
void setBypassMode(bool disable);
|
void setBypassMode(bool disable);
|
||||||
|
void run();
|
||||||
public:
|
public:
|
||||||
/* construct & destruct */
|
/* construct & destruct */
|
||||||
cAudio(void *, void *, void *);
|
cAudio(void *, void *, void *);
|
||||||
~cAudio(void);
|
~cAudio(void);
|
||||||
|
int64_t getPts() { return curr_pts; }
|
||||||
|
|
||||||
void *GetHandle() { return NULL; };
|
void *GetHandle() { return NULL; };
|
||||||
/* shut up */
|
/* shut up */
|
||||||
@@ -92,6 +98,7 @@ class cAudio
|
|||||||
void SetSpdifDD(bool enable);
|
void SetSpdifDD(bool enable);
|
||||||
void ScheduleMute(bool On);
|
void ScheduleMute(bool On);
|
||||||
void EnableAnalogOut(bool enable);
|
void EnableAnalogOut(bool enable);
|
||||||
|
int my_read(uint8_t *buf, int buf_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -104,6 +104,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe
|
|||||||
if (fd > -1)
|
if (fd > -1)
|
||||||
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
|
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
|
||||||
|
|
||||||
|
dmx_type = pes_type;
|
||||||
if (pes_type != DMX_PSI_CHANNEL)
|
if (pes_type != DMX_PSI_CHANNEL)
|
||||||
flags |= O_NONBLOCK;
|
flags |= O_NONBLOCK;
|
||||||
|
|
||||||
@@ -116,7 +117,8 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe
|
|||||||
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
|
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
|
||||||
num, DMX_T[pes_type], pes_type, uBufferSize, fd);
|
num, DMX_T[pes_type], pes_type, uBufferSize, fd);
|
||||||
|
|
||||||
dmx_type = pes_type;
|
if (dmx_type == DMX_VIDEO_CHANNEL)
|
||||||
|
uBufferSize = 0x40000;
|
||||||
#if 0
|
#if 0
|
||||||
if (!pesfds.empty())
|
if (!pesfds.empty())
|
||||||
{
|
{
|
||||||
@@ -379,10 +381,12 @@ bool cDemux::pesFilter(const unsigned short pid)
|
|||||||
p_flt.pes_type = DMX_PES_PCR;
|
p_flt.pes_type = DMX_PES_PCR;
|
||||||
break;
|
break;
|
||||||
case DMX_AUDIO_CHANNEL:
|
case DMX_AUDIO_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_AUDIO;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||||
break;
|
break;
|
||||||
case DMX_VIDEO_CHANNEL:
|
case DMX_VIDEO_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_VIDEO;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||||
break;
|
break;
|
||||||
case DMX_PES_CHANNEL:
|
case DMX_PES_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_OTHER;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
536
generic-pc/glfb.cpp
Normal file
536
generic-pc/glfb.cpp
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||||
|
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
openGL based framebuffer implementation
|
||||||
|
based on Carjay's neutrino-hd-dvbapi work, see
|
||||||
|
http://gitorious.org/neutrino-hd/neutrino-hd-dvbapi
|
||||||
|
|
||||||
|
TODO: AV-Sync code is not existent yet
|
||||||
|
cleanup carjay's crazy 3D stuff :-)
|
||||||
|
video mode setting (4:3, 16:9, panscan...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include "glfb.h"
|
||||||
|
#include "video_lib.h"
|
||||||
|
#include "audio_lib.h"
|
||||||
|
|
||||||
|
#include "lt_debug.h"
|
||||||
|
|
||||||
|
#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
#define lt_info_c(args...) _lt_info(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
#define lt_debug(args...) _lt_debug(HAL_DEBUG_INIT, this, args)
|
||||||
|
#define lt_info(args...) _lt_info(HAL_DEBUG_INIT, this, args)
|
||||||
|
|
||||||
|
|
||||||
|
extern cVideo *videoDecoder;
|
||||||
|
extern cAudio *audioDecoder;
|
||||||
|
|
||||||
|
static GLFramebuffer *gThiz = 0; /* GLUT does not allow for an arbitrary argument to the render func */
|
||||||
|
|
||||||
|
GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mInitDone(false)
|
||||||
|
{
|
||||||
|
mState.width = x;
|
||||||
|
mState.height = y;
|
||||||
|
mX = y * 16 / 9; /* hard coded 16:9 initial aspect ratio for now */
|
||||||
|
mY = y;
|
||||||
|
mVA = 1.0;
|
||||||
|
|
||||||
|
mState.blit = true;
|
||||||
|
last_apts = 0;
|
||||||
|
|
||||||
|
/* linux framebuffer compat mode */
|
||||||
|
screeninfo.bits_per_pixel = 32;
|
||||||
|
screeninfo.xres = mState.width;
|
||||||
|
screeninfo.xres_virtual = screeninfo.xres;
|
||||||
|
screeninfo.yres = mState.height;
|
||||||
|
screeninfo.yres_virtual = screeninfo.yres;
|
||||||
|
screeninfo.blue.length = 8;
|
||||||
|
screeninfo.blue.offset = 0;
|
||||||
|
screeninfo.green.length = 8;
|
||||||
|
screeninfo.green.offset = 8;
|
||||||
|
screeninfo.red.length = 8;
|
||||||
|
screeninfo.red.offset = 16;
|
||||||
|
screeninfo.transp.length = 8;
|
||||||
|
screeninfo.transp.offset = 24;
|
||||||
|
|
||||||
|
unlink("/tmp/neutrino.input");
|
||||||
|
mkfifo("/tmp/neutrino.input", 0600);
|
||||||
|
input_fd = open("/tmp/neutrino.input", O_RDWR|O_CLOEXEC|O_NONBLOCK);
|
||||||
|
if (input_fd < 0)
|
||||||
|
lt_info("%s: could not open /tmp/neutrino.input FIFO: %m\n", __func__);
|
||||||
|
initKeys();
|
||||||
|
OpenThreads::Thread::start();
|
||||||
|
while (!mInitDone)
|
||||||
|
usleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFramebuffer::~GLFramebuffer()
|
||||||
|
{
|
||||||
|
mShutDown = true;
|
||||||
|
OpenThreads::Thread::join();
|
||||||
|
if (input_fd >= 0)
|
||||||
|
close(input_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::initKeys()
|
||||||
|
{
|
||||||
|
mSpecialMap[GLUT_KEY_UP] = KEY_UP;
|
||||||
|
mSpecialMap[GLUT_KEY_DOWN] = KEY_DOWN;
|
||||||
|
mSpecialMap[GLUT_KEY_LEFT] = KEY_LEFT;
|
||||||
|
mSpecialMap[GLUT_KEY_RIGHT] = KEY_RIGHT;
|
||||||
|
|
||||||
|
mSpecialMap[GLUT_KEY_F1] = KEY_RED;
|
||||||
|
mSpecialMap[GLUT_KEY_F2] = KEY_GREEN;
|
||||||
|
mSpecialMap[GLUT_KEY_F3] = KEY_YELLOW;
|
||||||
|
mSpecialMap[GLUT_KEY_F4] = KEY_BLUE;
|
||||||
|
|
||||||
|
mSpecialMap[GLUT_KEY_PAGE_UP] = KEY_PAGEUP;
|
||||||
|
mSpecialMap[GLUT_KEY_PAGE_DOWN] = KEY_PAGEDOWN;
|
||||||
|
|
||||||
|
mKeyMap[0x0d] = KEY_OK;
|
||||||
|
mKeyMap[0x1b] = KEY_EXIT;
|
||||||
|
mKeyMap['i'] = KEY_INFO;
|
||||||
|
mKeyMap['m'] = KEY_MENU;
|
||||||
|
|
||||||
|
mKeyMap['+'] = KEY_VOLUMEUP;
|
||||||
|
mKeyMap['-'] = KEY_VOLUMEDOWN;
|
||||||
|
mKeyMap['.'] = KEY_MUTE;
|
||||||
|
mKeyMap['h'] = KEY_HELP;
|
||||||
|
mKeyMap['p'] = KEY_POWER;
|
||||||
|
|
||||||
|
mKeyMap['0'] = KEY_0;
|
||||||
|
mKeyMap['1'] = KEY_1;
|
||||||
|
mKeyMap['2'] = KEY_2;
|
||||||
|
mKeyMap['3'] = KEY_3;
|
||||||
|
mKeyMap['4'] = KEY_4;
|
||||||
|
mKeyMap['5'] = KEY_5;
|
||||||
|
mKeyMap['6'] = KEY_6;
|
||||||
|
mKeyMap['7'] = KEY_7;
|
||||||
|
mKeyMap['8'] = KEY_8;
|
||||||
|
mKeyMap['9'] = KEY_9;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::run()
|
||||||
|
{
|
||||||
|
setupCtx();
|
||||||
|
setupOSDBuffer();
|
||||||
|
mInitDone = true; /* signal that setup is finished */
|
||||||
|
|
||||||
|
/* init the good stuff */
|
||||||
|
GLenum err = glewInit();
|
||||||
|
if(err == GLEW_OK)
|
||||||
|
{
|
||||||
|
if((!GLEW_VERSION_1_5)||(!GLEW_EXT_pixel_buffer_object)||(!GLEW_ARB_texture_non_power_of_two))
|
||||||
|
{
|
||||||
|
lt_info("GLFB: Sorry, your graphics card is not supported. "
|
||||||
|
"Needs at least OpenGL 1.5, pixel buffer objects and NPOT textures.\n");
|
||||||
|
lt_info("incompatible graphics card: %m");
|
||||||
|
_exit(1); /* Life is hard */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gThiz = this;
|
||||||
|
glutDisplayFunc(GLFramebuffer::rendercb);
|
||||||
|
glutKeyboardFunc(GLFramebuffer::keyboardcb);
|
||||||
|
glutSpecialFunc(GLFramebuffer::specialcb);
|
||||||
|
setupGLObjects(); /* needs GLEW prototypes */
|
||||||
|
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
|
||||||
|
glutMainLoop();
|
||||||
|
releaseGLObjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lt_info("GLFB: error initializing glew: %d\n", err);
|
||||||
|
lt_info("GLFB: GL thread stopping\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLFramebuffer::setupCtx()
|
||||||
|
{
|
||||||
|
int argc = 1;
|
||||||
|
/* some dummy commandline for GLUT to be happy */
|
||||||
|
char const *argv[2] = { "neutrino", 0 };
|
||||||
|
lt_info("GLFB: GL thread starting\n");
|
||||||
|
glutInit(&argc, const_cast<char **>(argv));
|
||||||
|
glutInitWindowSize(mX, mY);
|
||||||
|
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
|
||||||
|
glutCreateWindow("Neutrino");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::setupOSDBuffer()
|
||||||
|
{ /* the OSD buffer size can be decoupled from the actual
|
||||||
|
window size since the GL can blit-stretch with no
|
||||||
|
trouble at all, ah, the luxury of ignorance... */
|
||||||
|
// mMutex.lock();
|
||||||
|
if (mState.width && mState.height)
|
||||||
|
{
|
||||||
|
/* 32bit FB depth, *2 because tuxtxt uses a shadow buffer */
|
||||||
|
int fbmem = mState.width * mState.height * 4 * 2;
|
||||||
|
mOSDBuffer.resize(fbmem);
|
||||||
|
lt_info("GLFB: OSD buffer set to %d bytes\n", fbmem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::setupGLObjects()
|
||||||
|
{
|
||||||
|
unsigned char buf[4] = { 0, 0, 0, 0 }; /* 1 black pixel */
|
||||||
|
glGenTextures(1, &mState.osdtex);
|
||||||
|
glGenTextures(1, &mState.displaytex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mState.width, mState.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.displaytex); /* we do not yet know the size so will set that inline */
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||||
|
|
||||||
|
glGenBuffers(1, &mState.pbo);
|
||||||
|
glGenBuffers(1, &mState.displaypbo);
|
||||||
|
|
||||||
|
/* hack to start with black video buffer instead of white */
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.displaypbo);
|
||||||
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(buf), buf, GL_STREAM_DRAW_ARB);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLFramebuffer::releaseGLObjects()
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &mState.pbo);
|
||||||
|
glDeleteBuffers(1, &mState.displaypbo);
|
||||||
|
glDeleteTextures(1, &mState.osdtex);
|
||||||
|
glDeleteTextures(1, &mState.displaytex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ void GLFramebuffer::rendercb()
|
||||||
|
{
|
||||||
|
gThiz->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ void GLFramebuffer::keyboardcb(unsigned char key, int /*x*/, int /*y*/)
|
||||||
|
{
|
||||||
|
lt_debug_c("GLFB::%s: 0x%x\n", __func__, key);
|
||||||
|
struct input_event ev;
|
||||||
|
std::map<unsigned char, int>::const_iterator i = gThiz->mKeyMap.find(key);
|
||||||
|
if (i == gThiz->mKeyMap.end())
|
||||||
|
return;
|
||||||
|
ev.code = i->second;
|
||||||
|
ev.value = 1; /* key own */
|
||||||
|
ev.type = EV_KEY;
|
||||||
|
gettimeofday(&ev.time, NULL);
|
||||||
|
lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code);
|
||||||
|
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||||
|
ev.value = 0; /* neutrino is stupid, so push key up directly after key down */
|
||||||
|
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void GLFramebuffer::specialcb(int key, int /*x*/, int /*y*/)
|
||||||
|
{
|
||||||
|
lt_debug_c("GLFB::%s: 0x%x\n", __func__, key);
|
||||||
|
struct input_event ev;
|
||||||
|
std::map<int, int>::const_iterator i = gThiz->mSpecialMap.find(key);
|
||||||
|
if (i == gThiz->mSpecialMap.end())
|
||||||
|
return;
|
||||||
|
ev.code = i->second;
|
||||||
|
ev.value = 1;
|
||||||
|
ev.type = EV_KEY;
|
||||||
|
gettimeofday(&ev.time, NULL);
|
||||||
|
lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code);
|
||||||
|
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||||
|
ev.value = 0;
|
||||||
|
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_us = 30000;
|
||||||
|
|
||||||
|
void GLFramebuffer::render()
|
||||||
|
{
|
||||||
|
if (!mReInit) /* for example if window is resized */
|
||||||
|
checkReinit();
|
||||||
|
|
||||||
|
if(mShutDown)
|
||||||
|
glutLeaveMainLoop();
|
||||||
|
|
||||||
|
if (mReInit)
|
||||||
|
{
|
||||||
|
mReInit = false;
|
||||||
|
glViewport(0, 0, mX, mY);
|
||||||
|
glutReshapeWindow(mX, mY);
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
float aspect = static_cast<float>(mX)/mY;
|
||||||
|
float osdaspect = 1.0/(static_cast<float>(16.0)/9);
|
||||||
|
// if(!mState.go3d)
|
||||||
|
{
|
||||||
|
glOrtho(aspect*-osdaspect, aspect*osdaspect, -1.0, 1.0, -1.0, 1.0 );
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
else
|
||||||
|
{ /* carjay is crazy... :-) */
|
||||||
|
gluPerspective(45.0, static_cast<float>(mX)/mY, 0.05, 1000.0);
|
||||||
|
glTranslatef(0.0, 0.0, -2.0);
|
||||||
|
glClearColor(0.25, 0.25, 0.25, 1.0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
bltDisplayBuffer(); /* decoded video stream */
|
||||||
|
if (mState.blit) {
|
||||||
|
/* only blit manually after fb->blit(), this helps to find missed blit() calls */
|
||||||
|
mState.blit = false;
|
||||||
|
lt_debug("GLFB::%s blit!\n", __func__);
|
||||||
|
bltOSDBuffer(); /* OSD */
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
#if 0
|
||||||
|
// cube test
|
||||||
|
if(mState.go3d)
|
||||||
|
{
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
static float ydeg = 0.0;
|
||||||
|
glPushMatrix();
|
||||||
|
glRotatef(ydeg, 0.0, 1.0, 0.0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||||
|
drawCube(0.5);
|
||||||
|
glScalef(1.01, 1.01, 1.01);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||||
|
drawCube(0.5);
|
||||||
|
glPopMatrix();
|
||||||
|
ydeg += 0.75f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||||
|
drawSquare(1.0, mVA / (16.0/9));
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||||
|
drawSquare(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glFlush();
|
||||||
|
glutSwapBuffers();
|
||||||
|
|
||||||
|
GLuint err = glGetError();
|
||||||
|
if (err != 0)
|
||||||
|
lt_info("GLFB::%s: GLError:%d 0x%04x\n", __func__, err, err);
|
||||||
|
if (sleep_us > 0)
|
||||||
|
usleep(sleep_us);
|
||||||
|
glutPostRedisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLFramebuffer::checkReinit()
|
||||||
|
{
|
||||||
|
int x = glutGet(GLUT_WINDOW_WIDTH);
|
||||||
|
int y = glutGet(GLUT_WINDOW_HEIGHT);
|
||||||
|
if ( x != mX || y != mY )
|
||||||
|
{
|
||||||
|
mX = x;
|
||||||
|
mY = y;
|
||||||
|
/* fix aspect ratio */
|
||||||
|
if (x < mY * 16 / 9)
|
||||||
|
mX = mY * 16 / 9;
|
||||||
|
else
|
||||||
|
mY = mX * 9 / 16;
|
||||||
|
mReInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void GLFramebuffer::drawCube(float size)
|
||||||
|
{
|
||||||
|
GLfloat vertices[] = {
|
||||||
|
1.0f, 1.0f, 1.0f,
|
||||||
|
-1.0f, 1.0f, 1.0f,
|
||||||
|
-1.0f, -1.0f, 1.0f,
|
||||||
|
1.0f, -1.0f, 1.0f,
|
||||||
|
1.0f, -1.0f, -1.0f,
|
||||||
|
1.0f, 1.0f, -1.0f,
|
||||||
|
-1.0f, 1.0f, -1.0f,
|
||||||
|
-1.0f, -1.0f, -1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
GLubyte indices[] = {
|
||||||
|
0, 1, 2, 3, /* front */
|
||||||
|
0, 3, 4, 5, /* right */
|
||||||
|
0, 5, 6, 1, /* top */
|
||||||
|
1, 6, 7, 2, /* left */
|
||||||
|
7, 4, 3, 2, /* bottom */
|
||||||
|
4, 7, 6, 5 /* back */
|
||||||
|
};
|
||||||
|
|
||||||
|
GLfloat texcoords[] = {
|
||||||
|
1.0, 0.0, // v0
|
||||||
|
0.0, 0.0, // v1
|
||||||
|
0.0, 1.0, // v2
|
||||||
|
1.0, 1.0, // v3
|
||||||
|
0.0, 1.0, // v4
|
||||||
|
0.0, 0.0, // v5
|
||||||
|
1.0, 0.0, // v6
|
||||||
|
1.0, 1.0 // v7
|
||||||
|
};
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glScalef(size, size, size);
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glVertexPointer(3, GL_FLOAT, 0, vertices);
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
|
||||||
|
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indices);
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GLFramebuffer::drawSquare(float size, float x_factor)
|
||||||
|
{
|
||||||
|
GLfloat vertices[] = {
|
||||||
|
1.0f, 1.0f,
|
||||||
|
-1.0f, 1.0f,
|
||||||
|
-1.0f, -1.0f,
|
||||||
|
1.0f, -1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
GLubyte indices[] = { 0, 1, 2, 3 };
|
||||||
|
|
||||||
|
GLfloat texcoords[] = {
|
||||||
|
1.0, 0.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
0.0, 1.0,
|
||||||
|
1.0, 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glScalef(size * x_factor, size, size);
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glVertexPointer(2, GL_FLOAT, 0, vertices);
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
|
||||||
|
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, indices);
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLFramebuffer::bltOSDBuffer()
|
||||||
|
{
|
||||||
|
/* FIXME: copy each time */
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.pbo);
|
||||||
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, mOSDBuffer.size(), &mOSDBuffer[0], GL_STREAM_DRAW_ARB);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mState.width, mState.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::bltDisplayBuffer()
|
||||||
|
{
|
||||||
|
if (!videoDecoder) /* cannot start yet */
|
||||||
|
return;
|
||||||
|
static bool warn = true;
|
||||||
|
cVideo::SWFramebuffer *buf = videoDecoder->getDecBuf();
|
||||||
|
if (!buf) {
|
||||||
|
if (warn)
|
||||||
|
lt_info("GLFB::%s did not get a buffer...\n", __func__);
|
||||||
|
warn = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
warn = true;
|
||||||
|
int w = buf->width(), h = buf->height();
|
||||||
|
if (w == 0 || h == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AVRational a = buf->AR();
|
||||||
|
if (a.den != 0)
|
||||||
|
mVA = static_cast<float>(w * a.num) / h / a.den;
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.displaypbo);
|
||||||
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, buf->size(), &(*buf)[0], GL_STREAM_DRAW_ARB);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
|
||||||
|
/* "rate control" mechanism starts here...
|
||||||
|
* this implementation is pretty naive and not working too well, but
|
||||||
|
* better this than nothing... :-) */
|
||||||
|
int64_t apts = 0;
|
||||||
|
/* 18000 is the magic value for A/V sync in my libao->pulseaudio->intel_hda setup */
|
||||||
|
int64_t vpts = buf->pts() + 18000;
|
||||||
|
if (audioDecoder)
|
||||||
|
apts = audioDecoder->getPts();
|
||||||
|
if (apts != last_apts) {
|
||||||
|
int rate, dummy1, dummy2;
|
||||||
|
if (apts < vpts)
|
||||||
|
sleep_us = (sleep_us * 2 + (vpts - apts)*10/9) / 3;
|
||||||
|
else if (sleep_us > 1000)
|
||||||
|
sleep_us -= 1000;
|
||||||
|
last_apts = apts;
|
||||||
|
videoDecoder->getPictureInfo(dummy1, dummy2, rate);
|
||||||
|
if (rate > 0)
|
||||||
|
rate = 2000000 / rate; /* limit to half the frame rate */
|
||||||
|
else
|
||||||
|
rate = 50000; /* minimum 20 fps */
|
||||||
|
if (sleep_us > rate)
|
||||||
|
sleep_us = rate;
|
||||||
|
else if (sleep_us < 1)
|
||||||
|
sleep_us = 1;
|
||||||
|
}
|
||||||
|
lt_debug("vpts: 0x%" PRIx64 " apts: 0x%" PRIx64 " diff: %6.3f sleep_us %d buf %d\n",
|
||||||
|
buf->pts(), apts, (buf->pts() - apts)/90000.0, sleep_us, videoDecoder->buf_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::clear()
|
||||||
|
{
|
||||||
|
/* clears front and back buffer */
|
||||||
|
memset(&mOSDBuffer[0], 0, mOSDBuffer.size());
|
||||||
|
}
|
92
generic-pc/glfb.h
Normal file
92
generic-pc/glfb.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||||
|
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __glthread__
|
||||||
|
#define __glthread__
|
||||||
|
#include <OpenThreads/Thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#include <GL/freeglut.h>
|
||||||
|
#include <GL/gl.h>
|
||||||
|
#include <linux/fb.h> /* for screeninfo etc. */
|
||||||
|
|
||||||
|
class GLFramebuffer : public OpenThreads::Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GLFramebuffer(int x, int y);
|
||||||
|
~GLFramebuffer();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
std::vector<unsigned char> *getOSDBuffer() { return &mOSDBuffer; } /* pointer to OSD bounce buffer */
|
||||||
|
|
||||||
|
int getOSDWidth() { return mState.width; }
|
||||||
|
int getOSDHeight() { return mState.height; }
|
||||||
|
void blit() { mState.blit = true; }
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
fb_var_screeninfo getScreenInfo() { return screeninfo; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
fb_var_screeninfo screeninfo;
|
||||||
|
int mX; /* window size */
|
||||||
|
int mY;
|
||||||
|
float mVA; /* video aspect ratio */;
|
||||||
|
|
||||||
|
bool mReInit; /* setup things for GL */
|
||||||
|
bool mShutDown; /* if set main loop is left */
|
||||||
|
bool mInitDone; /* condition predicate */
|
||||||
|
// OpenThreads::Condition mInitCond; /* condition variable for init */
|
||||||
|
// mutable OpenThreads::Mutex mMutex; /* lock our data */
|
||||||
|
|
||||||
|
std::vector<unsigned char> mOSDBuffer; /* silly bounce buffer */
|
||||||
|
|
||||||
|
std::map<unsigned char, int> mKeyMap;
|
||||||
|
std::map<int, int> mSpecialMap;
|
||||||
|
int input_fd;
|
||||||
|
int64_t last_apts;
|
||||||
|
|
||||||
|
void checkReinit(); /* e.g. in case window was resized */
|
||||||
|
static void rendercb(); /* callback for GLUT */
|
||||||
|
void render(); /* actual render function */
|
||||||
|
static void keyboardcb(unsigned char key, int x, int y);
|
||||||
|
static void specialcb(int key, int x, int y);
|
||||||
|
|
||||||
|
void initKeys(); /* setup key bindings for window */
|
||||||
|
void setupCtx(); /* create the window and make the context current */
|
||||||
|
void setupOSDBuffer(); /* create the OSD buffer */
|
||||||
|
void setupGLObjects(); /* PBOs, textures and stuff */
|
||||||
|
void releaseGLObjects();
|
||||||
|
// void drawCube(float size); /* cubes are the building blocks of our society */
|
||||||
|
void drawSquare(float size, float x_factor = 1); /* do not be square */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int width; /* width and height, fixed for a framebuffer instance */
|
||||||
|
int height;
|
||||||
|
GLuint osdtex; /* holds the OSD texture */
|
||||||
|
GLuint pbo; /* PBO we use for transfer to texture */
|
||||||
|
GLuint displaytex; /* holds the display texture */
|
||||||
|
GLuint displaypbo;
|
||||||
|
//int go3d;
|
||||||
|
bool blit;
|
||||||
|
} mState;
|
||||||
|
|
||||||
|
void bltOSDBuffer();
|
||||||
|
void bltDisplayBuffer();
|
||||||
|
};
|
||||||
|
#endif
|
@@ -1,21 +1,28 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "init_lib.h"
|
#include "init_lib.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
|
#include "glfb.h"
|
||||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
||||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
||||||
|
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
|
GLFramebuffer *glfb = NULL;
|
||||||
|
|
||||||
void init_td_api()
|
void init_td_api()
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
lt_debug_init();
|
lt_debug_init();
|
||||||
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel);
|
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel);
|
||||||
|
if (! glfb)
|
||||||
|
glfb = new GLFramebuffer(720, 576); /* hard coded to PAL resolution for now */
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown_td_api()
|
void shutdown_td_api()
|
||||||
{
|
{
|
||||||
lt_info("%s, initialized = %d\n", __func__, (int)initialized);
|
lt_info("%s, initialized = %d\n", __func__, (int)initialized);
|
||||||
|
if (glfb)
|
||||||
|
delete glfb;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
}
|
}
|
||||||
|
@@ -15,57 +15,62 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
|
* Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
|
||||||
|
*
|
||||||
|
* cVideo implementation with decoder.
|
||||||
|
* uses ffmpeg <http://ffmpeg.org> 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 <fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <pthread.h>
|
extern "C" {
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libswscale/swscale.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ffmpeg buf 32k */
|
||||||
|
#define INBUF_SIZE 0x8000
|
||||||
|
/* my own buf 256k */
|
||||||
|
#define DMX_BUF_SZ 0x20000
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/dvb/video.h>
|
|
||||||
#include "video_lib.h"
|
#include "video_lib.h"
|
||||||
#define VIDEO_DEVICE "/dev/dvb/adapter0/video0"
|
#include "dmx_lib.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
||||||
#define lt_info(args...) _lt_info(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;
|
int system_rev = 0;
|
||||||
|
|
||||||
|
static uint8_t *dmxbuf;
|
||||||
|
static int bufpos;
|
||||||
|
|
||||||
cVideo::cVideo(int, void *, void *)
|
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;
|
||||||
|
v_format = VIDEO_FORMAT_MPEG2;
|
||||||
}
|
}
|
||||||
|
|
||||||
cVideo::~cVideo(void)
|
cVideo::~cVideo(void)
|
||||||
{
|
{
|
||||||
|
Stop();
|
||||||
|
/* ouch :-( */
|
||||||
|
videoDecoder = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cVideo::setAspectRatio(int, int)
|
int cVideo::setAspectRatio(int, int)
|
||||||
@@ -75,7 +80,31 @@ int cVideo::setAspectRatio(int, int)
|
|||||||
|
|
||||||
int cVideo::getAspectRatio(void)
|
int cVideo::getAspectRatio(void)
|
||||||
{
|
{
|
||||||
return 1;
|
buf_m.lock();
|
||||||
|
int ret = 0;
|
||||||
|
int w, h, ar;
|
||||||
|
AVRational a;
|
||||||
|
if (buf_num == 0)
|
||||||
|
goto out;
|
||||||
|
a = buffers[buf_out].AR();
|
||||||
|
w = buffers[buf_out].width();
|
||||||
|
h = buffers[buf_out].height();
|
||||||
|
if (a.den == 0 || h == 0)
|
||||||
|
goto out;
|
||||||
|
ar = w * 100 * a.num / h / a.den;
|
||||||
|
if (ar < 100 || ar > 225) /* < 4:3, > 20:9 */
|
||||||
|
; /* ret = 0: N/A */
|
||||||
|
else if (ar < 140) /* 4:3 */
|
||||||
|
ret = 1;
|
||||||
|
else if (ar < 165) /* 14:9 */
|
||||||
|
ret = 2;
|
||||||
|
else if (ar < 200) /* 16:9 */
|
||||||
|
ret = 3;
|
||||||
|
else
|
||||||
|
ret = 4; /* 20:9 */
|
||||||
|
out:
|
||||||
|
buf_m.unlock();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cVideo::setCroppingMode(int)
|
int cVideo::setCroppingMode(int)
|
||||||
@@ -85,11 +114,21 @@ int cVideo::setCroppingMode(int)
|
|||||||
|
|
||||||
int cVideo::Start(void *, unsigned short, unsigned short, void *)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cVideo::Stop(bool)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,16 +174,212 @@ void cVideo::Pig(int, int, int, int, int, int)
|
|||||||
|
|
||||||
void cVideo::getPictureInfo(int &width, int &height, int &rate)
|
void cVideo::getPictureInfo(int &width, int &height, int &rate)
|
||||||
{
|
{
|
||||||
width = 720;
|
width = dec_w;
|
||||||
height = 576;
|
height = dec_h;
|
||||||
rate = 50;
|
rate = dec_r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cVideo::SetSyncMode(AVSYNC_TYPE)
|
void cVideo::SetSyncMode(AVSYNC_TYPE)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
int cVideo::SetStreamType(VIDEO_FORMAT)
|
int cVideo::SetStreamType(VIDEO_FORMAT v)
|
||||||
{
|
{
|
||||||
|
v_format = v;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cVideo::SWFramebuffer *cVideo::getDecBuf(void)
|
||||||
|
{
|
||||||
|
buf_m.lock();
|
||||||
|
if (buf_num == 0) {
|
||||||
|
buf_m.unlock();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
SWFramebuffer *p = &buffers[buf_out];
|
||||||
|
buf_out++;
|
||||||
|
buf_num--;
|
||||||
|
buf_out %= VDEC_MAXBUFS;
|
||||||
|
buf_m.unlock();
|
||||||
|
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;
|
||||||
|
dec_r = 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 {
|
||||||
|
buf_m.lock();
|
||||||
|
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);
|
||||||
|
int64_t vpts = av_frame_get_best_effort_timestamp(frame);
|
||||||
|
if (v_format == VIDEO_FORMAT_MPEG2)
|
||||||
|
vpts += 90000*3/10; /* 300ms */
|
||||||
|
f->pts(vpts);
|
||||||
|
AVRational a = av_guess_sample_aspect_ratio(avfc, avfc->streams[0], frame);
|
||||||
|
f->AR(a);
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
dec_r = c->time_base.den/(c->time_base.num * c->ticks_per_frame);
|
||||||
|
buf_m.unlock();
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
#ifndef _VIDEO_TD_H
|
#ifndef _VIDEO_TD_H
|
||||||
#define _VIDEO_TD_H
|
#define _VIDEO_TD_H
|
||||||
|
|
||||||
|
#include <OpenThreads/Thread>
|
||||||
|
#include <OpenThreads/Mutex>
|
||||||
|
#include <vector>
|
||||||
#include <linux/dvb/video.h>
|
#include <linux/dvb/video.h>
|
||||||
#include "../common/cs_types.h"
|
#include "../common/cs_types.h"
|
||||||
|
#include <libavutil/rational.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ANALOG_SD_RGB_CINCH = 0x00,
|
ANALOG_SD_RGB_CINCH = 0x00,
|
||||||
@@ -112,9 +116,30 @@ typedef enum
|
|||||||
} VIDEO_CONTROL;
|
} VIDEO_CONTROL;
|
||||||
|
|
||||||
|
|
||||||
class cVideo
|
#define VDEC_MAXBUFS 0x30
|
||||||
|
class cVideo : public OpenThreads::Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* called from GL thread */
|
||||||
|
class SWFramebuffer : public std::vector<unsigned char>
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
void AR(AVRational a) { mAR = a; }
|
||||||
|
int width() const { return mWidth; }
|
||||||
|
int height() const { return mHeight; }
|
||||||
|
int64_t pts() const { return mPts; }
|
||||||
|
AVRational AR() const { return mAR; }
|
||||||
|
private:
|
||||||
|
int mWidth;
|
||||||
|
int mHeight;
|
||||||
|
int64_t mPts;
|
||||||
|
AVRational mAR;
|
||||||
|
};
|
||||||
|
int buf_in, buf_out, buf_num;
|
||||||
/* constructor & destructor */
|
/* constructor & destructor */
|
||||||
cVideo(int mode, void *, void *);
|
cVideo(int mode, void *, void *);
|
||||||
~cVideo(void);
|
~cVideo(void);
|
||||||
@@ -163,6 +188,16 @@ class cVideo
|
|||||||
int CloseVBI(void) { return 0; };
|
int CloseVBI(void) { return 0; };
|
||||||
int StartVBI(unsigned short) { return 0; };
|
int StartVBI(unsigned short) { return 0; };
|
||||||
int StopVBI(void) { 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;
|
||||||
|
VIDEO_FORMAT v_format;
|
||||||
|
OpenThreads::Mutex buf_m;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
6
include/glfb.h
Normal file
6
include/glfb.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include <config.h>
|
||||||
|
#if HAVE_GENERIC_HARDWARE
|
||||||
|
#include "../generic-pc/glfb.h"
|
||||||
|
#else
|
||||||
|
#error glfb.h only works with HAVE_GENERIC_HARDWARE defined
|
||||||
|
#endif
|
Reference in New Issue
Block a user