Skip to content

Commit

Permalink
[Video][Subtitles] Use ffmpeg A53 sidedata instead of a half-baked de…
Browse files Browse the repository at this point in the history
…muxer
  • Loading branch information
enen92 committed Dec 28, 2022
1 parent 0f245a2 commit 6f4ce73
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 251 deletions.
1 change: 1 addition & 0 deletions xbmc/cores/VideoPlayer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ set(HEADERS AudioSinkAE.h
VideoPlayerTeletext.h
VideoPlayerVideo.h
VideoReferenceClock.h
Interface/CaptionBlock.h
Interface/StreamInfo.h
Interface/DemuxPacket.h
Interface/DemuxCrypto.h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ void CDecoderCC708::Init(void (*handler)(int service, void *userdata), void *use
ccx_decoders_708_init(m_cc708decoders, handler, userdata, this);
}

void CDecoderCC708::Decode(const unsigned char *data, int datalength)
void CDecoderCC708::Decode(const std::vector<uint8_t>& data)
{
decode_708(data, datalength, m_cc708decoders);
decode_708(data.data(), data.size(), m_cc708decoders);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <vector>

#include <sys/stat.h>

extern "C"{
Expand Down Expand Up @@ -292,7 +294,7 @@ class CDecoderCC708
CDecoderCC708();
virtual ~CDecoderCC708();
void Init(void (*handler)(int service, void *userdata), void *userdata);
void Decode(const unsigned char *data, int datalength);
void Decode(const std::vector<uint8_t>& data);
bool m_inited;
cc708_service_decoder* m_cc708decoders;
cc_decoder_t *m_cc608decoder;
Expand Down
26 changes: 26 additions & 0 deletions xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ struct VideoPicture
VideoPicture& SetParams(const VideoPicture &pic);
void Reset(); // reinitialize members, videoBuffer will be released if set!

/*!
* \brief Checks if the video picture has A53 side data (closed captions).
*
* \return true if the video picture has A53 side data, false otherwise
*/
bool HasA53SideData() const { return m_A53SideData != nullptr; }

/*!
* \brief Gets the A53 side data of the video picture (closed captions)
* \return the A53 side data, reseting the value
*/
AVBufferRef* ConsumeA53SideData()
{
AVBufferRef* sideData = m_A53SideData;
m_A53SideData = nullptr;
return sideData;
}

/*!
* \brief Set the A53 side data on the video picture
*
* \param[in] sideData the data to store as A53 side data
*/
void SetA53SideData(AVBufferRef* sideData) { m_A53SideData = av_buffer_ref(sideData); }

CVideoBuffer *videoBuffer = nullptr;

double pts; // timestamp in seconds, used in the CVideoPlayer class to keep track of pts
Expand Down Expand Up @@ -78,6 +103,7 @@ struct VideoPicture
private:
VideoPicture(VideoPicture const&);
VideoPicture& operator=(VideoPicture const&);
AVBufferRef* m_A53SideData;
};

#define DVP_FLAG_TOP_FIELD_FIRST 0x00000001
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,13 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
else
pVideoPicture->dts = m_dts;

// ATSC A53 Closed Captions (side data)
sd = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_A53_CC);
if (sd)
{
pVideoPicture->SetA53SideData(sd->buf);
}

m_dts = DVD_NOPTS_VALUE;

int64_t bpts = m_pFrame->best_effort_timestamp;
Expand Down
232 changes: 15 additions & 217 deletions xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxCC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,80 +10,17 @@

#include "DVDDemuxUtils.h"
#include "cores/VideoPlayer/DVDCodecs/Overlay/contrib/cc_decoder708.h"
#include "cores/VideoPlayer/Interface/CaptionBlock.h"
#include "cores/VideoPlayer/Interface/TimingConstants.h"

#include <algorithm>

class CBitstream
namespace
{
public:
CBitstream(uint8_t *data, int bits)
{
m_data = data;
m_offset = 0;
m_len = bits;
m_error = false;
}
unsigned int readBits(int num)
{
int r = 0;
while (num > 0)
{
if (m_offset >= m_len)
{
m_error = true;
return 0;
}
num--;
if (m_data[m_offset / 8] & (1 << (7 - (m_offset & 7))))
r |= 1 << num;
m_offset++;
}
return r;
}
unsigned int readGolombUE(int maxbits = 32)
{
int lzb = -1;
int bits = 0;
for (int b = 0; !b; lzb++, bits++)
{
if (bits > maxbits)
return 0;
b = readBits(1);
}
return (1 << lzb) - 1 + readBits(lzb);
}

private:
uint8_t *m_data;
int m_offset;
int m_len;
bool m_error;
};

class CCaptionBlock
{
CCaptionBlock(const CCaptionBlock&) = delete;
CCaptionBlock& operator=(const CCaptionBlock&) = delete;
public:
explicit CCaptionBlock(int size)
{
m_data = (uint8_t*)malloc(size);
m_size = size;
m_pts = 0.0; //silence coverity uninitialized warning, is set elsewhere
}
virtual ~CCaptionBlock()
{
free(m_data);
}
double m_pts;
uint8_t *m_data;
int m_size;
};

bool reorder_sort (CCaptionBlock *lhs, CCaptionBlock *rhs)
bool reorder_sort(CCaptionBlock* lhs, CCaptionBlock* rhs)
{
return (lhs->m_pts > rhs->m_pts);
return (lhs->GetPTS() > rhs->GetPTS());
}
}

