-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathmp4filehandler.cpp
More file actions
175 lines (136 loc) · 4.84 KB
/
Copy pathmp4filehandler.cpp
File metadata and controls
175 lines (136 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "mp4filehandler.h"
#include <QDebug>
#ifndef NO_LIBMP4V2
#include <mp4v2/mp4v2.h>
#endif // NO_LIBMP4V2
Mp4FileHandler::Mp4FileHandler()
{
}
Mp4FileHandler::~Mp4FileHandler()
{
}
FileHandlerInterface::Status Mp4FileHandler::setFile(const QString filePath)
{
m_file = filePath;
return SUCCESS;
}
ChapterItem *Mp4FileHandler::createChapterTree() const
{
if (m_file.isEmpty()) return nullptr;
#ifdef NO_LIBMP4V2
return nullptr;
#else
MP4FileHandle hM4a = MP4Read(m_file.toStdString().c_str());
if (hM4a == MP4_INVALID_FILE_HANDLE ) {
return nullptr;
}
MP4Chapter_t * chapters = 0;
uint32_t chapterCount = 0;
MP4ChapterType chapterType = MP4GetChapters(hM4a, &chapters, &chapterCount, MP4ChapterTypeAny);
if (chapterCount == 0) {
return nullptr;
}
qDebug() << chapterType;
ChapterItem * tocItem = new ChapterItem("pseudoTOC");
tocItem->setColumnCount(3);
MP4Duration durationMs = 0; // yeah, it's in msec since m4a chapter marker tracks uses scale 1000.
for (uint32_t i = 0; i < chapterCount; ++i) {
ChapterItem * chapterItem = new ChapterItem(QString::number(durationMs));
chapterItem->setColumnCount(3);
chapterItem->setItemProperty(ChapterTitle, QString(chapters[i].title));
chapterItem->setItemProperty(ChapterStartTimeMs, static_cast<int>(durationMs));
tocItem->appendRow(chapterItem);
// qDebug() << "start:" << durationMs << chapters[i].title;
durationMs += chapters[i].duration;
}
MP4Free(chapters);
MP4Close(hM4a);
return tocItem;
#endif // NO_LIBMP4V2
}
FileHandlerInterface::ChapterFeatures Mp4FileHandler::chapterFeatures() const
{
return ChapterFeatures(StartTimeMs | Title);
}
FileHandlerInterface::Status Mp4FileHandler::importFromFile()
{
return SUCCESS;
}
#ifndef NO_LIBMP4V2
MP4TrackId getFirstAudioTrack(MP4FileHandle file, bool & out_isVideoTrack)
{
uint32_t trackCnt = MP4GetNumberOfTracks(file);
if (trackCnt == 0) return MP4_INVALID_TRACK_ID;
out_isVideoTrack = false;
MP4TrackId firstTrackId = MP4_INVALID_TRACK_ID;
for (uint32_t i = 0; i < trackCnt; i++) {
MP4TrackId id = MP4FindTrackId(file, i);
const char * type = MP4GetTrackType(file, id);
if (MP4_IS_VIDEO_TRACK_TYPE(type)) {
firstTrackId = id;
out_isVideoTrack = true;
break;
} else if (MP4_IS_AUDIO_TRACK_TYPE(type)) {
firstTrackId = id;
out_isVideoTrack = false;
break;
}
}
return firstTrackId;
}
#endif // NO_LIBMP4V2
FileHandlerInterface::Status Mp4FileHandler::writeToFile(ChapterItem *chapterRoot)
{
#ifdef NO_LIBMP4V2
Q_UNUSED(chapterRoot);
return WRITE_NOT_SUPPORTED;
#else // NO_LIBMP4V2
MP4FileHandle hM4a = MP4Modify(m_file.toStdString().c_str());
if (hM4a == MP4_INVALID_FILE_HANDLE) {
return FILE_STAT_ERROR;
}
std::vector<MP4Chapter_t> chapters;
// before we start, let's remove all existed chapters...
MP4DeleteChapters(hM4a, MP4ChapterTypeAny);
ChapterItem::forEach(chapterRoot, [&](const ChapterItem * currentItem) {
MP4Chapter_t chap;
std::string chapterTitle(currentItem->data(ChapterTitle).toString().toStdString());
size_t titleLen = qMin(chapterTitle.length(), (size_t)MP4V2_CHAPTER_TITLE_MAX);
strncpy(chap.title, chapterTitle.c_str(), titleLen);
chap.title[titleLen] = 0;
chap.duration = currentItem->data(ChapterStartTimeMs).toUInt();
chapters.push_back(chap);
});
bool isVideoTrack = false;
MP4TrackId firstTrackId = getFirstAudioTrack(hM4a, isVideoTrack);
if (!MP4_IS_VALID_TRACK_ID(firstTrackId)) {
return FILE_STAT_ERROR;
}
MP4Duration durationTicks = MP4GetTrackDuration(hM4a, firstTrackId);
uint32_t tickPerSec = MP4GetTrackTimeScale(hM4a, firstTrackId);
uint32_t durationMs = durationTicks * 1.0 / tickPerSec * 1000;
// check out-of-duration chapter markers
for(std::vector<MP4Chapter_t>::iterator it = chapters.begin(); it != chapters.end(); ) {
if (durationMs <= it->duration) {
it = chapters.erase(it);
} else {
it++;
}
}
for(std::vector<MP4Chapter_t>::iterator it = chapters.begin(); it != chapters.end(); it++) {
MP4Duration currDuration = (*it).duration;
MP4Duration nextDuration = chapters.end() == it + 1 ? durationMs : (*(it+1)).duration;
(*it).duration = nextDuration - currDuration;
}
// finally, apply chapter markers
MP4SetChapters(hM4a, &chapters[0], (uint32_t)chapters.size(), MP4ChapterTypeAny);
MP4Close(hM4a);
// This is optional.
MP4Optimize(m_file.toStdString().c_str());
return SUCCESS;
#endif // NO_LIBMP4V2
}
FileHandlerInterface::Status Mp4FileHandler::exportToFile(ChapterItem *)
{
return EXPORT_NOT_SUPPORTED;
}