[vlc-commits] sout: sdi: add support for captions
Francois Cartegnie
git at videolan.org
Wed Sep 5 10:36:43 CEST 2018
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Tue Jul 24 12:52:31 2018 +0200| [03fa0d6151a3f35b81925712637e5f9f2efd3c43] | committer: Francois Cartegnie
sout: sdi: add support for captions
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=03fa0d6151a3f35b81925712637e5f9f2efd3c43
---
modules/stream_out/sdi/Ancillary.cpp | 124 ++++++++++++++++++++++++++++++++
modules/stream_out/sdi/Ancillary.hpp | 12 ++++
modules/stream_out/sdi/DBMSDIOutput.cpp | 23 ++++--
modules/stream_out/sdi/DBMSDIOutput.hpp | 4 +-
modules/stream_out/sdi/SDIOutput.cpp | 14 ++++
modules/stream_out/sdi/SDIOutput.hpp | 3 +
modules/stream_out/sdi/SDIStream.cpp | 64 ++++++++++++++++-
modules/stream_out/sdi/SDIStream.hpp | 24 +++++++
8 files changed, 259 insertions(+), 9 deletions(-)
diff --git a/modules/stream_out/sdi/Ancillary.cpp b/modules/stream_out/sdi/Ancillary.cpp
index 67f7acba2e..d4528b3f06 100644
--- a/modules/stream_out/sdi/Ancillary.cpp
+++ b/modules/stream_out/sdi/Ancillary.cpp
@@ -23,6 +23,7 @@
#endif
#include "Ancillary.hpp"
+#include <cassert>
using namespace sdi;
@@ -98,3 +99,126 @@ void AFD::FillBuffer(uint8_t *p_buf, size_t i_buf)
put_le32(&p_buf, afd[w*6+4] | (afd[w*6+5] << 20));
}
}
+
+Captions::Captions(const uint8_t *p, size_t s,
+ unsigned num, unsigned den)
+{
+ p_data = p;
+ i_data = s;
+ vlc_ureduce(&num, &den, num, den, 0);
+ if (num == 24000 && den == 1001) {
+ rate = 1;
+ } else if (num == 24 && den == 1) {
+ rate = 2;
+ } else if (num == 25 && den == 1) {
+ rate = 3;
+ } else if (num == 30000 && den == 1001) {
+ rate = 4;
+ } else if (num == 30 && den == 1) {
+ rate = 5;
+ } else if (num == 50 && den == 1) {
+ rate = 6;
+ } else if (num == 60000 && den == 1001) {
+ rate = 7;
+ } else if (num == 60 && den == 1) {
+ rate = 8;
+ } else {
+ rate = 1;
+ }
+}
+
+Captions::~Captions()
+{
+
+}
+
+void Captions::FillBuffer(uint8_t *p_buf, size_t i_buf)
+{
+ uint8_t cc_count = i_data / 3;
+ if (cc_count == 0)
+ return;
+
+ uint16_t len = 6 /* vanc header */ + 9 /* cdp header */ + 3 * cc_count +/* cc_data */
+ 4 /* cdp footer */ + 1 /* vanc checksum */;
+
+ static uint16_t hdr = 0; /* cdp counter */
+ size_t s = ((len + 5) / 6) * 6; /* align to 6 for v210 conversion */
+
+ if(i_buf < s / 6 * 16)
+ return;
+
+ uint16_t *cdp = new uint16_t[s];
+
+ uint16_t cdp_header[6+9] = {
+ /* VANC header = 6 words */
+ 0x000, 0x3ff, 0x3ff, /* Ancillary Data Flag */
+
+ /* following words need parity bits */
+
+ 0x61, /* Data ID */
+ 0x01, /* Secondary Data I D= CEA-708 */
+ (uint16_t)(len - 6 - 1), /* Data Count (not including VANC header) */
+
+ /* cdp header */
+
+ 0x96, // header id
+ 0x69,
+ (uint16_t)(len - 6 - 1),
+ (uint16_t)((rate << 4) | 0x0f),
+ 0x43, // cc_data_present | caption_service_active | reserved
+ (uint16_t)(hdr >> 8),
+ (uint16_t)(hdr & 0xff),
+ 0x72, // ccdata_id
+ (uint16_t)(0xe0 | cc_count), // cc_count
+ };
+
+ /* cdp header */
+ memcpy(cdp, cdp_header, sizeof(cdp_header));
+
+ /* cdp data */
+ for (size_t i = 0; i < cc_count; i++) { // copy cc_data
+ cdp[6+9+3*i+0] = p_data[3*i+0] /*| 0xfc*/; // marker bits + cc_valid
+ cdp[6+9+3*i+1] = p_data[3*i+1];
+ cdp[6+9+3*i+2] = p_data[3*i+2];
+ }
+
+ /* cdp footer */
+ cdp[len-5] = 0x74; // footer id
+ cdp[len-4] = hdr >> 8;
+ cdp[len-3] = hdr & 0xff;
+ hdr++;
+
+ /* cdp checksum */
+ uint8_t sum = 0;
+ for (uint16_t i = 6; i < len - 2; i++) {
+ sum += cdp[i];
+ sum &= 0xff;
+ }
+ cdp[len-2] = sum ? 256 - sum : 0;
+
+ /* parity bit */
+ for (uint16_t i = 3; i < len - 1; i++)
+ cdp[i] |= vlc_parity(cdp[i]) ? 0x100 : 0x200;
+
+ /* vanc checksum */
+ uint16_t vanc_sum = 0;
+ for (uint16_t i = 3; i < len - 1; i++) {
+ vanc_sum += cdp[i];
+ vanc_sum &= 0x1ff;
+ }
+ cdp[len - 1] = vanc_sum | ((~vanc_sum & 0x100) << 1);
+
+ /* pad */
+ for (size_t i = len; i < s; i++)
+ cdp[i] = 0x040;
+
+ /* convert to v210 and write into VBI line 15 of VANC */
+ for (size_t w = 0; w < s / 6 ; w++) {
+ put_le32(&p_buf, cdp[w*6+0] << 10);
+ put_le32(&p_buf, cdp[w*6+1] | (cdp[w*6+2] << 20));
+ put_le32(&p_buf, cdp[w*6+3] << 10);
+ put_le32(&p_buf, cdp[w*6+4] | (cdp[w*6+5] << 20));
+ }
+
+ delete[] cdp;
+}
diff --git a/modules/stream_out/sdi/Ancillary.hpp b/modules/stream_out/sdi/Ancillary.hpp
index 56e095f2b1..e2c18c73c2 100644
--- a/modules/stream_out/sdi/Ancillary.hpp
+++ b/modules/stream_out/sdi/Ancillary.hpp
@@ -44,6 +44,18 @@ namespace sdi
uint8_t ar;
};
+ class Captions : public Ancillary
+ {
+ public:
+ Captions(const uint8_t *, size_t, unsigned, unsigned);
+ virtual ~Captions();
+ virtual void FillBuffer(uint8_t *, size_t);
+
+ private:
+ const uint8_t *p_data;
+ size_t i_data;
+ unsigned rate;
+ };
}
#endif // ANCILLARY_HPP
diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index 57f48a4a0d..cf56f92e2a 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -505,7 +505,7 @@ int DBMSDIOutput::Process()
picture_t *p;
while((p = reinterpret_cast<picture_t *>(videoBuffer.Dequeue())))
- ProcessVideo(p);
+ ProcessVideo(p, reinterpret_cast<block_t *>(captionsBuffer.Dequeue()));
block_t *b;
while((b = reinterpret_cast<block_t *>(audioBuffer.Dequeue())))
@@ -549,7 +549,7 @@ int DBMSDIOutput::ProcessAudio(block_t *p_block)
return result != S_OK ? VLC_EGENERIC : VLC_SUCCESS;
}
-int DBMSDIOutput::ProcessVideo(picture_t *picture)
+int DBMSDIOutput::ProcessVideo(picture_t *picture, block_t *p_cc)
{
mtime_t now = vlc_tick_now();
@@ -566,13 +566,13 @@ int DBMSDIOutput::ProcessVideo(picture_t *picture)
picture_Hold(video.pic_nosignal);
video.pic_nosignal->date = now;
- doProcessVideo(picture);
+ doProcessVideo(picture, NULL);
}
- return doProcessVideo(picture);
+ return doProcessVideo(picture, p_cc);
}
-int DBMSDIOutput::doProcessVideo(picture_t *picture)
+int DBMSDIOutput::doProcessVideo(picture_t *picture, block_t *p_cc)
{
HRESULT result;
int w, h, stride, length, ret = VLC_EGENERIC;
@@ -616,6 +616,17 @@ int DBMSDIOutput::doProcessVideo(picture_t *picture)
sdi::AFD afd(ancillary.afd, ancillary.ar);
afd.FillBuffer(reinterpret_cast<uint8_t*>(buf), stride);
+ if(p_cc)
+ {
+ result = vanc->GetBufferForVerticalBlankingLine(ancillary.captions_line, &buf);
+ if (result != S_OK) {
+ msg_Err(p_stream, "Failed to get VBI line %u: %d", ancillary.captions_line, result);
+ goto error;
+ }
+ sdi::Captions captions(p_cc->p_buffer, p_cc->i_buffer, timescale, frameduration);
+ captions.FillBuffer(reinterpret_cast<uint8_t*>(buf), stride);
+ }
+
sdi::V210::Convert(picture, stride, frame_bytes);
result = pDLVideoFrame->SetAncillaryData(vanc);
@@ -662,6 +673,8 @@ end:
ret = VLC_SUCCESS;
error:
+ if(p_cc)
+ block_Release(p_cc);
picture_Release(picture);
if (pDLVideoFrame)
pDLVideoFrame->Release();
diff --git a/modules/stream_out/sdi/DBMSDIOutput.hpp b/modules/stream_out/sdi/DBMSDIOutput.hpp
index 915ac335a7..7c1de10bf2 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.hpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.hpp
@@ -39,7 +39,7 @@ namespace sdi_sout
virtual int Process(); /* impl */
protected:
- int ProcessVideo(picture_t *);
+ int ProcessVideo(picture_t *, block_t *);
int ProcessAudio(block_t *);
virtual int ConfigureVideo(const video_format_t *); /* impl */
virtual int ConfigureAudio(const audio_format_t *); /* impl */
@@ -59,7 +59,7 @@ namespace sdi_sout
const char *ErrorToString(long i_code);
IDeckLinkDisplayMode * MatchDisplayMode(const video_format_t *,
BMDDisplayMode = bmdDisplayModeNotSupported);
- int doProcessVideo(picture_t *);
+ int doProcessVideo(picture_t *, block_t *);
picture_t * CreateNoSignalPicture(const char*, const video_format_t *);
};
}
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index d71e4dd5f3..0048990077 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -49,14 +49,17 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
ancillary.afd = var_InheritInteger(p_stream, CFG_PREFIX "afd");
ancillary.ar = var_InheritInteger(p_stream, CFG_PREFIX "ar");
ancillary.afd_line = var_InheritInteger(p_stream, CFG_PREFIX "afd-line");
+ ancillary.captions_line = 15;
videoStream = NULL;
audioStream = NULL;
+ captionsStream = NULL;
}
SDIOutput::~SDIOutput()
{
videoBuffer.FlushQueued();
audioBuffer.FlushQueued();
+ captionsBuffer.FlushQueued();
if(video.pic_nosignal)
picture_Release(video.pic_nosignal);
es_format_Clean(&video.configuredfmt);
@@ -72,7 +75,10 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
if(ConfigureVideo(&fmt->video) == VLC_SUCCESS)
s = videoStream = dynamic_cast<VideoDecodedStream *>(createStream(id, fmt, &videoBuffer));
if(videoStream)
+ {
videoStream->setOutputFormat(&video.configuredfmt);
+ videoStream->setCaptionsOutputBuffer(&captionsBuffer);
+ }
}
else if(fmt->i_cat == AUDIO_ES && audio.i_channels && !audioStream)
{
@@ -81,6 +87,10 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
if(audioStream)
audioStream->setOutputFormat(&audio.configuredfmt);
}
+ else if(fmt->i_cat == SPU_ES && !captionsStream)
+ {
+ s = captionsStream = dynamic_cast<CaptionsStream *>(createStream(id, fmt, &captionsBuffer));
+ }
return s;
}
@@ -99,6 +109,8 @@ void SDIOutput::Del(AbstractStream *s)
videoStream = NULL;
else if(audioStream == s)
audioStream = NULL;
+ else if(captionsStream == s)
+ captionsStream = NULL;
delete s;
}
@@ -116,6 +128,8 @@ AbstractStream *SDIOutput::createStream(const StreamID &id,
s = new VideoDecodedStream(VLC_OBJECT(p_stream), id, buffer);
else if(fmt->i_cat == AUDIO_ES)
s = new AudioDecodedStream(VLC_OBJECT(p_stream), id, buffer);
+ else if(fmt->i_cat == SPU_ES)
+ s = new CaptionsStream(VLC_OBJECT(p_stream), id, buffer);
else
s = NULL;
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index 4d348f9cd5..f6836f5a93 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -46,8 +46,10 @@ namespace sdi_sout
sout_stream_t *p_stream;
VideoDecodedStream *videoStream;
AudioDecodedStream *audioStream;
+ CaptionsStream *captionsStream;
PictureStreamOutputBuffer videoBuffer;
BlockStreamOutputBuffer audioBuffer;
+ BlockStreamOutputBuffer captionsBuffer;
struct
{
@@ -67,6 +69,7 @@ namespace sdi_sout
{
uint8_t afd, ar;
unsigned afd_line;
+ unsigned captions_line;
} ancillary;
private:
diff --git a/modules/stream_out/sdi/SDIStream.cpp b/modules/stream_out/sdi/SDIStream.cpp
index 749c6b5a4d..892f4c1dc4 100644
--- a/modules/stream_out/sdi/SDIStream.cpp
+++ b/modules/stream_out/sdi/SDIStream.cpp
@@ -22,11 +22,9 @@
#endif
#include "SDIStream.hpp"
-
#include "sdiout.hpp"
#include <vlc_modules.h>
-#include <vlc_codec.h>
#include <vlc_meta.h>
#include <vlc_block.h>
@@ -311,10 +309,16 @@ void VideoDecodedStream::setCallbacks()
dec_cbs.video.format_update = VideoDecCallback_update_format;
dec_cbs.video.buffer_new = VideoDecCallback_new_buffer;
dec_cbs.video.queue = VideoDecCallback_queue;
+ dec_cbs.video.queue_cc = VideoDecCallback_queue_cc;
p_decoder->cbs = &dec_cbs;
}
+void VideoDecodedStream::setCaptionsOutputBuffer(AbstractStreamOutputBuffer *buf)
+{
+ captionsOutputBuffer = buf;
+}
+
void VideoDecodedStream::VideoDecCallback_queue(decoder_t *p_dec, picture_t *p_pic)
{
struct decoder_owner *p_owner;
@@ -322,6 +326,14 @@ void VideoDecodedStream::VideoDecCallback_queue(decoder_t *p_dec, picture_t *p_p
static_cast<VideoDecodedStream *>(p_owner->id)->Output(p_pic);
}
+void VideoDecodedStream::VideoDecCallback_queue_cc(decoder_t *p_dec, block_t *p_block,
+ const decoder_cc_desc_t *)
+{
+ struct decoder_owner *p_owner;
+ p_owner = container_of(p_dec, struct decoder_owner, dec);
+ static_cast<VideoDecodedStream *>(p_owner->id)->QueueCC(p_block);
+}
+
int VideoDecodedStream::VideoDecCallback_update_format(decoder_t *p_dec)
{
struct decoder_owner *p_owner;
@@ -415,6 +427,11 @@ void VideoDecodedStream::Output(picture_t *p_pic)
outputbuffer->Enqueue(p_pic);
}
+void VideoDecodedStream::QueueCC(block_t *p_block)
+{
+ captionsOutputBuffer->Enqueue(p_block);
+}
+
AudioDecodedStream::AudioDecodedStream(vlc_object_t *p_obj,
const StreamID &id,
AbstractStreamOutputBuffer *buffer)
@@ -509,3 +526,46 @@ void AudioDecodedStream::setCallbacks()
dec_cbs.audio.queue = AudioDecCallback_queue;
p_decoder->cbs = &dec_cbs;
}
+
+CaptionsStream::CaptionsStream(vlc_object_t *p_obj, const StreamID &id,
+ AbstractStreamOutputBuffer *buffer)
+ : AbstractStream(p_obj, id, buffer)
+{
+
+}
+
+CaptionsStream::~CaptionsStream()
+{
+ FlushQueued();
+}
+
+bool CaptionsStream::init(const es_format_t *fmt)
+{
+ return (fmt->i_codec == VLC_CODEC_CEA608);
+}
+
+int CaptionsStream::Send(block_t *p_block)
+{
+ if(p_block->i_buffer)
+ outputbuffer->Enqueue(p_block);
+ else
+ block_Release(p_block);
+ return VLC_SUCCESS;
+}
+
+void CaptionsStream::Flush()
+{
+
+}
+
+void CaptionsStream::Drain()
+{
+
+}
+
+void CaptionsStream::FlushQueued()
+{
+ block_t *p;
+ while((p = reinterpret_cast<block_t *>(outputbuffer->Dequeue())))
+ block_Release(p);
+}
diff --git a/modules/stream_out/sdi/SDIStream.hpp b/modules/stream_out/sdi/SDIStream.hpp
index c3c0b979bd..6618cd9b20 100644
--- a/modules/stream_out/sdi/SDIStream.hpp
+++ b/modules/stream_out/sdi/SDIStream.hpp
@@ -23,6 +23,7 @@
#include <vlc_common.h>
#include <vlc_filter.h>
#include <vlc_aout.h>
+#include <vlc_codec.h>
#include <queue>
#include <mutex>
@@ -118,14 +119,19 @@ namespace sdi_sout
AbstractStreamOutputBuffer *);
virtual ~VideoDecodedStream();
virtual void setCallbacks();
+ void setCaptionsOutputBuffer(AbstractStreamOutputBuffer *);
private:
static void VideoDecCallback_queue(decoder_t *, picture_t *);
+ static void VideoDecCallback_queue_cc( decoder_t *, block_t *,
+ const decoder_cc_desc_t * );
static int VideoDecCallback_update_format(decoder_t *);
static picture_t *VideoDecCallback_new_buffer(decoder_t *);
filter_chain_t * VideoFilterCreate(const es_format_t *);
void Output(picture_t *);
+ void QueueCC(block_t *);
filter_chain_t *p_filters_chain;
+ AbstractStreamOutputBuffer *captionsOutputBuffer;
};
# define FRAME_SIZE 1920
@@ -144,6 +150,24 @@ namespace sdi_sout
void Output(block_t *);
aout_filters_t *p_filters;
};
+
+ class CaptionsStream : public AbstractStream
+ {
+ public:
+ CaptionsStream(vlc_object_t *, const StreamID &,
+ AbstractStreamOutputBuffer *);
+ virtual ~CaptionsStream();
+ virtual bool init(const es_format_t *); /* impl */
+ virtual int Send(block_t*);
+ virtual void Flush();
+ virtual void Drain();
+
+ protected:
+ void FlushQueued();
+
+ private:
+ void Output(block_t *);
+ };
}
#endif
More information about the vlc-commits
mailing list