[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