Merge remote-tracking branch 'check/next-cc'

untested, probably needs some buildfixing
This commit is contained in:
Stefan Seyfried
2013-12-22 21:25:05 +01:00
67 changed files with 1637 additions and 894 deletions

View File

@@ -9,22 +9,22 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src/zapit/include \
-I$(top_srcdir)/lib/libconfigfile \
@FREETYPE_CFLAGS@ \
@VORBISIDEC_CFLAGS@ \
@HWLIB_CFLAGS@
noinst_LIBRARIES = libneutrino_driver_audiodec.a
if ENABLE_FLAC
FLACdec= flacdec.cpp
endif
libneutrino_driver_audiodec_a_SOURCES = \
basedec.cpp \
cdrdec.cpp \
if ENABLE_FFMPEGDEC
ASOURCES = ffmpegdec.cpp
else
ASOURCES = cdrdec.cpp \
crc.c \
$(FLACdec) \
flacdec.cpp \
mp3dec.cpp \
oggdec.cpp \
tag.c \
wavdec.cpp
# vis.cpp
endif
libneutrino_driver_audiodec_a_SOURCES = \
basedec.cpp \
$(ASOURCES)

View File

@@ -27,13 +27,11 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef ENABLE_FLAC
#include <flacdec.h>
#endif
#include <linux/soundcard.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <OpenThreads/ScopedLock>
#include <driver/audioplay.h> // for ShoutcastCallback()
@@ -42,13 +40,23 @@
#include <zapit/client/zapittools.h>
#include "basedec.h"
#ifdef ENABLE_FFMPEGDEC
#include "ffmpegdec.h"
#else
#include "cdrdec.h"
#include "mp3dec.h"
#include "oggdec.h"
#include "wavdec.h"
#ifdef ENABLE_FLAC
#include "flacdec.h"
#endif
#endif
#include <driver/netfile.h>
unsigned int CBaseDec::mSamplerate=0;
OpenThreads::Mutex CBaseDec::metaDataMutex;
std::map<const std::string,CAudiofile> CBaseDec::metaDataCache;
void ShoutcastCallback(void *arg)
{
@@ -79,57 +87,43 @@ CBaseDec::RetCode CBaseDec::DecoderBase(CAudiofile* const in,
if ( Status == OK )
{
#ifndef ENABLE_FFMPEGDEC
if( in->FileType == CFile::STREAM_AUDIO )
{
if ( fstatus( fp, ShoutcastCallback ) < 0 )
{
fprintf( stderr, "Error adding shoutcast callback: %s",
err_txt );
}
err_txt );
if (ftype(fp, "ogg"))
{
Status = COggDec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
else
{
Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
}
else if( in->FileType == CFile::FILE_MP3)
{
Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
else if( in->FileType == CFile::FILE_OGG )
{
Status = COggDec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
else if( in->FileType == CFile::FILE_WAV )
{
Status = CWavDec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
else if( in->FileType == CFile::FILE_CDR )
{
Status = CCdrDec::getInstance()->Decoder( fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
#ifdef ENABLE_FLAC
else if (in->FileType == CFile::FILE_FLAC)
{
Status = CFlacDec::getInstance()->Decoder(fp, OutputFd, state,
&in->MetaData, t,
secondsToSkip );
}
&in->MetaData, t,
secondsToSkip );
#endif
else
{
@@ -138,55 +132,118 @@ CBaseDec::RetCode CBaseDec::DecoderBase(CAudiofile* const in,
Status = INTERNAL_ERR;
}
#else
CFile::FileType ft = in->FileType;
if( in->FileType == CFile::STREAM_AUDIO )
{
if ( fstatus( fp, ShoutcastCallback ) < 0 )
fprintf( stderr, "Error adding shoutcast callback: %s", err_txt );
if (ftype(fp, "ogg"))
ft = CFile::FILE_OGG;
else if (ftype(fp, "mpeg"))
ft = CFile::FILE_MP3;
else
ft = CFile::FILE_UNKNOWN;
}
else
{
struct stat st;
if (!fstat(fileno(fp), &st))
in->MetaData.filesize = st.st_size;
}
in->MetaData.type = ft;
Status = CFfmpegDec::getInstance()->Decoder(fp, OutputFd, state, &in->MetaData, t, secondsToSkip );
#endif
if ( fclose( fp ) == EOF )
{
fprintf( stderr, "Could not close file %s.\n",
in->Filename.c_str() );
fprintf( stderr, "Could not close file %s.\n", in->Filename.c_str() );
}
}
return Status;
}
bool CBaseDec::LookupMetaData(CAudiofile* const in)
{
bool res = false;
metaDataMutex.lock();
std::map<const std::string,CAudiofile>::const_iterator it = metaDataCache.find(in->Filename);
if (it != metaDataCache.end()) {
*in = it->second;
res = true;
}
metaDataMutex.unlock();
return res;
}
void CBaseDec::CacheMetaData(CAudiofile* const in)
{
metaDataMutex.lock();
// FIXME: This places a limit on the cache size. A LRU scheme would be more appropriate.
if (metaDataCache.size() > 128)
metaDataCache.clear();
metaDataCache[in->Filename] = *in;
metaDataMutex.unlock();
}
void CBaseDec::ClearMetaData()
{
metaDataMutex.lock();
metaDataCache.clear();
metaDataMutex.unlock();
}
bool CBaseDec::GetMetaDataBase(CAudiofile* const in, const bool nice)
{
bool Status = true;
if (in->FileType == CFile::STREAM_AUDIO)
return true;
if (LookupMetaData(in))
return true;
bool Status = true;
#ifndef ENABLE_FFMPEGDEC
if (in->FileType == CFile::FILE_MP3 || in->FileType == CFile::FILE_OGG
|| in->FileType == CFile::FILE_WAV || in->FileType == CFile::FILE_CDR
|| in->FileType == CFile::FILE_WAV || in->FileType == CFile::FILE_CDR
#ifdef ENABLE_FLAC
|| in->FileType == CFile::FILE_FLAC
|| in->FileType == CFile::FILE_FLAC
#endif
)
#endif
{
FILE* fp = fopen( in->Filename.c_str(), "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file %s for meta data reading.\n",
in->Filename.c_str() );
in->Filename.c_str() );
Status = false;
}
else
{
#ifndef ENABLE_FFMPEGDEC
if(in->FileType == CFile::FILE_MP3)
{
Status = CMP3Dec::getInstance()->GetMetaData(fp, nice,
&in->MetaData);
&in->MetaData);
}
else if(in->FileType == CFile::FILE_OGG)
{
Status = COggDec::getInstance()->GetMetaData(fp, nice,
&in->MetaData);
&in->MetaData);
}
else if(in->FileType == CFile::FILE_WAV)
{
Status = CWavDec::getInstance()->GetMetaData(fp, nice,
&in->MetaData);
&in->MetaData);
}
else if(in->FileType == CFile::FILE_CDR)
{
Status = CCdrDec::getInstance()->GetMetaData(fp, nice,
&in->MetaData);
&in->MetaData);
}
#ifdef ENABLE_FLAC
else if (in->FileType == CFile::FILE_FLAC)
@@ -195,68 +252,39 @@ bool CBaseDec::GetMetaDataBase(CAudiofile* const in, const bool nice)
Status = FlacDec.GetMetaData(fp, nice, &in->MetaData);
}
#endif
#else
struct stat st;
if (!fstat(fileno(fp), &st))
in->MetaData.filesize = st.st_size;
in->MetaData.type = in->FileType;
CFfmpegDec d;
Status = d.GetMetaData(fp, nice, &in->MetaData);
#endif
if (Status)
CacheMetaData(in);
if ( fclose( fp ) == EOF )
{
fprintf( stderr, "Could not close file %s.\n",
in->Filename.c_str() );
in->Filename.c_str() );
}
}
}
#ifndef ENABLE_FFMPEGDEC
else
{
fprintf( stderr, "GetMetaDataBase: Filetype is not supported for " );
fprintf( stderr, "meta data reading.\n" );
Status = false;
}
#endif
return Status;
}
bool CBaseDec::SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels)
{
bool crit_error=false;
if (::ioctl(soundfd, SNDCTL_DSP_RESET))
printf("reset failed\n");
if(::ioctl(soundfd, SNDCTL_DSP_SETFMT, &fmt))
printf("setfmt failed\n");
if(::ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels))
printf("channel set failed\n");
if (dsp_speed != mSamplerate)
{
// mute audio to reduce pops when changing samplerate (avia_reset)
//bool was_muted = avs_mute(true);
if (::ioctl(soundfd, SNDCTL_DSP_SPEED, &dsp_speed))
{
printf("speed set failed\n");
crit_error=true;
}
else
{
#if 0
unsigned int rs = 0;
::ioctl(soundfd, SNDCTL_DSP_SPEED, &rs);
mSamplerate = dsp_speed;
// disable iec aka digi out (avia reset enables it again)
//g_Zapit->IecOff();
#endif
}
//usleep(400000);
//if (!was_muted)
// avs_mute(false);
}
//printf("Debug: SNDCTL_DSP_RESET %d / SNDCTL_DSP_SPEED %d / SNDCTL_DSP_CHANNELS %d / SNDCTL_DSP_SETFMT %d\n",
// SNDCTL_DSP_RESET, SNDCTL_DSP_SPEED, SNDCTL_DSP_CHANNELS, SNDCTL_DSP_SETFMT);
return crit_error;
}
bool CBaseDec::avs_mute(bool /*mute*/)
{
return true;
}
void CBaseDec::Init()
{
mSamplerate=0;
}
// vim:ts=4

