/* * Copyright (C) 2005-2014 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ #include "DVDDemuxUtils.h" #include "DVDClock.h" #include "DVDDemuxCC.h" #include "cores/dvdplayer/DVDCodecs/Overlay/contrib/cc_decoder708.h" #include "utils/log.h" #include class CBitstream { 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 { public: CCaptionBlock(int size) { m_data = (uint8_t*)malloc(size); m_size = size; } virtual ~CCaptionBlock() { free(m_data); } double m_pts; uint8_t *m_data; int m_size; }; bool reorder_sort (CCaptionBlock *lhs, CCaptionBlock *rhs) { return (lhs->m_pts > rhs->m_pts); } CDVDDemuxCC::CDVDDemuxCC(AVCodecID codec) { m_hasData = false; m_curPts = 0; m_ccDecoder = NULL; m_codec = codec; } CDVDDemuxCC::~CDVDDemuxCC() { Dispose(); } CDemuxStream* CDVDDemuxCC::GetStream(int iStreamId) { return &m_streams[iStreamId]; } int CDVDDemuxCC::GetNrOfStreams() { return m_streams.size(); } DemuxPacket* CDVDDemuxCC::Read(DemuxPacket *pSrcPacket) { 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 ((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++]; } if ((picType == 1 || picType == 2) && !m_ccReorderBuffer.empty()) { if (!m_ccDecoder) { if (!OpenDecoder()) return NULL; } std::sort(m_ccReorderBuffer.begin(), m_ccReorderBuffer.end(), reorder_sort); pPacket = Decode(); } return pPacket; } void CDVDDemuxCC::Handler(int service, void *userdata) { CDVDDemuxCC *ctx = (CDVDDemuxCC*)userdata; unsigned int idx; // switch back from 608 fallback if we got 708 if (ctx->m_ccDecoder->m_seen608 && ctx->m_ccDecoder->m_seen708) { for (idx = 0; idx < ctx->m_streamdata.size(); idx++) { if (ctx->m_streamdata[idx].service == 0) break; } if (idx < ctx->m_streamdata.size()) { ctx->m_streamdata.erase(ctx->m_streamdata.begin() + idx); ctx->m_ccDecoder->m_seen608 = false; } if (service == 0) return; } for (idx = 0; idx < ctx->m_streamdata.size(); idx++) { if (ctx->m_streamdata[idx].service == service) break; } if (idx >= ctx->m_streamdata.size()) { CDemuxStreamSubtitle stream; strcpy(stream.language, "cc"); stream.codec = AV_CODEC_ID_TEXT; stream.iPhysicalId = service; stream.iId = idx; ctx->m_streams.push_back(stream); streamdata data; data.streamIdx = idx; data.service = service; ctx->m_streamdata.push_back(data); if (service == 0) ctx->m_ccDecoder->m_seen608 = true; else ctx->m_ccDecoder->m_seen708 = true; } ctx->m_streamdata[idx].pts = ctx->m_curPts; ctx->m_streamdata[idx].hasData = true; ctx->m_hasData = true; } bool CDVDDemuxCC::OpenDecoder() { m_ccDecoder = new CDecoderCC708(); m_ccDecoder->Init(Handler, this); return true; } void CDVDDemuxCC::Dispose() { m_streams.clear(); m_streamdata.clear(); delete m_ccDecoder; m_ccDecoder = NULL; while (!m_ccReorderBuffer.empty()) { delete m_ccReorderBuffer.back(); m_ccReorderBuffer.pop_back(); } while (!m_ccTempBuffer.empty()) { delete m_ccTempBuffer.back(); m_ccTempBuffer.pop_back(); } } DemuxPacket* CDVDDemuxCC::Decode() { DemuxPacket *pPacket = NULL; while(!m_hasData && !m_ccReorderBuffer.empty()) { CCaptionBlock *cc = m_ccReorderBuffer.back(); m_ccReorderBuffer.pop_back(); m_curPts = cc->m_pts; m_ccDecoder->Decode(cc->m_data, cc->m_size); delete cc; } if (m_hasData) { for (unsigned int i=0; im_cc608decoder->text; len = m_ccDecoder->m_cc608decoder->textlen; } else { data = m_ccDecoder->m_cc708decoders[service].text; len = m_ccDecoder->m_cc708decoders[service].textlen; } pPacket = CDVDDemuxUtils::AllocateDemuxPacket(len); pPacket->iSize = len; memcpy(pPacket->pData, data, pPacket->iSize); pPacket->iStreamId = i; pPacket->pts = m_streamdata[i].pts; pPacket->duration = 0; m_streamdata[i].hasData = false; break; } m_hasData = false; } } return pPacket; }