CDVDDemuxCC::CDVDDemuxCC(AVCodecID codec) : m_codec(codec)
Expand Down Expand Up @@ -127,159 +64,20 @@ int CDVDDemuxCC::GetNrOfStreams() const
return m_streams.size();
}

DemuxPacket* CDVDDemuxCC::Read(DemuxPacket *pSrcPacket)
DemuxPacket* CDVDDemuxCC::Process(CCaptionBlock* captionBlock)
{
DemuxPacket *pPacket = NULL;
uint32_t startcode = 0xffffffff;
int picType = 0;
int p = 0;
int len;

if (!pSrcPacket)
{
pPacket = Decode();
return pPacket;
}
if (pSrcPacket->pts == DVD_NOPTS_VALUE)
{
return pPacket;
}

while (!m_ccTempBuffer.empty())
{
m_ccReorderBuffer.push_back(m_ccTempBuffer.back());
m_ccTempBuffer.pop_back();
}

while ((len = pSrcPacket->iSize - p) > 3)
if (captionBlock)
{
if ((startcode & 0xffffff00) == 0x00000100)
{
if (m_codec == AV_CODEC_ID_MPEG2VIDEO)
{
int scode = startcode & 0xFF;
if (scode == 0x00)
{
if (len > 4)
{
uint8_t *buf = pSrcPacket->pData + p;
picType = (buf[1] & 0x38) >> 3;
}
}
else if (scode == 0xb2) // user data
{
uint8_t *buf = pSrcPacket->pData + p;
if (len >= 6 &&
buf[0] == 'G' && buf[1] == 'A' && buf[2] == '9' && buf[3] == '4' &&
buf[4] == 3 && (buf[5] & 0x40))
{
int cc_count = buf[5] & 0x1f;
if (cc_count > 0 && len >= 7 + cc_count * 3)
{
CCaptionBlock *cc = new CCaptionBlock(cc_count * 3);
memcpy(cc->m_data, buf + 7, cc_count * 3);
cc->m_pts = pSrcPacket->pts;
if (picType == 1 || picType == 2)
m_ccTempBuffer.push_back(cc);
else
m_ccReorderBuffer.push_back(cc);
}
}
else if (len >= 6 &&
buf[0] == 'C' && buf[1] == 'C' && buf[2] == 1)
{
int oddidx = (buf[4] & 0x80) ? 0 : 1;
int cc_count = (buf[4] & 0x3e) >> 1;
int extrafield = buf[4] & 0x01;
if (extrafield)
cc_count++;

if (cc_count > 0 && len >= 5 + cc_count * 3 * 2)
{
CCaptionBlock *cc = new CCaptionBlock(cc_count * 3);
uint8_t *src = buf + 5;
uint8_t *dst = cc->m_data;

for (int i = 0; i < cc_count; i++)
{
for (int j = 0; j < 2; j++)
{
if (i == cc_count - 1 && extrafield && j == 1)
break;

if ((oddidx == j) && (src[0] == 0xFF))
{
dst[0] = 0x04;
dst[1] = src[1];
dst[2] = src[2];
dst += 3;
}
src += 3;
}
}
cc->m_pts = pSrcPacket->pts;
m_ccReorderBuffer.push_back(cc);
picType = 1;
}
}
}
}
else if (m_codec == AV_CODEC_ID_H264)
{
int scode = startcode & 0x9F;
// slice data comes after SEI
if (scode >= 1 && scode <= 5)
{
uint8_t *buf = pSrcPacket->pData + p;
CBitstream bs(buf, len * 8);
bs.readGolombUE();
int sliceType = bs.readGolombUE();
if (sliceType == 2 || sliceType == 7) // I slice
picType = 1;
else if (sliceType == 0 || sliceType == 5) // P slice
picType = 2;
if (picType == 0)
{
while (!m_ccTempBuffer.empty())
{
m_ccReorderBuffer.push_back(m_ccTempBuffer.back());
m_ccTempBuffer.pop_back();
}
}
}
if (scode == 0x06) // SEI
{
uint8_t *buf = pSrcPacket->pData + p;
if (len >= 12 &&
buf[3] == 0 && buf[4] == 49 &&
buf[5] == 'G' && buf[6] == 'A' && buf[7] == '9' && buf[8] == '4' && buf[9] == 3)
{
uint8_t *userdata = buf + 10;
int cc_count = userdata[0] & 0x1f;
if (len >= cc_count * 3 + 10)
{
CCaptionBlock *cc = new CCaptionBlock(cc_count * 3);
memcpy(cc->m_data, userdata + 2, cc_count * 3);
cc->m_pts = pSrcPacket->pts;
m_ccTempBuffer.push_back(cc);
}
}
}
}
}
startcode = startcode << 8 | pSrcPacket->pData[p++];
m_ccReorderBuffer.push_back(captionBlock);
}

if ((picType == 1 || picType == 2) && !m_ccReorderBuffer.empty())
if (!m_ccDecoder)
{
if (!m_ccDecoder)
{
if (!OpenDecoder())
return NULL;
}
std::sort(m_ccReorderBuffer.begin(), m_ccReorderBuffer.end(), reorder_sort);
pPacket = Decode();
if (!OpenDecoder())
return NULL;
}
std::sort(m_ccReorderBuffer.begin(), m_ccReorderBuffer.end(), reorder_sort);
DemuxPacket* pPacket = Decode();
return pPacket;
}

Expand Down Expand Up @@ -371,8 +169,8 @@ DemuxPacket* CDVDDemuxCC::Decode()
{
CCaptionBlock *cc = m_ccReorderBuffer.back();
m_ccReorderBuffer.pop_back();
m_curPts = cc->m_pts;
m_ccDecoder->Decode(cc->m_data, cc->m_size);
m_curPts = cc->GetPTS();
m_ccDecoder->Decode(cc->GetData());
delete cc;
}

Expand Down
2 changes: 1 addition & 1 deletion xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxCC.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CDVDDemuxCC : public CDVDDemux
std::vector<CDemuxStream*> GetStreams() const override;
int GetNrOfStreams() const override;

DemuxPacket* Read(DemuxPacket *packet);
DemuxPacket* Process(CCaptionBlock* captionBlock);
static void Handler(int service, void *userdata);

protected:
Expand Down
Loading

0 comments on commit 6f4ce73

Please sign in to comment.