View File

@@ -32,6 +32,9 @@
#include <stdio.h>
#include <driver/audiofile.h>
#include <driver/audiometadata.h>
#include <map>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
class CBaseDec
{
@@ -51,10 +54,14 @@ public:
static bool GetMetaDataBase(CAudiofile* const in, const bool nice);
static void Init();
CBaseDec(){};
static bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels);
CBaseDec() {};
static OpenThreads::Mutex metaDataMutex;
static std::map<const std::string,CAudiofile> metaDataCache;
static void CacheMetaData(CAudiofile* const in);
static bool LookupMetaData(CAudiofile* const in);
static void ClearMetaData();
private:
static bool avs_mute(bool mute);
unsigned static int mSamplerate;
};

View File

@@ -0,0 +1,543 @@
/*
Neutrino-GUI - DBoxII-Project
Copyright (C) 2004 Zwen
Copyright (C) 2013 martii
ffmpeg audio decoder layer
Homepage: http://forum.tuxbox.org/forum
License: GPL
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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <sstream>
#include <driver/audioplay.h>
#include <zapit/include/audio.h>
#include <eitd/edvbstring.h> // UTF8
#include "ffmpegdec.h"
extern "C" {
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
}
#include <OpenThreads/ScopedLock>
#include <driver/netfile.h>
#include <system/helpers.h>
extern cAudio * audioDecoder;
//#define FFDEC_DEBUG
#define ProgName "FfmpegDec"
#define COVERDIR "/tmp/cover"
static OpenThreads::Mutex mutex;
static int cover_count = 0;
#if 0
static void log_callback(void *, int, const char *format, va_list ap)
{
vfprintf(stderr, format, ap);
}
#endif
CFfmpegDec::CFfmpegDec(void)
{
//av_log_set_callback(log_callback);
meta_data_valid = false;
buffer_size = 0x1000;
buffer = NULL;
avc = NULL;
avcodec_register_all();
av_register_all();
}
CFfmpegDec::~CFfmpegDec(void)
{
DeInit();
}
int CFfmpegDec::Read(void *buf, size_t buf_size)
{
return (int) fread(buf, 1, buf_size, (FILE *) in);
}
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
return ((CFfmpegDec *) opaque)->Read(buf, (size_t) buf_size);
}
int64_t CFfmpegDec::Seek(int64_t offset, int whence)
{
if (whence == AVSEEK_SIZE)
return (int64_t) -1;
fseek((FILE *) in, (long) offset, whence);
return (int64_t) ftell((FILE *) in);
}
static int64_t seek_packet(void *opaque, int64_t offset, int whence)
{
return ((CFfmpegDec *) opaque)->Seek(offset, whence);
}
bool CFfmpegDec::Init(void *_in, const CFile::FileType /* ft */)
{
title = "";
artist = "";
date = "";
album = "";
genre = "";
type_info = "";
total_time = 0;
bitrate = 0;
#ifdef FFDEC_DEBUG
av_log_set_level(AV_LOG_DEBUG);
#endif
AVIOContext *avioc = NULL;
in = _in;
is_stream = fseek((FILE *)in, 0, SEEK_SET);
buffer = (unsigned char *) av_malloc(buffer_size);
if (!buffer)
return false;
avc = avformat_alloc_context();
if (!avc) {
av_freep(&buffer);
return false;
}
if (is_stream)
avc->probesize = 128 * 1024;
av_opt_set_int(avc, "analyzeduration", 1000000, 0);
avioc = avio_alloc_context (buffer, buffer_size, 0, this, read_packet, NULL, seek_packet);
if (!avioc) {
av_freep(&buffer);
avformat_free_context(avc);
return false;
}
avc->pb = avioc;
avc->flags |= AVFMT_FLAG_CUSTOM_IO|AVFMT_FLAG_KEEP_SIDE_DATA;
AVInputFormat *input_format = NULL;
#if 0
switch (ft) {
case CFile::FILE_OGG:
input_format = av_find_input_format("ogg");
break;
case CFile::FILE_MP3:
input_format = av_find_input_format("mp3");
break;
case CFile::FILE_WAV:
input_format = av_find_input_format("wav");
break;
case CFile::FILE_FLAC:
input_format = av_find_input_format("flac");
break;
case CFile::FILE_AAC:
input_format = av_find_input_format("aac");
break;
default:
break;
}
#endif
int r = avformat_open_input(&avc, "", input_format, NULL);
if (r) {
char buf[200]; av_strerror(r, buf, sizeof(buf));
fprintf(stderr, "%d %s %d: %s\n", __LINE__, __func__,r,buf);
if (avioc)
av_freep(avioc);
if (avc) {
avformat_close_input(&avc);
avformat_free_context(avc);
avc = NULL;
}
return false;
}
return true;
}
void CFfmpegDec::DeInit(void)
{
if (avc) {
avformat_close_input(&avc);
#if 0
av_freep(&avc->pb);
#endif
avformat_free_context(avc);
avc = NULL;
}
// if (buffer)
// av_freep(&buffer);
in = NULL;
}
CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, CAudioMetaData* _meta_data, time_t* time_played, unsigned int* secondsToSkip)
{
in = _in;
RetCode Status=OK;
is_stream = fseek((FILE *)in, 0, SEEK_SET);
if (!SetMetaData((FILE *)in, _meta_data, true)) {
DeInit();
Status=DATA_ERR;
return Status;
}
AVCodecContext *c = avc->streams[best_stream]->codec;
mutex.lock();
int r = avcodec_open2(c, codec, NULL);
mutex.unlock();
if (r)
{
DeInit();
Status=DATA_ERR;
return Status;
}
SwrContext *swr = swr_alloc();
if (!swr) {
mutex.lock();
avcodec_close(c);
mutex.unlock();
DeInit();
Status=DATA_ERR;
return Status;
}
mSampleRate = samplerate;
mChannels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
#if __BYTE_ORDER == __LITTLE_ENDIAN
audioDecoder->PrepareClipPlay(mChannels, mSampleRate, 16, 1);
#else
audioDecoder->PrepareClipPlay(mChannels, mSampleRate, 16, 0);
#endif
AVFrame *frame = NULL;
AVPacket rpacket;
av_init_packet(&rpacket);
av_opt_set_int(swr, "in_channel_layout", c->channel_layout, 0);
//av_opt_set_int(swr, "out_channel_layout", c->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr, "in_sample_rate", c->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", c->sample_rate, 0);
av_opt_set_int(swr, "in_sample_fmt", c->sample_fmt, 0);
av_opt_set_int(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
swr_init(swr);
uint8_t *outbuf = NULL;
int outsamples = 0;
int outsamples_max = 0;
int64_t pts = 0, start_pts = 0, next_skip_pts = 0;
uint64_t skip = 0;
int seek_flags = 0;
do
{
int actSecsToSkip = *secondsToSkip;
if (!is_stream && (actSecsToSkip || *state==FF || *state==REV) && avc->streams[best_stream]->time_base.num) {
if (!next_skip_pts || pts >= next_skip_pts) {
skip = avc->streams[best_stream]->time_base.den / avc->streams[best_stream]->time_base.num;
if (actSecsToSkip)
skip *= actSecsToSkip;
if (*state == REV) {
next_skip_pts = pts - skip;
pts = next_skip_pts - skip/4;
seek_flags = AVSEEK_FLAG_BACKWARD;
if (pts < start_pts) {
pts = start_pts;
*state = PAUSE;
}
} else {
pts += skip;
next_skip_pts = pts + skip/4;
seek_flags = 0;
}
av_seek_frame(avc, best_stream, pts, seek_flags);
skip = 0;
// if a custom value was set we only jump once
if (actSecsToSkip != 0) {
*state=PLAY;
*secondsToSkip = 0;
}
}
}
while(*state==PAUSE && !is_stream)
usleep(10000);
if (av_read_frame(avc, &rpacket)) {
Status=DATA_ERR;
break;
}
if (rpacket.stream_index != best_stream) {
av_free_packet(&rpacket);
continue;
}
AVPacket packet = rpacket;
while (packet.size > 0) {
int got_frame = 0;
if (!frame) {
if (!(frame = avcodec_alloc_frame())) {
Status=DATA_ERR;
break;
}
} else
avcodec_get_frame_defaults(frame);
int len = avcodec_decode_audio4(c, frame, &got_frame, &packet);
if (len < 0) {
// skip frame
packet.size = 0;
avcodec_flush_buffers(c);
mutex.lock();
avcodec_close(c);
avcodec_open2(c, codec, NULL);
mutex.unlock();
continue;
}
if (got_frame && *state!=PAUSE) {
int out_samples;
outsamples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + frame->nb_samples,
c->sample_rate, c->sample_rate, AV_ROUND_UP);
if (outsamples > outsamples_max) {
av_free(outbuf);
if (av_samples_alloc(&outbuf, &out_samples, mChannels, //c->channels,
frame->nb_samples, AV_SAMPLE_FMT_S16, 1) < 0) {
Status=WRITE_ERR;
packet.size = 0;
break;
}
outsamples_max = outsamples;
}
outsamples = swr_convert(swr, &outbuf, outsamples,
(const uint8_t **) &frame->data[0], frame->nb_samples);
int outbuf_size = av_samples_get_buffer_size(&out_samples, mChannels, //c->channels,
outsamples, AV_SAMPLE_FMT_S16, 1);
if(audioDecoder->WriteClip((unsigned char*) outbuf, outbuf_size) != outbuf_size)
{
fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno));
Status=WRITE_ERR;
}
pts = av_frame_get_best_effort_timestamp(frame);
if (!start_pts)
start_pts = pts;
}
packet.size -= len;
packet.data += len;
}
if (time_played && avc->streams[best_stream]->time_base.den)
*time_played = (pts - start_pts) * avc->streams[best_stream]->time_base.num / avc->streams[best_stream]->time_base.den;
av_free_packet(&rpacket);
} while (*state!=STOP_REQ && Status==OK);
audioDecoder->StopClip();
meta_data_valid = false;
swr_free(&swr);
av_free(outbuf);
av_free_packet(&rpacket);
avcodec_free_frame(&frame);
avcodec_close(c);
//av_free(avcc);
DeInit();
if (_meta_data->cover_temporary && !_meta_data->cover.empty()) {
_meta_data->cover_temporary = false;
unlink(_meta_data->cover.c_str());
}
return Status;
}
bool CFfmpegDec::GetMetaData(FILE *_in, const bool /*nice*/, CAudioMetaData* m)
{
return SetMetaData(_in, m);
}
CFfmpegDec* CFfmpegDec::getInstance()
{
static CFfmpegDec* FfmpegDec = NULL;
if(FfmpegDec == NULL)
{
FfmpegDec = new CFfmpegDec();
}
return FfmpegDec;
}
bool CFfmpegDec::SetMetaData(FILE *_in, CAudioMetaData* m, bool save_cover)
{
if (!meta_data_valid)
{
if (!Init(_in, (const CFile::FileType) m->type))
return false;
mutex.lock();
int ret = avformat_find_stream_info(avc, NULL);
if (ret < 0) {
mutex.unlock();
DeInit();
printf("avformat_find_stream_info error %d\n", ret);
return false;
}
mutex.unlock();
if (!is_stream) {
GetMeta(avc->metadata);
for(unsigned int i = 0; i < avc->nb_streams; i++) {
if (avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
GetMeta(avc->streams[i]->metadata);
}
}
//fseek((FILE *) in, 0, SEEK_SET);
#ifdef FFDEC_DEBUG
av_dump_format(avc, 0, "", 0);
#endif
codec = NULL;
best_stream = av_find_best_stream(avc, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
if (best_stream < 0) {
DeInit();
return false;
}
if (!codec)
codec = avcodec_find_decoder(avc->streams[best_stream]->codec->codec_id);
samplerate = avc->streams[best_stream]->codec->sample_rate;
mChannels = av_get_channel_layout_nb_channels(avc->streams[best_stream]->codec->channel_layout);
std::stringstream ss;
if (codec && codec->long_name != NULL)
type_info = codec->long_name;
else if(codec && codec->name != NULL)
type_info = codec->name;
else
type_info = "unknown";
ss << " / " << mChannels << " channel" << ( mChannels > 1 ? "s" : "");
type_info += ss.str();
bitrate = 0;
total_time = 0;
if (avc->duration != int64_t(AV_NOPTS_VALUE))
total_time = avc->duration / int64_t(AV_TIME_BASE);
printf("CFfmpegDec: format %s (%s) duration %ld\n", avc->iformat->name, type_info.c_str(), total_time);
for(unsigned int i = 0; i < avc->nb_streams; i++) {
if (avc->streams[i]->codec->bit_rate > 0)
bitrate += avc->streams[i]->codec->bit_rate;
if (save_cover && (avc->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
mkdir(COVERDIR, 0755);
std::string cover(COVERDIR);
cover += "/" + to_string(cover_count++) + ".jpg";
FILE *f = fopen(cover.c_str(), "wb");
if (f) {
AVPacket *pkt = &avc->streams[i]->attached_pic;
fwrite(pkt->data, pkt->size, 1, f);
fclose(f);
m->cover = cover;
m->cover_temporary = true;
}
}
}
if(!total_time && m->filesize && bitrate)
total_time = 8 * m->filesize / bitrate;
meta_data_valid = true;
m->changed = true;
}
if (!is_stream) {
m->title = title;
m->artist = artist;
m->date = date;
m->album = album;
m->genre = genre;
m->total_time = total_time;
}
m->type_info = type_info;
// make sure bitrate is set to prevent refresh metadata from gui, its a flag
m->bitrate = bitrate ? bitrate : 1;
m->samplerate = samplerate;
return true;
}
void CFfmpegDec::GetMeta(AVDictionary * metadata)
{
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if(!strcasecmp(tag->key,"Title")) {
if (title.empty()) {
title = isUTF8(tag->value) ? tag->value : convertLatin1UTF8(tag->value);
title = trim(title);
}
continue;
}
if(!strcasecmp(tag->key,"Artist")) {
if (artist.empty()) {
artist = isUTF8(tag->value) ? tag->value : convertLatin1UTF8(tag->value);
artist = trim(artist);
}
continue;
}
if(!strcasecmp(tag->key,"Year")) {
if (date.empty()) {
date = isUTF8(tag->value) ? tag->value : convertLatin1UTF8(tag->value);
date = trim(date);
}
continue;
}
if(!strcasecmp(tag->key,"Album")) {
if (album.empty()) {
album = isUTF8(tag->value) ? tag->value : convertLatin1UTF8(tag->value);
album = trim(album);
}
continue;
}
if(!strcasecmp(tag->key,"Genre")) {
if (genre.empty()) {
genre = isUTF8(tag->value) ? tag->value : convertLatin1UTF8(tag->value);
genre = trim(genre);
}
continue;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
Neutrino-GUI - DBoxII-Project
Copyright (C) 2004 Zwen
Copyright (C) 2013 martii
ffmpeg audio decoder layer
Homepage: http://forum.tuxbox.org/forum
License: GPL
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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __FFMPEG_DEC__
#define __FFMPEG_DEC__
#include <stdio.h>
#include <stdint.h>
#include <vector>
#include "basedec.h"
# if __WORDSIZE == 64
# define UINT64_C(c) c ## UL
# else
# define UINT64_C(c) c ## ULL
# endif
extern "C" {
#include <libavformat/avformat.h>
}
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
class CFfmpegDec : public CBaseDec
{
private:
bool meta_data_valid;
bool is_stream;
int mChannels;
int mSampleRate;
size_t buffer_size;
unsigned char *buffer;
AVFormatContext *avc;
AVCodec *codec;
int best_stream;
void *in;
bool Init(void *_in, const CFile::FileType ft);
void DeInit(void);
void GetMeta(AVDictionary * metadata);
std::string title;
std::string artist;
std::string date;
std::string album;
std::string genre;
std::string type_info;
time_t total_time;
int bitrate;
int samplerate;
public:
static CFfmpegDec* getInstance();
virtual RetCode Decoder(FILE *, int, State*, CAudioMetaData* m, time_t* t, unsigned int* secondsToSkip);
bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m);
CFfmpegDec();
~CFfmpegDec();
int Read(void *buf, size_t buf_size);
int64_t Seek(int64_t offset, int whence);
protected:
virtual bool SetMetaData(FILE* in, CAudioMetaData* m, bool save_cover = false);
};
#endif

View File

@@ -396,6 +396,7 @@ CBaseDec::RetCode CMP3Dec::Decoder(FILE *InputFp, const int /*OutputFd*/,
#endif
signed short ll, rr;
SaveCover(InputFp, meta_data);
/* First the structures used by libmad must be initialized. */
mad_stream_init(&Stream);
mad_frame_init(&Frame);
@@ -925,6 +926,8 @@ q * next mad_frame_decode() invocation. (See the comments marked
// fprintf(stderr,"%s: %lu frames decoded (%s).\n",
// ProgName,FrameCount,Buffer);
}
if (!meta_data->cover.empty())
unlink(meta_data->cover.c_str());
/* That's the end of the world (in the H. G. Wells way). */
return Status;
@@ -947,7 +950,7 @@ bool CMP3Dec::GetMetaData(FILE* in, const bool nice, CAudioMetaData* const m)
{
res = GetMP3Info(in, nice, m);
GetID3(in, m);
SaveCover(in, m);
//SaveCover(in, m);
}
else
{
@@ -1396,6 +1399,7 @@ bool CMP3Dec::SaveCover(FILE * in, CAudioMetaData * const m)
fwrite (data , 1 , size , pFile );
fclose (pFile);
m->cover = cover.str().c_str();
m->changed = true;
}
}
break;

View File

@@ -34,6 +34,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <unistd.h>
#include <driver/audiometadata.h>
// constructor
@@ -42,6 +43,13 @@ CAudioMetaData::CAudioMetaData()
clear();
}
// destructor
CAudioMetaData::~CAudioMetaData()
{
if (cover_temporary && !cover.empty())
unlink(cover.c_str());
}
// copy constructor
CAudioMetaData::CAudioMetaData( const CAudioMetaData& src )
: type( src.type ), type_info( src.type_info ),
@@ -52,6 +60,7 @@ CAudioMetaData::CAudioMetaData( const CAudioMetaData& src )
hasInfoOrXingTag( src.hasInfoOrXingTag ), artist( src.artist ),
title( src.title ), album( src.album ), sc_station( src.sc_station ),
date( src.date ), genre( src.genre ), track( src.track ),cover(src.cover),
cover_temporary( false ),
changed( src.changed )
{
}
@@ -84,11 +93,13 @@ void CAudioMetaData::operator=( const CAudioMetaData& src )
cover = src.cover;
sc_station = src.sc_station;
changed = src.changed;
changed = src.changed;
cover_temporary = false;
}
void CAudioMetaData::clear()
{
type=NONE;
type=0;
type_info.clear();
filesize=0;
bitrate=0;
@@ -105,6 +116,9 @@ void CAudioMetaData::clear()
date.clear();
genre.clear();
track.clear();
if (cover_temporary && !cover.empty())
unlink(cover.c_str());
cover.clear();
cover_temporary=false;
changed=false;
}

View File

@@ -44,6 +44,8 @@ class CAudioMetaData
public:
// constructor
CAudioMetaData();
// destructor
~CAudioMetaData();
// copy constructor
CAudioMetaData( const CAudioMetaData& src );
// assignment operator
@@ -59,7 +61,7 @@ public:
WAV,
FLAC
};
AudioType type;
int type;
std::string type_info;
long filesize; /* filesize in bits (for mp3: without leading id3 tag) */
@@ -85,6 +87,7 @@ public:
std::string genre;
std::string track;
std::string cover;
bool cover_temporary;
bool changed;
};
#endif /* __AUDIO_METADATA__ */

