mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
separation libarmbox, libmipsbox, dissolve sym link
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
../libarmbox/audio.cpp
|
|
445
libmipsbox/audio.cpp
Normal file
445
libmipsbox/audio.cpp
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/dvb/audio.h>
|
||||||
|
|
||||||
|
#include <proc_tools.h>
|
||||||
|
|
||||||
|
#include "audio_lib.h"
|
||||||
|
#include "hal_debug.h"
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0"
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_AUDIO, this, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_AUDIO, this, args)
|
||||||
|
|
||||||
|
#include <linux/soundcard.h>
|
||||||
|
|
||||||
|
cAudio * audioDecoder = NULL;
|
||||||
|
|
||||||
|
cAudio::cAudio(void *, void *, void *)
|
||||||
|
{
|
||||||
|
fd = -1;
|
||||||
|
clipfd = -1;
|
||||||
|
mixer_fd = -1;
|
||||||
|
openDevice();
|
||||||
|
Muted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cAudio::~cAudio(void)
|
||||||
|
{
|
||||||
|
closeDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::openDevice(void)
|
||||||
|
{
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0)
|
||||||
|
hal_info("openDevice: open failed (%m)\n");
|
||||||
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
//do_mute(true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hal_info("openDevice: already open (fd = %d)\n", fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::closeDevice(void)
|
||||||
|
{
|
||||||
|
if (fd > -1) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
if (clipfd > -1) {
|
||||||
|
close(clipfd);
|
||||||
|
clipfd = -1;
|
||||||
|
}
|
||||||
|
if (mixer_fd > -1) {
|
||||||
|
close(mixer_fd);
|
||||||
|
mixer_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::do_mute(bool enable, bool remember)
|
||||||
|
{
|
||||||
|
hal_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember);
|
||||||
|
|
||||||
|
char str[4];
|
||||||
|
|
||||||
|
if (remember)
|
||||||
|
Muted = enable;
|
||||||
|
|
||||||
|
sprintf(str, "%d", Muted);
|
||||||
|
proc_put("/proc/stb/audio/j1_mute", str, strlen(str));
|
||||||
|
|
||||||
|
if (fd > 0)
|
||||||
|
{
|
||||||
|
if (ioctl(fd, AUDIO_SET_MUTE, enable) < 0)
|
||||||
|
perror("AUDIO_SET_MUTE");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_volume(const int volume)
|
||||||
|
{
|
||||||
|
unsigned char vol = volume;
|
||||||
|
if (vol > 100)
|
||||||
|
vol = 100;
|
||||||
|
|
||||||
|
// convert to -1dB steps
|
||||||
|
vol = 63 - vol * 63 / 100;
|
||||||
|
// now range is 63..0, where 0 is loudest
|
||||||
|
|
||||||
|
#if BOXMODEL_VUPLUS_ALL
|
||||||
|
if (vol == 63)
|
||||||
|
vol = 255;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||||
|
{
|
||||||
|
hal_info("cAudio::%s(%d, %d)\n", __func__, left, right);
|
||||||
|
|
||||||
|
volume = (left + right) / 2;
|
||||||
|
int v = map_volume(volume);
|
||||||
|
|
||||||
|
left = map_volume(volume);
|
||||||
|
right = map_volume(volume);
|
||||||
|
|
||||||
|
audio_mixer_t mixer;
|
||||||
|
|
||||||
|
mixer.volume_left = left;
|
||||||
|
mixer.volume_right = right;
|
||||||
|
|
||||||
|
if (fd > 0)
|
||||||
|
{
|
||||||
|
if (ioctl(fd, AUDIO_SET_MIXER, &mixer) < 0)
|
||||||
|
perror("AUDIO_SET_MIXER");
|
||||||
|
}
|
||||||
|
|
||||||
|
char str[4];
|
||||||
|
sprintf(str, "%d", v);
|
||||||
|
|
||||||
|
proc_put("/proc/stb/avs/0/volume", str, strlen(str));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::Start(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = ioctl(fd, AUDIO_PLAY);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::Stop(void)
|
||||||
|
{
|
||||||
|
return ioctl(fd, AUDIO_STOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cAudio::Pause(bool Pcm)
|
||||||
|
{
|
||||||
|
ioctl(fd, Pcm ? AUDIO_PAUSE : AUDIO_CONTINUE, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
|
||||||
|
{
|
||||||
|
hal_debug("%s %d\n", __func__, Mode);
|
||||||
|
ioctl(fd, AUDIO_SET_AV_SYNC, Mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AUDIO_STREAMTYPE_AC3 0
|
||||||
|
#define AUDIO_STREAMTYPE_MPEG 1
|
||||||
|
#define AUDIO_STREAMTYPE_DTS 2
|
||||||
|
#define AUDIO_STREAMTYPE_AAC 8
|
||||||
|
#define AUDIO_STREAMTYPE_AACHE 9
|
||||||
|
|
||||||
|
void cAudio::SetStreamType(AUDIO_FORMAT type)
|
||||||
|
{
|
||||||
|
int bypass = AUDIO_STREAMTYPE_MPEG;
|
||||||
|
hal_debug("%s %d\n", __FUNCTION__, type);
|
||||||
|
StreamType = type;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case AUDIO_FMT_DD_PLUS:
|
||||||
|
case AUDIO_FMT_DOLBY_DIGITAL:
|
||||||
|
bypass = AUDIO_STREAMTYPE_AC3;
|
||||||
|
break;
|
||||||
|
case AUDIO_FMT_AAC:
|
||||||
|
bypass = AUDIO_STREAMTYPE_AAC;
|
||||||
|
break;
|
||||||
|
case AUDIO_FMT_AAC_PLUS:
|
||||||
|
bypass = AUDIO_STREAMTYPE_AACHE;
|
||||||
|
break;
|
||||||
|
case AUDIO_FMT_DTS:
|
||||||
|
bypass = AUDIO_STREAMTYPE_DTS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normaly the encoding should be set using AUDIO_SET_ENCODING
|
||||||
|
// But as we implemented the behavior to bypass (cause of e2) this is correct here
|
||||||
|
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
|
||||||
|
hal_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::setChannel(int channel)
|
||||||
|
{
|
||||||
|
hal_debug("%s %d\n", __FUNCTION__, channel);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian)
|
||||||
|
{
|
||||||
|
int fmt;
|
||||||
|
unsigned int devmask, stereo, usable;
|
||||||
|
const char *dsp_dev = getenv("DSP_DEVICE");
|
||||||
|
const char *mix_dev = getenv("MIX_DEVICE");
|
||||||
|
hal_info("cAudio::%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian);
|
||||||
|
if (clipfd > -1) {
|
||||||
|
hal_info("%s: clipfd already opened (%d)\n", __func__, clipfd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mixer_num = -1;
|
||||||
|
mixer_fd = -1;
|
||||||
|
/* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE
|
||||||
|
* if this device cannot be opened, we fall back to the internal OSS device
|
||||||
|
* Example:
|
||||||
|
* modprobe snd-usb-audio
|
||||||
|
* export DSP_DEVICE=/dev/sound/dsp2
|
||||||
|
* export MIX_DEVICE=/dev/sound/mixer2
|
||||||
|
* neutrino
|
||||||
|
*/
|
||||||
|
if ((!dsp_dev) || (access(dsp_dev, W_OK))) {
|
||||||
|
if (dsp_dev)
|
||||||
|
hal_info("%s: DSP_DEVICE is set (%s) but cannot be opened,"
|
||||||
|
" fall back to /dev/dsp\n", __func__, dsp_dev);
|
||||||
|
dsp_dev = "/dev/dsp";
|
||||||
|
}
|
||||||
|
if ((!mix_dev) || (access(mix_dev, W_OK))) {
|
||||||
|
if (mix_dev)
|
||||||
|
hal_info("%s: MIX_DEVICE is set (%s) but cannot be opened,"
|
||||||
|
" fall back to /dev/mixer\n", __func__, dsp_dev);
|
||||||
|
mix_dev = "/dev/mixer";
|
||||||
|
}
|
||||||
|
hal_info("cAudio::%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */
|
||||||
|
/* the tdoss dsp driver seems to work only on the second open(). really. */
|
||||||
|
clipfd = open(dsp_dev, O_WRONLY);
|
||||||
|
if (clipfd < 0) {
|
||||||
|
hal_info("%s open %s: %m\n", dsp_dev, __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fcntl(clipfd, F_SETFD, FD_CLOEXEC);
|
||||||
|
/* no idea if we ever get little_endian == 0 */
|
||||||
|
if (little_endian)
|
||||||
|
fmt = AFMT_S16_BE;
|
||||||
|
else
|
||||||
|
fmt = AFMT_S16_LE;
|
||||||
|
if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt))
|
||||||
|
perror("SNDCTL_DSP_SETFMT");
|
||||||
|
if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch))
|
||||||
|
perror("SNDCTL_DSP_CHANNELS");
|
||||||
|
if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate))
|
||||||
|
perror("SNDCTL_DSP_SPEED");
|
||||||
|
#if !BOXMODEL_BRE2ZE4K && !BOXMODEL_HD51 && !BOXMODEL_H7
|
||||||
|
if (ioctl(clipfd, SNDCTL_DSP_RESET))
|
||||||
|
perror("SNDCTL_DSP_RESET");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!mix_dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mixer_fd = open(mix_dev, O_RDWR);
|
||||||
|
if (mixer_fd < 0) {
|
||||||
|
hal_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev);
|
||||||
|
/* not a real error */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
|
||||||
|
hal_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__);
|
||||||
|
devmask = 0;
|
||||||
|
}
|
||||||
|
if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) {
|
||||||
|
hal_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__);
|
||||||
|
stereo = 0;
|
||||||
|
}
|
||||||
|
usable = devmask & stereo;
|
||||||
|
if (usable == 0) {
|
||||||
|
hal_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n",
|
||||||
|
__func__, devmask, stereo);
|
||||||
|
close(mixer_fd);
|
||||||
|
mixer_fd = -1;
|
||||||
|
return 0; /* TODO: should we treat this as error? */
|
||||||
|
}
|
||||||
|
/* __builtin_popcount needs GCC, it counts the set bits... */
|
||||||
|
if (__builtin_popcount (usable) != 1) {
|
||||||
|
/* TODO: this code is not yet tested as I have only single-mixer devices... */
|
||||||
|
hal_info("%s: more than one mixer control: devmask %08x stereo %08x\n"
|
||||||
|
"%s: querying MIX_NUMBER environment variable...\n",
|
||||||
|
__func__, devmask, stereo, __func__);
|
||||||
|
const char *tmp = getenv("MIX_NUMBER");
|
||||||
|
if (tmp)
|
||||||
|
mixer_num = atoi(tmp);
|
||||||
|
hal_info("%s: mixer_num is %d -> device %08x\n",
|
||||||
|
__func__, mixer_num, (mixer_num >= 0) ? (1 << mixer_num) : 0);
|
||||||
|
/* no error checking, you'd better know what you are doing... */
|
||||||
|
} else {
|
||||||
|
mixer_num = 0;
|
||||||
|
while (!(usable & 0x01)) {
|
||||||
|
mixer_num++;
|
||||||
|
usable >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setVolume(volume, volume);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudio::WriteClip(unsigned char *buffer, int size)
|
||||||
|
{
|
||||||
|
int ret, __attribute__ ((unused)) count = 1;
|
||||||
|
// hal_debug("cAudio::%s\n", __FUNCTION__);
|
||||||
|
if (clipfd < 0) {
|
||||||
|
hal_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#if BOXMODEL_BRE2ZE4K || BOXMODEL_HD51 || BOXMODEL_H7
|
||||||
|
again:
|
||||||
|
#endif
|
||||||
|
ret = write(clipfd, buffer, size);
|
||||||
|
if (ret < 0) {
|
||||||
|
hal_info("%s: write error (%m)\n", __FUNCTION__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#if BOXMODEL_BRE2ZE4K || BOXMODEL_HD51 || BOXMODEL_H7
|
||||||
|
if (ret != size) {
|
||||||
|
hal_info("cAudio::%s: difference > to write (%d) != written (%d) try (%d) > reset dsp and restart write\n", __FUNCTION__, size, ret, count);
|
||||||
|
if (ioctl(clipfd, SNDCTL_DSP_RESET))
|
||||||
|
perror("SNDCTL_DSP_RESET");
|
||||||
|
count++;
|
||||||
|
if (count < 3)
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
int cAudio::StopClip()
|
||||||
|
{
|
||||||
|
hal_info("cAudio::%s\n", __FUNCTION__);
|
||||||
|
|
||||||
|
if (clipfd < 0) {
|
||||||
|
hal_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#if BOXMODEL_VUPLUS_ARM
|
||||||
|
ioctl(clipfd, SNDCTL_DSP_RESET);
|
||||||
|
#endif
|
||||||
|
close(clipfd);
|
||||||
|
clipfd = -1;
|
||||||
|
if (mixer_fd > -1) {
|
||||||
|
close(mixer_fd);
|
||||||
|
mixer_fd = -1;
|
||||||
|
}
|
||||||
|
setVolume(volume, volume);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode)
|
||||||
|
{
|
||||||
|
hal_debug("%s\n", __FUNCTION__);
|
||||||
|
type = 0;
|
||||||
|
layer = 0;
|
||||||
|
freq = 0;
|
||||||
|
bitrate = 0;
|
||||||
|
mode = 0;
|
||||||
|
#if 0
|
||||||
|
unsigned int atype;
|
||||||
|
static const int freq_mpg[] = {44100, 48000, 32000, 0};
|
||||||
|
static const int freq_ac3[] = {48000, 44100, 32000, 0};
|
||||||
|
scratchl2 i;
|
||||||
|
if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0)
|
||||||
|
perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP");
|
||||||
|
if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0)
|
||||||
|
perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS");
|
||||||
|
|
||||||
|
type = atype;
|
||||||
|
#if 0
|
||||||
|
/* this does not work, some of the values are negative?? */
|
||||||
|
AMPEGStatus A;
|
||||||
|
memcpy(&A, &i.word00, sizeof(i.word00));
|
||||||
|
layer = A.audio_mpeg_layer;
|
||||||
|
mode = A.audio_mpeg_mode;
|
||||||
|
bitrate = A.audio_mpeg_bitrate;
|
||||||
|
switch(A.audio_mpeg_frequency)
|
||||||
|
#endif
|
||||||
|
/* layer and bitrate are not used anyway... */
|
||||||
|
layer = 0; //(i.word00 >> 17) & 3;
|
||||||
|
bitrate = 0; //(i.word00 >> 12) & 3;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0: /* MPEG */
|
||||||
|
mode = (i.word00 >> 6) & 3;
|
||||||
|
freq = freq_mpg[(i.word00 >> 10) & 3];
|
||||||
|
break;
|
||||||
|
case 1: /* AC3 */
|
||||||
|
mode = (i.word00 >> 28) & 7;
|
||||||
|
freq = freq_ac3[(i.word00 >> 16) & 3];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = 0;
|
||||||
|
freq = 0;
|
||||||
|
}
|
||||||
|
//fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
|
||||||
|
{
|
||||||
|
hal_debug("%s\n", __FUNCTION__);
|
||||||
|
};
|
||||||
|
|
||||||
|
void cAudio::SetHdmiDD(bool enable)
|
||||||
|
{
|
||||||
|
const char *opt[] = { "downmix", "passthrough" };
|
||||||
|
hal_debug("%s %d\n", __func__, enable);
|
||||||
|
proc_put("/proc/stb/audio/ac3", opt[enable], strlen(opt[enable]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::SetSpdifDD(bool enable)
|
||||||
|
{
|
||||||
|
//using this function for dts passthrough
|
||||||
|
const char *opt[] = { "downmix", "passthrough" };
|
||||||
|
hal_debug("%s %d\n", __func__, enable);
|
||||||
|
proc_put("/proc/stb/audio/dts", opt[enable], strlen(opt[enable]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::ScheduleMute(bool On)
|
||||||
|
{
|
||||||
|
hal_debug("%s %d\n", __FUNCTION__, On);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cAudio::EnableAnalogOut(bool enable)
|
||||||
|
{
|
||||||
|
hal_debug("%s %d\n", __FUNCTION__, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AUDIO_BYPASS_ON 0
|
||||||
|
#define AUDIO_BYPASS_OFF 1
|
||||||
|
void cAudio::setBypassMode(bool disable)
|
||||||
|
{
|
||||||
|
int mode = disable ? AUDIO_BYPASS_OFF : AUDIO_BYPASS_ON;
|
||||||
|
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, mode) < 0)
|
||||||
|
hal_info("%s AUDIO_SET_BYPASS_MODE %d: %m\n", __func__, mode);
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/audio_lib.h
|
|
101
libmipsbox/audio_lib.h
Normal file
101
libmipsbox/audio_lib.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/* public header file */
|
||||||
|
|
||||||
|
#ifndef __AUDIO_LIB_H__
|
||||||
|
#define __AUDIO_LIB_H__
|
||||||
|
|
||||||
|
#include "cs_types.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
AUDIO_SYNC_WITH_PTS,
|
||||||
|
AUDIO_NO_SYNC,
|
||||||
|
AUDIO_SYNC_AUDIO_MASTER
|
||||||
|
} AUDIO_SYNC_MODE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HDMI_ENCODED_OFF,
|
||||||
|
HDMI_ENCODED_AUTO,
|
||||||
|
HDMI_ENCODED_FORCED
|
||||||
|
} HDMI_ENCODED_MODE;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
AUDIO_FMT_AUTO = 0,
|
||||||
|
AUDIO_FMT_MPEG,
|
||||||
|
AUDIO_FMT_MP3,
|
||||||
|
AUDIO_FMT_DOLBY_DIGITAL,
|
||||||
|
AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL,
|
||||||
|
AUDIO_FMT_AAC,
|
||||||
|
AUDIO_FMT_AAC_PLUS,
|
||||||
|
AUDIO_FMT_DD_PLUS,
|
||||||
|
AUDIO_FMT_DTS,
|
||||||
|
AUDIO_FMT_AVS,
|
||||||
|
AUDIO_FMT_MLP,
|
||||||
|
AUDIO_FMT_WMA,
|
||||||
|
AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback
|
||||||
|
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
||||||
|
} AUDIO_FORMAT;
|
||||||
|
|
||||||
|
class mixerVolume;
|
||||||
|
|
||||||
|
class cAudio
|
||||||
|
{
|
||||||
|
friend class cPlayback;
|
||||||
|
private:
|
||||||
|
int fd;
|
||||||
|
bool Muted;
|
||||||
|
|
||||||
|
int clipfd; /* for pcm playback */
|
||||||
|
int mixer_fd; /* if we are using the OSS mixer */
|
||||||
|
int mixer_num; /* oss mixer to use, if any */
|
||||||
|
|
||||||
|
AUDIO_FORMAT StreamType;
|
||||||
|
AUDIO_SYNC_MODE SyncMode;
|
||||||
|
bool started;
|
||||||
|
|
||||||
|
int volume;
|
||||||
|
|
||||||
|
int do_mute(bool enable, bool remember);
|
||||||
|
void setBypassMode(bool disable);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* construct & destruct */
|
||||||
|
cAudio(void *, void *, void *);
|
||||||
|
~cAudio(void);
|
||||||
|
|
||||||
|
void openDevice(void);
|
||||||
|
void closeDevice(void);
|
||||||
|
|
||||||
|
void *GetHandle() { return NULL; };
|
||||||
|
/* shut up */
|
||||||
|
int mute(bool remember = true) { return do_mute(true, remember); };
|
||||||
|
int unmute(bool remember = true) { return do_mute(false, remember); };
|
||||||
|
|
||||||
|
/* volume, min = 0, max = 255 */
|
||||||
|
int setVolume(unsigned int left, unsigned int right);
|
||||||
|
int getVolume(void) { return volume;}
|
||||||
|
bool getMuteStatus(void) { return Muted; };
|
||||||
|
|
||||||
|
/* start and stop audio */
|
||||||
|
int Start(void);
|
||||||
|
int Stop(void);
|
||||||
|
bool Pause(bool Pcm = true);
|
||||||
|
void SetStreamType(AUDIO_FORMAT type);
|
||||||
|
AUDIO_FORMAT GetStreamType(void) { return StreamType; }
|
||||||
|
void SetSyncMode(AVSYNC_TYPE Mode);
|
||||||
|
|
||||||
|
/* select channels */
|
||||||
|
int setChannel(int channel);
|
||||||
|
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
|
||||||
|
int WriteClip(unsigned char * buffer, int size);
|
||||||
|
int StopClip();
|
||||||
|
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
|
||||||
|
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
|
||||||
|
bool IsHdmiDDSupported() { return true; };
|
||||||
|
void SetHdmiDD(bool enable);
|
||||||
|
void SetSpdifDD(bool enable);
|
||||||
|
void ScheduleMute(bool On);
|
||||||
|
void EnableAnalogOut(bool enable);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __AUDIO_LIB_H__
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/dmx.cpp
|
|
623
libmipsbox/dmx.cpp
Normal file
623
libmipsbox/dmx.cpp
Normal file
@@ -0,0 +1,623 @@
|
|||||||
|
/*
|
||||||
|
* cDemux implementation for arm receivers (tested on mutant hd51
|
||||||
|
* hardware)
|
||||||
|
*
|
||||||
|
* derived from libtriple/dmx_td.cpp
|
||||||
|
*
|
||||||
|
* (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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <OpenThreads/Mutex>
|
||||||
|
#include <OpenThreads/ScopedLock>
|
||||||
|
#include "dmx_hal.h"
|
||||||
|
#include "hal_debug.h"
|
||||||
|
|
||||||
|
#include "video_lib.h"
|
||||||
|
/* needed for getSTC... */
|
||||||
|
extern cVideo *videoDecoder;
|
||||||
|
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_DEMUX, this, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_DEMUX, this, args)
|
||||||
|
#define hal_debug_c(args...) _hal_debug(HAL_DEBUG_DEMUX, NULL, args)
|
||||||
|
#define hal_info_c(args...) _hal_info(HAL_DEBUG_DEMUX, NULL, args)
|
||||||
|
#define hal_info_z(args...) _hal_info(HAL_DEBUG_DEMUX, thiz, args)
|
||||||
|
#define hal_debug_z(args...) _hal_debug(HAL_DEBUG_DEMUX, thiz, args)
|
||||||
|
|
||||||
|
#define dmx_err(_errfmt, _errstr, _revents) do { \
|
||||||
|
hal_info("%s " _errfmt " fd:%d, ev:0x%x %s pid:0x%04hx flt:0x%02hx\n", \
|
||||||
|
__func__, _errstr, fd, _revents, DMX_T[dmx_type], pid, flt); \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
cDemux *videoDemux = NULL;
|
||||||
|
cDemux *audioDemux = NULL;
|
||||||
|
//cDemux *pcrDemux = NULL;
|
||||||
|
|
||||||
|
static const char *DMX_T[] = {
|
||||||
|
"DMX_INVALID",
|
||||||
|
"DMX_VIDEO",
|
||||||
|
"DMX_AUDIO",
|
||||||
|
"DMX_PES",
|
||||||
|
"DMX_PSI",
|
||||||
|
"DMX_PIP",
|
||||||
|
"DMX_TP",
|
||||||
|
"DMX_PCR"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* this is the number of different cDemux() units, not the number of
|
||||||
|
* /dev/dvb/.../demuxX devices! */
|
||||||
|
#if BOXMODEL_VUULTIMO4K
|
||||||
|
#define NUM_DEMUX 24
|
||||||
|
#else
|
||||||
|
#if BOXMODEL_VUSOLO4K || BOXMODEL_VUDUO4K || BOXMODEL_VUDUO4KSE || BOXMODEL_VUUNO4KSE || BOXMODEL_VUUNO4K
|
||||||
|
#define NUM_DEMUX 16
|
||||||
|
#else
|
||||||
|
#define NUM_DEMUX 4
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/* the current source of each cDemux unit */
|
||||||
|
#if BOXMODEL_VUULTIMO4K
|
||||||
|
static int dmx_source[NUM_DEMUX] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
#else
|
||||||
|
#if BOXMODEL_VUSOLO4K || BOXMODEL_VUDUO4K || BOXMODEL_VUDUO4KSE || BOXMODEL_VUUNO4KSE || BOXMODEL_VUUNO4K
|
||||||
|
static int dmx_source[NUM_DEMUX] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
#else
|
||||||
|
static int dmx_source[NUM_DEMUX] = { 0, 0, 0, 0 };
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char dmxdev[32];
|
||||||
|
static char* devname(int adapter, int demux) {
|
||||||
|
snprintf(dmxdev, sizeof(dmxdev), "/dev/dvb/adapter%d/demux%d", adapter, demux);
|
||||||
|
return dmxdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* map the device numbers. */
|
||||||
|
#if BOXMODEL_VUULTIMO4K
|
||||||
|
#define NUM_DEMUXDEV 24
|
||||||
|
#else
|
||||||
|
#if BOXMODEL_VUSOLO4K || BOXMODEL_VUDUO4K || BOXMODEL_VUDUO4KSE || BOXMODEL_VUUNO4KSE || BOXMODEL_VUUNO4K
|
||||||
|
#define NUM_DEMUXDEV 16
|
||||||
|
#else
|
||||||
|
#define NUM_DEMUXDEV 8
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* did we already DMX_SET_SOURCE on that demux device? */
|
||||||
|
#if BOXMODEL_VUULTIMO4K
|
||||||
|
static bool init[NUM_DEMUXDEV] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
|
||||||
|
#else
|
||||||
|
#if BOXMODEL_VUSOLO4K || BOXMODEL_VUDUO4K || BOXMODEL_VUDUO4KSE || BOXMODEL_VUUNO4KSE || BOXMODEL_VUUNO4K
|
||||||
|
static bool init[NUM_DEMUXDEV] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
|
||||||
|
#else
|
||||||
|
static bool init[NUM_DEMUXDEV] = { false, false, false, false, false, false, false, false };
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct dmx_pdata {
|
||||||
|
int last_source;
|
||||||
|
OpenThreads::Mutex *mutex;
|
||||||
|
} dmx_pdata;
|
||||||
|
#define P ((dmx_pdata *)pdata)
|
||||||
|
|
||||||
|
cDemux::cDemux(int n)
|
||||||
|
{
|
||||||
|
if (n < 0 || n >= NUM_DEMUX)
|
||||||
|
{
|
||||||
|
hal_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n);
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
num = n;
|
||||||
|
fd = -1;
|
||||||
|
pdata = (void *)calloc(1, sizeof(dmx_pdata));
|
||||||
|
P->last_source = -1;
|
||||||
|
P->mutex = new OpenThreads::Mutex;
|
||||||
|
dmx_type = DMX_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
cDemux::~cDemux()
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd);
|
||||||
|
Close();
|
||||||
|
/* wait until Read() has released the mutex */
|
||||||
|
(*P->mutex).lock();
|
||||||
|
(*P->mutex).unlock();
|
||||||
|
free(P->mutex);
|
||||||
|
free(pdata);
|
||||||
|
pdata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize)
|
||||||
|
{
|
||||||
|
if (fd > -1)
|
||||||
|
hal_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
|
||||||
|
|
||||||
|
dmx_type = pes_type;
|
||||||
|
buffersize = uBufferSize;
|
||||||
|
|
||||||
|
/* return code is unchecked anyway... */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _open(cDemux *thiz, int num, int &fd, int &last_source, DMX_CHANNEL_TYPE dmx_type, int buffersize)
|
||||||
|
{
|
||||||
|
int flags = O_RDWR|O_CLOEXEC;
|
||||||
|
int devnum = dmx_source[num];
|
||||||
|
if (last_source == devnum) {
|
||||||
|
hal_debug_z("%s #%d: source (%d) did not change\n", __func__, num, last_source);
|
||||||
|
if (fd > -1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (fd > -1) {
|
||||||
|
/* we changed source -> close and reopen the fd */
|
||||||
|
hal_debug_z("%s #%d: FD ALREADY OPENED fd = %d lastsource %d devnum %d\n",
|
||||||
|
__func__, num, fd, last_source, devnum);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dmx_type != DMX_PSI_CHANNEL)
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
|
||||||
|
fd = open(devname(0, devnum), flags);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
hal_info_z("%s %s: %m\n", __FUNCTION__, devname(0, devnum));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hal_debug_z("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
|
||||||
|
num, DMX_T[dmx_type], dmx_type, buffersize, fd);
|
||||||
|
|
||||||
|
/* this would actually need locking, but the worst that weill happen is, that
|
||||||
|
* we'll DMX_SET_SOURCE twice per device, so don't bother... */
|
||||||
|
if (!init[devnum])
|
||||||
|
{
|
||||||
|
/* this should not change anything... */
|
||||||
|
int n = DMX_SOURCE_FRONT0 + devnum;
|
||||||
|
hal_info_z("%s: setting %s to source %d\n", __func__, devname(0, devnum), n);
|
||||||
|
if (ioctl(fd, DMX_SET_SOURCE, &n) < 0)
|
||||||
|
hal_info_z("%s DMX_SET_SOURCE failed!\n", __func__);
|
||||||
|
else
|
||||||
|
init[devnum] = true;
|
||||||
|
}
|
||||||
|
if (buffersize == 0)
|
||||||
|
buffersize = 0xffff; // may or may not be reasonable --martii
|
||||||
|
if (buffersize > 0)
|
||||||
|
{
|
||||||
|
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
|
||||||
|
if (ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) < 0)
|
||||||
|
hal_info_z("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_source = devnum;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDemux::Close(void)
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
hal_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pesfds.clear();
|
||||||
|
ioctl(fd, DMX_STOP);
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::Start(bool)
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
hal_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ioctl(fd, DMX_START);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::Stop(void)
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
hal_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ioctl(fd, DMX_STOP);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cDemux::Read(unsigned char *buff, int len, int timeout)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (len != 4095 && timeout != 10)
|
||||||
|
fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n",
|
||||||
|
__FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout);
|
||||||
|
#endif
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
hal_info("%s #%d: not open!\n", __func__, num);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* avoid race in destructor: ~cDemux needs to wait until Read() returns */
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(*P->mutex);
|
||||||
|
int rc;
|
||||||
|
int to = timeout;
|
||||||
|
struct pollfd ufds;
|
||||||
|
ufds.fd = fd;
|
||||||
|
ufds.events = POLLIN|POLLPRI|POLLERR;
|
||||||
|
ufds.revents = 0;
|
||||||
|
|
||||||
|
/* hack: if the frontend loses and regains lock, the demuxer often will not
|
||||||
|
* return from read(), so as a "emergency exit" for e.g. NIT scan, set a (long)
|
||||||
|
* timeout here */
|
||||||
|
if (dmx_type == DMX_PSI_CHANNEL && timeout <= 0)
|
||||||
|
to = 60 * 1000;
|
||||||
|
|
||||||
|
if (to > 0)
|
||||||
|
{
|
||||||
|
retry:
|
||||||
|
rc = ::poll(&ufds, 1, to);
|
||||||
|
if (ufds.fd != fd)
|
||||||
|
{
|
||||||
|
/* Close() will set fd to -1, this is normal. Everything else is not. */
|
||||||
|
hal_info("%s:1 ========== fd has changed, %d->%d ==========\n", __func__, ufds.fd, fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
if (timeout == 0) /* we took the emergency exit */
|
||||||
|
{
|
||||||
|
dmx_err("timed out for timeout=0!, %s", "", 0);
|
||||||
|
return -1; /* this timeout is an error */
|
||||||
|
}
|
||||||
|
return 0; // timeout
|
||||||
|
}
|
||||||
|
else if (rc < 0)
|
||||||
|
{
|
||||||
|
dmx_err("poll: %s,", strerror(errno), 0)
|
||||||
|
//hal_info("%s poll: %m\n", __FUNCTION__);
|
||||||
|
/* happens, when running under gdb... */
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto retry;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */
|
||||||
|
{
|
||||||
|
dmx_err("received %s,", "POLLERR", ufds.revents);
|
||||||
|
/* this seems to happen sometimes at recording start, without bad effects */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */
|
||||||
|
{
|
||||||
|
dmx_err("received %s,", "POLLHUP", ufds.revents);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */
|
||||||
|
{
|
||||||
|
dmx_err("received %s, please report!", "POLLIN", ufds.revents);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ufds.fd != fd) /* does this ever happen? and if, is it harmful? */
|
||||||
|
{ /* read(-1,...) will just return EBADF anyway... */
|
||||||
|
hal_info("%s:2 ========== fd has changed, %d->%d ==========\n", __func__, ufds.fd, fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ::read(fd, buff, len);
|
||||||
|
//fprintf(stderr, "fd %d ret: %d\n", fd, rc);
|
||||||
|
if (rc < 0)
|
||||||
|
dmx_err("read: %s", strerror(errno), 0);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::sectionFilter(unsigned short _pid, const unsigned char * const filter,
|
||||||
|
const unsigned char * const mask, int len, int timeout,
|
||||||
|
const unsigned char * const negmask)
|
||||||
|
{
|
||||||
|
struct dmx_sct_filter_params s_flt;
|
||||||
|
memset(&s_flt, 0, sizeof(s_flt));
|
||||||
|
pid = _pid;
|
||||||
|
|
||||||
|
_open(this, num, fd, P->last_source, dmx_type, buffersize);
|
||||||
|
|
||||||
|
if (len > DMX_FILTER_SIZE)
|
||||||
|
{
|
||||||
|
hal_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE);
|
||||||
|
len = DMX_FILTER_SIZE;
|
||||||
|
}
|
||||||
|
flt = filter[0];
|
||||||
|
s_flt.pid = pid;
|
||||||
|
s_flt.timeout = timeout;
|
||||||
|
memcpy(s_flt.filter.filter, filter, len);
|
||||||
|
memcpy(s_flt.filter.mask, mask, len);
|
||||||
|
if (negmask != NULL)
|
||||||
|
memcpy(s_flt.filter.mode, negmask, len);
|
||||||
|
|
||||||
|
s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC;
|
||||||
|
|
||||||
|
int to = 0;
|
||||||
|
switch (filter[0])
|
||||||
|
{
|
||||||
|
case 0x00: /* program_association_section */
|
||||||
|
to = 2000;
|
||||||
|
break;
|
||||||
|
case 0x01: /* conditional_access_section */
|
||||||
|
to = 6000;
|
||||||
|
break;
|
||||||
|
case 0x02: /* program_map_section */
|
||||||
|
to = 1500;
|
||||||
|
break;
|
||||||
|
case 0x03: /* transport_stream_description_section */
|
||||||
|
to = 10000;
|
||||||
|
break;
|
||||||
|
/* 0x04 - 0x3F: reserved */
|
||||||
|
case 0x40: /* network_information_section - actual_network */
|
||||||
|
to = 10000;
|
||||||
|
break;
|
||||||
|
case 0x41: /* network_information_section - other_network */
|
||||||
|
to = 15000;
|
||||||
|
break;
|
||||||
|
case 0x42: /* service_description_section - actual_transport_stream */
|
||||||
|
to = 10000;
|
||||||
|
break;
|
||||||
|
/* 0x43 - 0x45: reserved for future use */
|
||||||
|
case 0x46: /* service_description_section - other_transport_stream */
|
||||||
|
to = 10000;
|
||||||
|
break;
|
||||||
|
/* 0x47 - 0x49: reserved for future use */
|
||||||
|
case 0x4A: /* bouquet_association_section */
|
||||||
|
to = 11000;
|
||||||
|
break;
|
||||||
|
/* 0x4B - 0x4D: reserved for future use */
|
||||||
|
case 0x4E: /* event_information_section - actual_transport_stream, present/following */
|
||||||
|
to = 2000;
|
||||||
|
break;
|
||||||
|
case 0x4F: /* event_information_section - other_transport_stream, present/following */
|
||||||
|
to = 10000;
|
||||||
|
break;
|
||||||
|
/* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */
|
||||||
|
/* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */
|
||||||
|
case 0x70: /* time_date_section */
|
||||||
|
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||||
|
s_flt.flags |= DMX_ONESHOT;
|
||||||
|
//s_flt.pid = 0x0014;
|
||||||
|
to = 30000;
|
||||||
|
break;
|
||||||
|
case 0x71: /* running_status_section */
|
||||||
|
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||||
|
to = 0;
|
||||||
|
break;
|
||||||
|
case 0x72: /* stuffing_section */
|
||||||
|
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||||
|
to = 0;
|
||||||
|
break;
|
||||||
|
case 0x73: /* time_offset_section */
|
||||||
|
s_flt.flags |= DMX_ONESHOT;
|
||||||
|
//s_flt.pid = 0x0014;
|
||||||
|
to = 30000;
|
||||||
|
break;
|
||||||
|
/* 0x74 - 0x7D: reserved for future use */
|
||||||
|
case 0x7E: /* discontinuity_information_section */
|
||||||
|
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||||
|
to = 0;
|
||||||
|
break;
|
||||||
|
case 0x7F: /* selection_information_section */
|
||||||
|
to = 0;
|
||||||
|
break;
|
||||||
|
/* 0x80 - 0x8F: ca_message_section */
|
||||||
|
/* 0x90 - 0xFE: user defined */
|
||||||
|
/* 0xFF: reserved */
|
||||||
|
default:
|
||||||
|
//return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* the negmask == NULL is a hack: the users of negmask are PMT-update
|
||||||
|
* and sectionsd EIT-Version change. And they really want no timeout
|
||||||
|
* if timeout == 0 instead of "default timeout" */
|
||||||
|
if (timeout == 0 && negmask == NULL)
|
||||||
|
s_flt.timeout = to;
|
||||||
|
|
||||||
|
hal_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num,
|
||||||
|
pid, fd, DMX_T[dmx_type], len, s_flt.timeout, s_flt.flags, s_flt.filter.filter[0]);
|
||||||
|
#if 0
|
||||||
|
fprintf(stderr,"filt: "); for (int i = 0; i < len; i++) fprintf(stderr, "%02hhx ", s_flt.filter.filter[i]); fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr,"mask: "); for (int i = 0; i < len; i++) fprintf(stderr, "%02hhx ", s_flt.filter.mask [i]); fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr,"mode: "); for (int i = 0; i < len; i++) fprintf(stderr, "%02hhx ", s_flt.filter.mode [i]); fprintf(stderr, "\n");
|
||||||
|
#endif
|
||||||
|
ioctl (fd, DMX_STOP);
|
||||||
|
if (ioctl(fd, DMX_SET_FILTER, &s_flt) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::pesFilter(const unsigned short _pid)
|
||||||
|
{
|
||||||
|
struct dmx_pes_filter_params p_flt;
|
||||||
|
pid = _pid;
|
||||||
|
flt = 0;
|
||||||
|
/* allow PID 0 for web streaming e.g.
|
||||||
|
* this check originally is from tuxbox cvs but I'm not sure
|
||||||
|
* what it is good for...
|
||||||
|
if (pid <= 0x0001 && dmx_type != DMX_PCR_ONLY_CHANNEL)
|
||||||
|
return false;
|
||||||
|
*/
|
||||||
|
if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hal_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]);
|
||||||
|
|
||||||
|
_open(this, num, fd, P->last_source, dmx_type, buffersize);
|
||||||
|
|
||||||
|
memset(&p_flt, 0, sizeof(p_flt));
|
||||||
|
p_flt.pid = pid;
|
||||||
|
p_flt.output = DMX_OUT_DECODER;
|
||||||
|
p_flt.input = DMX_IN_FRONTEND;
|
||||||
|
p_flt.flags = 0;
|
||||||
|
|
||||||
|
switch (dmx_type)
|
||||||
|
{
|
||||||
|
case DMX_PCR_ONLY_CHANNEL:
|
||||||
|
p_flt.pes_type = DMX_PES_PCR;
|
||||||
|
break;
|
||||||
|
case DMX_AUDIO_CHANNEL:
|
||||||
|
p_flt.pes_type = DMX_PES_AUDIO;
|
||||||
|
break;
|
||||||
|
case DMX_VIDEO_CHANNEL:
|
||||||
|
p_flt.pes_type = DMX_PES_VIDEO;
|
||||||
|
break;
|
||||||
|
case DMX_PIP_CHANNEL: /* PIP is a special version of DMX_VIDEO_CHANNEL */
|
||||||
|
p_flt.pes_type = DMX_PES_VIDEO1;
|
||||||
|
break;
|
||||||
|
case DMX_PES_CHANNEL:
|
||||||
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
p_flt.output = DMX_OUT_TAP;
|
||||||
|
break;
|
||||||
|
case DMX_TP_CHANNEL:
|
||||||
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hal_info("%s #%d invalid dmx_type %d!\n", __func__, num, dmx_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (ioctl(fd, DMX_SET_PES_FILTER, &p_flt) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/)
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d\n", __FUNCTION__, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cDemux::getBuffer()
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d\n", __FUNCTION__, num);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cDemux::getChannel()
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d\n", __FUNCTION__, num);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::addPid(unsigned short Pid)
|
||||||
|
{
|
||||||
|
hal_debug("%s: pid 0x%04hx\n", __func__, Pid);
|
||||||
|
pes_pids pfd;
|
||||||
|
int ret;
|
||||||
|
if (dmx_type != DMX_TP_CHANNEL)
|
||||||
|
{
|
||||||
|
hal_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_open(this, num, fd, P->last_source, dmx_type, buffersize);
|
||||||
|
if (fd == -1)
|
||||||
|
hal_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
|
||||||
|
pfd.fd = fd; /* dummy */
|
||||||
|
pfd.pid = Pid;
|
||||||
|
pesfds.push_back(pfd);
|
||||||
|
ret = (ioctl(fd, DMX_ADD_PID, &Pid));
|
||||||
|
if (ret < 0)
|
||||||
|
hal_info("%s: DMX_ADD_PID (%m) pid=%hx\n", __func__, Pid);
|
||||||
|
return (ret != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDemux::removePid(unsigned short Pid)
|
||||||
|
{
|
||||||
|
if (dmx_type != DMX_TP_CHANNEL)
|
||||||
|
{
|
||||||
|
hal_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (std::vector<pes_pids>::iterator i = pesfds.begin(); i != pesfds.end(); ++i)
|
||||||
|
{
|
||||||
|
if ((*i).pid == Pid) {
|
||||||
|
hal_debug("removePid: removing demux fd %d pid 0x%04x\n", fd, Pid);
|
||||||
|
if (ioctl(fd, DMX_REMOVE_PID, Pid) < 0)
|
||||||
|
hal_info("%s: (DMX_REMOVE_PID, 0x%04hx): %m\n", __func__, Pid);
|
||||||
|
pesfds.erase(i);
|
||||||
|
return; /* TODO: what if the same PID is there multiple times */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hal_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDemux::getSTC(int64_t * STC)
|
||||||
|
{
|
||||||
|
/* apparently I can only get the PTS of the video decoder,
|
||||||
|
* but that's good enough for dvbsub */
|
||||||
|
hal_debug("%s #%d\n", __func__, num);
|
||||||
|
int64_t pts = 0;
|
||||||
|
if (videoDecoder)
|
||||||
|
pts = videoDecoder->GetPTS();
|
||||||
|
*STC = pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cDemux::getUnit(void)
|
||||||
|
{
|
||||||
|
hal_debug("%s #%d\n", __FUNCTION__, num);
|
||||||
|
/* just guessed that this is the right thing to do.
|
||||||
|
right now this is only used by the CA code which is stubbed out
|
||||||
|
anyway */
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDemux::SetSource(int unit, int source)
|
||||||
|
{
|
||||||
|
if (unit >= NUM_DEMUX || unit < 0) {
|
||||||
|
hal_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hal_debug_c("%s(%d, %d) => %d to %d\n", __func__, unit, source, dmx_source[unit], source);
|
||||||
|
if (source < 0 || source >= NUM_DEMUXDEV)
|
||||||
|
hal_info_c("%s(%d, %d) ERROR: source %d out of range!\n", __func__, unit, source, source);
|
||||||
|
else
|
||||||
|
dmx_source[unit] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cDemux::GetSource(int unit)
|
||||||
|
{
|
||||||
|
if (unit >= NUM_DEMUX || unit < 0) {
|
||||||
|
hal_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
hal_debug_c("%s(%d) => %d\n", __func__, unit, dmx_source[unit]);
|
||||||
|
return dmx_source[unit];
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/hdmi_cec.cpp
|
|
836
libmipsbox/hdmi_cec.cpp
Normal file
836
libmipsbox/hdmi_cec.cpp
Normal file
@@ -0,0 +1,836 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2018-2020 TangoCash
|
||||||
|
|
||||||
|
License: GPLv2
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utime.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
#include "linux-uapi-cec.h"
|
||||||
|
#include "hdmi_cec.h"
|
||||||
|
#include "hdmi_cec_types.h"
|
||||||
|
#include "hal_debug.h"
|
||||||
|
|
||||||
|
#define RED "\x1B[31m"
|
||||||
|
#define GREEN "\x1B[32m"
|
||||||
|
#define NORMAL "\x1B[0m"
|
||||||
|
|
||||||
|
#define EPOLL_WAIT_TIMEOUT (-1)
|
||||||
|
#define EPOLL_MAX_EVENTS (1)
|
||||||
|
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_INIT, this, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_INIT, this, args)
|
||||||
|
#define hal_debug_c(args...) _hal_debug(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
#define hal_info_c(args...) _hal_info(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
|
||||||
|
#define fop(cmd, args...) ({ \
|
||||||
|
int _r; \
|
||||||
|
if (fd >= 0) { \
|
||||||
|
if ((_r = ::cmd(fd, args)) < 0) \
|
||||||
|
hal_info(#cmd"(fd, "#args")\n"); \
|
||||||
|
else \
|
||||||
|
hal_debug(#cmd"(fd, "#args")\n");\
|
||||||
|
} \
|
||||||
|
else { _r = fd; } \
|
||||||
|
_r; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define CEC_FALLBACK_DEVICE "/dev/cec0"
|
||||||
|
#define CEC_HDMIDEV "/dev/hdmi_cec"
|
||||||
|
#if BOXMODEL_H7
|
||||||
|
#define RC_DEVICE "/dev/input/event2"
|
||||||
|
#else
|
||||||
|
#define RC_DEVICE "/dev/input/event1"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hdmi_cec * hdmi_cec::hdmi_cec_instance = NULL;
|
||||||
|
|
||||||
|
//hack to get an instance before first call
|
||||||
|
hdmi_cec * CEC = hdmi_cec::getInstance();
|
||||||
|
|
||||||
|
hdmi_cec::hdmi_cec()
|
||||||
|
{
|
||||||
|
standby_cec_activ = autoview_cec_activ = standby = muted = false;
|
||||||
|
hdmiFd = -1;
|
||||||
|
volume = 0;
|
||||||
|
fallback = false;
|
||||||
|
tv_off = true;
|
||||||
|
deviceType = CEC_LOG_ADDR_TYPE_UNREGISTERED;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdmi_cec::~hdmi_cec()
|
||||||
|
{
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
close(hdmiFd);
|
||||||
|
hdmiFd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hdmi_cec* hdmi_cec::getInstance()
|
||||||
|
{
|
||||||
|
if (hdmi_cec_instance == NULL)
|
||||||
|
{
|
||||||
|
hdmi_cec_instance = new hdmi_cec();
|
||||||
|
hal_info_c(GREEN "[CEC] new instance created \n" NORMAL);
|
||||||
|
}
|
||||||
|
return hdmi_cec_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hdmi_cec::SetCECMode(VIDEO_HDMI_CEC_MODE _deviceType)
|
||||||
|
{
|
||||||
|
physicalAddress[0] = 0x10;
|
||||||
|
physicalAddress[1] = 0x00;
|
||||||
|
logicalAddress = 1;
|
||||||
|
|
||||||
|
if (_deviceType == VIDEO_HDMI_CEC_MODE_OFF)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
hal_info(GREEN "[CEC] switch off %s\n" NORMAL, __func__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
deviceType = _deviceType;
|
||||||
|
|
||||||
|
hal_info(GREEN "[CEC] switch on %s\n" NORMAL, __func__);
|
||||||
|
|
||||||
|
#if BOXMODEL_VUPLUS_ALL
|
||||||
|
if (hdmiFd == -1)
|
||||||
|
{
|
||||||
|
hdmiFd = ::open(CEC_HDMIDEV, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
::ioctl(hdmiFd, 0); /* flush old messages */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (hdmiFd == -1)
|
||||||
|
{
|
||||||
|
hdmiFd = open(CEC_FALLBACK_DEVICE, O_RDWR | O_CLOEXEC);
|
||||||
|
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
fallback = true;
|
||||||
|
#if BOXMODEL_VUPLUS_ALL
|
||||||
|
hal_info(RED "[CEC] fallback on %s\n" NORMAL, __func__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__u32 monitor = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER;
|
||||||
|
struct cec_caps caps = {};
|
||||||
|
|
||||||
|
if (ioctl(hdmiFd, CEC_ADAP_G_CAPS, &caps) < 0)
|
||||||
|
hal_info(RED "[CEC] %s: get caps failed (%m)\n" NORMAL, __func__);
|
||||||
|
|
||||||
|
if (caps.capabilities & CEC_CAP_LOG_ADDRS)
|
||||||
|
{
|
||||||
|
struct cec_log_addrs laddrs = {};
|
||||||
|
|
||||||
|
if (ioctl(hdmiFd, CEC_ADAP_S_LOG_ADDRS, &laddrs) < 0)
|
||||||
|
hal_info(RED "[CEC] %s: reset log addr failed (%m)\n" NORMAL, __func__);
|
||||||
|
|
||||||
|
memset(&laddrs, 0, sizeof(laddrs));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: cec_version, osd_name and deviceType should be made configurable,
|
||||||
|
* CEC_ADAP_S_LOG_ADDRS delayed till the desired values are available
|
||||||
|
* (saves us some startup speed as well, polling for a free logical address
|
||||||
|
* takes some time)
|
||||||
|
*/
|
||||||
|
laddrs.cec_version = CEC_OP_CEC_VERSION_2_0;
|
||||||
|
strcpy(laddrs.osd_name, "neutrino");
|
||||||
|
laddrs.vendor_id = CEC_VENDOR_ID_NONE;
|
||||||
|
|
||||||
|
switch (deviceType)
|
||||||
|
{
|
||||||
|
case CEC_LOG_ADDR_TV:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_TV;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_TV;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_RECORD_1:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_RECORD;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_RECORD;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_RECORD;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TUNER_1:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_TUNER;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_TUNER;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_TUNER;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_PLAYBACK_1:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_PLAYBACK;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_PLAYBACK;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_AUDIOSYSTEM:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
|
||||||
|
laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_SWITCH;
|
||||||
|
laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_SWITCH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
laddrs.num_log_addrs++;
|
||||||
|
|
||||||
|
if (ioctl(hdmiFd, CEC_ADAP_S_LOG_ADDRS, &laddrs) < 0)
|
||||||
|
hal_info(RED "[CEC] %s: et log addr failed (%m)\n" NORMAL, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(hdmiFd, CEC_S_MODE, &monitor) < 0)
|
||||||
|
hal_info(RED "[CEC] %s: monitor failed (%m)\n" NORMAL, __func__);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
GetCECAddressInfo();
|
||||||
|
|
||||||
|
if(autoview_cec_activ)
|
||||||
|
SetCECState(false);
|
||||||
|
|
||||||
|
Start();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::GetCECAddressInfo()
|
||||||
|
{
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
bool hasdata = false;
|
||||||
|
struct addressinfo addressinfo;
|
||||||
|
|
||||||
|
if (fallback)
|
||||||
|
{
|
||||||
|
__u16 phys_addr;
|
||||||
|
struct cec_log_addrs laddrs = {};
|
||||||
|
|
||||||
|
::ioctl(hdmiFd, CEC_ADAP_G_PHYS_ADDR, &phys_addr);
|
||||||
|
addressinfo.physical[0] = (phys_addr >> 8) & 0xff;
|
||||||
|
addressinfo.physical[1] = phys_addr & 0xff;
|
||||||
|
|
||||||
|
::ioctl(hdmiFd, CEC_ADAP_G_LOG_ADDRS, &laddrs);
|
||||||
|
addressinfo.logical = laddrs.log_addr[0];
|
||||||
|
|
||||||
|
switch (laddrs.log_addr_type[0])
|
||||||
|
{
|
||||||
|
case CEC_LOG_ADDR_TYPE_TV:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_TV;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TYPE_RECORD:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_RECORD_1;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TYPE_TUNER:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_TUNER_1;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TYPE_PLAYBACK:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_PLAYBACK_1;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TYPE_AUDIOSYSTEM:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_AUDIOSYSTEM;
|
||||||
|
break;
|
||||||
|
case CEC_LOG_ADDR_TYPE_UNREGISTERED:
|
||||||
|
default:
|
||||||
|
addressinfo.type = CEC_LOG_ADDR_UNREGISTERED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasdata = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (::ioctl(hdmiFd, 1, &addressinfo) >= 0)
|
||||||
|
{
|
||||||
|
hasdata = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasdata)
|
||||||
|
{
|
||||||
|
deviceType = addressinfo.type;
|
||||||
|
logicalAddress = addressinfo.logical;
|
||||||
|
if (memcmp(physicalAddress, addressinfo.physical, sizeof(physicalAddress)))
|
||||||
|
{
|
||||||
|
hal_info(GREEN "[CEC] %s: detected physical address change: %02X%02X --> %02X%02X\n" NORMAL, __func__, physicalAddress[0], physicalAddress[1], addressinfo.physical[0], addressinfo.physical[1]);
|
||||||
|
memcpy(physicalAddress, addressinfo.physical, sizeof(physicalAddress));
|
||||||
|
ReportPhysicalAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::ReportPhysicalAddress()
|
||||||
|
{
|
||||||
|
struct cec_message txmessage;
|
||||||
|
txmessage.initiator = logicalAddress;
|
||||||
|
txmessage.destination = CEC_LOG_ADDR_BROADCAST;
|
||||||
|
txmessage.data[0] = CEC_MSG_REPORT_PHYSICAL_ADDR;
|
||||||
|
txmessage.data[1] = physicalAddress[0];
|
||||||
|
txmessage.data[2] = physicalAddress[1];
|
||||||
|
txmessage.data[3] = deviceType;
|
||||||
|
txmessage.length = 4;
|
||||||
|
SendCECMessage(txmessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::SendCECMessage(struct cec_message &txmessage, int sleeptime)
|
||||||
|
{
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
char str[txmessage.length*6];
|
||||||
|
for (int i = 0; i < txmessage.length; i++)
|
||||||
|
{
|
||||||
|
sprintf(str+(i*6),"[0x%02X]", txmessage.data[i]);
|
||||||
|
}
|
||||||
|
hal_info(GREEN "[CEC] send message %s to %s (0x%02X>>0x%02X) '%s' (%s)\n" NORMAL,ToString((cec_logical_address)txmessage.initiator), txmessage.destination == 0xf ? "all" : ToString((cec_logical_address)txmessage.destination), txmessage.initiator, txmessage.destination, ToString((cec_opcode)txmessage.data[0]), str);
|
||||||
|
|
||||||
|
if (fallback)
|
||||||
|
{
|
||||||
|
struct cec_msg msg;
|
||||||
|
cec_msg_init(&msg, txmessage.initiator, txmessage.destination);
|
||||||
|
memcpy(&msg.msg[1], txmessage.data, txmessage.length);
|
||||||
|
msg.len = txmessage.length + 1;
|
||||||
|
ioctl(hdmiFd, CEC_TRANSMIT, &msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct cec_message_fb message;
|
||||||
|
message.address = txmessage.destination;
|
||||||
|
message.length = txmessage.length;
|
||||||
|
memcpy(&message.data, txmessage.data, txmessage.length);
|
||||||
|
::write(hdmiFd, &message, 2 + message.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(sleeptime * 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::SetCECAutoStandby(bool state)
|
||||||
|
{
|
||||||
|
standby_cec_activ = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::SetCECAutoView(bool state)
|
||||||
|
{
|
||||||
|
autoview_cec_activ = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::SetCECState(bool state)
|
||||||
|
{
|
||||||
|
struct cec_message message;
|
||||||
|
|
||||||
|
standby = state;
|
||||||
|
|
||||||
|
if ((standby_cec_activ) && state)
|
||||||
|
{
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_STANDBY;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((autoview_cec_activ) && !state)
|
||||||
|
{
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_GET_CEC_VERSION;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
#if BOXMODEL_VUPLUS_ALL
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
while (tv_off && (cnt < 5))
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_IMAGE_VIEW_ON;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_OP_PRIM_DEVTYPE_TV;
|
||||||
|
message.data[0] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
|
||||||
|
message.length = 1;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
#if BOXMODEL_VUPLUS_ALL
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GetCECAddressInfo();
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_LOG_ADDR_BROADCAST;
|
||||||
|
message.data[0] = CEC_MSG_ACTIVE_SOURCE;
|
||||||
|
message.data[1] = physicalAddress[0];
|
||||||
|
message.data[2] = physicalAddress[1];
|
||||||
|
message.length = 3;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
message.initiator = logicalAddress;
|
||||||
|
message.destination = CEC_LOG_ADDR_BROADCAST;
|
||||||
|
message.data[0] = CEC_OPCODE_SET_OSD_NAME;
|
||||||
|
message.data[1] = 0x6e; //n
|
||||||
|
message.data[2] = 0x65; //e
|
||||||
|
message.data[3] = 0x75; //u
|
||||||
|
message.data[4] = 0x74; //t
|
||||||
|
message.data[5] = 0x72; //r
|
||||||
|
message.data[6] = 0x69; //i
|
||||||
|
message.data[7] = 0x6e; //n
|
||||||
|
message.data[8] = 0x6f; //o
|
||||||
|
message.length = 9;
|
||||||
|
SendCECMessage(message);
|
||||||
|
|
||||||
|
request_audio_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
long hdmi_cec::translateKey(unsigned char code)
|
||||||
|
{
|
||||||
|
long key = 0;
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
|
||||||
|
key = KEY_MENU;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER0:
|
||||||
|
key = KEY_0;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER1:
|
||||||
|
key = KEY_1;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER2:
|
||||||
|
key = KEY_2;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER3:
|
||||||
|
key = KEY_3;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER4:
|
||||||
|
key = KEY_4;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER5:
|
||||||
|
key = KEY_5;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER6:
|
||||||
|
key = KEY_6;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER7:
|
||||||
|
key = KEY_7;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER8:
|
||||||
|
key = KEY_8;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER9:
|
||||||
|
key = KEY_9;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_CHANNEL_UP:
|
||||||
|
key = KEY_CHANNELUP;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
|
||||||
|
key = KEY_CHANNELDOWN;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_PLAY:
|
||||||
|
key = KEY_PLAY;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_STOP:
|
||||||
|
key = KEY_STOP;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE:
|
||||||
|
key = KEY_PAUSE;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_RECORD:
|
||||||
|
key = KEY_RECORD;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_REWIND:
|
||||||
|
key = KEY_REWIND;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_FAST_FORWARD:
|
||||||
|
key = KEY_FASTFORWARD;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
|
||||||
|
key = KEY_INFO;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
|
||||||
|
key = KEY_PROGRAM;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
|
||||||
|
key = KEY_PLAY;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
|
||||||
|
key = KEY_PLAYPAUSE;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
|
||||||
|
key = KEY_RECORD;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
|
||||||
|
key = KEY_STOP;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT:
|
||||||
|
key = KEY_OK;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_LEFT:
|
||||||
|
key = KEY_LEFT;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_RIGHT:
|
||||||
|
key = KEY_RIGHT;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_UP:
|
||||||
|
key = KEY_UP;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_DOWN:
|
||||||
|
key = KEY_DOWN;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_EXIT:
|
||||||
|
key = KEY_EXIT;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_F2_RED:
|
||||||
|
key = KEY_RED;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_F3_GREEN:
|
||||||
|
key = KEY_GREEN;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_F4_YELLOW:
|
||||||
|
key = KEY_YELLOW;
|
||||||
|
break;
|
||||||
|
case CEC_USER_CONTROL_CODE_F1_BLUE:
|
||||||
|
key = KEY_BLUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
key = KEY_MENU;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hdmi_cec::Start()
|
||||||
|
{
|
||||||
|
if (running)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hdmiFd == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
OpenThreads::Thread::setSchedulePriority(THREAD_PRIORITY_MIN);
|
||||||
|
return (OpenThreads::Thread::start() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hdmi_cec::Stop()
|
||||||
|
{
|
||||||
|
if (!running)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
OpenThreads::Thread::cancel();
|
||||||
|
|
||||||
|
if (hdmiFd >= 0)
|
||||||
|
{
|
||||||
|
close(hdmiFd);
|
||||||
|
hdmiFd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (OpenThreads::Thread::join() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::run()
|
||||||
|
{
|
||||||
|
OpenThreads::Thread::setCancelModeAsynchronous();
|
||||||
|
int n;
|
||||||
|
int epollfd = epoll_create1(0);
|
||||||
|
struct epoll_event event;
|
||||||
|
event.data.fd = hdmiFd;
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
|
||||||
|
epoll_ctl(epollfd, EPOLL_CTL_ADD, hdmiFd, &event);
|
||||||
|
|
||||||
|
std::array<struct epoll_event, EPOLL_MAX_EVENTS> events;
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
n = epoll_wait(epollfd, events.data(), EPOLL_MAX_EVENTS, EPOLL_WAIT_TIMEOUT);
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (events[i].events & EPOLLIN)
|
||||||
|
Receive(events[i].events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::Receive(int what)
|
||||||
|
{
|
||||||
|
if (what & EPOLLIN)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool hasdata = false;
|
||||||
|
struct cec_message rxmessage;
|
||||||
|
struct cec_message txmessage;
|
||||||
|
|
||||||
|
if (fallback)
|
||||||
|
{
|
||||||
|
struct cec_msg msg;
|
||||||
|
if (::ioctl(hdmiFd, CEC_RECEIVE, &msg) >= 0)
|
||||||
|
{
|
||||||
|
rxmessage.length = msg.len - 1;
|
||||||
|
rxmessage.initiator = cec_msg_initiator(&msg);
|
||||||
|
rxmessage.destination = cec_msg_destination(&msg);
|
||||||
|
rxmessage.opcode = cec_msg_opcode(&msg);
|
||||||
|
memcpy(&rxmessage.data, &msg.msg[1], rxmessage.length);
|
||||||
|
hasdata = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct cec_message_fb rx_message;
|
||||||
|
if (::read(hdmiFd, &rx_message, 2) == 2)
|
||||||
|
{
|
||||||
|
if (::read(hdmiFd, &rx_message.data, rx_message.length) == rx_message.length)
|
||||||
|
{
|
||||||
|
rxmessage.length = rx_message.length;
|
||||||
|
rxmessage.initiator = rx_message.address;
|
||||||
|
rxmessage.destination = logicalAddress;
|
||||||
|
rxmessage.opcode = rx_message.data[0];
|
||||||
|
memcpy(&rxmessage.data, rx_message.data, rx_message.length);
|
||||||
|
hasdata = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasdata)
|
||||||
|
{
|
||||||
|
bool keypressed = false;
|
||||||
|
static unsigned char pressedkey = 0;
|
||||||
|
|
||||||
|
char str[rxmessage.length*6];
|
||||||
|
for (int i = 0; i < rxmessage.length; i++)
|
||||||
|
{
|
||||||
|
sprintf(str+(i*6),"[0x%02X]", rxmessage.data[i]);
|
||||||
|
}
|
||||||
|
hal_info(GREEN "[CEC] received message %s to %s (0x%02X>>0x%02X) '%s' (%s)\n" NORMAL,ToString((cec_logical_address)rxmessage.initiator), rxmessage.destination == 0xf ? "all" : ToString((cec_logical_address)rxmessage.destination), rxmessage.initiator, rxmessage.destination, ToString((cec_opcode)rxmessage.opcode), str);
|
||||||
|
|
||||||
|
switch (rxmessage.opcode)
|
||||||
|
{
|
||||||
|
//case CEC_OPCODE_ACTIVE_SOURCE:
|
||||||
|
case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
|
||||||
|
{
|
||||||
|
txmessage.destination = CEC_LOG_ADDR_BROADCAST; //rxmessage.initiator;
|
||||||
|
txmessage.initiator = logicalAddress; //rxmessage.destination;
|
||||||
|
txmessage.data[0] = CEC_MSG_ACTIVE_SOURCE;
|
||||||
|
txmessage.data[1] = physicalAddress[0];
|
||||||
|
txmessage.data[2] = physicalAddress[1];
|
||||||
|
txmessage.length = 3;
|
||||||
|
if (!standby)
|
||||||
|
SendCECMessage(txmessage);
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_REPORT_AUDIO_STATUS:
|
||||||
|
{
|
||||||
|
muted = ((rxmessage.data[1] & 0x80) == 0x80);
|
||||||
|
volume = ((rxmessage.data[1] & 0x7F) / 127.0) * 100.0;
|
||||||
|
if (muted)
|
||||||
|
hal_info(GREEN "[CEC] %s volume muted\n" NORMAL, ToString((cec_logical_address)rxmessage.initiator));
|
||||||
|
else
|
||||||
|
hal_info(GREEN "[CEC] %s volume %d \n" NORMAL, ToString((cec_logical_address)rxmessage.initiator), volume);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_DEVICE_VENDOR_ID:
|
||||||
|
case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
|
||||||
|
{
|
||||||
|
uint64_t iVendorId = ((uint64_t)rxmessage.data[1] << 16) +
|
||||||
|
((uint64_t)rxmessage.data[2] << 8) +
|
||||||
|
(uint64_t)rxmessage.data[3];
|
||||||
|
hal_info(GREEN "[CEC] decoded message '%s' (%s)\n" NORMAL, ToString((cec_opcode)rxmessage.opcode), ToString((cec_vendor_id)iVendorId));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
|
||||||
|
{
|
||||||
|
txmessage.destination = rxmessage.initiator;
|
||||||
|
txmessage.initiator = rxmessage.destination;
|
||||||
|
txmessage.data[0] = GetResponseOpcode((cec_opcode)rxmessage.opcode);
|
||||||
|
txmessage.data[1] = standby ? CEC_POWER_STATUS_STANDBY : CEC_POWER_STATUS_ON;
|
||||||
|
txmessage.length = 2;
|
||||||
|
SendCECMessage(txmessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_REPORT_POWER_STATUS:
|
||||||
|
{
|
||||||
|
if ((rxmessage.data[1] == CEC_POWER_STATUS_ON) || (rxmessage.data[1] == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON))
|
||||||
|
{
|
||||||
|
hal_info(GREEN "[CEC] %s reporting state on (%d)\n" NORMAL, ToString((cec_logical_address)rxmessage.initiator), rxmessage.data[1]);
|
||||||
|
if (rxmessage.initiator == CEC_OP_PRIM_DEVTYPE_TV)
|
||||||
|
tv_off = false;
|
||||||
|
} else {
|
||||||
|
hal_info(GREEN "[CEC] %s reporting state off (%d)\n" NORMAL, ToString((cec_logical_address)rxmessage.initiator), rxmessage.data[1]);
|
||||||
|
if (rxmessage.initiator == CEC_OP_PRIM_DEVTYPE_TV)
|
||||||
|
tv_off = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_STANDBY:
|
||||||
|
{
|
||||||
|
if (rxmessage.initiator == CEC_OP_PRIM_DEVTYPE_TV)
|
||||||
|
tv_off = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CEC_OPCODE_USER_CONTROL_PRESSED: /* key pressed */
|
||||||
|
{
|
||||||
|
keypressed = true;
|
||||||
|
pressedkey = rxmessage.data[1];
|
||||||
|
} // fall through
|
||||||
|
case CEC_OPCODE_USER_CONTROL_RELEASE: /* key released */
|
||||||
|
{
|
||||||
|
long code = translateKey(pressedkey);
|
||||||
|
hal_info(GREEN "[CEC] decoded key %s (%ld)\n" NORMAL,ToString((cec_user_control_code)pressedkey), code);
|
||||||
|
handleCode(code,keypressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::handleCode(long code, bool keypressed)
|
||||||
|
{
|
||||||
|
int evd = open(RC_DEVICE, O_RDWR);
|
||||||
|
if (evd < 0)
|
||||||
|
{
|
||||||
|
hal_info(RED "[CEC] opening " RC_DEVICE " failed" NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keypressed)
|
||||||
|
{
|
||||||
|
if (rc_send(evd, code, CEC_KEY_PRESSED) < 0)
|
||||||
|
{
|
||||||
|
hal_info(RED "[CEC] writing 'KEY_PRESSED' event failed" NORMAL);
|
||||||
|
close(evd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rc_sync(evd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (rc_send(evd, code, CEC_KEY_RELEASED) < 0)
|
||||||
|
{
|
||||||
|
hal_info(RED "[CEC] writing 'KEY_RELEASED' event failed" NORMAL);
|
||||||
|
close(evd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rc_sync(evd);
|
||||||
|
}
|
||||||
|
close(evd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hdmi_cec::rc_send(int fd, unsigned int code, unsigned int value)
|
||||||
|
{
|
||||||
|
struct input_event ev;
|
||||||
|
|
||||||
|
ev.type = EV_KEY;
|
||||||
|
ev.code = code;
|
||||||
|
ev.value = value;
|
||||||
|
return write(fd, &ev, sizeof(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::rc_sync(int fd)
|
||||||
|
{
|
||||||
|
struct input_event ev;
|
||||||
|
|
||||||
|
gettimeofday(&ev.time, NULL);
|
||||||
|
ev.type = EV_SYN;
|
||||||
|
ev.code = SYN_REPORT;
|
||||||
|
ev.value = 0;
|
||||||
|
write(fd, &ev, sizeof(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::send_key(unsigned char key, unsigned char destination)
|
||||||
|
{
|
||||||
|
struct cec_message txmessage;
|
||||||
|
txmessage.destination = destination;
|
||||||
|
txmessage.initiator = logicalAddress;
|
||||||
|
txmessage.data[0] = CEC_OPCODE_USER_CONTROL_PRESSED;
|
||||||
|
txmessage.data[1] = key;
|
||||||
|
txmessage.length = 2;
|
||||||
|
SendCECMessage(txmessage, 1);
|
||||||
|
|
||||||
|
txmessage.destination = destination;
|
||||||
|
txmessage.initiator = logicalAddress;
|
||||||
|
txmessage.data[0] = CEC_OPCODE_USER_CONTROL_RELEASE;
|
||||||
|
txmessage.length = 1;
|
||||||
|
SendCECMessage(txmessage, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::request_audio_status()
|
||||||
|
{
|
||||||
|
struct cec_message txmessage;
|
||||||
|
txmessage.destination = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
|
||||||
|
txmessage.initiator = logicalAddress;
|
||||||
|
txmessage.data[0] = CEC_OPCODE_GIVE_AUDIO_STATUS;
|
||||||
|
txmessage.length = 1;
|
||||||
|
SendCECMessage(txmessage, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hdmi_cec::vol_up()
|
||||||
|
{
|
||||||
|
send_key(CEC_USER_CONTROL_CODE_VOLUME_UP, CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM);
|
||||||
|
request_audio_status();
|
||||||
|
}
|
||||||
|
void hdmi_cec::vol_down()
|
||||||
|
{
|
||||||
|
send_key(CEC_USER_CONTROL_CODE_VOLUME_DOWN, CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM);
|
||||||
|
request_audio_status();
|
||||||
|
}
|
||||||
|
void hdmi_cec::toggle_mute()
|
||||||
|
{
|
||||||
|
send_key(CEC_USER_CONTROL_CODE_MUTE, CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM);
|
||||||
|
request_audio_status();
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/hdmi_cec.h
|
|
108
libmipsbox/hdmi_cec.h
Normal file
108
libmipsbox/hdmi_cec.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#ifndef __HDMI_CEC_H__
|
||||||
|
#define __HDMI_CEC_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (C) 2018-2020 TangoCash
|
||||||
|
|
||||||
|
License: GPLv2
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <OpenThreads/Thread>
|
||||||
|
#include <OpenThreads/Condition>
|
||||||
|
|
||||||
|
#include "video_lib.h"
|
||||||
|
|
||||||
|
struct cec_message
|
||||||
|
{
|
||||||
|
unsigned char initiator;
|
||||||
|
unsigned char destination;
|
||||||
|
unsigned char opcode;
|
||||||
|
unsigned char data[256];
|
||||||
|
unsigned char length;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct cec_message_fb
|
||||||
|
{
|
||||||
|
unsigned char address;
|
||||||
|
unsigned char length;
|
||||||
|
unsigned char data[256];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct addressinfo
|
||||||
|
{
|
||||||
|
unsigned char logical;
|
||||||
|
unsigned char physical[2];
|
||||||
|
unsigned char type;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CEC_KEY_RELEASED = 0,
|
||||||
|
CEC_KEY_PRESSED,
|
||||||
|
CEC_KEY_AUTOREPEAT
|
||||||
|
};
|
||||||
|
|
||||||
|
class hdmi_cec : public OpenThreads::Thread
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
hdmi_cec();
|
||||||
|
static hdmi_cec *hdmi_cec_instance;
|
||||||
|
void run();
|
||||||
|
bool Start();
|
||||||
|
bool Stop();
|
||||||
|
void Receive(int what);
|
||||||
|
unsigned char physicalAddress[2];
|
||||||
|
bool autoview_cec_activ;
|
||||||
|
unsigned char deviceType, logicalAddress;
|
||||||
|
int hdmiFd;
|
||||||
|
long translateKey(unsigned char code);
|
||||||
|
void handleCode(long code, bool keypressed);
|
||||||
|
int rc_send(int fd, unsigned int code, unsigned int value);
|
||||||
|
void rc_sync(int fd);
|
||||||
|
bool standby;
|
||||||
|
void send_key(unsigned char key, unsigned char destination);
|
||||||
|
void request_audio_status();
|
||||||
|
bool muted;
|
||||||
|
int volume;
|
||||||
|
bool fallback;
|
||||||
|
bool tv_off;
|
||||||
|
protected:
|
||||||
|
bool running;
|
||||||
|
public:
|
||||||
|
~hdmi_cec();
|
||||||
|
static hdmi_cec* getInstance();
|
||||||
|
bool SetCECMode(VIDEO_HDMI_CEC_MODE);
|
||||||
|
void SetCECAutoView(bool);
|
||||||
|
void SetCECAutoStandby(bool);
|
||||||
|
void GetCECAddressInfo();
|
||||||
|
void SendCECMessage(struct cec_message &message, int sleeptime = 10);
|
||||||
|
void SetCECState(bool state);
|
||||||
|
void ReportPhysicalAddress();
|
||||||
|
bool standby_cec_activ;
|
||||||
|
void vol_up();
|
||||||
|
void vol_down();
|
||||||
|
void toggle_mute();
|
||||||
|
int GetVolume()
|
||||||
|
{
|
||||||
|
return volume;
|
||||||
|
};
|
||||||
|
bool isMuted()
|
||||||
|
{
|
||||||
|
return muted;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __HDMI_CEC_H__
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/hdmi_cec_types.h
|
|
713
libmipsbox/hdmi_cec_types.h
Normal file
713
libmipsbox/hdmi_cec_types.h
Normal file
@@ -0,0 +1,713 @@
|
|||||||
|
#ifndef __HDMI_CEC_TYPES_H__
|
||||||
|
#define __HDMI_CEC_TYPES_H__
|
||||||
|
|
||||||
|
typedef enum cec_vendor_id
|
||||||
|
{
|
||||||
|
CEC_VENDOR_TOSHIBA = 0x000039,
|
||||||
|
CEC_VENDOR_SAMSUNG = 0x0000F0,
|
||||||
|
CEC_VENDOR_DENON = 0x0005CD,
|
||||||
|
CEC_VENDOR_MARANTZ = 0x000678,
|
||||||
|
CEC_VENDOR_LOEWE = 0x000982,
|
||||||
|
CEC_VENDOR_ONKYO = 0x0009B0,
|
||||||
|
CEC_VENDOR_MEDION = 0x000CB8,
|
||||||
|
CEC_VENDOR_TOSHIBA2 = 0x000CE7,
|
||||||
|
CEC_VENDOR_PULSE_EIGHT = 0x001582,
|
||||||
|
CEC_VENDOR_HARMAN_KARDON2 = 0x001950,
|
||||||
|
CEC_VENDOR_GOOGLE = 0x001A11,
|
||||||
|
CEC_VENDOR_AKAI = 0x0020C7,
|
||||||
|
CEC_VENDOR_AOC = 0x002467,
|
||||||
|
CEC_VENDOR_PANASONIC = 0x008045,
|
||||||
|
CEC_VENDOR_PHILIPS = 0x00903E,
|
||||||
|
CEC_VENDOR_DAEWOO = 0x009053,
|
||||||
|
CEC_VENDOR_YAMAHA = 0x00A0DE,
|
||||||
|
CEC_VENDOR_GRUNDIG = 0x00D0D5,
|
||||||
|
CEC_VENDOR_PIONEER = 0x00E036,
|
||||||
|
CEC_VENDOR_LG = 0x00E091,
|
||||||
|
CEC_VENDOR_SHARP = 0x08001F,
|
||||||
|
CEC_VENDOR_SONY = 0x080046,
|
||||||
|
CEC_VENDOR_BROADCOM = 0x18C086,
|
||||||
|
CEC_VENDOR_SHARP2 = 0x534850,
|
||||||
|
CEC_VENDOR_VIZIO = 0x6B746D,
|
||||||
|
CEC_VENDOR_BENQ = 0x8065E9,
|
||||||
|
CEC_VENDOR_HARMAN_KARDON = 0x9C645E,
|
||||||
|
CEC_VENDOR_UNKNOWN = 0
|
||||||
|
} cec_vendor_id;
|
||||||
|
|
||||||
|
typedef enum cec_user_control_code
|
||||||
|
{
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT = 0x00,
|
||||||
|
CEC_USER_CONTROL_CODE_UP = 0x01,
|
||||||
|
CEC_USER_CONTROL_CODE_DOWN = 0x02,
|
||||||
|
CEC_USER_CONTROL_CODE_LEFT = 0x03,
|
||||||
|
CEC_USER_CONTROL_CODE_RIGHT = 0x04,
|
||||||
|
CEC_USER_CONTROL_CODE_RIGHT_UP = 0x05,
|
||||||
|
CEC_USER_CONTROL_CODE_RIGHT_DOWN = 0x06,
|
||||||
|
CEC_USER_CONTROL_CODE_LEFT_UP = 0x07,
|
||||||
|
CEC_USER_CONTROL_CODE_LEFT_DOWN = 0x08,
|
||||||
|
CEC_USER_CONTROL_CODE_ROOT_MENU = 0x09,
|
||||||
|
CEC_USER_CONTROL_CODE_SETUP_MENU = 0x0A,
|
||||||
|
CEC_USER_CONTROL_CODE_CONTENTS_MENU = 0x0B,
|
||||||
|
CEC_USER_CONTROL_CODE_FAVORITE_MENU = 0x0C,
|
||||||
|
CEC_USER_CONTROL_CODE_EXIT = 0x0D,
|
||||||
|
// reserved: 0x0E, 0x0F
|
||||||
|
CEC_USER_CONTROL_CODE_TOP_MENU = 0x10,
|
||||||
|
CEC_USER_CONTROL_CODE_DVD_MENU = 0x11,
|
||||||
|
// reserved: 0x12 ... 0x1C
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER_ENTRY_MODE = 0x1D,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER11 = 0x1E,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER12 = 0x1F,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER0 = 0x20,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER1 = 0x21,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER2 = 0x22,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER3 = 0x23,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER4 = 0x24,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER5 = 0x25,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER6 = 0x26,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER7 = 0x27,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER8 = 0x28,
|
||||||
|
CEC_USER_CONTROL_CODE_NUMBER9 = 0x29,
|
||||||
|
CEC_USER_CONTROL_CODE_DOT = 0x2A,
|
||||||
|
CEC_USER_CONTROL_CODE_ENTER = 0x2B,
|
||||||
|
CEC_USER_CONTROL_CODE_CLEAR = 0x2C,
|
||||||
|
CEC_USER_CONTROL_CODE_NEXT_FAVORITE = 0x2F,
|
||||||
|
CEC_USER_CONTROL_CODE_CHANNEL_UP = 0x30,
|
||||||
|
CEC_USER_CONTROL_CODE_CHANNEL_DOWN = 0x31,
|
||||||
|
CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL = 0x32,
|
||||||
|
CEC_USER_CONTROL_CODE_SOUND_SELECT = 0x33,
|
||||||
|
CEC_USER_CONTROL_CODE_INPUT_SELECT = 0x34,
|
||||||
|
CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION = 0x35,
|
||||||
|
CEC_USER_CONTROL_CODE_HELP = 0x36,
|
||||||
|
CEC_USER_CONTROL_CODE_PAGE_UP = 0x37,
|
||||||
|
CEC_USER_CONTROL_CODE_PAGE_DOWN = 0x38,
|
||||||
|
// reserved: 0x39 ... 0x3F
|
||||||
|
CEC_USER_CONTROL_CODE_POWER = 0x40,
|
||||||
|
CEC_USER_CONTROL_CODE_VOLUME_UP = 0x41,
|
||||||
|
CEC_USER_CONTROL_CODE_VOLUME_DOWN = 0x42,
|
||||||
|
CEC_USER_CONTROL_CODE_MUTE = 0x43,
|
||||||
|
CEC_USER_CONTROL_CODE_PLAY = 0x44,
|
||||||
|
CEC_USER_CONTROL_CODE_STOP = 0x45,
|
||||||
|
CEC_USER_CONTROL_CODE_PAUSE = 0x46,
|
||||||
|
CEC_USER_CONTROL_CODE_RECORD = 0x47,
|
||||||
|
CEC_USER_CONTROL_CODE_REWIND = 0x48,
|
||||||
|
CEC_USER_CONTROL_CODE_FAST_FORWARD = 0x49,
|
||||||
|
CEC_USER_CONTROL_CODE_EJECT = 0x4A,
|
||||||
|
CEC_USER_CONTROL_CODE_FORWARD = 0x4B,
|
||||||
|
CEC_USER_CONTROL_CODE_BACKWARD = 0x4C,
|
||||||
|
CEC_USER_CONTROL_CODE_STOP_RECORD = 0x4D,
|
||||||
|
CEC_USER_CONTROL_CODE_PAUSE_RECORD = 0x4E,
|
||||||
|
// reserved: 0x4F
|
||||||
|
CEC_USER_CONTROL_CODE_ANGLE = 0x50,
|
||||||
|
CEC_USER_CONTROL_CODE_SUB_PICTURE = 0x51,
|
||||||
|
CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND = 0x52,
|
||||||
|
CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE = 0x53,
|
||||||
|
CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING = 0x54,
|
||||||
|
CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION = 0x55,
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT_BROADCAST_TYPE = 0x56,
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT_SOUND_PRESENTATION = 0x57,
|
||||||
|
// reserved: 0x58 ... 0x5F
|
||||||
|
CEC_USER_CONTROL_CODE_PLAY_FUNCTION = 0x60,
|
||||||
|
CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION = 0x61,
|
||||||
|
CEC_USER_CONTROL_CODE_RECORD_FUNCTION = 0x62,
|
||||||
|
CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION = 0x63,
|
||||||
|
CEC_USER_CONTROL_CODE_STOP_FUNCTION = 0x64,
|
||||||
|
CEC_USER_CONTROL_CODE_MUTE_FUNCTION = 0x65,
|
||||||
|
CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION = 0x66,
|
||||||
|
CEC_USER_CONTROL_CODE_TUNE_FUNCTION = 0x67,
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION = 0x68,
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION = 0x69,
|
||||||
|
CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION = 0x6A,
|
||||||
|
CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION = 0x6B,
|
||||||
|
CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION = 0x6C,
|
||||||
|
CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION = 0x6D,
|
||||||
|
// reserved: 0x6E ... 0x70
|
||||||
|
CEC_USER_CONTROL_CODE_F1_BLUE = 0x71,
|
||||||
|
CEC_USER_CONTROL_CODE_F2_RED = 0X72,
|
||||||
|
CEC_USER_CONTROL_CODE_F3_GREEN = 0x73,
|
||||||
|
CEC_USER_CONTROL_CODE_F4_YELLOW = 0x74,
|
||||||
|
CEC_USER_CONTROL_CODE_F5 = 0x75,
|
||||||
|
CEC_USER_CONTROL_CODE_DATA = 0x76,
|
||||||
|
// reserved: 0x77 ... 0xFF
|
||||||
|
CEC_USER_CONTROL_CODE_AN_RETURN = 0x91, // return (Samsung)
|
||||||
|
CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST = 0x96, // channels list (Samsung)
|
||||||
|
CEC_USER_CONTROL_CODE_MAX = 0x96,
|
||||||
|
CEC_USER_CONTROL_CODE_UNKNOWN = 0xFF
|
||||||
|
} cec_user_control_code;
|
||||||
|
|
||||||
|
typedef enum cec_opcode
|
||||||
|
{
|
||||||
|
CEC_OPCODE_ACTIVE_SOURCE = 0x82,
|
||||||
|
CEC_OPCODE_IMAGE_VIEW_ON = 0x04,
|
||||||
|
CEC_OPCODE_TEXT_VIEW_ON = 0x0D,
|
||||||
|
CEC_OPCODE_INACTIVE_SOURCE = 0x9D,
|
||||||
|
CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85,
|
||||||
|
CEC_OPCODE_ROUTING_CHANGE = 0x80,
|
||||||
|
CEC_OPCODE_ROUTING_INFORMATION = 0x81,
|
||||||
|
CEC_OPCODE_SET_STREAM_PATH = 0x86,
|
||||||
|
CEC_OPCODE_STANDBY = 0x36,
|
||||||
|
CEC_OPCODE_RECORD_OFF = 0x0B,
|
||||||
|
CEC_OPCODE_RECORD_ON = 0x09,
|
||||||
|
CEC_OPCODE_RECORD_STATUS = 0x0A,
|
||||||
|
CEC_OPCODE_RECORD_TV_SCREEN = 0x0F,
|
||||||
|
CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33,
|
||||||
|
CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99,
|
||||||
|
CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1,
|
||||||
|
CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34,
|
||||||
|
CEC_OPCODE_SET_DIGITAL_TIMER = 0x97,
|
||||||
|
CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2,
|
||||||
|
CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67,
|
||||||
|
CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43,
|
||||||
|
CEC_OPCODE_TIMER_STATUS = 0x35,
|
||||||
|
CEC_OPCODE_CEC_VERSION = 0x9E,
|
||||||
|
CEC_OPCODE_GET_CEC_VERSION = 0x9F,
|
||||||
|
CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83,
|
||||||
|
CEC_OPCODE_GET_MENU_LANGUAGE = 0x91,
|
||||||
|
CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84,
|
||||||
|
CEC_OPCODE_SET_MENU_LANGUAGE = 0x32,
|
||||||
|
CEC_OPCODE_DECK_CONTROL = 0x42,
|
||||||
|
CEC_OPCODE_DECK_STATUS = 0x1B,
|
||||||
|
CEC_OPCODE_GIVE_DECK_STATUS = 0x1A,
|
||||||
|
CEC_OPCODE_PLAY = 0x41,
|
||||||
|
CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08,
|
||||||
|
CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92,
|
||||||
|
CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93,
|
||||||
|
CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07,
|
||||||
|
CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06,
|
||||||
|
CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05,
|
||||||
|
CEC_OPCODE_DEVICE_VENDOR_ID = 0x87,
|
||||||
|
CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C,
|
||||||
|
CEC_OPCODE_VENDOR_COMMAND = 0x89,
|
||||||
|
CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0,
|
||||||
|
CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
|
||||||
|
CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B,
|
||||||
|
CEC_OPCODE_SET_OSD_STRING = 0x64,
|
||||||
|
CEC_OPCODE_GIVE_OSD_NAME = 0x46,
|
||||||
|
CEC_OPCODE_SET_OSD_NAME = 0x47,
|
||||||
|
CEC_OPCODE_MENU_REQUEST = 0x8D,
|
||||||
|
CEC_OPCODE_MENU_STATUS = 0x8E,
|
||||||
|
CEC_OPCODE_USER_CONTROL_PRESSED = 0x44,
|
||||||
|
CEC_OPCODE_USER_CONTROL_RELEASE = 0x45,
|
||||||
|
CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F,
|
||||||
|
CEC_OPCODE_REPORT_POWER_STATUS = 0x90,
|
||||||
|
CEC_OPCODE_FEATURE_ABORT = 0x00,
|
||||||
|
CEC_OPCODE_ABORT = 0xFF,
|
||||||
|
CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71,
|
||||||
|
CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
|
||||||
|
CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A,
|
||||||
|
CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72,
|
||||||
|
CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70,
|
||||||
|
CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E,
|
||||||
|
CEC_OPCODE_SET_AUDIO_RATE = 0x9A,
|
||||||
|
|
||||||
|
/* CEC 1.4 */
|
||||||
|
CEC_OPCODE_START_ARC = 0xC0,
|
||||||
|
CEC_OPCODE_REPORT_ARC_STARTED = 0xC1,
|
||||||
|
CEC_OPCODE_REPORT_ARC_ENDED = 0xC2,
|
||||||
|
CEC_OPCODE_REQUEST_ARC_START = 0xC3,
|
||||||
|
CEC_OPCODE_REQUEST_ARC_END = 0xC4,
|
||||||
|
CEC_OPCODE_END_ARC = 0xC5,
|
||||||
|
CEC_OPCODE_CDC = 0xF8,
|
||||||
|
/* when this opcode is set, no opcode will be sent to the device. this is one of the reserved numbers */
|
||||||
|
CEC_OPCODE_NONE = 0xFD
|
||||||
|
} cec_opcode;
|
||||||
|
|
||||||
|
typedef enum cec_logical_address
|
||||||
|
{
|
||||||
|
CECDEVICE_UNKNOWN = -1, //not a valid logical address
|
||||||
|
CECDEVICE_TV = 0,
|
||||||
|
CECDEVICE_RECORDINGDEVICE1 = 1,
|
||||||
|
CECDEVICE_RECORDINGDEVICE2 = 2,
|
||||||
|
CECDEVICE_TUNER1 = 3,
|
||||||
|
CECDEVICE_PLAYBACKDEVICE1 = 4,
|
||||||
|
CECDEVICE_AUDIOSYSTEM = 5,
|
||||||
|
CECDEVICE_TUNER2 = 6,
|
||||||
|
CECDEVICE_TUNER3 = 7,
|
||||||
|
CECDEVICE_PLAYBACKDEVICE2 = 8,
|
||||||
|
CECDEVICE_RECORDINGDEVICE3 = 9,
|
||||||
|
CECDEVICE_TUNER4 = 10,
|
||||||
|
CECDEVICE_PLAYBACKDEVICE3 = 11,
|
||||||
|
CECDEVICE_RESERVED1 = 12,
|
||||||
|
CECDEVICE_RESERVED2 = 13,
|
||||||
|
CECDEVICE_FREEUSE = 14,
|
||||||
|
CECDEVICE_UNREGISTERED = 15,
|
||||||
|
CECDEVICE_BROADCAST = 15
|
||||||
|
} cec_logical_address;
|
||||||
|
|
||||||
|
typedef enum cec_power_status
|
||||||
|
{
|
||||||
|
CEC_POWER_STATUS_ON = 0x00,
|
||||||
|
CEC_POWER_STATUS_STANDBY = 0x01,
|
||||||
|
CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON = 0x02,
|
||||||
|
CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY = 0x03,
|
||||||
|
CEC_POWER_STATUS_UNKNOWN = 0x99
|
||||||
|
} cec_power_status;
|
||||||
|
|
||||||
|
static const char *ToString(const cec_opcode opcode)
|
||||||
|
{
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case CEC_OPCODE_ACTIVE_SOURCE:
|
||||||
|
return "active source";
|
||||||
|
case CEC_OPCODE_IMAGE_VIEW_ON:
|
||||||
|
return "image view on";
|
||||||
|
case CEC_OPCODE_TEXT_VIEW_ON:
|
||||||
|
return "text view on";
|
||||||
|
case CEC_OPCODE_INACTIVE_SOURCE:
|
||||||
|
return "inactive source";
|
||||||
|
case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
|
||||||
|
return "request active source";
|
||||||
|
case CEC_OPCODE_ROUTING_CHANGE:
|
||||||
|
return "routing change";
|
||||||
|
case CEC_OPCODE_ROUTING_INFORMATION:
|
||||||
|
return "routing information";
|
||||||
|
case CEC_OPCODE_SET_STREAM_PATH:
|
||||||
|
return "set stream path";
|
||||||
|
case CEC_OPCODE_STANDBY:
|
||||||
|
return "standby";
|
||||||
|
case CEC_OPCODE_RECORD_OFF:
|
||||||
|
return "record off";
|
||||||
|
case CEC_OPCODE_RECORD_ON:
|
||||||
|
return "record on";
|
||||||
|
case CEC_OPCODE_RECORD_STATUS:
|
||||||
|
return "record status";
|
||||||
|
case CEC_OPCODE_RECORD_TV_SCREEN:
|
||||||
|
return "record tv screen";
|
||||||
|
case CEC_OPCODE_CLEAR_ANALOGUE_TIMER:
|
||||||
|
return "clear analogue timer";
|
||||||
|
case CEC_OPCODE_CLEAR_DIGITAL_TIMER:
|
||||||
|
return "clear digital timer";
|
||||||
|
case CEC_OPCODE_CLEAR_EXTERNAL_TIMER:
|
||||||
|
return "clear external timer";
|
||||||
|
case CEC_OPCODE_SET_ANALOGUE_TIMER:
|
||||||
|
return "set analogue timer";
|
||||||
|
case CEC_OPCODE_SET_DIGITAL_TIMER:
|
||||||
|
return "set digital timer";
|
||||||
|
case CEC_OPCODE_SET_EXTERNAL_TIMER:
|
||||||
|
return "set external timer";
|
||||||
|
case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:
|
||||||
|
return "set timer program title";
|
||||||
|
case CEC_OPCODE_TIMER_CLEARED_STATUS:
|
||||||
|
return "timer cleared status";
|
||||||
|
case CEC_OPCODE_TIMER_STATUS:
|
||||||
|
return "timer status";
|
||||||
|
case CEC_OPCODE_CEC_VERSION:
|
||||||
|
return "cec version";
|
||||||
|
case CEC_OPCODE_GET_CEC_VERSION:
|
||||||
|
return "get cec version";
|
||||||
|
case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
|
||||||
|
return "give physical address";
|
||||||
|
case CEC_OPCODE_GET_MENU_LANGUAGE:
|
||||||
|
return "get menu language";
|
||||||
|
case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
|
||||||
|
return "report physical address";
|
||||||
|
case CEC_OPCODE_SET_MENU_LANGUAGE:
|
||||||
|
return "set menu language";
|
||||||
|
case CEC_OPCODE_DECK_CONTROL:
|
||||||
|
return "deck control";
|
||||||
|
case CEC_OPCODE_DECK_STATUS:
|
||||||
|
return "deck status";
|
||||||
|
case CEC_OPCODE_GIVE_DECK_STATUS:
|
||||||
|
return "give deck status";
|
||||||
|
case CEC_OPCODE_PLAY:
|
||||||
|
return "play";
|
||||||
|
case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
|
||||||
|
return "give tuner status";
|
||||||
|
case CEC_OPCODE_SELECT_ANALOGUE_SERVICE:
|
||||||
|
return "select analogue service";
|
||||||
|
case CEC_OPCODE_SELECT_DIGITAL_SERVICE:
|
||||||
|
return "set digital service";
|
||||||
|
case CEC_OPCODE_TUNER_DEVICE_STATUS:
|
||||||
|
return "tuner device status";
|
||||||
|
case CEC_OPCODE_TUNER_STEP_DECREMENT:
|
||||||
|
return "tuner step decrement";
|
||||||
|
case CEC_OPCODE_TUNER_STEP_INCREMENT:
|
||||||
|
return "tuner step increment";
|
||||||
|
case CEC_OPCODE_DEVICE_VENDOR_ID:
|
||||||
|
return "device vendor id";
|
||||||
|
case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
|
||||||
|
return "give device vendor id";
|
||||||
|
case CEC_OPCODE_VENDOR_COMMAND:
|
||||||
|
return "vendor command";
|
||||||
|
case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
|
||||||
|
return "vendor command with id";
|
||||||
|
case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
|
||||||
|
return "vendor remote button down";
|
||||||
|
case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
|
||||||
|
return "vendor remote button up";
|
||||||
|
case CEC_OPCODE_SET_OSD_STRING:
|
||||||
|
return "set osd string";
|
||||||
|
case CEC_OPCODE_GIVE_OSD_NAME:
|
||||||
|
return "give osd name";
|
||||||
|
case CEC_OPCODE_SET_OSD_NAME:
|
||||||
|
return "set osd name";
|
||||||
|
case CEC_OPCODE_MENU_REQUEST:
|
||||||
|
return "menu request";
|
||||||
|
case CEC_OPCODE_MENU_STATUS:
|
||||||
|
return "menu status";
|
||||||
|
case CEC_OPCODE_USER_CONTROL_PRESSED:
|
||||||
|
return "user control pressed";
|
||||||
|
case CEC_OPCODE_USER_CONTROL_RELEASE:
|
||||||
|
return "user control release";
|
||||||
|
case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
|
||||||
|
return "give device power status";
|
||||||
|
case CEC_OPCODE_REPORT_POWER_STATUS:
|
||||||
|
return "report power status";
|
||||||
|
case CEC_OPCODE_FEATURE_ABORT:
|
||||||
|
return "feature abort";
|
||||||
|
case CEC_OPCODE_ABORT:
|
||||||
|
return "abort";
|
||||||
|
case CEC_OPCODE_GIVE_AUDIO_STATUS:
|
||||||
|
return "give audio status";
|
||||||
|
case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
|
||||||
|
return "give audio mode status";
|
||||||
|
case CEC_OPCODE_REPORT_AUDIO_STATUS:
|
||||||
|
return "report audio status";
|
||||||
|
case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
|
||||||
|
return "set system audio mode";
|
||||||
|
case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
|
||||||
|
return "system audio mode request";
|
||||||
|
case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
|
||||||
|
return "system audio mode status";
|
||||||
|
case CEC_OPCODE_SET_AUDIO_RATE:
|
||||||
|
return "set audio rate";
|
||||||
|
case CEC_OPCODE_START_ARC:
|
||||||
|
return "start ARC";
|
||||||
|
case CEC_OPCODE_REPORT_ARC_STARTED:
|
||||||
|
return "report ARC started";
|
||||||
|
case CEC_OPCODE_REPORT_ARC_ENDED:
|
||||||
|
return "report ARC ended";
|
||||||
|
case CEC_OPCODE_REQUEST_ARC_START:
|
||||||
|
return "request ARC start";
|
||||||
|
case CEC_OPCODE_REQUEST_ARC_END:
|
||||||
|
return "request ARC end";
|
||||||
|
case CEC_OPCODE_END_ARC:
|
||||||
|
return "end ARC";
|
||||||
|
case CEC_OPCODE_CDC:
|
||||||
|
return "CDC";
|
||||||
|
case CEC_OPCODE_NONE:
|
||||||
|
return "poll";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *ToString(const cec_vendor_id vendor)
|
||||||
|
{
|
||||||
|
switch (vendor)
|
||||||
|
{
|
||||||
|
case CEC_VENDOR_SAMSUNG:
|
||||||
|
return "Samsung";
|
||||||
|
case CEC_VENDOR_LG:
|
||||||
|
return "LG";
|
||||||
|
case CEC_VENDOR_PANASONIC:
|
||||||
|
return "Panasonic";
|
||||||
|
case CEC_VENDOR_PIONEER:
|
||||||
|
return "Pioneer";
|
||||||
|
case CEC_VENDOR_ONKYO:
|
||||||
|
return "Onkyo";
|
||||||
|
case CEC_VENDOR_YAMAHA:
|
||||||
|
return "Yamaha";
|
||||||
|
case CEC_VENDOR_PHILIPS:
|
||||||
|
return "Philips";
|
||||||
|
case CEC_VENDOR_SONY:
|
||||||
|
return "Sony";
|
||||||
|
case CEC_VENDOR_TOSHIBA:
|
||||||
|
case CEC_VENDOR_TOSHIBA2:
|
||||||
|
return "Toshiba";
|
||||||
|
case CEC_VENDOR_AKAI:
|
||||||
|
return "Akai";
|
||||||
|
case CEC_VENDOR_AOC:
|
||||||
|
return "AOC";
|
||||||
|
case CEC_VENDOR_BENQ:
|
||||||
|
return "Benq";
|
||||||
|
case CEC_VENDOR_DAEWOO:
|
||||||
|
return "Daewoo";
|
||||||
|
case CEC_VENDOR_GRUNDIG:
|
||||||
|
return "Grundig";
|
||||||
|
case CEC_VENDOR_MEDION:
|
||||||
|
return "Medion";
|
||||||
|
case CEC_VENDOR_SHARP:
|
||||||
|
case CEC_VENDOR_SHARP2:
|
||||||
|
return "Sharp";
|
||||||
|
case CEC_VENDOR_VIZIO:
|
||||||
|
return "Vizio";
|
||||||
|
case CEC_VENDOR_BROADCOM:
|
||||||
|
return "Broadcom";
|
||||||
|
case CEC_VENDOR_LOEWE:
|
||||||
|
return "Loewe";
|
||||||
|
case CEC_VENDOR_DENON:
|
||||||
|
return "Denon";
|
||||||
|
case CEC_VENDOR_MARANTZ:
|
||||||
|
return "Marantz";
|
||||||
|
case CEC_VENDOR_HARMAN_KARDON:
|
||||||
|
case CEC_VENDOR_HARMAN_KARDON2:
|
||||||
|
return "Harman/Kardon";
|
||||||
|
case CEC_VENDOR_PULSE_EIGHT:
|
||||||
|
return "Pulse Eight";
|
||||||
|
case CEC_VENDOR_GOOGLE:
|
||||||
|
return "Google";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *ToString(const cec_user_control_code key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT:
|
||||||
|
return "select";
|
||||||
|
case CEC_USER_CONTROL_CODE_UP:
|
||||||
|
return "up";
|
||||||
|
case CEC_USER_CONTROL_CODE_DOWN:
|
||||||
|
return "down";
|
||||||
|
case CEC_USER_CONTROL_CODE_LEFT:
|
||||||
|
return "left";
|
||||||
|
case CEC_USER_CONTROL_CODE_RIGHT:
|
||||||
|
return "right";
|
||||||
|
case CEC_USER_CONTROL_CODE_RIGHT_UP:
|
||||||
|
return "right+up";
|
||||||
|
case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
|
||||||
|
return "right+down";
|
||||||
|
case CEC_USER_CONTROL_CODE_LEFT_UP:
|
||||||
|
return "left+up";
|
||||||
|
case CEC_USER_CONTROL_CODE_LEFT_DOWN:
|
||||||
|
return "left+down";
|
||||||
|
case CEC_USER_CONTROL_CODE_ROOT_MENU:
|
||||||
|
return "root menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_SETUP_MENU:
|
||||||
|
return "setup menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
|
||||||
|
return "contents menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
|
||||||
|
return "favourite menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_EXIT:
|
||||||
|
return "exit";
|
||||||
|
case CEC_USER_CONTROL_CODE_TOP_MENU:
|
||||||
|
return "top menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_DVD_MENU:
|
||||||
|
return "dvd menu";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER_ENTRY_MODE:
|
||||||
|
return "number entry mode";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER11:
|
||||||
|
return "11";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER12:
|
||||||
|
return "12";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER0:
|
||||||
|
return "0";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER1:
|
||||||
|
return "1";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER2:
|
||||||
|
return "2";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER3:
|
||||||
|
return "3";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER4:
|
||||||
|
return "4";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER5:
|
||||||
|
return "5";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER6:
|
||||||
|
return "6";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER7:
|
||||||
|
return "7";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER8:
|
||||||
|
return "8";
|
||||||
|
case CEC_USER_CONTROL_CODE_NUMBER9:
|
||||||
|
return "9";
|
||||||
|
case CEC_USER_CONTROL_CODE_DOT:
|
||||||
|
return ".";
|
||||||
|
case CEC_USER_CONTROL_CODE_ENTER:
|
||||||
|
return "enter";
|
||||||
|
case CEC_USER_CONTROL_CODE_CLEAR:
|
||||||
|
return "clear";
|
||||||
|
case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
|
||||||
|
return "next favourite";
|
||||||
|
case CEC_USER_CONTROL_CODE_CHANNEL_UP:
|
||||||
|
return "channel up";
|
||||||
|
case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
|
||||||
|
return "channel down";
|
||||||
|
case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
|
||||||
|
return "previous channel";
|
||||||
|
case CEC_USER_CONTROL_CODE_SOUND_SELECT:
|
||||||
|
return "sound select";
|
||||||
|
case CEC_USER_CONTROL_CODE_INPUT_SELECT:
|
||||||
|
return "input select";
|
||||||
|
case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
|
||||||
|
return "display information";
|
||||||
|
case CEC_USER_CONTROL_CODE_HELP:
|
||||||
|
return "help";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAGE_UP:
|
||||||
|
return "page up";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAGE_DOWN:
|
||||||
|
return "page down";
|
||||||
|
case CEC_USER_CONTROL_CODE_POWER:
|
||||||
|
return "power";
|
||||||
|
case CEC_USER_CONTROL_CODE_VOLUME_UP:
|
||||||
|
return "volume up";
|
||||||
|
case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
|
||||||
|
return "volume down";
|
||||||
|
case CEC_USER_CONTROL_CODE_MUTE:
|
||||||
|
return "mute";
|
||||||
|
case CEC_USER_CONTROL_CODE_PLAY:
|
||||||
|
return "play";
|
||||||
|
case CEC_USER_CONTROL_CODE_STOP:
|
||||||
|
return "stop";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE:
|
||||||
|
return "pause";
|
||||||
|
case CEC_USER_CONTROL_CODE_RECORD:
|
||||||
|
return "record";
|
||||||
|
case CEC_USER_CONTROL_CODE_REWIND:
|
||||||
|
return "rewind";
|
||||||
|
case CEC_USER_CONTROL_CODE_FAST_FORWARD:
|
||||||
|
return "Fast forward";
|
||||||
|
case CEC_USER_CONTROL_CODE_EJECT:
|
||||||
|
return "eject";
|
||||||
|
case CEC_USER_CONTROL_CODE_FORWARD:
|
||||||
|
return "forward";
|
||||||
|
case CEC_USER_CONTROL_CODE_BACKWARD:
|
||||||
|
return "backward";
|
||||||
|
case CEC_USER_CONTROL_CODE_STOP_RECORD:
|
||||||
|
return "stop record";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
|
||||||
|
return "pause record";
|
||||||
|
case CEC_USER_CONTROL_CODE_ANGLE:
|
||||||
|
return "angle";
|
||||||
|
case CEC_USER_CONTROL_CODE_SUB_PICTURE:
|
||||||
|
return "sub picture";
|
||||||
|
case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
|
||||||
|
return "video on demand";
|
||||||
|
case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
|
||||||
|
return "electronic program guide";
|
||||||
|
case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
|
||||||
|
return "timer programming";
|
||||||
|
case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
|
||||||
|
return "initial configuration";
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT_BROADCAST_TYPE:
|
||||||
|
return "select broadcast type";
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT_SOUND_PRESENTATION:
|
||||||
|
return "select sound presentation";
|
||||||
|
case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
|
||||||
|
return "play (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
|
||||||
|
return "pause play (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
|
||||||
|
return "record (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
|
||||||
|
return "pause record (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
|
||||||
|
return "stop (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
|
||||||
|
return "mute (function)";
|
||||||
|
case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
|
||||||
|
return "restore volume";
|
||||||
|
case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
|
||||||
|
return "tune";
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
|
||||||
|
return "select media";
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
|
||||||
|
return "select AV input";
|
||||||
|
case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
|
||||||
|
return "select audio input";
|
||||||
|
case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
|
||||||
|
return "power toggle";
|
||||||
|
case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
|
||||||
|
return "power off";
|
||||||
|
case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
|
||||||
|
return "power on";
|
||||||
|
case CEC_USER_CONTROL_CODE_F1_BLUE:
|
||||||
|
return "F1 (blue)";
|
||||||
|
case CEC_USER_CONTROL_CODE_F2_RED:
|
||||||
|
return "F2 (red)";
|
||||||
|
case CEC_USER_CONTROL_CODE_F3_GREEN:
|
||||||
|
return "F3 (green)";
|
||||||
|
case CEC_USER_CONTROL_CODE_F4_YELLOW:
|
||||||
|
return "F4 (yellow)";
|
||||||
|
case CEC_USER_CONTROL_CODE_F5:
|
||||||
|
return "F5";
|
||||||
|
case CEC_USER_CONTROL_CODE_DATA:
|
||||||
|
return "data";
|
||||||
|
case CEC_USER_CONTROL_CODE_AN_RETURN:
|
||||||
|
return "return (Samsung)";
|
||||||
|
case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:
|
||||||
|
return "channels list (Samsung)";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *ToString(cec_logical_address la)
|
||||||
|
{
|
||||||
|
switch (la & 0xf)
|
||||||
|
{
|
||||||
|
case CECDEVICE_TV:
|
||||||
|
return "TV";
|
||||||
|
case CECDEVICE_RECORDINGDEVICE1:
|
||||||
|
return "Recording Device 1";
|
||||||
|
case CECDEVICE_RECORDINGDEVICE2:
|
||||||
|
return "Recording Device 2";
|
||||||
|
case CECDEVICE_TUNER1:
|
||||||
|
return "Tuner 1";
|
||||||
|
case CECDEVICE_PLAYBACKDEVICE1:
|
||||||
|
return "Playback Device 1";
|
||||||
|
case CECDEVICE_AUDIOSYSTEM:
|
||||||
|
return "Audio System";
|
||||||
|
case CECDEVICE_TUNER2:
|
||||||
|
return "Tuner 2";
|
||||||
|
case CECDEVICE_TUNER3:
|
||||||
|
return "Tuner 3";
|
||||||
|
case CECDEVICE_PLAYBACKDEVICE2:
|
||||||
|
return "Playback Device 2";
|
||||||
|
case CECDEVICE_RECORDINGDEVICE3:
|
||||||
|
return "Recording Device 3";
|
||||||
|
case CECDEVICE_TUNER4:
|
||||||
|
return "Tuner 4";
|
||||||
|
case CECDEVICE_PLAYBACKDEVICE3:
|
||||||
|
return "Playback Device 3";
|
||||||
|
case CECDEVICE_RESERVED1:
|
||||||
|
return "Reserved 1";
|
||||||
|
case CECDEVICE_RESERVED2:
|
||||||
|
return "Reserved 2";
|
||||||
|
case CECDEVICE_FREEUSE:
|
||||||
|
return "Free use";
|
||||||
|
case CECDEVICE_UNREGISTERED:
|
||||||
|
default:
|
||||||
|
return "Unregistered";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cec_opcode GetResponseOpcode(cec_opcode opcode)
|
||||||
|
{
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
|
||||||
|
return CEC_OPCODE_ACTIVE_SOURCE;
|
||||||
|
case CEC_OPCODE_GET_CEC_VERSION:
|
||||||
|
return CEC_OPCODE_CEC_VERSION;
|
||||||
|
case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
|
||||||
|
return CEC_OPCODE_REPORT_PHYSICAL_ADDRESS;
|
||||||
|
case CEC_OPCODE_GET_MENU_LANGUAGE:
|
||||||
|
return CEC_OPCODE_SET_MENU_LANGUAGE;
|
||||||
|
case CEC_OPCODE_GIVE_DECK_STATUS:
|
||||||
|
return CEC_OPCODE_DECK_STATUS;
|
||||||
|
case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
|
||||||
|
return CEC_OPCODE_TUNER_DEVICE_STATUS;
|
||||||
|
case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
|
||||||
|
return CEC_OPCODE_DEVICE_VENDOR_ID;
|
||||||
|
case CEC_OPCODE_GIVE_OSD_NAME:
|
||||||
|
return CEC_OPCODE_SET_OSD_NAME;
|
||||||
|
case CEC_OPCODE_MENU_REQUEST:
|
||||||
|
return CEC_OPCODE_MENU_STATUS;
|
||||||
|
case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
|
||||||
|
return CEC_OPCODE_REPORT_POWER_STATUS;
|
||||||
|
case CEC_OPCODE_GIVE_AUDIO_STATUS:
|
||||||
|
return CEC_OPCODE_REPORT_AUDIO_STATUS;
|
||||||
|
case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
|
||||||
|
return CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS;
|
||||||
|
case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
|
||||||
|
return CEC_OPCODE_SET_SYSTEM_AUDIO_MODE;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CEC_OPCODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __HDMI_CEC_TYPES_H__
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/init.cpp
|
|
54
libmipsbox/init.cpp
Normal file
54
libmipsbox/init.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
#include "pwrmngr.h"
|
||||||
|
#include <proc_tools.h>
|
||||||
|
|
||||||
|
#include "hal_debug.h"
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_INIT, NULL, args)
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
void hal_api_init()
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
hal_debug_init();
|
||||||
|
hal_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel);
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
cCpuFreqManager f;
|
||||||
|
f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */
|
||||||
|
char buffer[64];
|
||||||
|
sprintf(buffer, "%x", 0);
|
||||||
|
proc_put("/proc/stb/fb/dst_top", buffer, strlen(buffer));
|
||||||
|
proc_put("/proc/stb/fb/dst_left", buffer, strlen(buffer));
|
||||||
|
sprintf(buffer, "%x", 576);
|
||||||
|
proc_put("/proc/stb/fb/dst_height", buffer, strlen(buffer));
|
||||||
|
sprintf(buffer, "%x", 720);
|
||||||
|
proc_put("/proc/stb/fb/dst_width", buffer, strlen(buffer));
|
||||||
|
sprintf(buffer, "%x", 1);
|
||||||
|
proc_put("/proc/stb/fb/dst_apply", buffer, strlen(buffer));
|
||||||
|
#if BOXMODEL_VUSOLO4K || BOXMODEL_VUDUO4K || BOXMODEL_VUDUO4KSE || BOXMODEL_VUULTIMO4K || BOXMODEL_VUUNO4KSE || BOXMODEL_VUUNO4K
|
||||||
|
sprintf(buffer, "%s", "enable");
|
||||||
|
proc_put("/proc/stb/frontend/fbc/fcc", buffer, strlen(buffer));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
hal_info("%s end\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hal_api_exit()
|
||||||
|
{
|
||||||
|
hal_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized);
|
||||||
|
initialized = false;
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/linux-uapi-cec.h
|
|
1014
libmipsbox/linux-uapi-cec.h
Normal file
1014
libmipsbox/linux-uapi-cec.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
../libarmbox/playback_libeplayer3.cpp
|
|
880
libmipsbox/playback_libeplayer3.cpp
Normal file
880
libmipsbox/playback_libeplayer3.cpp
Normal file
@@ -0,0 +1,880 @@
|
|||||||
|
#define __USE_FILE_OFFSET64 1
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <audio_lib.h>
|
||||||
|
#include <video_lib.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <common.h>
|
||||||
|
extern OutputHandler_t OutputHandler;
|
||||||
|
extern PlaybackHandler_t PlaybackHandler;
|
||||||
|
extern ContainerHandler_t ContainerHandler;
|
||||||
|
extern ManagerHandler_t ManagerHandler;
|
||||||
|
extern int32_t ffmpeg_av_dict_set( const char *key, const char *value, int32_t flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "playback_libeplayer3.h"
|
||||||
|
#include "hal_debug.h"
|
||||||
|
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_PLAYBACK, this, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_PLAYBACK, this, args)
|
||||||
|
|
||||||
|
static Context_t *player = NULL;
|
||||||
|
|
||||||
|
extern cAudio *audioDecoder;
|
||||||
|
extern cVideo *videoDecoder;
|
||||||
|
OpenThreads::Mutex cPlayback::mutex;
|
||||||
|
|
||||||
|
//Used by Fileplay
|
||||||
|
bool cPlayback::Open(playmode_t PlayMode)
|
||||||
|
{
|
||||||
|
const char *aPLAYMODE[] =
|
||||||
|
{
|
||||||
|
"PLAYMODE_TS",
|
||||||
|
"PLAYMODE_FILE"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (PlayMode != PLAYMODE_TS)
|
||||||
|
{
|
||||||
|
audioDecoder->closeDevice();
|
||||||
|
videoDecoder->closeDevice();
|
||||||
|
decoders_closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm = PlayMode;
|
||||||
|
got_vpts_ts = false;
|
||||||
|
vpts_ts = 0;
|
||||||
|
fn_ts = "";
|
||||||
|
fn_xml = "";
|
||||||
|
last_size = 0;
|
||||||
|
nPlaybackSpeed = 0;
|
||||||
|
init_jump = -1;
|
||||||
|
avft = avformat_alloc_context();
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
{
|
||||||
|
player = (Context_t *) malloc(sizeof(Context_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
player->playback = &PlaybackHandler;
|
||||||
|
player->output = &OutputHandler;
|
||||||
|
player->container = &ContainerHandler;
|
||||||
|
player->manager = &ManagerHandler;
|
||||||
|
|
||||||
|
hal_info("%s - player output name: %s PlayMode: %s\n", __func__, player->output->Name, aPLAYMODE[PlayMode]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Registration of output devices
|
||||||
|
if (player && player->output)
|
||||||
|
{
|
||||||
|
player->output->Command(player, OUTPUT_ADD, (void *)"audio");
|
||||||
|
player->output->Command(player, OUTPUT_ADD, (void *)"video");
|
||||||
|
player->output->Command(player, OUTPUT_ADD, (void *)"subtitle");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::Close(void)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
//Dagobert: movieplayer does not call stop, it calls close ;)
|
||||||
|
mutex.lock();
|
||||||
|
if(playing)
|
||||||
|
Stop();
|
||||||
|
mutex.unlock();
|
||||||
|
|
||||||
|
if (decoders_closed)
|
||||||
|
{
|
||||||
|
audioDecoder->openDevice();
|
||||||
|
videoDecoder->openDevice();
|
||||||
|
decoders_closed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::Start(std::string filename, std::string headers, std::string filename2)
|
||||||
|
{
|
||||||
|
return Start((char *) filename.c_str(), 0, 0, 0, 0, 0, headers,filename2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::Start(char *filename, int vpid, int vtype, int apid, int ac3, int, std::string headers, std::string filename2)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
bool isHTTP = false;
|
||||||
|
no_probe = false;
|
||||||
|
|
||||||
|
hal_info("%s - filename=%s vpid=%u vtype=%d apid=%u ac3=%d\n", __func__, filename, vpid, vtype, apid, ac3);
|
||||||
|
|
||||||
|
init_jump = -1;
|
||||||
|
//create playback path
|
||||||
|
mAudioStream = 0;
|
||||||
|
mSubtitleStream = -1;
|
||||||
|
mTeletextStream = -1;
|
||||||
|
std::string file;
|
||||||
|
|
||||||
|
if (*filename == '/')
|
||||||
|
file = "file://";
|
||||||
|
file += filename;
|
||||||
|
|
||||||
|
if ((file.find(":31339/id=") != std::string::npos) || (file.find(":10000") != std::string::npos) || (file.find(":8001/") != std::string::npos)) // for LocalTV and Entertain-TV streaming
|
||||||
|
no_probe = true;
|
||||||
|
|
||||||
|
if (file.substr(0, 7) == "file://")
|
||||||
|
{
|
||||||
|
if (file.substr(file.length() - 3) == ".ts")
|
||||||
|
{
|
||||||
|
fn_ts = file.substr(7);
|
||||||
|
fn_xml = file.substr(7, file.length() - 9);
|
||||||
|
fn_xml += "xml";
|
||||||
|
no_probe = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isHTTP = true;
|
||||||
|
|
||||||
|
if(isHTTP && headers.empty())
|
||||||
|
{
|
||||||
|
size_t pos = file.find('#');
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
headers = file.substr(pos + 1);
|
||||||
|
pos = headers.find("User-Agent=");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
headers.replace(pos+10, 1, ": ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!headers.empty()){
|
||||||
|
const char hkey[] = "headers";
|
||||||
|
ffmpeg_av_dict_set(hkey, headers.c_str(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string szSecondFile;
|
||||||
|
char *file2 = NULL;
|
||||||
|
if(!filename2.empty()){
|
||||||
|
szSecondFile = filename2;
|
||||||
|
file2 = (char *) szSecondFile.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayFiles_t playbackFiles = { (char *) file.c_str(), file2, NULL, NULL, 0, 0, 0, 0};
|
||||||
|
if (player->playback->Command(player, PLAYBACK_OPEN, &playbackFiles) == 0)
|
||||||
|
{
|
||||||
|
if (pm == PLAYMODE_TS)
|
||||||
|
{
|
||||||
|
struct stat64 s;
|
||||||
|
if (!stat64(file.c_str(), &s))
|
||||||
|
last_size = s.st_size;
|
||||||
|
ret = true;
|
||||||
|
videoDecoder->Stop(false);
|
||||||
|
audioDecoder->Stop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//AUDIO
|
||||||
|
if (player && player->manager && player->manager->audio)
|
||||||
|
{
|
||||||
|
char ** TrackList = NULL;
|
||||||
|
player->manager->audio->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("AudioTrack List\n");
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//SUB
|
||||||
|
if (player && player->manager && player->manager->subtitle)
|
||||||
|
{
|
||||||
|
char ** TrackList = NULL;
|
||||||
|
player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("SubtitleTrack List\n");
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i+=2)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//Teletext
|
||||||
|
if (player && player->manager && player->manager->teletext)
|
||||||
|
{
|
||||||
|
char ** TrackList = NULL;
|
||||||
|
player->manager->teletext->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("TeletextTrack List\n");
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
//Chapters
|
||||||
|
if (player && player->manager && player->manager->chapter)
|
||||||
|
{
|
||||||
|
char ** TrackList = NULL;
|
||||||
|
player->manager->chapter->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("Chapter List\n");
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playing = true;
|
||||||
|
first = true;
|
||||||
|
player->output->Command(player, OUTPUT_OPEN, NULL);
|
||||||
|
ret = (player->playback->Command(player, PLAYBACK_PLAY, NULL) == 0);
|
||||||
|
|
||||||
|
if (ret && !isHTTP)
|
||||||
|
playing = ret = (player->playback->Command(player, PLAYBACK_PAUSE, NULL) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::Stop(void)
|
||||||
|
{
|
||||||
|
hal_info("%s playing %d\n", __func__, playing);
|
||||||
|
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_STOP, NULL);
|
||||||
|
|
||||||
|
if (player && player->output)
|
||||||
|
player->output->Command(player, OUTPUT_CLOSE, NULL);
|
||||||
|
|
||||||
|
if (player && player->output)
|
||||||
|
{
|
||||||
|
player->output->Command(player, OUTPUT_DEL, (void *)"audio");
|
||||||
|
player->output->Command(player, OUTPUT_DEL, (void *)"video");
|
||||||
|
player->output->Command(player, OUTPUT_DEL, (void *)"subtitle");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_CLOSE, NULL);
|
||||||
|
|
||||||
|
playing = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetAPid(int pid, bool /* ac3 */)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
int i = pid;
|
||||||
|
|
||||||
|
if (pid != mAudioStream)
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_SWITCH_AUDIO, (void *)&i);
|
||||||
|
mAudioStream = pid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetVPid(int /*pid*/)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetSubtitlePid(int pid)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
int i = pid;
|
||||||
|
|
||||||
|
if (pid != mSubtitleStream)
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_SWITCH_SUBTITLE, (void *)&i);
|
||||||
|
mSubtitleStream = pid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetTeletextPid(int pid)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
//int i = pid;
|
||||||
|
|
||||||
|
if (pid != mTeletextStream)
|
||||||
|
{
|
||||||
|
//if (player && player->playback)
|
||||||
|
// player->playback->Command(player, PLAYBACK_SWITCH_TELETEXT, (void*)&i);
|
||||||
|
mTeletextStream = pid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetSpeed(int speed)
|
||||||
|
{
|
||||||
|
hal_info("%s playing %d speed %d\n", __func__, playing, speed);
|
||||||
|
|
||||||
|
if (!decoders_closed)
|
||||||
|
{
|
||||||
|
audioDecoder->closeDevice();
|
||||||
|
videoDecoder->closeDevice();
|
||||||
|
decoders_closed = true;
|
||||||
|
usleep(500000);
|
||||||
|
if (player && player->output && player->playback)
|
||||||
|
{
|
||||||
|
player->output->Command(player, OUTPUT_OPEN, NULL);
|
||||||
|
if (player->playback->Command(player, PLAYBACK_PLAY, NULL) == 0)
|
||||||
|
playing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playing)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (player && player->playback)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
if(nPlaybackSpeed == 0 && speed > 1)
|
||||||
|
{
|
||||||
|
result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
nPlaybackSpeed = speed;
|
||||||
|
|
||||||
|
if (speed > 1)
|
||||||
|
{
|
||||||
|
/* direction switch ? */
|
||||||
|
if (player->playback->BackWard)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
result = player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void *)&r);
|
||||||
|
printf("result = %d\n", result);
|
||||||
|
}
|
||||||
|
result = player->playback->Command(player, PLAYBACK_FASTFORWARD, (void *)&speed);
|
||||||
|
}
|
||||||
|
else if (speed < 0)
|
||||||
|
{
|
||||||
|
/* direction switch ? */
|
||||||
|
if (player->playback->isForwarding)
|
||||||
|
{
|
||||||
|
result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL);
|
||||||
|
printf("result = %d\n", result);
|
||||||
|
}
|
||||||
|
result = player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void *)&speed);
|
||||||
|
}
|
||||||
|
else if (speed == 0)
|
||||||
|
{
|
||||||
|
/* konfetti: hmmm accessing the member isn't very proper */
|
||||||
|
if ((player->playback->isForwarding) || (!player->playback->BackWard))
|
||||||
|
player->playback->Command(player, PLAYBACK_PAUSE, NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int _speed = 0; /* means end of reverse playback */
|
||||||
|
player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void *)&_speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_jump > -1)
|
||||||
|
{
|
||||||
|
SetPosition(init_jump, true);
|
||||||
|
init_jump = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("returning false\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::GetSpeed(int &speed) const
|
||||||
|
{
|
||||||
|
hal_debug("%s\n", __func__);
|
||||||
|
speed = nPlaybackSpeed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::GetPts(uint64_t &pts)
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_PTS, (void *)&pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in milliseconds
|
||||||
|
bool cPlayback::GetPosition(int &position, int &duration)
|
||||||
|
{
|
||||||
|
bool got_duration = false;
|
||||||
|
hal_debug("%s %d %d\n", __func__, position, duration);
|
||||||
|
|
||||||
|
/* hack: if the file is growing (timeshift), then determine its length
|
||||||
|
* by comparing the mtime with the mtime of the xml file */
|
||||||
|
if (pm == PLAYMODE_TS)
|
||||||
|
{
|
||||||
|
struct stat64 s;
|
||||||
|
if (!stat64(fn_ts.c_str(), &s))
|
||||||
|
{
|
||||||
|
if (!playing || last_size != s.st_size)
|
||||||
|
{
|
||||||
|
last_size = s.st_size;
|
||||||
|
time_t curr_time = s.st_mtime;
|
||||||
|
if (!stat64(fn_xml.c_str(), &s))
|
||||||
|
{
|
||||||
|
duration = (curr_time - s.st_mtime) * 1000;
|
||||||
|
if (!playing)
|
||||||
|
return true;
|
||||||
|
got_duration = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playing)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (player && player->playback && !player->playback->isPlaying)
|
||||||
|
{
|
||||||
|
hal_info("%s !!!!EOF!!!! < -1\n", __func__);
|
||||||
|
position = duration + 1000;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t vpts = 0;
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_PTS, &vpts);
|
||||||
|
|
||||||
|
if (vpts <= 0)
|
||||||
|
{
|
||||||
|
//printf("ERROR: vpts==0");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* workaround for crazy vpts value during timeshift */
|
||||||
|
if (!got_vpts_ts && pm == PLAYMODE_TS)
|
||||||
|
{
|
||||||
|
vpts_ts = vpts;
|
||||||
|
got_vpts_ts = true;
|
||||||
|
}
|
||||||
|
if (got_vpts_ts)
|
||||||
|
vpts -= vpts_ts;
|
||||||
|
/* end workaround */
|
||||||
|
/* len is in nanoseconds. we have 90 000 pts per second. */
|
||||||
|
position = vpts / 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (got_duration)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int64_t length = 0;
|
||||||
|
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, PLAYBACK_LENGTH, &length);
|
||||||
|
|
||||||
|
if (length <= 0)
|
||||||
|
{
|
||||||
|
duration = duration + 1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duration = length * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetPosition(int position, bool absolute)
|
||||||
|
{
|
||||||
|
hal_info("%s %d\n", __func__, position);
|
||||||
|
|
||||||
|
if (playing && first)
|
||||||
|
{
|
||||||
|
/* the calling sequence is:
|
||||||
|
* Start() - paused
|
||||||
|
* SetPosition() - which fails if not running
|
||||||
|
* SetSpeed() - to start playing
|
||||||
|
* so let's remember the initial jump position and later jump to it
|
||||||
|
*/
|
||||||
|
init_jump = position;
|
||||||
|
first = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pos = (position / 1000.0);
|
||||||
|
|
||||||
|
if (player && player->playback)
|
||||||
|
player->playback->Command(player, absolute ? PLAYBACK_SEEK_ABS : PLAYBACK_SEEK, (void *)&pos);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
int max_numpida = *numpida;
|
||||||
|
*numpida = 0;
|
||||||
|
|
||||||
|
if (player && player->playback && player->playback->isPlaying && player->manager && player->manager->audio)
|
||||||
|
{
|
||||||
|
char **TrackList = NULL;
|
||||||
|
player->manager->audio->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("AudioTrack List\n");
|
||||||
|
int i = 0, j = 0;
|
||||||
|
for (i = 0, j = 0; TrackList[i] != NULL; i += 2, j++)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
if (j < max_numpida)
|
||||||
|
{
|
||||||
|
int _pid = 0;
|
||||||
|
std::string _lang ;
|
||||||
|
std::istringstream iss(TrackList[i]) ;
|
||||||
|
iss >> _pid;
|
||||||
|
iss >> _lang;
|
||||||
|
if (_pid && !_lang.empty())
|
||||||
|
{
|
||||||
|
apids[j] = _pid;
|
||||||
|
// atUnknown, atMPEG, atMP3, atAC3, atDTS, atAAC, atPCM, atOGG, atFLAC
|
||||||
|
if (!strncmp("A_MPEG/L3", TrackList[i + 1], 9))
|
||||||
|
ac3flags[j] = 3;
|
||||||
|
if (!strncmp("A_MP3", TrackList[i + 1], 5))
|
||||||
|
ac3flags[j] = 4;
|
||||||
|
else if (!strncmp("A_AC3", TrackList[i + 1], 5))
|
||||||
|
ac3flags[j] = 1;
|
||||||
|
else if (!strncmp("A_EAC3", TrackList[i + 1], 6))
|
||||||
|
ac3flags[j] = 7;
|
||||||
|
else if (!strncmp("A_DTS", TrackList[i + 1], 5))
|
||||||
|
ac3flags[j] = 6;
|
||||||
|
else if (!strncmp("A_AAC", TrackList[i + 1], 5))
|
||||||
|
ac3flags[j] = 5;
|
||||||
|
else if (!strncmp("A_PCM", TrackList[i + 1], 5))
|
||||||
|
ac3flags[j] = 0; //todo
|
||||||
|
else if (!strncmp("A_VORBIS", TrackList[i + 1], 8))
|
||||||
|
ac3flags[j] = 0; //todo
|
||||||
|
else if (!strncmp("A_FLAC", TrackList[i + 1], 6))
|
||||||
|
ac3flags[j] = 0; //todo
|
||||||
|
else
|
||||||
|
ac3flags[j] = 0; //todo
|
||||||
|
std::string _language = "";
|
||||||
|
_language += _lang;
|
||||||
|
_language += " - ";
|
||||||
|
_language += "(";
|
||||||
|
_language += TrackList[i + 1];
|
||||||
|
_language += ")";
|
||||||
|
language[j] = _language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
*numpida = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
int max_numpids = *numpids;
|
||||||
|
*numpids = 0;
|
||||||
|
|
||||||
|
if (player && player->manager && player->manager->subtitle)
|
||||||
|
{
|
||||||
|
char **TrackList = NULL;
|
||||||
|
player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("SubtitleTrack List\n");
|
||||||
|
int i = 0, j = 0;
|
||||||
|
for (i = 0, j = 0; TrackList[i] != NULL; i += 2, j++)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
if (j < max_numpids)
|
||||||
|
{
|
||||||
|
int _pid = 0;
|
||||||
|
std::string _lang ;
|
||||||
|
std::istringstream iss(TrackList[i]) ;
|
||||||
|
iss >> _pid;
|
||||||
|
iss >> _lang;
|
||||||
|
if (_pid && !_lang.empty())
|
||||||
|
{
|
||||||
|
pids[j] = _pid;
|
||||||
|
language[j] = _lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
*numpids = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::FindAllTeletextsubtitlePids(int */*pids*/, unsigned int *numpids, std::string */*language*/, int */*mags*/, int */*pages*/)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
//int max_numpids = *numpids;
|
||||||
|
*numpids = 0;
|
||||||
|
|
||||||
|
/* if (player && player->manager && player->manager->teletext)
|
||||||
|
{
|
||||||
|
char **TrackList = NULL;
|
||||||
|
player->manager->teletext->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("Teletext List\n");
|
||||||
|
int i = 0, j = 0;
|
||||||
|
for (i = 0, j = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
int type = 0;
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
if (j < max_numpids)
|
||||||
|
{
|
||||||
|
int _pid;
|
||||||
|
if (2 != sscanf(TrackList[i], "%d %*s %d %*d %*d", &_pid, &type))
|
||||||
|
continue;
|
||||||
|
if (type != 2 && type != 5) // return subtitles only
|
||||||
|
continue;
|
||||||
|
pids[j] = _pid;
|
||||||
|
language[j] = std::string(TrackList[i]);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
*numpids = j;
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
int cPlayback::GetTeletextPid(void)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
int pid = -1;
|
||||||
|
|
||||||
|
/* if (player && player->manager && player->manager->teletext)
|
||||||
|
{
|
||||||
|
char **TrackList = NULL;
|
||||||
|
player->manager->teletext->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("Teletext List\n");
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
int type = 0;
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i+1]);
|
||||||
|
if (pid < 0)
|
||||||
|
{
|
||||||
|
if (2 != sscanf(TrackList[i], "%*d %d %*s %d %*d %*d", &pid, &type))
|
||||||
|
continue;
|
||||||
|
if (type != 1)
|
||||||
|
pid = -1;
|
||||||
|
}
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
printf("teletext pid id %d (0x%x)\n", pid, pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* dummy functions for subtitles */
|
||||||
|
void cPlayback::FindAllSubs(uint16_t * /*pids*/, unsigned short * /*supp*/, uint16_t *num, std::string * /*lang*/)
|
||||||
|
{
|
||||||
|
*num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SelectSubtitles(int /*pid*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cPlayback::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
|
||||||
|
{
|
||||||
|
positions.clear();
|
||||||
|
titles.clear();
|
||||||
|
|
||||||
|
if (player && player->manager && player->manager->chapter)
|
||||||
|
{
|
||||||
|
char **TrackList = NULL;
|
||||||
|
player->manager->chapter->Command(player, MANAGER_LIST, &TrackList);
|
||||||
|
if (TrackList != NULL)
|
||||||
|
{
|
||||||
|
printf("%s: Chapter List\n", __func__);
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
||||||
|
{
|
||||||
|
printf("\t%s - %s\n", TrackList[i], TrackList[i + 1]);
|
||||||
|
int pos = atoi(TrackList[i]);
|
||||||
|
std::string title(TrackList[i + 1]);
|
||||||
|
positions.push_back(pos);
|
||||||
|
titles.push_back(title);
|
||||||
|
free(TrackList[i]);
|
||||||
|
free(TrackList[i + 1]);
|
||||||
|
}
|
||||||
|
free(TrackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||||
|
{
|
||||||
|
keys.clear();
|
||||||
|
values.clear();
|
||||||
|
char **metadata = NULL;
|
||||||
|
if (player && player->playback)
|
||||||
|
{
|
||||||
|
player->playback->Command(player, PLAYBACK_METADATA, &metadata);
|
||||||
|
if (metadata)
|
||||||
|
{
|
||||||
|
for (char **m = metadata; *m;)
|
||||||
|
{
|
||||||
|
keys.push_back(*m);
|
||||||
|
free(*m++);
|
||||||
|
values.push_back(*m);
|
||||||
|
free(*m++);
|
||||||
|
}
|
||||||
|
free(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cPlayback::cPlayback(int num __attribute__((unused)))
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
playing = false;
|
||||||
|
decoders_closed = false;
|
||||||
|
first = false;
|
||||||
|
player = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cPlayback::~cPlayback()
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
RequestAbort();
|
||||||
|
mutex.lock();
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
free(player);
|
||||||
|
player = NULL;
|
||||||
|
}
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::RequestAbort()
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
mutex.lock();
|
||||||
|
|
||||||
|
if (player && player->playback && player->playback->isPlaying)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
player->playback->abortRequested = 1;
|
||||||
|
}
|
||||||
|
else if(player->playback->isHttp && !player->playback->isPlaying &&!player->playback->abortRequested)
|
||||||
|
{
|
||||||
|
player->playback->abortRequested = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.unlock();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::IsPlaying()
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
return player->playback->isPlaying;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t cPlayback::GetReadCount()
|
||||||
|
{
|
||||||
|
if (player && player->playback)
|
||||||
|
{
|
||||||
|
return player->playback->readCount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVFormatContext *cPlayback::GetAVFormatContext()
|
||||||
|
{
|
||||||
|
if (player && player->container && player->container->selectedContainer)
|
||||||
|
{
|
||||||
|
player->container->selectedContainer->Command(player, CONTAINER_GET_AVFCONTEXT, avft);
|
||||||
|
}
|
||||||
|
return avft;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::ReleaseAVFormatContext()
|
||||||
|
{
|
||||||
|
avft->streams = NULL;
|
||||||
|
avft->nb_streams = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool cPlayback::IsPlaying(void) const
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
/* konfetti: there is no event/callback mechanism in libeplayer2
|
||||||
|
* so in case of ending playback we have no information on a
|
||||||
|
* terminated stream currently (or did I oversee it?).
|
||||||
|
* So let's ask the player the state.
|
||||||
|
*/
|
||||||
|
if (playing)
|
||||||
|
{
|
||||||
|
return player->playback->isPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
|
#endif
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/playback_libeplayer3.h
|
|
88
libmipsbox/playback_libeplayer3.h
Normal file
88
libmipsbox/playback_libeplayer3.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#ifndef __PLAYBACK_LIBEPLAYER3_H__
|
||||||
|
#define __PLAYBACK_LIBEPLAYER3_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <OpenThreads/Mutex>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
PLAYMODE_TS = 0,
|
||||||
|
PLAYMODE_FILE
|
||||||
|
} playmode_t;
|
||||||
|
|
||||||
|
struct AVFormatContext;
|
||||||
|
|
||||||
|
class cPlayback
|
||||||
|
{
|
||||||
|
friend class CStreamInfo2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OpenThreads::Mutex mutex;
|
||||||
|
bool enabled;
|
||||||
|
bool playing, first;
|
||||||
|
bool no_probe;
|
||||||
|
bool got_vpts_ts;
|
||||||
|
int nPlaybackSpeed;
|
||||||
|
int mAudioStream;
|
||||||
|
int mSubtitleStream;
|
||||||
|
int mTeletextStream;
|
||||||
|
int64_t vpts_ts;
|
||||||
|
bool Stop(void);
|
||||||
|
bool decoders_closed;
|
||||||
|
playmode_t pm;
|
||||||
|
std::string fn_ts;
|
||||||
|
std::string fn_xml;
|
||||||
|
off64_t last_size;
|
||||||
|
int init_jump;
|
||||||
|
AVFormatContext *avft;
|
||||||
|
public:
|
||||||
|
cPlayback(int num = 0);
|
||||||
|
~cPlayback();
|
||||||
|
|
||||||
|
bool Open(playmode_t PlayMode);
|
||||||
|
void Close(void);
|
||||||
|
bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration, std::string headers = "", std::string filename2 = "");
|
||||||
|
bool Start(std::string filename, std::string headers = "", std::string filename2 = "");
|
||||||
|
bool SetAPid(int pid, bool ac3 = false);
|
||||||
|
bool SetVPid(int /*pid*/);
|
||||||
|
bool SetSubtitlePid(int pid);
|
||||||
|
bool SetTeletextPid(int pid);
|
||||||
|
int GetAPid(void) { return mAudioStream; }
|
||||||
|
int GetVPid(void) { return 0; }
|
||||||
|
int GetSubtitlePid(void) { return mSubtitleStream; }
|
||||||
|
int GetTeletextPid(void);
|
||||||
|
bool SetSpeed(int speed);
|
||||||
|
bool GetSpeed(int &speed) const;
|
||||||
|
bool GetPosition(int &position, int &duration);
|
||||||
|
void GetPts(uint64_t &pts);
|
||||||
|
bool SetPosition(int position, bool absolute = false);
|
||||||
|
void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language);
|
||||||
|
void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language);
|
||||||
|
void FindAllTeletextsubtitlePids(int */*pids*/, unsigned int *numpidt, std::string */*tlanguage*/, int */*mags*/, int */*pages*/);
|
||||||
|
void RequestAbort(void);
|
||||||
|
bool IsPlaying(void);
|
||||||
|
uint64_t GetReadCount(void);
|
||||||
|
|
||||||
|
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
||||||
|
void GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||||
|
|
||||||
|
AVFormatContext *GetAVFormatContext();
|
||||||
|
void ReleaseAVFormatContext();
|
||||||
|
#if 0
|
||||||
|
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
|
||||||
|
bool SelectSubtitles(int pid);
|
||||||
|
|
||||||
|
// Functions that are not used by movieplayer.cpp:
|
||||||
|
bool GetOffset(off64_t &offset);
|
||||||
|
bool IsPlaying(void) const;
|
||||||
|
bool IsEnabled(void) const;
|
||||||
|
void *GetHandle(void);
|
||||||
|
void *GetDmHandle(void);
|
||||||
|
int GetCurrPlaybackSpeed(void) const;
|
||||||
|
void PlaybackNotify(int Event, void *pData, void *pTag);
|
||||||
|
void DMNotify(int Event, void *pTsBuf, void *Tag);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PLAYBACK_LIBEPLAYER3_H__
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/record.cpp
|
|
403
libmipsbox/record.cpp
Normal file
403
libmipsbox/record.cpp
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
/*
|
||||||
|
* (C)
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <aio.h>
|
||||||
|
|
||||||
|
#include "record_lib.h"
|
||||||
|
#include "hal_debug.h"
|
||||||
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_RECORD, this, args)
|
||||||
|
#define hal_info(args...) _hal_info(HAL_DEBUG_RECORD, this, args)
|
||||||
|
|
||||||
|
/* helper function to call the cpp thread loop */
|
||||||
|
void *execute_record_thread(void *c)
|
||||||
|
{
|
||||||
|
cRecord *obj = (cRecord *)c;
|
||||||
|
obj->RecordThread();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *execute_writer_thread(void *c)
|
||||||
|
{
|
||||||
|
cRecord *obj = (cRecord *)c;
|
||||||
|
obj->WriterThread();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cRecord::cRecord(int num, int bs_dmx, int bs)
|
||||||
|
{
|
||||||
|
hal_info("%s %d\n", __func__, num);
|
||||||
|
dmx = NULL;
|
||||||
|
record_thread_running = false;
|
||||||
|
file_fd = -1;
|
||||||
|
exit_flag = RECORD_STOPPED;
|
||||||
|
dmx_num = num;
|
||||||
|
bufsize = bs;
|
||||||
|
bufsize_dmx = bs_dmx;
|
||||||
|
failureCallback = NULL;
|
||||||
|
failureData = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cRecord::~cRecord()
|
||||||
|
{
|
||||||
|
hal_info("%s: calling ::Stop()\n", __func__);
|
||||||
|
Stop();
|
||||||
|
hal_info("%s: end\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecord::Open(void)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
exit_flag = RECORD_STOPPED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// unused
|
||||||
|
void cRecord::Close(void)
|
||||||
|
{
|
||||||
|
hal_info("%s: \n", __func__);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool cRecord::Start(int fd, unsigned short vpid, unsigned short *apids, int numpids, uint64_t)
|
||||||
|
{
|
||||||
|
hal_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!dmx)
|
||||||
|
dmx = new cDemux(dmx_num);
|
||||||
|
|
||||||
|
dmx->Open(DMX_TP_CHANNEL, NULL, bufsize_dmx);
|
||||||
|
dmx->pesFilter(vpid);
|
||||||
|
|
||||||
|
for (i = 0; i < numpids; i++)
|
||||||
|
dmx->addPid(apids[i]);
|
||||||
|
|
||||||
|
file_fd = fd;
|
||||||
|
exit_flag = RECORD_RUNNING;
|
||||||
|
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
|
||||||
|
perror("posix_fadvise");
|
||||||
|
|
||||||
|
i = pthread_create(&record_thread, 0, execute_record_thread, this);
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
exit_flag = RECORD_FAILED_READ;
|
||||||
|
errno = i;
|
||||||
|
hal_info("%s: error creating thread! (%m)\n", __func__);
|
||||||
|
delete dmx;
|
||||||
|
dmx = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
record_thread_running = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecord::Stop(void)
|
||||||
|
{
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
|
||||||
|
if (exit_flag != RECORD_RUNNING)
|
||||||
|
hal_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag);
|
||||||
|
|
||||||
|
exit_flag = RECORD_STOPPED;
|
||||||
|
if (record_thread_running)
|
||||||
|
pthread_join(record_thread, NULL);
|
||||||
|
record_thread_running = false;
|
||||||
|
|
||||||
|
/* We should probably do that from the destructor... */
|
||||||
|
if (!dmx)
|
||||||
|
hal_info("%s: dmx == NULL?\n", __func__);
|
||||||
|
else
|
||||||
|
delete dmx;
|
||||||
|
dmx = NULL;
|
||||||
|
|
||||||
|
if (file_fd != -1)
|
||||||
|
close(file_fd);
|
||||||
|
else
|
||||||
|
hal_info("%s: file_fd not open??\n", __func__);
|
||||||
|
file_fd = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids)
|
||||||
|
{
|
||||||
|
std::vector<pes_pids> pids;
|
||||||
|
int j;
|
||||||
|
bool found;
|
||||||
|
unsigned short pid;
|
||||||
|
hal_info("%s\n", __func__);
|
||||||
|
if (!dmx) {
|
||||||
|
hal_info("%s: DMX = NULL\n", __func__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pids = dmx->pesfds;
|
||||||
|
/* the first PID is the video pid, so start with the second PID... */
|
||||||
|
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
|
||||||
|
found = false;
|
||||||
|
pid = (*i).pid;
|
||||||
|
for (j = 0; j < numapids; j++) {
|
||||||
|
if (pid == apids[j]) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
dmx->removePid(pid);
|
||||||
|
}
|
||||||
|
for (j = 0; j < numapids; j++) {
|
||||||
|
found = false;
|
||||||
|
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
|
||||||
|
if ((*i).pid == apids[j]) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
dmx->addPid(apids[j]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecord::AddPid(unsigned short pid)
|
||||||
|
{
|
||||||
|
std::vector<pes_pids> pids;
|
||||||
|
hal_info("%s: \n", __func__);
|
||||||
|
if (!dmx) {
|
||||||
|
hal_info("%s: DMX = NULL\n", __func__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pids = dmx->pesfds;
|
||||||
|
for (std::vector<pes_pids>::const_iterator i = pids.begin(); i != pids.end(); ++i) {
|
||||||
|
if ((*i).pid == pid)
|
||||||
|
return true; /* or is it an error to try to add the same PID twice? */
|
||||||
|
}
|
||||||
|
return dmx->addPid(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRecord::WriterThread()
|
||||||
|
{
|
||||||
|
char threadname[17];
|
||||||
|
strncpy(threadname, "WriterThread", sizeof(threadname));
|
||||||
|
threadname[16] = 0;
|
||||||
|
prctl (PR_SET_NAME, (unsigned long)&threadname);
|
||||||
|
unsigned int chunk = 0;
|
||||||
|
while (!sem_wait(&sem)) {
|
||||||
|
if (!io_len[chunk]) // empty, assume end of recording
|
||||||
|
return;
|
||||||
|
unsigned char *p_buf = io_buf[chunk];
|
||||||
|
size_t p_len = io_len[chunk];
|
||||||
|
while (p_len) {
|
||||||
|
ssize_t written = write(file_fd, p_buf, p_len);
|
||||||
|
if (written < 0)
|
||||||
|
break;
|
||||||
|
p_len -= written;
|
||||||
|
p_buf += written;
|
||||||
|
}
|
||||||
|
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
|
||||||
|
perror("posix_fadvise");
|
||||||
|
chunk++;
|
||||||
|
chunk %= RECORD_WRITER_CHUNKS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRecord::RecordThread()
|
||||||
|
{
|
||||||
|
hal_info("%s: begin\n", __func__);
|
||||||
|
char threadname[17];
|
||||||
|
strncpy(threadname, "RecordThread", sizeof(threadname));
|
||||||
|
threadname[16] = 0;
|
||||||
|
prctl (PR_SET_NAME, (unsigned long)&threadname);
|
||||||
|
int readsize = bufsize / 16;
|
||||||
|
int buf_pos = 0;
|
||||||
|
int count = 0;
|
||||||
|
int queued = 0;
|
||||||
|
uint8_t *buf;
|
||||||
|
struct aiocb a;
|
||||||
|
|
||||||
|
buf = (uint8_t *)malloc(bufsize);
|
||||||
|
hal_info("BUFSIZE=0x%x READSIZE=0x%x\n", bufsize, readsize);
|
||||||
|
if (!buf)
|
||||||
|
{
|
||||||
|
exit_flag = RECORD_FAILED_MEMORY;
|
||||||
|
hal_info("%s: unable to allocate buffer! (out of memory)\n", __func__);
|
||||||
|
if (failureCallback)
|
||||||
|
failureCallback(failureData);
|
||||||
|
hal_info("%s: end\n", __func__);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = fcntl(file_fd, F_GETFL);
|
||||||
|
if (fcntl(file_fd, F_SETFL, val|O_APPEND))
|
||||||
|
hal_info("%s: O_APPEND? (%m)\n", __func__);
|
||||||
|
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
|
a.aio_fildes = file_fd;
|
||||||
|
a.aio_sigevent.sigev_notify = SIGEV_NONE;
|
||||||
|
|
||||||
|
dmx->Start();
|
||||||
|
int overflow_count = 0;
|
||||||
|
bool overflow = false;
|
||||||
|
int r = 0;
|
||||||
|
while (exit_flag == RECORD_RUNNING)
|
||||||
|
{
|
||||||
|
if (buf_pos < bufsize)
|
||||||
|
{
|
||||||
|
if (overflow_count) {
|
||||||
|
hal_info("%s: Overflow cleared after %d iterations\n", __func__, overflow_count);
|
||||||
|
overflow_count = 0;
|
||||||
|
}
|
||||||
|
int toread = bufsize - buf_pos;
|
||||||
|
if (toread > readsize)
|
||||||
|
toread = readsize;
|
||||||
|
ssize_t s = dmx->Read(buf + buf_pos, toread, 50);
|
||||||
|
hal_debug("%s: buf_pos %6d s %6d / %6d\n", __func__,
|
||||||
|
buf_pos, (int)s, bufsize - buf_pos);
|
||||||
|
if (s < 0)
|
||||||
|
{
|
||||||
|
if (errno != EAGAIN && (errno != EOVERFLOW || !overflow))
|
||||||
|
{
|
||||||
|
hal_info("%s: read failed: %m\n", __func__);
|
||||||
|
exit_flag = RECORD_FAILED_READ;
|
||||||
|
state = REC_STATUS_OVERFLOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
overflow = false;
|
||||||
|
buf_pos += s;
|
||||||
|
if (count > 100)
|
||||||
|
{
|
||||||
|
if (buf_pos < bufsize / 2)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!overflow)
|
||||||
|
overflow_count = 0;
|
||||||
|
overflow = true;
|
||||||
|
if (!(overflow_count % 10))
|
||||||
|
hal_info("%s: buffer full! Overflow? (%d)\n", __func__, ++overflow_count);
|
||||||
|
state = REC_STATUS_SLOW;
|
||||||
|
}
|
||||||
|
r = aio_error(&a);
|
||||||
|
if (r == EINPROGRESS)
|
||||||
|
{
|
||||||
|
hal_debug("%s: aio in progress, free: %d\n", __func__, bufsize - buf_pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// not calling aio_return causes a memory leak --martii
|
||||||
|
r = aio_return(&a);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
exit_flag = RECORD_FAILED_FILE;
|
||||||
|
hal_debug("%s: aio_return = %d (%m)\n", __func__, r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hal_debug("%s: aio_return = %d, free: %d\n", __func__, r, bufsize - buf_pos);
|
||||||
|
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
|
||||||
|
perror("posix_fadvise");
|
||||||
|
if (queued)
|
||||||
|
{
|
||||||
|
memmove(buf, buf + queued, buf_pos - queued);
|
||||||
|
buf_pos -= queued;
|
||||||
|
}
|
||||||
|
queued = buf_pos;
|
||||||
|
a.aio_buf = buf;
|
||||||
|
a.aio_nbytes = queued;
|
||||||
|
r = aio_write(&a);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
hal_info("%s: aio_write %d (%m)\n", __func__, r);
|
||||||
|
exit_flag = RECORD_FAILED_FILE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dmx->Stop();
|
||||||
|
while (true) /* write out the unwritten buffer content */
|
||||||
|
{
|
||||||
|
hal_debug("%s: run-out write, buf_pos %d\n", __func__, buf_pos);
|
||||||
|
r = aio_error(&a);
|
||||||
|
if (r == EINPROGRESS)
|
||||||
|
{
|
||||||
|
usleep(50000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r = aio_return(&a);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
exit_flag = RECORD_FAILED_FILE;
|
||||||
|
hal_info("%s: aio_result: %d (%m)\n", __func__, r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!queued)
|
||||||
|
break;
|
||||||
|
memmove(buf, buf + queued, buf_pos - queued);
|
||||||
|
buf_pos -= queued;
|
||||||
|
queued = buf_pos;
|
||||||
|
a.aio_buf = buf;
|
||||||
|
a.aio_nbytes = queued;
|
||||||
|
r = aio_write(&a);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO: do we need to notify neutrino about failing recording?
|
||||||
|
CEventServer eventServer;
|
||||||
|
eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock");
|
||||||
|
stream2file_status2_t s;
|
||||||
|
s.status = exit_flag;
|
||||||
|
strncpy(s.filename,basename(myfilename),512);
|
||||||
|
s.filename[511] = '\0';
|
||||||
|
strncpy(s.dir,dirname(myfilename),100);
|
||||||
|
s.dir[99] = '\0';
|
||||||
|
eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s));
|
||||||
|
printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((exit_flag != RECORD_STOPPED) && failureCallback)
|
||||||
|
failureCallback(failureData);
|
||||||
|
hal_info("%s: end\n", __func__);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cRecord::GetStatus()
|
||||||
|
{
|
||||||
|
return (exit_flag == RECORD_STOPPED) ? REC_STATUS_STOPPED : REC_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRecord::ResetStatus()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/record_lib.h
|
|
57
libmipsbox/record_lib.h
Normal file
57
libmipsbox/record_lib.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef __RECORD_LIB_H__
|
||||||
|
#define __RECORD_LIB_H__
|
||||||
|
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include "dmx_hal.h"
|
||||||
|
|
||||||
|
#define REC_STATUS_OK 0
|
||||||
|
#define REC_STATUS_SLOW 1
|
||||||
|
#define REC_STATUS_OVERFLOW 2
|
||||||
|
#define REC_STATUS_STOPPED 4
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RECORD_RUNNING,
|
||||||
|
RECORD_STOPPED,
|
||||||
|
RECORD_FAILED_READ, /* failed to read from DMX */
|
||||||
|
RECORD_FAILED_OVERFLOW, /* cannot write fast enough */
|
||||||
|
RECORD_FAILED_FILE, /* cannot write to file */
|
||||||
|
RECORD_FAILED_MEMORY /* out of memory */
|
||||||
|
} record_state_t;
|
||||||
|
|
||||||
|
class cRecord
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int file_fd;
|
||||||
|
int dmx_num;
|
||||||
|
cDemux *dmx;
|
||||||
|
pthread_t record_thread;
|
||||||
|
bool record_thread_running;
|
||||||
|
record_state_t exit_flag;
|
||||||
|
int state;
|
||||||
|
int bufsize;
|
||||||
|
int bufsize_dmx;
|
||||||
|
void (*failureCallback)(void *);
|
||||||
|
void *failureData;
|
||||||
|
|
||||||
|
sem_t sem;
|
||||||
|
#define RECORD_WRITER_CHUNKS 16
|
||||||
|
unsigned char *io_buf[RECORD_WRITER_CHUNKS];
|
||||||
|
size_t io_len[RECORD_WRITER_CHUNKS];
|
||||||
|
public:
|
||||||
|
cRecord(int num = 0, int bs_dmx = 2048 * 1024, int bs = 4096 * 1024);
|
||||||
|
void setFailureCallback(void (*f)(void *), void *d) { failureCallback = f; failureData = d; }
|
||||||
|
~cRecord();
|
||||||
|
|
||||||
|
bool Open();
|
||||||
|
bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids, uint64_t ch = 0);
|
||||||
|
bool Stop(void);
|
||||||
|
bool AddPid(unsigned short pid);
|
||||||
|
int GetStatus();
|
||||||
|
void ResetStatus();
|
||||||
|
bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids);
|
||||||
|
|
||||||
|
void RecordThread();
|
||||||
|
void WriterThread();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __RECORD_LIB_H__
|
@@ -1 +0,0 @@
|
|||||||
../libarmbox/video.cpp
|
|
1280
libmipsbox/video.cpp
Normal file
1280
libmipsbox/video.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
../libarmbox/video_lib.h
|
|
256
libmipsbox/video_lib.h
Normal file
256
libmipsbox/video_lib.h
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
#ifndef __VIDEO_LIB_H__
|
||||||
|
#define __VIDEO_LIB_H__
|
||||||
|
|
||||||
|
#include <linux/dvb/video.h>
|
||||||
|
#include "cs_types.h"
|
||||||
|
#include "dmx_hal.h"
|
||||||
|
|
||||||
|
typedef struct cs_vs_format_t
|
||||||
|
{
|
||||||
|
char format[16];
|
||||||
|
} cs_vs_format_struct_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ANALOG_SD_RGB_CINCH = 0x00,
|
||||||
|
ANALOG_SD_YPRPB_CINCH,
|
||||||
|
ANALOG_HD_RGB_CINCH,
|
||||||
|
ANALOG_HD_YPRPB_CINCH,
|
||||||
|
ANALOG_SD_RGB_SCART = 0x10,
|
||||||
|
ANALOG_SD_YPRPB_SCART,
|
||||||
|
ANALOG_HD_RGB_SCART,
|
||||||
|
ANALOG_HD_YPRPB_SCART,
|
||||||
|
ANALOG_SCART_MASK = 0x10
|
||||||
|
} analog_mode_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
COLORFORMAT_RGB = 0x10, // keep compatible with analog_mode_t
|
||||||
|
COLORFORMAT_YUV,
|
||||||
|
COLORFORMAT_CVBS,
|
||||||
|
COLORFORMAT_SVIDEO,
|
||||||
|
COLORFORMAT_HDMI_AUTO,
|
||||||
|
COLORFORMAT_HDMI_RGB,
|
||||||
|
COLORFORMAT_HDMI_YCBCR444,
|
||||||
|
COLORFORMAT_HDMI_YCBCR422,
|
||||||
|
COLORFORMAT_HDMI_YCBCR420
|
||||||
|
} COLOR_FORMAT;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_FORMAT_MPEG2 = 0,
|
||||||
|
VIDEO_FORMAT_MPEG4_H264,
|
||||||
|
VIDEO_FORMAT_VC1,
|
||||||
|
VIDEO_FORMAT_JPEG,
|
||||||
|
VIDEO_FORMAT_GIF,
|
||||||
|
VIDEO_FORMAT_PNG,
|
||||||
|
VIDEO_FORMAT_MPEG4_H265,
|
||||||
|
VIDEO_FORMAT_AVS = 16
|
||||||
|
} VIDEO_FORMAT;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_SD = 0,
|
||||||
|
VIDEO_HD,
|
||||||
|
VIDEO_120x60i,
|
||||||
|
VIDEO_320x240i,
|
||||||
|
VIDEO_1440x800i,
|
||||||
|
VIDEO_360x288i
|
||||||
|
} VIDEO_DEFINITION;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_FRAME_RATE_23_976 = 0,
|
||||||
|
VIDEO_FRAME_RATE_24,
|
||||||
|
VIDEO_FRAME_RATE_25,
|
||||||
|
VIDEO_FRAME_RATE_29_97,
|
||||||
|
VIDEO_FRAME_RATE_30,
|
||||||
|
VIDEO_FRAME_RATE_50,
|
||||||
|
VIDEO_FRAME_RATE_59_94,
|
||||||
|
VIDEO_FRAME_RATE_60
|
||||||
|
} VIDEO_FRAME_RATE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DISPLAY_AR_1_1,
|
||||||
|
DISPLAY_AR_4_3,
|
||||||
|
DISPLAY_AR_14_9,
|
||||||
|
DISPLAY_AR_16_9,
|
||||||
|
DISPLAY_AR_20_9,
|
||||||
|
DISPLAY_AR_RAW
|
||||||
|
} DISPLAY_AR;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DISPLAY_AR_MODE_PANSCAN = 0,
|
||||||
|
DISPLAY_AR_MODE_LETTERBOX,
|
||||||
|
DISPLAY_AR_MODE_NONE,
|
||||||
|
DISPLAY_AR_MODE_PANSCAN2
|
||||||
|
} DISPLAY_AR_MODE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_DB_DR_NEITHER = 0,
|
||||||
|
VIDEO_DB_ON,
|
||||||
|
VIDEO_DB_DR_BOTH
|
||||||
|
} VIDEO_DB_DR;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_PLAY_STILL = 0,
|
||||||
|
VIDEO_PLAY_CLIP,
|
||||||
|
VIDEO_PLAY_TRICK,
|
||||||
|
VIDEO_PLAY_MOTION,
|
||||||
|
VIDEO_PLAY_MOTION_NO_SYNC
|
||||||
|
} VIDEO_PLAY_MODE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_STD_NTSC,
|
||||||
|
VIDEO_STD_SECAM,
|
||||||
|
VIDEO_STD_PAL,
|
||||||
|
VIDEO_STD_480P,
|
||||||
|
VIDEO_STD_576P,
|
||||||
|
VIDEO_STD_720P60,
|
||||||
|
VIDEO_STD_1080I60,
|
||||||
|
VIDEO_STD_720P50,
|
||||||
|
VIDEO_STD_1080I50,
|
||||||
|
VIDEO_STD_1080P30,
|
||||||
|
VIDEO_STD_1080P24,
|
||||||
|
VIDEO_STD_1080P25,
|
||||||
|
VIDEO_STD_1080P50,
|
||||||
|
VIDEO_STD_1080P60,
|
||||||
|
VIDEO_STD_1080P2397,
|
||||||
|
VIDEO_STD_1080P2997,
|
||||||
|
VIDEO_STD_2160P24,
|
||||||
|
VIDEO_STD_2160P25,
|
||||||
|
VIDEO_STD_2160P30,
|
||||||
|
VIDEO_STD_2160P50,
|
||||||
|
VIDEO_STD_AUTO,
|
||||||
|
VIDEO_STD_MAX = VIDEO_STD_AUTO
|
||||||
|
} VIDEO_STD;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_HDMI_CEC_MODE_OFF = 0,
|
||||||
|
VIDEO_HDMI_CEC_MODE_TUNER = 3,
|
||||||
|
VIDEO_HDMI_CEC_MODE_RECORDER = 1
|
||||||
|
} VIDEO_HDMI_CEC_MODE;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
VIDEO_CONTROL_BRIGHTNESS = 0,
|
||||||
|
VIDEO_CONTROL_CONTRAST,
|
||||||
|
VIDEO_CONTROL_SATURATION,
|
||||||
|
VIDEO_CONTROL_HUE,
|
||||||
|
VIDEO_CONTROL_SHARPNESS,
|
||||||
|
VIDEO_CONTROL_BLOCK_NOISE_REDUCTION,
|
||||||
|
VIDEO_CONTROL_MOSQUITO_NOISE_REDUCTION,
|
||||||
|
VIDEO_CONTROL_DIGITAL_CONTOUR_REMOVAL,
|
||||||
|
VIDEO_CONTROL_AUTO_FLESH,
|
||||||
|
VIDEO_CONTROL_GREEN_BOOST,
|
||||||
|
VIDEO_CONTROL_BLUE_BOOST,
|
||||||
|
VIDEO_CONTROL_DYNAMIC_CONTRAST,
|
||||||
|
VIDEO_CONTROL_SCALER_SHARPNESS,
|
||||||
|
VIDEO_CONTROL_ZAPPING_MODE,
|
||||||
|
VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS
|
||||||
|
} VIDEO_CONTROL;
|
||||||
|
|
||||||
|
class cDemux;
|
||||||
|
class cPlayback;
|
||||||
|
|
||||||
|
class cVideo
|
||||||
|
{
|
||||||
|
friend class cPlayback;
|
||||||
|
friend class cDemux;
|
||||||
|
private:
|
||||||
|
/* video device */
|
||||||
|
int fd;
|
||||||
|
unsigned int devnum;
|
||||||
|
/* apparently we cannot query the driver's state
|
||||||
|
=> remember it */
|
||||||
|
video_play_state_t playstate;
|
||||||
|
int /*vidDispMode_t*/ croppingMode;
|
||||||
|
int /*vidOutFmt_t*/ outputformat;
|
||||||
|
|
||||||
|
VIDEO_FORMAT StreamType;
|
||||||
|
VIDEO_DEFINITION VideoDefinition;
|
||||||
|
DISPLAY_AR DisplayAR;
|
||||||
|
VIDEO_PLAY_MODE SyncMode;
|
||||||
|
DISPLAY_AR_MODE ARMode;
|
||||||
|
VIDEO_DB_DR eDbDr;
|
||||||
|
DISPLAY_AR PictureAR;
|
||||||
|
VIDEO_FRAME_RATE FrameRate;
|
||||||
|
int video_standby;
|
||||||
|
int brightness;
|
||||||
|
int contrast;
|
||||||
|
int saturation;
|
||||||
|
int hue;
|
||||||
|
int sharpness;
|
||||||
|
int block_noise_reduction;
|
||||||
|
int mosquito_noise_reduction;
|
||||||
|
int digital_contour_removal;
|
||||||
|
int auto_flesh;
|
||||||
|
int green_boost;
|
||||||
|
int blue_boost;
|
||||||
|
int dynamic_contrast;
|
||||||
|
int scaler_sharpness;
|
||||||
|
int zapping_mode;
|
||||||
|
int blank_mode;
|
||||||
|
|
||||||
|
/* used internally by dmx */
|
||||||
|
int64_t GetPTS(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructor & destructor */
|
||||||
|
cVideo(int mode, void *, void *, unsigned int unit = 0);
|
||||||
|
~cVideo(void);
|
||||||
|
|
||||||
|
/* used internally by playback */
|
||||||
|
void openDevice(void);
|
||||||
|
void closeDevice(void);
|
||||||
|
|
||||||
|
void * GetTVEnc() { return NULL; };
|
||||||
|
void * GetTVEncSD() { return NULL; };
|
||||||
|
|
||||||
|
/* aspect ratio */
|
||||||
|
int getAspectRatio(void);
|
||||||
|
void getPictureInfo(int &width, int &height, int &rate);
|
||||||
|
int setAspectRatio(int aspect, int mode);
|
||||||
|
|
||||||
|
/* cropping mode */
|
||||||
|
int setCroppingMode(int x = 0 /*vidDispMode_t x = VID_DISPMODE_NORM*/);
|
||||||
|
|
||||||
|
/* get play state */
|
||||||
|
int getPlayState(void);
|
||||||
|
|
||||||
|
/* blank on freeze */
|
||||||
|
int getBlank(void);
|
||||||
|
int setBlank(int enable);
|
||||||
|
|
||||||
|
/* change video play state. Parameters are all unused. */
|
||||||
|
int Start(void *PcrChannel = NULL, unsigned short PcrPid = 0, unsigned short VideoPid = 0, void *x = NULL);
|
||||||
|
int Stop(bool blank = true);
|
||||||
|
bool Pause(void);
|
||||||
|
|
||||||
|
/* get video system infos */
|
||||||
|
int GetVideoSystem(void);
|
||||||
|
/* when system = -1 then use current video system */
|
||||||
|
void GetVideoSystemFormatName(cs_vs_format_t* format, int system);
|
||||||
|
|
||||||
|
/* set video_system */
|
||||||
|
int SetVideoSystem(int video_system, bool remember = true);
|
||||||
|
int SetStreamType(VIDEO_FORMAT type);
|
||||||
|
void SetSyncMode(AVSYNC_TYPE mode);
|
||||||
|
bool SetCECMode(VIDEO_HDMI_CEC_MODE);
|
||||||
|
void SetCECAutoView(bool);
|
||||||
|
void SetCECAutoStandby(bool);
|
||||||
|
void ShowPicture(const char * fname);
|
||||||
|
void StopPicture();
|
||||||
|
void Standby(unsigned int bOn);
|
||||||
|
void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600, int startx = 0, int starty = 0, int endx = 1279, int endy = 719);
|
||||||
|
void SetControl(int, int);
|
||||||
|
void setContrast(int val);
|
||||||
|
void SetVideoMode(analog_mode_t mode);
|
||||||
|
void SetDBDR(int) { return; };
|
||||||
|
void SetAudioHandle(void *) { return; };
|
||||||
|
void SetAutoModes(int [VIDEO_STD_MAX]) { return; };
|
||||||
|
int OpenVBI(int) { return 0; };
|
||||||
|
int CloseVBI(void) { return 0; };
|
||||||
|
int StartVBI(unsigned short) { return 0; };
|
||||||
|
int StopVBI(void) { return 0; };
|
||||||
|
void SetDemux(cDemux *dmx);
|
||||||
|
void SetColorFormat(COLOR_FORMAT color_format);
|
||||||
|
bool GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video = true, bool get_osd = false, bool scale_to_video = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __VIDEO_LIB_H__
|
Reference in New Issue
Block a user