summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mp4.c430
-rw-r--r--mp4.h75
2 files changed, 505 insertions, 0 deletions
diff --git a/mp4.c b/mp4.c
new file mode 100644
index 0000000..eaab233
--- /dev/null
+++ b/mp4.c
@@ -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;
+
+
+ }
+ }
+}
diff --git a/mp4.h b/mp4.h
new file mode 100644
index 0000000..7a230ea
--- /dev/null
+++ b/mp4.h
@@ -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