View File

@@ -42,6 +42,7 @@
#include <neutrino.h>
#include <driver/audioplay.h>
#include <driver/netfile.h>
#include <eitd/edvbstring.h> // UTF8
void CAudioPlayer::stop()
{
@@ -163,30 +164,37 @@ void CAudioPlayer::init()
CBaseDec::Init();
state = CBaseDec::STOP;
thrPlay = 0;
m_SecondsToSkip = 0;
}
void CAudioPlayer::sc_callback(void *arg)
{
bool changed=false;
CSTATE *stat = (CSTATE*)arg;
if(m_Audiofile.MetaData.artist != stat->artist)
const std::string artist = isUTF8(stat->artist) ? stat->artist : convertLatin1UTF8(stat->artist);
const std::string title = isUTF8(stat->title) ? stat->title : convertLatin1UTF8(stat->title);
const std::string station = isUTF8(stat->station) ? stat->station : convertLatin1UTF8(stat->station);
const std::string genre = isUTF8(stat->genre) ? stat->artist : convertLatin1UTF8(stat->genre);
if(m_Audiofile.MetaData.artist != artist)
{
m_Audiofile.MetaData.artist = stat->artist;
m_Audiofile.MetaData.artist = artist;
changed=true;
}
if (m_Audiofile.MetaData.title != stat->title)
if (m_Audiofile.MetaData.title != title)
{
m_Audiofile.MetaData.title = stat->title;
m_Audiofile.MetaData.title = title;
changed=true;
}
if (m_Audiofile.MetaData.sc_station != stat->station)
if (m_Audiofile.MetaData.sc_station != station)
{
m_Audiofile.MetaData.sc_station = stat->station;
m_Audiofile.MetaData.sc_station = station;
changed=true;
}
if (m_Audiofile.MetaData.genre != stat->genre)
if (m_Audiofile.MetaData.genre != genre)
{
m_Audiofile.MetaData.genre = stat->genre;
m_Audiofile.MetaData.genre = genre;
changed=true;
}
if(changed)
@@ -203,6 +211,7 @@ void CAudioPlayer::clearFileData()
m_Audiofile.clear();
m_played_time=0;
m_sc_buffered=0;
m_SecondsToSkip = 0;
}
CAudioMetaData CAudioPlayer::getMetaData()

