Skip to content
Browse files

Adds mythccextractor, which extracts closed caption and subtitle stre…

…ams from DVB and ATSC recordings.

This is modeled on the functionality of ccextractor, but currently extacts all streams in one go rather than having you select the streams you want. It places text based captions in SRT files and places image based ones in png's one subdirectory for each language.

I've tested that this doesn't break 608, 708 and DVB teletext and subtitle parsing within the player for the test files I have and does successfully extract all caption and subtitle streams.

One deficiency I noticed was that the primary caption/teletext stream seems to have multiple lines added to the SRT files for each piece of text, perhaps this has to do with text being flushed to the screen more often in these files than in secondary streams, but in any case the secondary streams appear to read better.

This will currently only work with completed recordings. My hope is that limitation will be addressed in a future commit.

This is a code contribution from Digital Nirvana, Inc.
  • Loading branch information
daniel-kristjansson committed Jul 19, 2011
1 parent bfacfb9 commit 48079bbda6e274754e3db72df8aca159d02aeb8e
@@ -16,6 +16,7 @@ const char *kFlaggerInUseID = "flagger";
const char *kTranscoderInUseID = "transcoder";
const char *kPreviewGeneratorInUseID = "preview_generator";
const char *kJobQueueInUseID = "jobqueue";
const char *kCCExtractorInUseID = "ccextractor";

QString toString(MarkTypes type)
@@ -31,6 +31,7 @@ MPUBLIC extern const char *kFlaggerInUseID;
MPUBLIC extern const char *kTranscoderInUseID;
MPUBLIC extern const char *kPreviewGeneratorInUseID;
MPUBLIC extern const char *kJobQueueInUseID;
MPUBLIC extern const char *kCCExtractorInUseID;

typedef QHash<QString,QString> InfoMap;

@@ -18,6 +18,7 @@
#define MYTH_APPNAME_MYTHTV_SETUP "mythtv-setup"
#define MYTH_APPNAME_MYTHFILLDATABASE "mythfilldatabase"
#define MYTH_APPNAME_MYTHCOMMFLAG "mythcommflag"
#define MYTH_APPNAME_MYTHCCEXTRACTOR "mythccextractor"
#define MYTH_APPNAME_MYTHPREVIEWGEN "mythpreviewgen"
#define MYTH_APPNAME_MYTHTRANSCODE "mythtranscode"
#define MYTH_APPNAME_MYTHWELCOME "mythwelcome"
@@ -292,7 +292,6 @@ AvFormatDecoder::AvFormatDecoder(MythPlayer *parent,
ccd608(new CC608Decoder(parent->GetCC608Reader())),
ccd708(new CC708Decoder(parent->GetCC708Reader())),
ttd(new TeletextDecoder(parent->GetTeletextReader())),
// Interactive TV
// Audio
@@ -741,7 +740,10 @@ void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
if (decoded_video_frame)
decoded_video_frame = NULL;

if (doflush)
@@ -1003,7 +1005,7 @@ int AvFormatDecoder::OpenFile(RingBuffer *rbuffer, bool novideo,
#endif // USING_MHEG

// Try to get a position map from the recorder if we don't have one yet.
if (!recordingHasPositionMap)
if (!recordingHasPositionMap && !is_db_ignored)
if ((m_playbackinfo) || livetv || watchingrecording)
@@ -3042,7 +3044,13 @@ bool AvFormatDecoder::ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic)

VideoFrame *picframe = (VideoFrame *)(mpa_pic->opaque);

if (!directrendering)
if (special_decode & kAVSpecialDecode_NoDecode)
// Do nothing, we just want the pts, captions, subtites, etc.
// So we can release the unconverted blank video frame to the
// display queue.
else if (!directrendering)
AVPicture tmppicture;

@@ -3301,7 +3309,7 @@ void AvFormatDecoder::ProcessDSMCCPacket(

bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)
if (!subReader)
if (!m_parent->GetSubReader(pkt->stream_index))
return true;

long long pts = 0;
@@ -3334,7 +3342,7 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)
else if (pkt->stream_index == subIdx)
else if (decodeAllSubtitles || pkt->stream_index == subIdx)
QMutexLocker locker(avcodeclock);
avcodec_decode_subtitle2(curstream->codec, &subtitle, &gotSubtitles,
@@ -3351,30 +3359,33 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)

if (subReader)
curstream->codec->codec_id == CODEC_ID_XSUB);
subtitle, curstream->codec->codec_id == CODEC_ID_XSUB);

return true;

bool AvFormatDecoder::ProcessRawTextPacket(AVPacket *pkt)
if (!subReader ||
if (!decodeAllSubtitles ||
selectedTrack[kTrackTypeRawText].av_stream_index != pkt->stream_index)
return false;

if (!m_parent->GetSubReader(pkt->stream_index+0x2000))
return false;

QTextCodec *codec = QTextCodec::codecForName("utf-8");
QTextDecoder *dec = codec->makeDecoder();
QString text = dec->toUnicode((const char*)pkt->data, pkt->size);
QStringList list = text.split('\n', QString::SkipEmptyParts);
delete dec;
subReader->AddRawTextSubtitle(list, pkt->convergence_duration);

AddRawTextSubtitle(list, pkt->convergence_duration);

return true;

@@ -314,7 +314,6 @@ class AvFormatDecoder : public DecoderBase
CC608Decoder *ccd608;
CC708Decoder *ccd708;
TeletextDecoder *ttd;
SubtitleReader *subReader;
int cc608_parity_table[256];
/// Lookup table for whether a stream was seen in the PMT
/// entries 0-3 correspond to CEA-608 CC1 through CC4, while

0 comments on commit 48079bb

Please sign in to comment.
You can’t perform that action at this time.