2015-09-06 19:57:33 +02:00
|
|
|
#include "./abstracttrack.h"
|
|
|
|
#include "./exceptions.h"
|
|
|
|
#include "./mediaformat.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-09-06 19:57:33 +02:00
|
|
|
#include "./mp4/mp4ids.h"
|
2015-09-06 15:42:18 +02:00
|
|
|
|
|
|
|
#include "mpegaudio/mpegaudioframe.h"
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
using namespace std;
|
2019-06-10 22:49:11 +02:00
|
|
|
using namespace CppUtilities;
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2018-03-06 23:09:15 +01:00
|
|
|
namespace TagParser {
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
2018-06-03 20:38:32 +02:00
|
|
|
* \class TagParser::AbstractTrack
|
2015-04-22 19:22:01 +02:00
|
|
|
* \brief The AbstractTrack class parses and stores technical information about
|
|
|
|
* video, audio and other kinds of media tracks.
|
|
|
|
*
|
|
|
|
* The tag class only provides the interface and common functionality. It is meant to be subclassed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new track.
|
|
|
|
* \param inputStream Specifies the stream the track will read from to perform particular operations such
|
|
|
|
* as reading header information.
|
|
|
|
* \param outputStream Specifies the stream the track will write to to perform particular operations such
|
|
|
|
* as updating or making header information.
|
|
|
|
* \param startOffset The start offset of the track in the specified \a stream.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
AbstractTrack::AbstractTrack(istream &inputStream, ostream &outputStream, std::uint64_t startOffset)
|
2018-03-07 01:17:50 +01:00
|
|
|
: m_istream(&inputStream)
|
|
|
|
, m_ostream(&outputStream)
|
|
|
|
, m_reader(BinaryReader(&inputStream))
|
|
|
|
, m_writer(BinaryWriter(&outputStream))
|
|
|
|
, m_startOffset(startOffset)
|
2020-12-14 20:27:54 +01:00
|
|
|
, m_flags(TrackFlags::Enabled | TrackFlags::UsedInPresentation | TrackFlags::UsedWhenPreviewing)
|
2018-03-07 01:17:50 +01:00
|
|
|
, m_format()
|
|
|
|
, m_mediaType(MediaType::Unknown)
|
|
|
|
, m_version(0.0)
|
|
|
|
, m_size(0)
|
|
|
|
, m_trackNumber(0)
|
|
|
|
, m_id(0)
|
|
|
|
, m_bitrate(0.0)
|
|
|
|
, m_maxBitrate(0.0)
|
|
|
|
, m_samplingFrequency(0)
|
|
|
|
, m_extensionSamplingFrequency(0)
|
|
|
|
, m_bitsPerSample(0)
|
|
|
|
, m_bytesPerSecond(0)
|
|
|
|
, m_channelCount(0)
|
|
|
|
, m_channelConfig(0)
|
|
|
|
, m_extensionChannelConfig(0)
|
|
|
|
, m_sampleCount(0)
|
|
|
|
, m_quality(0)
|
|
|
|
, m_depth(0)
|
|
|
|
, m_fps(0)
|
|
|
|
, m_chromaFormat(nullptr)
|
|
|
|
, m_timeScale(0)
|
|
|
|
, m_colorSpace(0)
|
|
|
|
{
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new track.
|
|
|
|
* \param stream Specifies the stream the track will read from or write
|
|
|
|
* to to perform particular operations such as reading header
|
|
|
|
* information.
|
|
|
|
* \param startOffset The start offset of the track in the specified \a stream.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
AbstractTrack::AbstractTrack(std::iostream &stream, std::uint64_t startOffset)
|
2018-03-07 01:17:50 +01:00
|
|
|
: AbstractTrack(stream, stream, startOffset)
|
|
|
|
{
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Destroys the track.
|
|
|
|
*/
|
|
|
|
AbstractTrack::~AbstractTrack()
|
2018-03-07 01:17:50 +01:00
|
|
|
{
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-07-31 01:09:41 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns a string with the channel configuration if available; otherwise returns nullptr.
|
|
|
|
*/
|
|
|
|
const char *AbstractTrack::channelConfigString() const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (m_format.general) {
|
2015-07-31 01:09:41 +02:00
|
|
|
case GeneralMediaFormat::Aac:
|
2015-08-01 20:47:26 +02:00
|
|
|
return m_channelConfig ? Mpeg4ChannelConfigs::channelConfigString(m_channelConfig) : nullptr;
|
2018-03-07 01:17:50 +01:00
|
|
|
case GeneralMediaFormat::Mpeg1Audio:
|
|
|
|
case GeneralMediaFormat::Mpeg2Audio:
|
2015-07-31 01:09:41 +02:00
|
|
|
return mpegChannelModeString(static_cast<MpegChannelMode>(m_channelConfig));
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:15:27 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns the extension channel configuration if available; otherwise returns nullptr.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint8_t AbstractTrack::extensionChannelConfig() const
|
2015-09-24 01:15:27 +02:00
|
|
|
{
|
|
|
|
return m_extensionChannelConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns a string with the extension channel configuration if available; otherwise returns nullptr.
|
|
|
|
*/
|
|
|
|
const char *AbstractTrack::extensionChannelConfigString() const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (m_format.general) {
|
2015-09-24 01:15:27 +02:00
|
|
|
case GeneralMediaFormat::Aac:
|
|
|
|
return m_extensionChannelConfig ? Mpeg4ChannelConfigs::channelConfigString(m_extensionChannelConfig) : nullptr;
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns a label for the track.
|
2017-08-17 19:04:58 +02:00
|
|
|
*
|
|
|
|
* The label contains the ID, type, name and language of the track. It is intended to be used in a menu for
|
|
|
|
* selecting a track from the file.
|
2015-04-22 19:22:01 +02:00
|
|
|
*/
|
|
|
|
string AbstractTrack::label() const
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
ss << "ID: " << id();
|
|
|
|
ss << ", type: " << mediaTypeName();
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!name().empty()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
ss << ", name: \"" << name() << "\"";
|
|
|
|
}
|
2020-12-13 18:37:15 +01:00
|
|
|
if (const auto &language = locale().fullOrSomeAbbreviatedName(); !language.empty()) {
|
|
|
|
ss << ", language: " << language << "";
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
2020-10-01 19:58:03 +02:00
|
|
|
/// \cond
|
|
|
|
string AbstractTrack::makeDescription(bool verbose) const
|
2017-08-17 18:47:42 +02:00
|
|
|
{
|
|
|
|
// use abbreviated format
|
2020-10-01 19:58:03 +02:00
|
|
|
const auto format = MediaFormat(m_format.general, verbose ? m_format.sub : 0, verbose ? m_format.extension : 0);
|
|
|
|
const char *formatName = format.shortAbbreviation();
|
|
|
|
if (!formatName || !*formatName) {
|
2017-08-17 18:47:42 +02:00
|
|
|
// fall back to media type name if no abbreviation available
|
2020-10-01 19:58:03 +02:00
|
|
|
formatName = mediaTypeName();
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
|
|
|
|
2020-10-01 19:58:03 +02:00
|
|
|
// find additional info and level
|
|
|
|
const char *additionalInfoRef = nullptr;
|
|
|
|
string level;
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (m_mediaType) {
|
2017-08-17 18:47:42 +02:00
|
|
|
case MediaType::Video:
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!displaySize().isNull()) {
|
2020-10-01 19:58:03 +02:00
|
|
|
additionalInfoRef = displaySize().abbreviation();
|
2018-03-07 01:17:50 +01:00
|
|
|
} else if (!pixelSize().isNull()) {
|
2020-10-01 19:58:03 +02:00
|
|
|
additionalInfoRef = pixelSize().abbreviation();
|
|
|
|
}
|
|
|
|
if (verbose) {
|
|
|
|
switch (format.general) {
|
|
|
|
case GeneralMediaFormat::Mpeg4Video:
|
|
|
|
case GeneralMediaFormat::Avc:
|
|
|
|
case GeneralMediaFormat::Hevc:
|
|
|
|
if (version()) {
|
|
|
|
level = "@L" + numberToString(version());
|
|
|
|
}
|
|
|
|
break;
|
2020-11-14 22:58:55 +01:00
|
|
|
default:;
|
2020-10-01 19:58:03 +02:00
|
|
|
}
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MediaType::Audio:
|
|
|
|
case MediaType::Text:
|
2018-03-07 01:17:50 +01:00
|
|
|
if (channelCount()) {
|
2020-12-13 18:37:15 +01:00
|
|
|
if (const auto &localeName = locale().someAbbreviatedName(); !localeName.empty()) {
|
|
|
|
return argsToString(formatName, '-', channelCount(), "ch-", localeName);
|
2017-08-17 18:47:42 +02:00
|
|
|
} else {
|
2020-10-01 19:58:03 +02:00
|
|
|
return argsToString(formatName, '-', channelCount(), 'c', 'h');
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
2020-12-13 18:37:15 +01:00
|
|
|
} else if (const auto &localeName = locale().someAbbreviatedName(); !localeName.empty()) {
|
|
|
|
additionalInfoRef = localeName.data();
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
|
|
|
break;
|
2018-03-07 01:17:50 +01:00
|
|
|
default:;
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
|
|
|
|
2020-10-01 19:58:03 +02:00
|
|
|
if (additionalInfoRef) {
|
|
|
|
return argsToString(formatName, level, '-', additionalInfoRef);
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
2020-10-01 19:58:03 +02:00
|
|
|
return argsToString(formatName, level);
|
|
|
|
}
|
|
|
|
/// \endcond
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns a description about the track.
|
|
|
|
*
|
|
|
|
* The description contains the abbreviated format and further information depending on the media
|
|
|
|
* type (eg. display size in case of video, language in case of audio/text). It is intended to be joined
|
|
|
|
* with descriptions of other tracks to get a short technical description about the file.
|
|
|
|
*
|
|
|
|
* Examples (exact format might change in the future!):
|
|
|
|
* - H.264-High-10@5.1-720p
|
|
|
|
* - HE-AAC-6ch-eng
|
|
|
|
*/
|
|
|
|
string AbstractTrack::description() const
|
|
|
|
{
|
|
|
|
return makeDescription(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns a short description about the track.
|
|
|
|
*
|
|
|
|
* See description() for details.
|
|
|
|
*
|
|
|
|
* Examples (exact format might change in the future!):
|
|
|
|
* - H.264-720p
|
|
|
|
* - HE-AAC-6ch-eng
|
|
|
|
*/
|
|
|
|
string AbstractTrack::shortDescription() const
|
|
|
|
{
|
|
|
|
return makeDescription(false);
|
2017-08-17 18:47:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Parses technical information about the track from the header.
|
|
|
|
*
|
|
|
|
* The information will be read from the associated stream. The stream and the
|
|
|
|
* start offset of the track have been specified when constructing the Track.
|
|
|
|
*
|
|
|
|
* The parsed information can be accessed using the corresponding methods.
|
|
|
|
*
|
|
|
|
* \throws Throws std::ios_base::failure when an IO error occurs.
|
2018-06-03 20:38:32 +02:00
|
|
|
* \throws Throws TagParser::Failure or a derived exception when a parsing
|
2015-04-22 19:22:01 +02:00
|
|
|
* error occurs.
|
|
|
|
*/
|
2018-03-05 17:49:29 +01:00
|
|
|
void AbstractTrack::parseHeader(Diagnostics &diag)
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
2020-12-14 20:27:54 +01:00
|
|
|
m_flags -= TrackFlags::HeaderValid;
|
2018-06-02 22:56:08 +02:00
|
|
|
m_istream->seekg(static_cast<streamoff>(m_startOffset), ios_base::beg);
|
2015-04-22 19:22:01 +02:00
|
|
|
try {
|
2018-03-05 17:49:29 +01:00
|
|
|
internalParseHeader(diag);
|
2020-12-14 20:27:54 +01:00
|
|
|
m_flags += TrackFlags::HeaderValid;
|
|
|
|
} catch (const Failure &) {
|
2015-04-22 19:22:01 +02:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \fn AbstractTrack::internalParseHeader()
|
|
|
|
* \brief This method is internally called to parse header information.
|
|
|
|
* It needs to be implemented when subclassing this class.
|
|
|
|
*
|
|
|
|
* \throws Throws std::ios_base::failure when an IO error occurs.
|
2018-06-03 20:38:32 +02:00
|
|
|
* \throws Throws TagParser::Failure or a derived exception when a parsing
|
2015-04-22 19:22:01 +02:00
|
|
|
* error occurs.
|
|
|
|
*/
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
} // namespace TagParser
|