View File

@@ -37,9 +37,7 @@ class CAudioPlayer
private:
time_t m_played_time;
int m_sc_buffered;
FILE *soundfd;
pthread_t thrPlay;
FILE *fp;
CBaseDec::State state;
static void* PlayThread(void*);
void clearFileData();
@@ -47,7 +45,6 @@ private:
protected:
CAudiofile m_Audiofile;
bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels);
public:
static CAudioPlayer* getInstance();

View File

@@ -42,9 +42,9 @@
/* ATTENTION: the array file_extension_list MUST BE SORTED ASCENDING (cf. sort, man bsearch) - otherwise bsearch will not work correctly! */
const char * const file_extension_list[] =
{
"asf", "avi", "bmp", "cdr", "crw",
"flac", "gif", "imu", "jpeg", "jpg",
"m2a", "m3u", "mkv", "mp2", "mp3",
"aac", "asf", "avi", "bmp", "cdr", "crw",
"dts", "flac", "gif", "imu", "jpeg", "jpg",
"m2a", "m3u", "m4a", "mkv", "mp2", "mp3",
"mpa", "ogg", "pls", "png", "sh",
"txt", "url", "wav", "xml"
};
@@ -52,9 +52,9 @@ const char * const file_extension_list[] =
const CFile::FileType file_type_list[] =
{
CFile::FILE_ASF , CFile::FILE_AVI , CFile::FILE_PICTURE , CFile::FILE_CDR , CFile::FILE_PICTURE ,
CFile::FILE_FLAC , CFile::FILE_PICTURE , CFile::STREAM_PICTURE, CFile::FILE_PICTURE , CFile::FILE_PICTURE ,
CFile::FILE_MP3 , CFile::FILE_PLAYLIST , CFile::FILE_MKV , CFile::FILE_MP3 , CFile::FILE_MP3 ,
CFile::FILE_AAC , CFile::FILE_ASF , CFile::FILE_AVI , CFile::FILE_PICTURE , CFile::FILE_CDR , CFile::FILE_PICTURE ,
CFile::FILE_WAV , CFile::FILE_FLAC , CFile::FILE_PICTURE , CFile::STREAM_PICTURE, CFile::FILE_PICTURE , CFile::FILE_PICTURE ,
CFile::FILE_MP3 , CFile::FILE_PLAYLIST , CFile::FILE_AAC , CFile::FILE_MKV , CFile::FILE_MP3 , CFile::FILE_MP3 ,
CFile::FILE_MP3 , CFile::FILE_OGG , CFile::FILE_PLAYLIST, CFile::FILE_PICTURE , CFile::FILE_TEXT ,
CFile::FILE_TEXT , CFile::STREAM_AUDIO , CFile::FILE_WAV , CFile::FILE_XML
};

