vlc | branch: master | Francois Cartegnie <[email protected]> | 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 _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
