Skip to content

Commit

Permalink
Merge pull request #1330 from CastagnaIT/stream_info
Browse files Browse the repository at this point in the history
[codechandler] Check and correct stream codec info
  • Loading branch information
CastagnaIT authored Jul 23, 2023
2 parents ebe8f36 + 325df68 commit 2e33e44
Show file tree
Hide file tree
Showing 25 changed files with 561 additions and 269 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ set(ADP_SOURCES
src/AdaptiveByteStream.cpp
src/main.cpp
src/codechandler/CodecHandler.cpp
src/codechandler/AudioCodecHandler.cpp
src/codechandler/AV1CodecHandler.cpp
src/codechandler/AVCCodecHandler.cpp
src/codechandler/HEVCCodecHandler.cpp
src/codechandler/MPEGCodecHandler.cpp
src/codechandler/TTMLCodecHandler.cpp
src/codechandler/VP9CodecHandler.cpp
src/codechandler/WebVTTCodecHandler.cpp
Expand Down Expand Up @@ -77,10 +77,10 @@ set(ADP_HEADERS
src/oscompat.h
src/SSD_dll.h
src/codechandler/CodecHandler.h
src/codechandler/AudioCodecHandler.h
src/codechandler/AV1CodecHandler.h
src/codechandler/AVCCodecHandler.h
src/codechandler/HEVCCodecHandler.h
src/codechandler/MPEGCodecHandler.h
src/codechandler/TTMLCodecHandler.h
src/codechandler/VP9CodecHandler.h
src/codechandler/WebVTTCodecHandler.h
Expand Down
70 changes: 67 additions & 3 deletions src/ADTSReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@

#include "parser/CodecParser.h"
#include "utils/log.h"
#include "utils/Utils.h"

#include <stdlib.h>

#include <bento4/Ap4ByteStream.h>

using namespace adaptive;
using namespace UTILS;

uint64_t ID3TAG::getSize(const uint8_t *data, unsigned int len, unsigned int shift)
uint64_t ID3TAG::getSize(const uint8_t* data, unsigned int len, unsigned int shift)
{
uint64_t size(0);
const uint8_t *dataE(data + len);
const uint8_t* dataE(data + len);
for (; data < dataE; ++data)
size = size << shift | *data;
return size;
};

ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream *stream)
ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream* stream)
{
uint8_t buffer[64];
unsigned int retCount(0);
Expand All @@ -36,6 +39,8 @@ ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream *stream)
return PARSE_FAIL;
}

// ID3v2 header
// 3 byte "ID3" + 1 byte ver + 1 byte revision + 1 byte flags + 4 byte size
if (memcmp(buffer, "ID3", 3) != 0)
{
AP4_Position pos;
Expand All @@ -45,6 +50,7 @@ ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream *stream)
}

m_majorVer = buffer[3];
m_revisionVer = buffer[4];
m_flags = buffer[5];
uint32_t size = static_cast<uint32_t>(getSize(buffer + 6, 4, 7));

Expand All @@ -61,6 +67,7 @@ ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream *stream)
if (!AP4_SUCCEEDED(stream->Read(buffer, frameSize)))
return PARSE_FAIL;

// HLS audio packet timestamp: https://datatracker.ietf.org/doc/html/rfc8216
if (strncmp(reinterpret_cast<const char*>(buffer), "com.apple.streaming.transportStreamTimestamp", 44) == 0 && buffer[44] == 0)
{
m_timestamp = getSize(buffer + 45, 8, 8);
Expand All @@ -77,6 +84,33 @@ ID3TAG::PARSECODE ID3TAG::parse(AP4_ByteStream *stream)
return PARSE_SUCCESS;
}

void ID3TAG::SkipID3Data(AP4_ByteStream* stream)
{
uint8_t buffer[64];

if (!AP4_SUCCEEDED(stream->Read(buffer, HEADER_SIZE)))
{
// Try again
if (!AP4_SUCCEEDED(stream->Read(buffer, HEADER_SIZE)))
return;
}

if (std::memcmp(buffer, "ID3", 3) != 0) // No ID3 header
{
AP4_Position currentPos;
stream->Tell(currentPos);
stream->Seek(currentPos - HEADER_SIZE);
return;
}

// Get ID3v2 data size
uint32_t headerSize = static_cast<uint32_t>(getSize(buffer + 6, 4, 7));

AP4_Position currentPos;
stream->Tell(currentPos);
stream->Seek(currentPos + headerSize);
}

