diff --git a/abstracttrack.h b/abstracttrack.h index 2de0474..24054f0 100644 --- a/abstracttrack.h +++ b/abstracttrack.h @@ -170,19 +170,18 @@ inline std::istream &AbstractTrack::inputStream() } /*! - * \brief Assigns an other input stream. + * \brief Assigns another input stream. * * The track will read from the \a stream to perform * particular operations such as reading header information. */ inline void AbstractTrack::setInputStream(std::istream &stream) { - m_istream = &stream; - m_reader.setStream(m_istream); + m_reader.setStream(m_istream = &stream); } /*! - * \brief Returns the associated input stream. + * \brief Returns the associated output stream. */ inline std::ostream &AbstractTrack::outputStream() { @@ -190,15 +189,14 @@ inline std::ostream &AbstractTrack::outputStream() } /*! - * \brief Assigns an other output stream. + * \brief Assigns another output stream. * * The track will write to the \a stream to perform * particular operations such as updating or making header information. */ inline void AbstractTrack::setOutputStream(std::ostream &stream) { - m_ostream = &stream; - m_writer.setStream(m_ostream); + m_writer.setStream(m_ostream = &stream); } /*! @@ -335,6 +333,7 @@ inline void AbstractTrack::setId(uint64 id) m_id = id; } +/*! * \brief Returns the track name if known; otherwise returns an empty string. */ inline const std::string AbstractTrack::name() const diff --git a/mediafileinfo.cpp b/mediafileinfo.cpp index 2e4ffaf..acc8f4c 100644 --- a/mediafileinfo.cpp +++ b/mediafileinfo.cpp @@ -282,8 +282,8 @@ startParsingSignature: * * This method parses the tracks of the current file if not been parsed yet. * After calling this method the methods trackCount(), tracks(), and - * hasTracksOfType() will return the parsed - * information. + * hasTracksOfType() will return the parsed information. + * * \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws Media::Failure or a derived exception when a parsing * error occurs. @@ -777,13 +777,21 @@ const char *MediaFileInfo::mimeType() const vector MediaFileInfo::tracks() const { vector res; + size_t trackCount = 0; + size_t containerTrackCount = 0; + if(m_singleTrack) { + trackCount = 1; + } + if(m_container) { + trackCount += (containerTrackCount = m_container->trackCount()); + } + res.reserve(trackCount); + if(m_singleTrack) { res.push_back(m_singleTrack.get()); } - if(m_container) { - for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) { - res.push_back(m_container->track(i)); - } + for(size_t i = 0; i != containerTrackCount; ++i) { + res.push_back(m_container->track(i)); } return res; } diff --git a/mp4/mp4container.cpp b/mp4/mp4container.cpp index 56ac90a..dc07e59 100644 --- a/mp4/mp4container.cpp +++ b/mp4/mp4container.cpp @@ -579,6 +579,16 @@ calculatePadding: progressiveDownloadInfoAtom->discardBuffer(); } + // set input/output streams of each track + for(auto &track : tracks()) { + // ensure the track reads from the original file + if(&track->inputStream() == &outputStream) { + track->setInputStream(backupStream); + } + // ensure the track writes to the output file + track->setOutputStream(outputStream); + } + // write movie atom / padding and media data for(byte pass = 0; pass != 2; ++pass) { if(newTagPos == (pass ? ElementPosition::AfterData : ElementPosition::BeforeData)) { @@ -701,11 +711,6 @@ calculatePadding: throw OperationAbortedException(); } - // ensure the track reads from the original file - if(&track->inputStream() == &outputStream) { - track->setInputStream(backupStream); - } - // emplace information trackInfos.emplace_back(&track->inputStream(), track->readChunkOffsets(), track->readChunkSizes()); @@ -718,7 +723,7 @@ calculatePadding: // increase total chunk count and size totalChunkCount += track->chunkCount(); - totalMediaDataSize = accumulate(chunkSizesTable.cbegin(), chunkSizesTable.cend(), totalMediaDataSize); + totalMediaDataSize += accumulate(chunkSizesTable.cbegin(), chunkSizesTable.cend(), totalMediaDataSize); } // write media data chunk-by-chunk @@ -758,7 +763,7 @@ calculatePadding: } // incrase chunk index within track, update progress percentage - if(++chunkIndexWithinTrack % 10) { + if(!(++chunkIndexWithinTrack % 10)) { updatePercentage(static_cast(totalChunksCopied) / totalChunkCount); } @@ -832,24 +837,24 @@ calculatePadding: // check whether track count of new file equals track count of old file if(trackCount != tracks().size()) { addNotification(NotificationType::Critical, - "Unable to update chunk offsets (\"stco\"-atom): Number of tracks in the output file (" - % numberToString(tracks().size()) - % ") differs from the number of tracks in the original file (" - % numberToString(trackCount) - + ").", context); + argsToString("Unable to update chunk offsets (\"stco\"-atom): Number of tracks in the output file (", + tracks().size(), + ") differs from the number of tracks in the original file (", + trackCount, + ")."), context); throw Failure(); } // update chunk offset table if(writeChunkByChunk) { updateStatus("Updating chunk offset table for each track ..."); - for(size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) { + for(size_t trackIndex = 0; trackIndex != trackCount; ++trackIndex) { const auto &track = tracks()[trackIndex]; const auto &chunkOffsetTable = get<1>(trackInfos[trackIndex]); if(track->chunkCount() == chunkOffsetTable.size()) { track->updateChunkOffsets(chunkOffsetTable); } else { - addNotification(NotificationType::Critical, "Unable to update chunk offsets of track " % numberToString(trackIndex + 1) + ": Number of chunks in the output file differs from the number of chunks in the orignal file.", context); + addNotification(NotificationType::Critical, argsToString("Unable to update chunk offsets of track ", (trackIndex + 1), ": Number of chunks in the output file differs from the number of chunks in the orignal file."), context); throw Failure(); } } diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index e9b5a1a..88e51f4 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -918,6 +918,55 @@ void Mp4Track::addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track) } +/*! + * \brief Returns the number of bytes written when calling makeTrack(). + * \todo Get rid of const_cast. This is currently required because GenericFileElement::childById() is + * not const. + */ +uint64 Mp4Track::requiredSize() const +{ + // add size of + // ... trak header and tkhd total size + uint64 size = 8 + 100; + // ... tref atom (if one exists) + if(Mp4Atom *trefAtom = m_trakAtom->childById(Mp4AtomIds::TrackReference)) { + size += trefAtom->totalSize(); + } + // ... edts atom (if one exists) + if(Mp4Atom *edtsAtom = m_trakAtom->childById(Mp4AtomIds::Edit)) { + size += edtsAtom->totalSize(); + } + // ... mdia header + mdhd total size + hdlr total size + minf header + size += 8 + 44 + (33 + m_name.size()) + 8; + // ... minf childs + bool dinfAtomWritten = false; + if(m_minfAtom) { + if(Mp4Atom *vmhdAtom = m_minfAtom->childById(Mp4AtomIds::VideoMediaHeader)) { + size += vmhdAtom->totalSize(); + } + if(Mp4Atom *smhdAtom = m_minfAtom->childById(Mp4AtomIds::SoundMediaHeader)) { + size += smhdAtom->totalSize(); + } + if(Mp4Atom *hmhdAtom = m_minfAtom->childById(Mp4AtomIds::HintMediaHeader)) { + size += hmhdAtom->totalSize(); + } + if(Mp4Atom *nmhdAtom = m_minfAtom->childById(Mp4AtomIds::NullMediaHeaderBox)) { + size += nmhdAtom->totalSize(); + } + if(Mp4Atom *dinfAtom = m_minfAtom->childById(Mp4AtomIds::DataInformation)) { + size += dinfAtom->totalSize(); + dinfAtomWritten = true; + } + if(Mp4Atom *stblAtom = m_minfAtom->childById(Mp4AtomIds::SampleTable)) { + size += stblAtom->totalSize(); + } + } + if(!dinfAtomWritten) { + size += 36; + } + return size; +} + /*! * \brief Makes the track entry ("trak"-atom) for the track. The data is written to the assigned output stream * at the current position. @@ -925,35 +974,24 @@ void Mp4Track::addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track) */ void Mp4Track::makeTrack() { - /* // write header ostream::pos_type trakStartOffset = outputStream().tellp(); - writer.writeUInt32(0); // write size later - writer.writeUInt32(Mp4AtomIds::Track); + m_writer.writeUInt32BE(0); // write size later + m_writer.writeUInt32BE(Mp4AtomIds::Track); // write tkhd atom makeTrackHeader(); // write tref atom (if one exists) if(Mp4Atom *trefAtom = trakAtom().childById(Mp4AtomIds::TrackReference)) { - trefAtom->copyEntireAtomToStream(outputStream()); + trefAtom->copyEntirely(outputStream()); } // write edts atom (if one exists) if(Mp4Atom *edtsAtom = trakAtom().childById(Mp4AtomIds::Edit)) { - edtsAtom->copyEntireAtomToStream(outputStream()); + edtsAtom->copyEntirely(outputStream()); } // write mdia atom makeMedia(); // write size (of trak atom) - Mp4Atom::seekBackAndWriteAtomSize(outputStream(), trakStartOffset, false); - */ - trakAtom().copyEntirely(outputStream()); -} - -/*! - * \brief Returns the number of bytes written when calling makeTrack(). - */ -uint64 Mp4Track::requiredSize() const -{ - return m_trakAtom->totalSize(); + Mp4Atom::seekBackAndWriteAtomSize(outputStream(), trakStartOffset); } /*! @@ -1013,7 +1051,8 @@ void Mp4Track::makeMedia() writer().writeUInt32BE(0); // write size later writer().writeUInt32BE(Mp4AtomIds::Media); // write mdhd atom - writer().writeUInt32BE(36); // size + writer().writeUInt32BE(44); // size + writer().writeUInt32BE(Mp4AtomIds::MediaHeader); writer().writeByte(1); // version writer().writeUInt24BE(0); // flags writer().writeUInt64BE(static_cast((m_creationTime - startDate).totalSeconds())); @@ -1022,9 +1061,10 @@ void Mp4Track::makeMedia() writer().writeUInt64BE(static_cast(m_duration.totalSeconds() * m_timeScale)); // convert and write language uint16 language = 0; - for(size_t charIndex = 0; charIndex < m_language.length() && charIndex < 3; ++charIndex) { - if(m_language[charIndex] >= 'a' && m_language[charIndex] <= 'z') { - language |= static_cast(m_language[charIndex]) << (0xA - charIndex * 0x5); + for(size_t charIndex = 0; charIndex < m_language.size() && charIndex != 3; ++charIndex) { + const char langChar = m_language[charIndex]; + if(langChar >= 'a' && langChar <= 'z') { + language |= static_cast(langChar - 0x60) << (0xA - charIndex * 0x5); } else { // invalid character addNotification(NotificationType::Warning, "Assigned language \"" % m_language + "\" is of an invalid format and will be ignored.", "making mdhd atom"); language = 0x55C4; // und @@ -1034,7 +1074,7 @@ void Mp4Track::makeMedia() writer().writeUInt16BE(language); writer().writeUInt16BE(0); // pre defined // write hdlr atom - writer().writeUInt32BE(33 + m_name.length()); // size + writer().writeUInt32BE(33 + m_name.size()); // size writer().writeUInt32BE(Mp4AtomIds::HandlerReference); writer().writeUInt64BE(0); // version, flags, pre defined switch(m_mediaType) { @@ -1112,7 +1152,18 @@ void Mp4Track::makeMediaInfo() writer().writeUInt24BE(0x000001); // flags (media data is in the same file as the movie box) } // write stbl atom - makeSampleTable(); + // -> just copy existing stbl atom because makeSampleTable() is not fully implemented (yet) + bool stblAtomWritten = false; + if(m_minfAtom) { + if(Mp4Atom *stblAtom = m_minfAtom->childById(Mp4AtomIds::SampleTable)) { + stblAtom->copyEntirely(outputStream()); + stblAtomWritten = true; + } + } + if(!stblAtomWritten) { + addNotification(NotificationType::Critical, "Unable to make stbl atom from scratch.", "making stbl atom"); + throw NotImplementedException(); + } // write size (of minf atom) Mp4Atom::seekBackAndWriteAtomSize(outputStream(), minfStartOffset); } @@ -1151,6 +1202,7 @@ void Mp4Track::makeSampleTable() cttsAtom->copyEntirely(outputStream()); } // write stsc atom (sample-to-chunk table) + throw NotImplementedException(); // write stsz atom (sample sizes) @@ -1287,10 +1339,11 @@ void Mp4Track::internalParseHeader() } uint16 tmp = reader.readUInt16BE(); if(tmp) { - char buff[3]; - buff[0] = ((tmp & 0x7C00) >> 0xA) + 0x60; - buff[1] = ((tmp & 0x03E0) >> 0x5) + 0x60; - buff[2] = ((tmp & 0x001F) >> 0x0) + 0x60; + const char buff[] = { + static_cast(((tmp & 0x7C00) >> 0xA) + 0x60), + static_cast(((tmp & 0x03E0) >> 0x5) + 0x60), + static_cast(((tmp & 0x001F) >> 0x0) + 0x60), + }; m_language = string(buff, 3); } else { m_language.clear();