View File

@@ -56,6 +56,7 @@ public:
enum FileType
{
FILE_UNKNOWN = 0,
FILE_AAC,
FILE_AVI,
FILE_ASF,
FILE_DIR,

View File

@@ -543,7 +543,7 @@ int request_file(URL *url)
#define getHeaderVal(a,b) { \
char *_ptr; \
_ptr = strstr(header, a); \
_ptr = strcasestr(header, a); \
if(_ptr) \
{ \
_ptr = strchr(_ptr, ':'); \
@@ -553,7 +553,7 @@ int request_file(URL *url)
#define getHeaderStr(a,b) { \
char *_ptr; \
_ptr = strstr(header, a); \
_ptr = strcasestr(header, a); \
if(_ptr) \
{ \
unsigned int i; \
@@ -570,14 +570,13 @@ void readln(int fd, char *buf)
*buf = 0;
}
int parse_response(URL *url, void *opt, CSTATE *state)
int parse_response(URL *url, void * /*opt*/, CSTATE *state)
{
char header[2049], /*str[255]*/ str[2048]; // combined with 2nd local str from id3 part
char *ptr, chr=0, lastchr=0;
int hlen = 0, response;
int meta_interval = 0, rval;
int fd = url->fd;
ID3 *id3 = (ID3*)opt;
memset(header, 0, 2048);
ptr = header;
@@ -671,6 +670,8 @@ int parse_response(URL *url, void *opt, CSTATE *state)
getHeaderStr("icy-url:", state->station_url);
getHeaderVal("icy-br:", state->bitrate);
}
#if 0
ID3 *id3 = (ID3*)opt;
/********************* dirty hack **********************/
/* we parse the stream header sent by the server and */
/* build based on this information an arteficial id3 */
@@ -755,6 +756,7 @@ int parse_response(URL *url, void *opt, CSTATE *state)
id3->len = 14 + cnt;
}
#endif
return meta_interval;
}