/**********************************************************************************************************************************/

void ADTSFrame::AdjustStreamForPadding(AP4_ByteStream* stream)
Expand All @@ -90,6 +124,11 @@ void ADTSFrame::AdjustStreamForPadding(AP4_ByteStream* stream)
stream->Seek(currentPos);
}

AdtsType ADTSFrame::GetAdtsType(AP4_ByteStream* stream)
{
return CAdaptiveAdtsHeaderParser::GetAdtsType(stream);
}

bool ADTSFrame::parse(AP4_ByteStream* stream)
{
AdtsType adtsType = CAdaptiveAdtsHeaderParser::GetAdtsType(stream);
Expand Down Expand Up @@ -234,6 +273,31 @@ void ADTSReader::Reset()

bool ADTSReader::GetInformation(kodi::addon::InputstreamInfo& info)
{
m_id3TagParser.SkipID3Data(m_stream);
AdtsType adtsType = m_frameParser.GetAdtsType(m_stream);
m_stream->Seek(0); // Seek back because data has been consumed

std::string codecName;
switch (adtsType)
{
case AdtsType::AAC:
codecName = CODEC::NAME_AAC;
break;
case AdtsType::AC3:
codecName = CODEC::NAME_AC3;
break;
case AdtsType::EAC3:
codecName = CODEC::NAME_EAC3;
break;
default:
break;
}

if (!codecName.empty() && info.GetCodecName() != codecName)
{
info.SetCodecName(codecName);
return true;
}
return false;
}

Expand Down
11 changes: 10 additions & 1 deletion src/ADTSReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
#include <bento4/Ap4DataBuffer.h>
#include <kodi/addon-instance/Inputstream.h>

// Forwards
class AP4_ByteStream;

namespace adaptive
{
enum class AdtsType;
}

class ATTR_DLL_LOCAL ID3TAG
{
public:
Expand All @@ -25,7 +31,8 @@ class ATTR_DLL_LOCAL ID3TAG
PARSE_NO_ID3
};

PARSECODE parse(AP4_ByteStream *stream);
PARSECODE parse(AP4_ByteStream* stream);
void SkipID3Data(AP4_ByteStream* stream);
bool getPts(uint64_t &pts) { if (m_timestamp) { pts = m_timestamp; m_timestamp = 0; return true; } return false; }

private:
Expand All @@ -34,6 +41,7 @@ class ATTR_DLL_LOCAL ID3TAG
static const unsigned int HEADER_SIZE = 10;

uint8_t m_majorVer;
uint8_t m_revisionVer;
uint8_t m_flags;
uint64_t m_timestamp;
};
Expand All @@ -46,6 +54,7 @@ class ATTR_DLL_LOCAL ADTSFrame
* \param stream The stream to check
*/
void AdjustStreamForPadding(AP4_ByteStream* stream);
adaptive::AdtsType GetAdtsType(AP4_ByteStream* stream);
bool parse(AP4_ByteStream *stream);
bool ParseAac(AP4_ByteStream* stream);
bool ParseAc3(AP4_ByteStream* stream);
Expand Down
2 changes: 2 additions & 0 deletions src/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,8 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp,

void CSession::UpdateStream(CStream& stream)
{
// On this method we set stream info provided by manifest parsing, but these info could be
// changed by sample readers just before the start of playback by using GetInformation() methods
const StreamType streamType = stream.m_adStream.getAdaptationSet()->GetStreamType();
CRepresentation* rep{stream.m_adStream.getRepresentation()};

Expand Down
111 changes: 58 additions & 53 deletions src/TSReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,69 +115,74 @@ bool TSReader::GetInformation(kodi::addon::InputstreamInfo& info)
CODEC::NAME_UNKNOWN, CODEC::NAME_SRT, CODEC::NAME_MPEG4, CODEC::NAME_VC1,
CODEC::NAME_UNKNOWN, CODEC::NAME_DTS, CODEC::NAME_UNKNOWN};

for (auto &tsInfo : m_streamInfos)
bool isChanged{false};

