mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-09-17 02:13:41 +02:00
Merge remote-tracking branch 'check/next-cc'
untested, probably needs some buildfixing
This commit is contained in:
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
543
src/driver/audiodec/ffmpegdec.cpp
Normal file
543
src/driver/audiodec/ffmpegdec.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
86
src/driver/audiodec/ffmpegdec.h
Normal file
86
src/driver/audiodec/ffmpegdec.h
Normal 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
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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()
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
};
|
||||
|
@@ -56,6 +56,7 @@ public:
|
||||
enum FileType
|
||||
{
|
||||
FILE_UNKNOWN = 0,
|
||||
FILE_AAC,
|
||||
FILE_AVI,
|
||||
FILE_ASF,
|
||||
FILE_DIR,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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()
|
||||
|
@@ -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();
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user