fixed some (mostly DASH related) errors

This commit is contained in:
Martchus 2015-12-27 18:05:50 +01:00
parent 60d85d9574
commit b1ce8dde9f
1 changed files with 76 additions and 17 deletions

View File

@ -220,8 +220,10 @@ void Mp4Container::internalMakeFile()
ElementPosition newTagPos = fileInfo().tagPosition();
// -> current tag position (determined later)
ElementPosition currentTagPos;
// -> holds new padding
// -> holds new padding (before actual data)
uint64 newPadding;
// -> holds new padding (after actual data)
uint64 newPaddingEnd;
// -> holds current offset
uint64 currentOffset;
// -> holds track information, used when writing chunk-by-chunk
@ -236,8 +238,8 @@ void Mp4Container::internalMakeFile()
const auto trackCount = this->trackCount();
// find relevant atoms in original file
Mp4Atom *fileTypeAtom, *progressiveDownloadInfoAtom, *movieAtom, *firstMediaDataAtom, *userDataAtom, *metaAtom;
Mp4Atom *level0Atom, *level1Atom, *level2Atom, *movieNextSibling;
Mp4Atom *fileTypeAtom, *progressiveDownloadInfoAtom, *movieAtom, *firstMediaDataAtom, *firstMovieFragmentAtom, *userDataAtom, *metaAtom;
Mp4Atom *level0Atom, *level1Atom, *level2Atom, *lastAtomToBeWritten;
try {
// file type atom (mandatory)
if((fileTypeAtom = firstElement()->siblingById(Mp4AtomIds::FileType, true))) {
@ -255,26 +257,42 @@ void Mp4Container::internalMakeFile()
progressiveDownloadInfoAtom->makeBuffer();
}
// movie atom (mandatory) and next sibling
// movie atom (mandatory)
if(!(movieAtom = firstElement()->siblingById(Mp4AtomIds::Movie, true))) {
// throw error if missing
addNotification(NotificationType::Critical, "Mandatory \"moov\"-atom not in the source file found.", context);
throw InvalidDataException();
}
for(movieNextSibling = movieAtom->nextSibling(); movieNextSibling; movieNextSibling = movieNextSibling->nextSibling()) {
movieNextSibling->parse();
switch(movieNextSibling->id()) {
// movie fragment atom (indicates dash file)
if((firstMovieFragmentAtom = firstElement()->siblingById(Mp4AtomIds::MovieFragment))) {
// there is at least one movie fragment atom -> consider file being dash
// -> can not write chunk-by-chunk (currently)
if(writeChunkByChunk) {
addNotification(NotificationType::Critical, "Writing chunk-by-chunk is not implemented for DASH files.", context);
throw NotImplementedException();
}
// -> tags must be placed at the beginning
newTagPos = ElementPosition::BeforeData;
}
// media data atom (mandatory?)
// -> consider not only mdat as media data atom; consider everything not handled otherwise as media data
for(firstMediaDataAtom = nullptr, level0Atom = firstElement(); level0Atom; level0Atom = level0Atom->nextSibling()) {
level0Atom->parse();
switch(level0Atom->id()) {
case Mp4AtomIds::FileType: case Mp4AtomIds::ProgressiveDownloadInformation:
case Mp4AtomIds::Movie: case Mp4AtomIds::Free: case Mp4AtomIds::Skip:
continue;
default:
firstMediaDataAtom = level0Atom;
}
break;
}
// media data atom (mandatory?)
// -> determine current tag position
// -> since tags are nested in the move atom its position is relevant here
if((firstMediaDataAtom = firstElement()->siblingById(Mp4AtomIds::MediaData, true))) {
// determine current tag position
// -> since tags are nested in the movie atom its position is relevant here
if(firstMediaDataAtom) {
currentTagPos = firstMediaDataAtom->startOffset() < movieAtom->startOffset()
? ElementPosition::AfterData : ElementPosition::BeforeData;
if(newTagPos == ElementPosition::Keep) {
@ -289,6 +307,9 @@ void Mp4Container::internalMakeFile()
metaAtom = userDataAtom->childById(Mp4AtomIds::Meta);
}
} catch (const NotImplementedException &) {
throw;
} catch (const Failure &) {
// can't ignore parsing errors here
addNotification(NotificationType::Critical, "Unable to parse the overall atom structure of the source file.", context);
@ -382,6 +403,26 @@ void Mp4Container::internalMakeFile()
throw OperationAbortedException();
}
// check whether there are atoms to be voided after movie next sibling (only relevant when not rewriting)
if(!rewriteRequired) {
newPaddingEnd = 0;
uint64 currentSum = 0;
for(Mp4Atom *level0Atom = firstMediaDataAtom; level0Atom; level0Atom = level0Atom->nextSibling()) {
level0Atom->parse();
switch(level0Atom->id()) {
case Mp4AtomIds::FileType: case Mp4AtomIds::ProgressiveDownloadInformation:
case Mp4AtomIds::Movie: case Mp4AtomIds::Free: case Mp4AtomIds::Skip:
// must void these if they occur "between" the media data
currentSum += level0Atom->totalSize();
break;
default:
newPaddingEnd += currentSum;
currentSum = 0;
lastAtomToBeWritten = level0Atom;
}
}
}
// calculate padding if no rewrite is required; otherwise use the preferred padding
calculatePadding:
if(rewriteRequired) {
@ -406,15 +447,18 @@ calculatePadding:
}
// check whether there is sufficiant space before the next atom
if(!(rewriteRequired = movieNextSibling && currentOffset > movieNextSibling->startOffset())) {
if(!(rewriteRequired = firstMediaDataAtom && currentOffset > firstMediaDataAtom->startOffset())) {
// there is sufficiant space
// -> check whether the padding matches specifications
newPadding = movieNextSibling->startOffset() - currentOffset;
rewriteRequired = (newPadding > 0 && newPadding < 8) || newPadding < fileInfo().minPadding() || newPadding > fileInfo().maxPadding();
// min padding: says "at least ... byte should be reserved to prepend further tag info", so the padding at the end
// shouldn't be tanken into account (it can't be used to prepend further tag info)
// max padding: says "do not waste more then ... byte", so here all padding should be taken into account
newPadding = firstMediaDataAtom->startOffset() - currentOffset;
rewriteRequired = (newPadding > 0 && newPadding < 8) || newPadding < fileInfo().minPadding() || (newPadding + newPaddingEnd) > fileInfo().maxPadding();
}
if(rewriteRequired) {
// can't put the tags before media data
if(!fileInfo().forceTagPosition() || newTagPos == ElementPosition::Keep) {
if(!firstMovieFragmentAtom && (!fileInfo().forceTagPosition() || newTagPos == ElementPosition::Keep)) {
// writing tag before media data is not forced -> just put the tags at the end
newTagPos = ElementPosition::AfterData;
rewriteRequired = false;
@ -571,7 +615,7 @@ calculatePadding:
// write media data
if(rewriteRequired) {
for(level0Atom = firstElement(); level0Atom; level0Atom = level0Atom->nextSibling()) {
for(level0Atom = firstMediaDataAtom; level0Atom; level0Atom = level0Atom->nextSibling()) {
level0Atom->parse();
switch(level0Atom->id()) {
case Mp4AtomIds::FileType: case Mp4AtomIds::ProgressiveDownloadInformation:
@ -672,7 +716,22 @@ calculatePadding:
}
} else {
outputStream.seekp(movieNextSibling->totalSize(), ios_base::cur);
// can't just skip next movie sibling
for(Mp4Atom *level0Atom = firstMediaDataAtom; level0Atom; level0Atom = level0Atom->nextSibling()) {
level0Atom->parse();
switch(level0Atom->id()) {
case Mp4AtomIds::FileType: case Mp4AtomIds::ProgressiveDownloadInformation: case Mp4AtomIds::Movie:
// must void these if they occur "between" the media data
outputStream.seekp(4, ios_base::cur);
outputWriter.writeUInt32BE(Mp4AtomIds::Free);
break;
default:
outputStream.seekp(level0Atom->totalSize(), ios_base::cur);
}
if(level0Atom == lastAtomToBeWritten) {
break;
}
}
}
}
}