View File

@@ -50,7 +50,7 @@ class CPictureViewer
COLOR=2
};
CPictureViewer();
~CPictureViewer();
virtual~CPictureViewer();
bool ShowImage(const std::string & filename, bool unscaled=false);
bool DecodeImage(const std::string & name, bool showBusySign=false, bool unscaled=false);
bool DisplayNextImage();

View File

@@ -38,6 +38,8 @@
#include <driver/scanepg.h>
#include <driver/record.h>
#define EPG_RESCAN_TIME (24*60*60)
extern CBouquetList * bouquetList;
extern CBouquetList * TVfavList;
@@ -45,6 +47,8 @@ CEpgScan::CEpgScan()
{
current_mode = 0;
standby = false;
rescan_timer = 0;
scan_in_progress = false;
Clear();
}
@@ -108,7 +112,6 @@ void CEpgScan::AddTransponders()
current_mode = g_settings.epg_scan;
Clear();
}
/* TODO: add interval check to clear scanned ? */
int mode = CNeutrinoApp::getInstance()->GetChannelMode();
if ((g_settings.epg_scan == 1) || (mode == LIST_MODE_FAV)) {
@@ -129,45 +132,69 @@ void CEpgScan::AddTransponders()
}
}
void CEpgScan::StartStandby()
void CEpgScan::Start(bool instandby)
{
if (!g_settings.epg_scan)
return;
if (!standby && (CFEManager::getInstance()->getEnabledCount() <= 1))
return;
live_channel_id = CZapit::getInstance()->GetCurrentChannelID();
AddTransponders();
INFO("starting standby scan, scan map size: %d", scanmap.size());
if (!scanmap.empty()) {
standby = true;
standby = instandby;
//g_RCInput->killTimer(rescan_timer);
INFO("starting %s scan, scanning %d, scan map size: %d", standby ? "standby" : "live", scan_in_progress, scanmap.size());
if (standby || !scan_in_progress)
Next();
}
}
void CEpgScan::StopStandby()
void CEpgScan::Stop()
{
if (!g_settings.epg_scan)
return;
INFO("stopping standby scan...");
standby = false;
CZapit::getInstance()->SetCurrentChannelID(live_channel_id);
INFO("stopping %s scan...", standby ? "standby" : "live");
if (standby) {
standby = false;
CZapit::getInstance()->SetCurrentChannelID(live_channel_id);
}
}
int CEpgScan::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data)
{
if ((msg == NeutrinoMessages::EVT_TIMER) && (data == rescan_timer)) {
INFO("rescan timer in %s mode, scanning %d", standby ? "standby" : "live", scan_in_progress);
scanned.clear();
Clear();
g_RCInput->killTimer(rescan_timer);
if (standby || (CFEManager::getInstance()->getEnabledCount() > 1)) {
if (standby)
g_Zapit->setStandby(false);
Start(standby);
}
return messages_return::handled;
}
if (!g_settings.epg_scan || (!standby && (CFEManager::getInstance()->getEnabledCount() <= 1))) {
if ((msg == NeutrinoMessages::EVT_EIT_COMPLETE) || (msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE))
return messages_return::handled;
return messages_return::unhandled;
int ret = messages_return::handled;
if (msg == NeutrinoMessages::EVT_EIT_COMPLETE)
scan_in_progress = false;
else if (msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE)
scan_in_progress = true;
else
ret = messages_return::unhandled;
return ret;
}
CZapitChannel * newchan;
if(msg == NeutrinoMessages::EVT_ZAP_COMPLETE) {
if (msg == NeutrinoMessages::EVT_ZAP_COMPLETE) {
/* live channel changed, block scan channel change by timer */
scan_in_progress = true;
AddTransponders();
INFO("EVT_ZAP_COMPLETE, scan map size: %d\n", scanmap.size());
return messages_return::handled;
}
else if (msg == NeutrinoMessages::EVT_EIT_COMPLETE) {
scan_in_progress = false;
t_channel_id chid = *(t_channel_id *)data;
newchan = CServiceManager::getInstance()->FindChannel(chid);
if (newchan) {
@@ -180,6 +207,7 @@ int CEpgScan::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data)
return messages_return::handled;
}
else if (msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE) {
scan_in_progress = true;
t_channel_id chid = *(t_channel_id *)data;
INFO("EVT_BACK_ZAP_COMPLETE [" PRINTF_CHANNEL_ID_TYPE "]", chid);
if (next_chid) {
@@ -208,10 +236,14 @@ void CEpgScan::EnterStandby()
{
if (standby) {
CZapit::getInstance()->SetCurrentChannelID(live_channel_id);
CZapit::getInstance()->EnablePlayback(true);
//CZapit::getInstance()->EnablePlayback(true);
g_Zapit->setStandby(true);
g_Sectionsd->setPauseScanning(true);
}
//g_RCInput->killTimer(rescan_timer);
if (rescan_timer == 0)
rescan_timer = g_RCInput->addTimer(EPG_RESCAN_TIME*1000ULL*1000ULL, true);
INFO("rescan timer id %d", rescan_timer);
}
void CEpgScan::Next()

View File

@@ -38,6 +38,9 @@ class CEpgScan
t_channel_id next_chid;
t_channel_id live_channel_id;
std::set<transponder_id_t> scanned;
uint32_t rescan_timer;
bool scan_in_progress;
void AddBouquet(CChannelList * clist);
bool AddFavorites();
void AddTransponders();
@@ -51,8 +54,8 @@ class CEpgScan
int handleMsg(const neutrino_msg_t _msg, neutrino_msg_data_t data);
void Next();
void Clear();
void StartStandby();
void StopStandby();
void Start(bool instandby = false);
void Stop();
bool Running();
};