for (auto& tsInfo : m_streamInfos)
{
if (tsInfo.m_streamType == info.GetStreamType())
{
if (!tsInfo.m_changed)
return false;
tsInfo.m_changed = false;
if (tsInfo.m_streamType != info.GetStreamType())
continue;

bool ret(false);
if (!tsInfo.m_changed)
return false;
tsInfo.m_changed = false;

if (tsInfo.m_streamType == INPUTSTREAM_TYPE_VIDEO)
if (tsInfo.m_streamType == INPUTSTREAM_TYPE_VIDEO)
{
if (tsInfo.m_stream->stream_info.fps_scale != static_cast<int>(info.GetFpsScale()) ||
tsInfo.m_stream->stream_info.fps_rate != static_cast<int>(info.GetFpsRate()) ||
tsInfo.m_stream->stream_info.height != static_cast<int>(info.GetHeight()) ||
tsInfo.m_stream->stream_info.width != static_cast<int>(info.GetWidth()) ||
(tsInfo.m_stream->stream_info.aspect > 0 &&
tsInfo.m_stream->stream_info.aspect != info.GetAspect()))
{
if ((!info.GetFpsScale() &&
tsInfo.m_stream->stream_info.fps_scale != static_cast<int>(info.GetFpsScale())) ||
(!info.GetFpsRate() &&
tsInfo.m_stream->stream_info.fps_rate != static_cast<int>(info.GetFpsRate())) ||
(tsInfo.m_stream->stream_info.height != static_cast<int>(info.GetHeight())) ||
(tsInfo.m_stream->stream_info.width != static_cast<int>(info.GetWidth())) ||
(tsInfo.m_stream->stream_info.aspect &&
tsInfo.m_stream->stream_info.aspect != info.GetAspect()))
{
info.SetFpsRate(tsInfo.m_stream->stream_info.fps_rate);
info.SetFpsScale(tsInfo.m_stream->stream_info.fps_scale);
info.SetWidth(tsInfo.m_stream->stream_info.width);
info.SetHeight(tsInfo.m_stream->stream_info.height);
if (tsInfo.m_stream->stream_info.aspect)
info.SetAspect(tsInfo.m_stream->stream_info.aspect);
ret = true;
}
info.SetFpsRate(tsInfo.m_stream->stream_info.fps_rate);
info.SetFpsScale(tsInfo.m_stream->stream_info.fps_scale);
info.SetWidth(tsInfo.m_stream->stream_info.width);
info.SetHeight(tsInfo.m_stream->stream_info.height);
if (tsInfo.m_stream->stream_info.aspect > 0)
info.SetAspect(tsInfo.m_stream->stream_info.aspect);

isChanged = true;
}
else if (tsInfo.m_streamType == INPUTSTREAM_TYPE_AUDIO)
}
else if (tsInfo.m_streamType == INPUTSTREAM_TYPE_AUDIO)
{
if (tsInfo.m_stream->stream_info.language[0])
info.SetLanguage(tsInfo.m_stream->stream_info.language);

if (tsInfo.m_stream->stream_info.channels != static_cast<int>(info.GetChannels()) ||
tsInfo.m_stream->stream_info.sample_rate != static_cast<int>(info.GetSampleRate()) ||
tsInfo.m_stream->stream_info.block_align != static_cast<int>(info.GetBlockAlign()) ||
tsInfo.m_stream->stream_info.bit_rate != static_cast<int>(info.GetBitRate()) ||
tsInfo.m_stream->stream_info.bits_per_sample != static_cast<int>(info.GetBitsPerSample()))
{
if (tsInfo.m_stream->stream_info.language[0])
info.SetLanguage(tsInfo.m_stream->stream_info.language);

if ((tsInfo.m_stream->stream_info.channels != static_cast<int>(info.GetChannels())) ||
(tsInfo.m_stream->stream_info.sample_rate != static_cast<int>(info.GetSampleRate())) ||
(tsInfo.m_stream->stream_info.block_align != static_cast<int>(info.GetBlockAlign())) ||
(tsInfo.m_stream->stream_info.bit_rate != static_cast<int>(info.GetBitRate())) ||
(tsInfo.m_stream->stream_info.bits_per_sample !=
static_cast<int>(info.GetBitsPerSample())))
{
info.SetChannels(tsInfo.m_stream->stream_info.channels);
info.SetSampleRate(tsInfo.m_stream->stream_info.sample_rate);
info.SetBlockAlign(tsInfo.m_stream->stream_info.block_align);
info.SetBitRate(tsInfo.m_stream->stream_info.bit_rate);
info.SetBitsPerSample(tsInfo.m_stream->stream_info.bits_per_sample);
ret = true;
}
info.SetChannels(tsInfo.m_stream->stream_info.channels);
info.SetSampleRate(tsInfo.m_stream->stream_info.sample_rate);
info.SetBlockAlign(tsInfo.m_stream->stream_info.block_align);
info.SetBitRate(tsInfo.m_stream->stream_info.bit_rate);
info.SetBitsPerSample(tsInfo.m_stream->stream_info.bits_per_sample);
isChanged = true;
}
info.SetCodecName(STREAMTYPEMAP[tsInfo.m_stream->stream_type]);
}

if (!info.CompareExtraData(tsInfo.m_stream->stream_info.extra_data,
tsInfo.m_stream->stream_info.extra_data_size))
{
info.SetExtraData(tsInfo.m_stream->stream_info.extra_data,
tsInfo.m_stream->stream_info.extra_data_size);
ret = true;
}
return ret;
const char* codecName = STREAMTYPEMAP[tsInfo.m_stream->stream_type];
if (info.GetCodecName() != codecName)
{
info.SetCodecName(codecName);
isChanged = true;
}

if (!info.CompareExtraData(tsInfo.m_stream->stream_info.extra_data,
tsInfo.m_stream->stream_info.extra_data_size))
{
info.SetExtraData(tsInfo.m_stream->stream_info.extra_data,
tsInfo.m_stream->stream_info.extra_data_size);
isChanged = true;
}
break;
}
return false;

return isChanged;
}

