diff options
Diffstat (limited to 'misc')
-rw-r--r-- | misc/avdetector.cpp | 250 | ||||
-rw-r--r-- | misc/avdetector.h | 82 | ||||
-rw-r--r-- | misc/util.cpp | 44 | ||||
-rw-r--r-- | misc/util.h | 2 |
4 files changed, 346 insertions, 32 deletions
diff --git a/misc/avdetector.cpp b/misc/avdetector.cpp index 8f3bc8f..bb1c9c8 100644 --- a/misc/avdetector.cpp +++ b/misc/avdetector.cpp @@ -6,31 +6,207 @@ */ #include "avdetector.h" -extern "C" { -#include <libavformat/avformat.h> -#include <libavcodec/avcodec.h> +#include <sys/stat.h> + +cAudioVideoDetector::cAudioVideoDetector(const char* Filename) : mResourceType(UPNP_RESOURCE_FILE) { + this->mResource.Filename = Filename; + this->init(); } -using namespace std; +cAudioVideoDetector::cAudioVideoDetector(const cChannel* Channel) : mResourceType(UPNP_RESOURCE_CHANNEL) { + this->mResource.Channel = Channel; + this->init(); +} -int cAudioVideoDetector::detectVideoProperties(cUPnPResource* Resource, const char* Filename){ -// // Register avformat +cAudioVideoDetector::cAudioVideoDetector(const cRecording* Recording) : mResourceType(UPNP_RESOURCE_RECORDING) { + this->mResource.Recording = Recording; + this->init(); +} + +cAudioVideoDetector::~cAudioVideoDetector(){ + this->uninit(); +} + +void cAudioVideoDetector::init(){ + this->mBitrate = 0; + this->mBitsPerSample = 0; + this->mColorDepth = 0; + this->mDLNAProfile = NULL; + this->mDuration = 0; + this->mHeight = 0; + this->mNrAudioChannels = 0; + this->mSampleFrequency = 0; + this->mSize = 0; + this->mWidth = 0; +} + +void cAudioVideoDetector::uninit(){ + this->init(); +} + +int cAudioVideoDetector::detectProperties(){ + int ret = 0; + switch(this->mResourceType){ + case UPNP_RESOURCE_CHANNEL: + ret = this->detectChannelProperties(); + break; + case UPNP_RESOURCE_RECORDING: + ret = this->detectRecordingProperties(); + break; + case UPNP_RESOURCE_FILE: + ret = this->detectFileProperties(); + break; + default: + WARNING("This resource type is not yet implemented."); + ret = -1; + break; + } + + return ret; +} + +int cAudioVideoDetector::detectChannelProperties(){ + this->mBitrate = 0; + this->mBitsPerSample = 0; + this->mColorDepth = 0; + this->mDuration = 0; + this->mHeight = 0; + this->mNrAudioChannels = 0; + this->mSampleFrequency = 0; + this->mSize = (off64_t)-1; + this->mWidth = 0; + + switch(this->mResource.Channel->Vtype()){ + case 0x02: + // MPEG2 Video + this->mDLNAProfile = &DLNA_PROFILE_MPEG_TS_SD_EU_ISO; + break; + case 0x1B: + this->mDLNAProfile = &DLNA_PROFILE_AVC_TS_HD_EU_ISO; + break; + default: + ERROR("Unknown video type %d for channel %s!", this->mResource.Channel->Vtype(), this->mResource.Channel->Name()); + this->mDLNAProfile = NULL; + return -1; + } + + return 0; +} + +int cAudioVideoDetector::detectRecordingProperties(){ + + if(this->mResource.Recording->IsPesRecording()){ + ERROR("Sorry, PES Recordings are not supported"); + return -1; + } + + int ret = 0; + AVFormatContext *FormatCtx = NULL; + + cIndexFile* Index = new cIndexFile(this->mResource.Recording->FileName(), false, this->mResource.Recording->IsPesRecording()); + cFileName* RecFile = new cFileName(this->mResource.Recording->FileName(), false, false, this->mResource.Recording->IsPesRecording()); + if(Index && Index->Ok()){ + this->mDuration = (off64_t) (Index->Last() * AVDETECTOR_TIME_BASE / SecondsToFrames(1, this->mResource.Recording->FramesPerSecond())); + MESSAGE(VERBOSE_METADATA,"Record length: %llds", this->mDuration); + + uint16_t FileNumber = 0; + off_t FileOffset = 0; + + if(Index->Get(Index->Last()-1, &FileNumber, &FileOffset)) + for(int i = 0; i < FileNumber; i++){ + struct stat Stats; + RecFile->SetOffset(i+1); + stat(RecFile->Name(),&Stats); + this->mSize += (off64_t) Stats.st_size; + } + + av_register_all(); + + if(!(ret = av_open_input_file(&FormatCtx, RecFile->Name(), NULL, 0, NULL))){ + if((ret = av_find_stream_info(FormatCtx))<0){ + ERROR("AVDetector: Cannot find the stream information"); + } + else { + if((ret = this->analyseVideo(FormatCtx))<0){ + ERROR("AVDetector: Error while analysing video"); + } + if((ret = this->analyseAudio(FormatCtx))<0){ + ERROR("AVDetector: Error while analysing audio"); + } + } + } + } + else { + ret = -1; + } + + if(ret != 0){ + ERROR("Error occured while detecting properties"); + } + + delete RecFile; + delete Index; + av_free(FormatCtx); + + return ret; +} + +int cAudioVideoDetector::detectFileProperties(){ av_register_all(); + int ret = 0; + AVFormatContext *FormatCtx = NULL; - if(av_open_input_file(&FormatCtx, Filename, NULL, 0, NULL)){ - ERROR("AVDetector: Error while opening file %s", Filename); + + if(av_open_input_file(&FormatCtx, this->mResource.Filename, NULL, 0, NULL)){ + ERROR("AVDetector: Error while opening file %s", this->mResource.Filename); return -1; } + else { + if(av_find_stream_info(FormatCtx)<0){ + ERROR("AVDetector: Cannot find the stream information"); + return -1; + } + else { - if(av_find_stream_info(FormatCtx)<0){ - ERROR("AVDetector: Cannot find the stream information"); - return -1; + if((ret = this->analyseFormat(FormatCtx))<0){ + ERROR("AVDetector: Error while analysing format"); + return ret; + } + if((ret = this->analyseVideo(FormatCtx))<0){ + ERROR("AVDetector: Error while analysing video"); + return ret; + } + if((ret = this->analyseAudio(FormatCtx))<0){ + ERROR("AVDetector: Error while analysing audio"); + return ret; + } + + return 0; + } } +} -#ifdef DEBUG - dump_format(FormatCtx, 0, Filename, 0); -#endif +int cAudioVideoDetector::analyseFormat(AVFormatContext* FormatCtx){ + if(!FormatCtx) return -1; + + this->mSize = FormatCtx->file_size; + this->mDuration = FormatCtx->duration; + + MESSAGE(VERBOSE_METADATA, "Format properties: %lld and %lld Bytes", this->mDuration, this->mSize); + + AVMetadataTag* Tag = NULL; + + while((Tag=av_metadata_get(FormatCtx->metadata,"",Tag,AV_METADATA_IGNORE_SUFFIX))){ + MESSAGE(VERBOSE_METADATA, "%s: %s", Tag->key, Tag->value); + } + + return 0; +} + +int cAudioVideoDetector::analyseVideo(AVFormatContext* FormatCtx) +{ +// // Register avformat unsigned int i; int videoStream = -1; @@ -43,24 +219,52 @@ int cAudioVideoDetector::detectVideoProperties(cUPnPResource* Resource, const ch } if(videoStream == -1){ ERROR("AVDetector: No video stream found"); - return -1; + return 1; } CodecCtx = FormatCtx->streams[videoStream]->codec; AVCodec* Codec = avcodec_find_decoder(CodecCtx->codec_id); - unsigned int width = CodecCtx->width; - unsigned int height = CodecCtx->height; - unsigned int bitrate = CodecCtx->bit_rate; + this->mWidth = CodecCtx->width; + this->mHeight = CodecCtx->height; + this->mBitrate = CodecCtx->bit_rate; + this->mSampleFrequency = CodecCtx->sample_rate; + this->mBitsPerSample = CodecCtx->bits_per_raw_sample; + + // TODO: what's the color depth of the stream + const char* codecName = (Codec)?Codec->name:"unknown"; - MESSAGE(VERBOSE_METADATA, "AVDetector: %s-stream %dx%d at %d bit/s", codecName, width, height, bitrate); + MESSAGE(VERBOSE_METADATA, "AVDetector: %s-stream %dx%d at %d bit/s", codecName, this->mWidth, this->mHeight, this->mBitrate); + return 0; +} - Resource->mBitrate = bitrate; - Resource->mSampleFrequency = CodecCtx->sample_rate; - Resource->mResolution = cString::sprintf("%dx%d", width, height); - +int cAudioVideoDetector::analyseAudio(AVFormatContext* FormatCtx){ + + unsigned int i; int audioStream = -1; + + AVCodecContext *CodecCtx = NULL; + for(i = 0; i < FormatCtx->nb_streams; i++){ + if(FormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO){ + audioStream = i; + break; + } + } + if(audioStream == -1){ + ERROR("AVDetector: No audio stream found"); + return 1; + } + + CodecCtx = FormatCtx->streams[audioStream]->codec; + AVCodec* Codec = avcodec_find_decoder(CodecCtx->codec_id); + + this->mNrAudioChannels = CodecCtx->channels; + + const char* codecName = (Codec)?Codec->name:"unknown"; + + MESSAGE(VERBOSE_METADATA, "AVDetector: %s-stream at %d bit/s", codecName, CodecCtx->bit_rate); return 0; } + diff --git a/misc/avdetector.h b/misc/avdetector.h index b043c59..33b3298 100644 --- a/misc/avdetector.h +++ b/misc/avdetector.h @@ -9,6 +9,13 @@ #define _AVDETECTOR_H #include "../database/object.h" +#include <vdr/recording.h> +#include <vdr/channels.h> + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +} /** * The audio/video detector @@ -18,23 +25,80 @@ * required for determination of a suitable DLNA profile. */ class cAudioVideoDetector { -public: - cAudioVideoDetector(){}; - virtual ~cAudioVideoDetector(){}; +private: + void init(); + void uninit(); + int detectChannelProperties(); + int detectFileProperties(); + int detectRecordingProperties(); /** * Detect video properties * - * This detects video properties of a video stream and stores them in the - * Resource object. + * This detects video properties of a video stream * - * @param Resource the resource, where to save the data - * @param Filename the file, which shall be read * @return returns * - \bc 0, if the detection was successful * - \bc <0, otherwise */ - int detectVideoProperties(cUPnPResource* Resource, const char* Filename); -private: + int analyseVideo(AVFormatContext* FormatCtx); + /** + * Detect audio properties + * + * This detects audio properties of a video or audio stream + * + * @return returns + * - \bc 0, if the detection was successful + * - \bc <0, otherwise + */ + int analyseAudio(AVFormatContext* FormatCtx); + int analyseFormat(AVFormatContext* FormatCtx); + int detectDLNAProfile(); + UPNP_RESOURCE_TYPES mResourceType; + union { + const cChannel* Channel; + const cRecording* Recording; + const char* Filename; + } mResource; + int mWidth; + int mHeight; + int mBitrate; + int mBitsPerSample; + int mColorDepth; + off64_t mDuration; + off64_t mSize; + int mSampleFrequency; + int mNrAudioChannels; + DLNAProfile* mDLNAProfile; +public: + cAudioVideoDetector(const char* Filename); + cAudioVideoDetector(const cChannel* Channel); + cAudioVideoDetector(const cRecording* Recording); + virtual ~cAudioVideoDetector(); + /** + * Detect resource properties of the file + * + * This detects the resource properties of a file. If the returned value + * is 0, no erros occured while detection and the properties are properly + * set. Otherwise, in case of an error, the properties may have + * unpredictable values. + * + * @return returns + * - \bc 0, if the detection was successful + * - \bc <0, otherwise + */ + int detectProperties(); + DLNAProfile* getDLNAProfile() const { return this->mDLNAProfile; } + const char* getContentType() const { return (this->mDLNAProfile) ? this->mDLNAProfile->mime : NULL; } + const char* getProtocolInfo() const; + int getWidth() const { return this->mWidth; } + int getHeight() const { return this->mHeight; } + int getBitrate() const { return this->mBitrate; } + int getBitsPerSample() const { return this->mBitsPerSample; } + int getSampleFrequency() const { return this->mSampleFrequency; } + int getNumberOfAudioChannels() const { return this->mNrAudioChannels; } + int getColorDepth() const { return this->mColorDepth; } + off64_t getFileSize() const { return this->mSize; } + off64_t getDuration() const { return this->mDuration; } }; #endif /* _AVDETECTOR_H */ diff --git a/misc/util.cpp b/misc/util.cpp index 8cfc524..d93d027 100644 --- a/misc/util.cpp +++ b/misc/util.cpp @@ -16,6 +16,50 @@ #include <upnp/ixml.h> #include <arpa/inet.h> #include <iosfwd> +#include <time.h> + +#define DURATION_MAX_STRING_LENGTH 13 // DLNA: 1-5 DIGIT hours : + // 2 DIGIT minutes : + // 2 DIGIT seconds . + // 3 DIGIT fraction + +char* duration(off64_t duration, unsigned int timeBase){ + if (timeBase == 0) timeBase = 1; + + int seconds, minutes, hours, fraction; + + seconds = duration / timeBase; + fraction = duration % timeBase; + minutes = seconds / 60; + seconds %= 60; + hours = minutes / 60; + minutes %= 60; + + char* output = new char[DURATION_MAX_STRING_LENGTH]; + + if(!snprintf( + output, + DURATION_MAX_STRING_LENGTH, + UPNP_DURATION_FORMAT_STRING, + hours, minutes, seconds) + ){ + delete output; + return NULL; + } + else { + if(timeBase > 1 && + !snprintf( + output, + DURATION_MAX_STRING_LENGTH, + "%s" UPNP_DURATION_FRAME_FORMAT, + output, fraction) + ){ + delete output; + return NULL; + } + else return output; + } +} char* substr(const char* str, unsigned int offset, unsigned int length){ if(offset > strlen(str)) return NULL; diff --git a/misc/util.h b/misc/util.h index 6ff2a54..e5bc061 100644 --- a/misc/util.h +++ b/misc/util.h @@ -105,6 +105,8 @@ int ixmlAddProperty(IN IXML_Document* document, IN IXML_Element* node, const cha * @param length the length of the new string */ char* substr(const char* str, unsigned int offset, unsigned int length); + +char* duration(off64_t duration, unsigned int timeBase = 1); #if 0 { #endif |