diff options
-rw-r--r-- | mp4.c | 430 | ||||
-rw-r--r-- | mp4.h | 75 |
2 files changed, 505 insertions, 0 deletions
@@ -0,0 +1,430 @@ +/* + * mp4.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 - 2015 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#include "mp4.h" +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <cstring> + +#define DEBUGPREFIX mLog->getTimeString() << " MP4:" + +//#define DEBUG + + +uint32_t parseUInt32(uint8_t *p) { + return + (((uint32_t) p[0]) << 24) | + (((uint32_t) p[1]) << 16) | + (((uint32_t) p[2]) << 8) | + ((uint32_t) p[3]) ; +} + +uint64_t parseUInt64 ( uint8_t *p) { + return (((uint64_t)p[0]) << 56) | + (((uint64_t)p[1]) << 48) | + (((uint64_t)p[2]) << 40) | + (((uint64_t)p[3]) << 32) | + (((uint64_t)p[4]) << 24) | + (((uint64_t)p[5]) << 16) | + (((uint64_t)p[6]) << 8) | + ((uint64_t)p[7]); +} + +class cDataHdr { + public: + uint32_t mType; + uint32_t mLocale; + + bool readDataHdr(FILE*); +}; + +bool cDataHdr::readDataHdr(FILE* ifd) { + uint8_t p[16]; + size_t res; + + res = fread (p, 1, 4, ifd); + if (res < 4) + return false; + + mType = parseUInt32(p); + + res = fread (p, 1, 4, ifd); + if (res < 4) + return false; + + mLocale = parseUInt32(p); + return true; +} + +class cBoxHdr { + public: + uint64_t mSize; + string mName; + + uint8_t mVersion; + uint32_t mFlags; + int mHdrLen; + + cBoxHdr(); + void reset(); + bool readHdr(FILE*); + bool readFullHdr(FILE*); +}; + +cBoxHdr::cBoxHdr() : mSize(0), mName(""), mHdrLen(0) { + reset(); +}; + +void cBoxHdr::reset() { + mSize = 0; + mName = ""; + mHdrLen = 0; +} + +bool cBoxHdr::readFullHdr (FILE* ifd) { + uint8_t p[4]; + size_t res; + + res = fread (p, 1, 4, ifd); + mVersion = p[0]; + mFlags = (((uint32_t) p[1]) << 16) | + (((uint32_t) p[2]) << 8) | + ((uint32_t) p[3]) ; + + return true; +} + +bool cBoxHdr::readHdr(FILE* ifd) { + uint8_t p[16]; + size_t res; + + res = fread (p, 1, 8, ifd); + if (res < 8) + return false; + + mSize = parseUInt32(p); + + char f[10]; + snprintf(f, sizeof(f), "%c%c%c%c", p[4], p[5], p[6], p[7]); + mName = f; + mHdrLen = 8; + + if (mSize == 1) { + mHdrLen +=8; + res = fread (p, 1, 8, ifd); + if (res < 8) + return false; + + mSize = 0; + mSize= parseUInt64(p); + } + return true; +} + + +cMp4Metadata::cMp4Metadata(string fn, uint64_t size) : mIsValidFile(false), mHaveTitle(false), + mHaveShortDesc(false), mHaveLongDesc(false), mHaveCovrPos(false), + mFilename(fn), + mFilesize(size), mDuration(0.0), mTitle(""), mTVNetwork ("NA"), + mShortDesc ("NA"), mLongDesc("NA"), mCovrPos(0), mCovrSize(0), mCovrType(0), mCovr(NULL) { + + mLog = Log::getInstance(); + + *(mLog->log())<< DEBUGPREFIX << " mp4 fn= " << fn << " size= " << size << endl; + +} + +cMp4Metadata::~cMp4Metadata() { + if (mCovr != NULL) + delete[] mCovr; +} +void cMp4Metadata::parseMetadata() { + FILE * ifd; + ifd = fopen (mFilename.c_str(), "r"); + + if (ifd == NULL) + return ; + + cBoxHdr m_box; + + if (!m_box.readHdr(ifd)) { + *(mLog->log())<< DEBUGPREFIX << " " << mFilename << " first read header failed" << endl; + fclose (ifd); + return ; + } + + if (m_box.mName != "ftyp") { + *(mLog->log())<< DEBUGPREFIX << " " << mFilename << " is not an MP4 file" << endl; + fclose (ifd); + return; + } + + if (fseek(ifd, (m_box.mSize - m_box.mHdrLen), SEEK_CUR) != 0) { + *(mLog->log())<< DEBUGPREFIX << " mp4 seek-error " << endl; + } + + if (findMetadata(ifd, root)) { + mIsValidFile = true; + *(mLog->log())<< DEBUGPREFIX << " IsValidFile " << endl; + } + fclose (ifd); +} + +bool cMp4Metadata::parseMovieHeader (FILE* ifd, uint64_t s) { + cBoxHdr m_box; + int read_size =4; + m_box.readFullHdr(ifd); // 4Byte + + mDuration=0.0; + uint8_t p[28]; + size_t res; + + if (m_box.mFlags == 1) { + uint32_t ts; + uint64_t dur; + read_size += 28; + res = fread (p, 1, 28, ifd); + if (res < 28) + return false; + + mCreationTime = parseUInt64(p) - 2082844800; + ts = parseUInt32 (&(p[16])); + dur = parseUInt64 (&(p[20])); + + mDuration = dur *1.0/ ts; + *(mLog->log())<< DEBUGPREFIX << " parseMovieHeader 64bit ts= " << ts + << " dur= " << dur << " mDuration= " << mDuration<< endl; + } + else { + uint32_t ts; + uint32_t dur; + uint32_t c; + + read_size += 16; + res = fread (p, 1, 16, ifd); + if (res < 16) + return false; + + c = parseUInt32(p) - 2082844800; + mCreationTime = c; + + ts= parseUInt32(&(p[8])); + dur= parseUInt32(&(p[12])); + + mDuration = dur *1.0/ ts; +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " parseMovieHeader 32bit ts= " << ts + << " dur= " << dur << " mDuration= " << mDuration<< endl; +#endif + } + + + if (fseek(ifd, (s - read_size), SEEK_CUR) != 0) + *(mLog->log()) << DEBUGPREFIX << " mp4 mvhd seek-error " << endl; + + return true; +} + + +string cMp4Metadata::parseString(FILE* ifd, uint64_t s) { + + uint64_t rem= s; + char buf[1024]; + string val = ""; + + while (rem != 0) { + int tgt = (rem > sizeof(buf)) ? sizeof(buf): rem; + rem -= tgt; + int res = fread (buf, 1, tgt, ifd); + if (res < tgt) + return val; + val += string(buf, tgt); + } + + return val; +} + + +bool cMp4Metadata::findMetadata(FILE* ifd, eBoxes parent) { + cBoxHdr m_box; + long int pos = ftell (ifd); + int box_count = 0; + int res; + + // *(mLog->log())<< DEBUGPREFIX << " mp4 findMetadata " << " pos= " << pos << endl; + + if (pos < 0) { + *(mLog->log())<< DEBUGPREFIX << " end reached" << endl; + return false; + } + if (pos >= mFilesize) { + *(mLog->log())<< DEBUGPREFIX << " end reached. pos larger filesize - done" << endl; + return true; + } + while(true) { + if (box_count >= 40) { + *(mLog->log())<< DEBUGPREFIX << " box_count is 40 - done" << endl; + return true; + } + box_count++; + + if ((m_box.mHdrLen !=0) && (m_box.mSize == 0)) { + *(mLog->log())<< DEBUGPREFIX << " last box was of length zero (until eof)" << endl; + return true; + } + pos = ftell (ifd); + + if (!m_box.readHdr(ifd)) { + // done reading. + // *(mLog->log())<< DEBUGPREFIX << " readHdr returns false - done" << endl; + return true; + } + + +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " pos= " << pos << " Name= " << m_box.mName + << " size= " << m_box.mSize << " HdrLen= " << m_box.mHdrLen + << endl; +#endif + + if (m_box.mName == "moov") { +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " moov found" << endl; +#endif + return findMetadata(ifd, moov); + } + + if (m_box.mName == "mvhd") { +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " mvhd found" << endl; +#endif + parseMovieHeader (ifd, (m_box.mSize - m_box.mHdrLen)); + } + + else if (m_box.mName == "udta") { +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " udta found" << endl; +#endif + return findMetadata(ifd, udta); + } + else if (m_box.mName == "meta") { +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " meta found" << endl; +#endif + m_box.readFullHdr(ifd); + return findMetadata(ifd, meta); + } + else if (m_box.mName == "ilst") { +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " ilst found" << endl; +#endif + return findMetadata(ifd, ilst); + } + else if (m_box.mName == "covr") { + return findMetadata(ifd, covr); + } + else if (m_box.mName == "tvnn") { + return findMetadata(ifd, tvnn); + } + else if (m_box.mName == "desc") { + return findMetadata(ifd, desc); + } + else if (m_box.mName == "ldes") { + return findMetadata(ifd, ldes); + } + else if (((uint8_t)m_box.mName[0] == 0xa9) && (m_box.mName.compare(1, 3, "nam") == 0)) { + return findMetadata(ifd, cnam); + } + + else if (m_box.mName == "data") { + cDataHdr d_hdr; + d_hdr.readDataHdr(ifd); + + switch (parent) { + case cnam: + mTitle = parseString(ifd, (m_box.mSize - m_box.mHdrLen)-8); + mHaveTitle= true; +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " title= " << mTitle << endl; +#endif + break; + case tvnn: + mTVNetwork = parseString(ifd, (m_box.mSize - m_box.mHdrLen)-8); +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " tvnn= " << mTVNetwork << endl; +#endif + break; + case desc: + mShortDesc = parseString(ifd, (m_box.mSize - m_box.mHdrLen)-8); + mHaveShortDesc= true; +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " desc= " << mShortDesc << endl; +#endif + break; + case ldes: + mLongDesc = parseString(ifd, (m_box.mSize - m_box.mHdrLen)-8); + mHaveLongDesc= true; +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " ldes " << endl; +#endif + break; + case covr: +#ifdef DEBUG + *(mLog->log())<< DEBUGPREFIX << " covr found" << endl; +#endif + mCovrType = d_hdr.mType; + if ((mCovrType != 14) && (mCovrType != 13)) { + *(mLog->log())<< DEBUGPREFIX << " COVR (type= " << mCovrType << ") is neither PNG nor JPG - Ignore " + << endl; + if (fseek(ifd, (m_box.mSize - m_box.mHdrLen)-8, SEEK_CUR) != 0) + *(mLog->log())<< DEBUGPREFIX << " mp4 seek-error " << endl; + break; + } + mCovrPos = ftell (ifd); + mCovrSize = m_box.mSize - m_box.mHdrLen -8; + mHaveCovrPos= true; + *(mLog->log())<< DEBUGPREFIX << " covr found pos= " << mCovrPos + << " size= " << mCovrSize << endl; + mCovr = new char[mCovrSize]; + res = fread (mCovr, 1, m_box.mSize - m_box.mHdrLen -8, ifd); + if (res < (m_box.mSize - m_box.mHdrLen -8)) { + *(mLog->log())<< DEBUGPREFIX << " covr read ERROR. res= " << res << endl; + } + break; + default: + *(mLog->log())<< DEBUGPREFIX << " unhandled data field " << endl; + if (fseek(ifd, (m_box.mSize - m_box.mHdrLen)-8, SEEK_CUR) != 0) + *(mLog->log())<< DEBUGPREFIX << " mp4 seek-error " << endl; + break; + } + } + + else { + if (fseek(ifd, (m_box.mSize - m_box.mHdrLen), SEEK_CUR) != 0) + *(mLog->log())<< DEBUGPREFIX << " mp4 seek-error " << endl; + + + } + } +} @@ -0,0 +1,75 @@ +/* + * mp4.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 - 2015 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef __MP4_H__ +#define __MP4_H__ + +#include <stdint.h> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include "log.h" + +using namespace std; + +enum eBoxes {root, moov, udta, meta, ilst, covr, cnam, tvnn, ldes, desc}; + +class cMp4Metadata { + public: + cMp4Metadata(string fn, uint64_t); + ~cMp4Metadata(); + + void parseMetadata (); + + protected: + bool findMetadata(FILE*, eBoxes); + + bool parseMovieHeader (FILE* ifd, uint64_t s); + string parseString(FILE*, uint64_t); + + Log* mLog; + public: + bool mIsValidFile; + + bool mHaveTitle; + bool mHaveShortDesc; + bool mHaveLongDesc; + bool mHaveCovrPos; + + string mFilename; + uint64_t mFilesize; + + float mDuration; + uint64_t mCreationTime; + + string mTitle; + string mTVNetwork; + string mShortDesc; + string mLongDesc; + + uint64_t mCovrPos; + uint64_t mCovrSize; + int mCovrType; + char *mCovr; +}; + +#endif |