// We assume that m_startpos is the current I-Frame position
Expand Down
30 changes: 29 additions & 1 deletion src/WebmReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
*/

#include "WebmReader.h"
#include <bento4/Ap4ByteStream.h>

#include "utils/StringUtils.h"
#include "utils/Utils.h"

#include <bento4/Ap4ByteStream.h>
#include <webm/reader.h>
#include <webm/webm_parser.h>

using namespace UTILS;

class ATTR_DLL_LOCAL WebmAP4Reader : public webm::Reader
{
public:
Expand Down Expand Up @@ -116,6 +121,26 @@ bool WebmReader::GetInformation(kodi::addon::InputstreamInfo& info)
ret = true;
}

if (!m_codecId.empty())
{
// Verify codec name for officially supported codecs only
std::string codecName;
if (STRING::Contains(m_codecId, "VP9"))
codecName = CODEC::NAME_VP9;
else if (STRING::Contains(m_codecId, "AV1"))
codecName = CODEC::NAME_AV1;
else if (STRING::Contains(m_codecId, "VORBIS"))
codecName = CODEC::NAME_VORBIS;
else if (STRING::Contains(m_codecId, "OPUS"))
codecName = CODEC::NAME_OPUS;

if (!codecName.empty() && info.GetCodecName() != codecName)
{
info.SetCodecName(codecName);
ret = true;
}
}

if (m_codecProfile && info.GetCodecProfile() != m_codecProfile)
info.SetCodecProfile(m_codecProfile), ret = true;

Expand Down Expand Up @@ -257,6 +282,9 @@ webm::Status WebmReader::OnFrame(const webm::FrameMetadata& metadata, webm::Read

webm::Status WebmReader::OnTrackEntry(const webm::ElementMetadata& metadata, const webm::TrackEntry& track_entry)
{
if (track_entry.codec_id.is_present())
m_codecId = track_entry.codec_id.value();

if (track_entry.audio.is_present())
{
m_metadataChanged = true;
Expand Down
1 change: 1 addition & 0 deletions src/WebmReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class ATTR_DLL_LOCAL WebmReader : public webm::Callback
//Video section
uint32_t m_width = 0;
uint32_t m_height = 0;
std::string m_codecId;
STREAMCODEC_PROFILE m_codecProfile = CodecProfileUnknown;
bool m_metadataChanged = true;

Expand Down
Loading

0 comments on commit 2e33e44

Please sign in to comment.