Exemplo n.º 1
0
void CPGSSub::EnqueuePresentationSegment()
{
    if (m_pCurrentPresentationSegment) {
        if (m_pCurrentPresentationSegment->objectCount > 0) {
            m_pCurrentPresentationSegment->CLUT = m_CLUTs[m_pCurrentPresentationSegment->CLUT.id];

            // Get the objects' data
            for (auto& pObject : m_pCurrentPresentationSegment->objects) {
                const CompositionObject& pObjectData = m_compositionObjects[pObject->m_object_id_ref];

                pObject->m_width = pObjectData.m_width;
                pObject->m_height = pObjectData.m_height;

                if (pObjectData.GetRLEData()) {
                    pObject->SetRLEData(pObjectData.GetRLEData(), pObjectData.GetRLEPos(), pObjectData.GetRLEDataSize());
                }
            }

            TRACE_PGSSUB(_T("CPGSSub: Enqueue Presentation Segment %d - %s => ?\n"), m_pCurrentPresentationSegment->composition_descriptor.nNumber,
                         ReftimeToString(m_pCurrentPresentationSegment->rtStart));
            m_pPresentationSegments.AddTail(m_pCurrentPresentationSegment);
        } else {
            TRACE_PGSSUB(_T("CPGSSub: Delete empty Presentation Segment %d\n"), m_pCurrentPresentationSegment->composition_descriptor.nNumber);
            m_pCurrentPresentationSegment.Free();
        }
    }
}
Exemplo n.º 2
0
HRESULT CPGSSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox, bool bRemoveOldSegments)
{
    CAutoLock cAutoLock(&m_csCritSec);

    bool bRendered = false;

    rt -= m_rtCurrentSegmentStart; // Make sure the timing are relative to the current segment start

    if (bRemoveOldSegments) {
        RemoveOldSegments(rt);
    }

    POSITION posPresentationSegment = FindPresentationSegment(rt);

    if (posPresentationSegment) {
        const auto& pPresentationSegment = m_pPresentationSegments.GetAt(posPresentationSegment);

        bool BT709 = m_infoSourceTarget.sourceMatrix == BT_709 ? true : m_infoSourceTarget.sourceMatrix == NONE ? (pPresentationSegment->video_descriptor.nVideoWidth > 720) : false;

        TRACE_PGSSUB(_T("CPGSSub:Render Presentation segment %d --> %s - %s\n"), pPresentationSegment->composition_descriptor.nNumber,
                     ReftimeToString(pPresentationSegment->rtStart + m_rtCurrentSegmentStart),
                     (pPresentationSegment->rtStop == INFINITE_TIME) ? _T("?") : ReftimeToString(pPresentationSegment->rtStop + m_rtCurrentSegmentStart));

        bbox.left = bbox.top = LONG_MAX;
        bbox.right = bbox.bottom = 0;

        POSITION pos = pPresentationSegment->objects.GetHeadPosition();
        while (pos) {
            const auto& pObject = pPresentationSegment->objects.GetNext(pos);

            if (pObject->GetRLEDataSize() && pObject->m_width > 0 && pObject->m_height > 0
                    && spd.w >= (pObject->m_horizontal_position + pObject->m_width) && spd.h >= (pObject->m_vertical_position + pObject->m_height)) {
                pObject->SetPalette(pPresentationSegment->CLUT.size, pPresentationSegment->CLUT.palette, BT709,
                                    m_infoSourceTarget.sourceBlackLevel, m_infoSourceTarget.sourceWhiteLevel, m_infoSourceTarget.targetBlackLevel, m_infoSourceTarget.targetWhiteLevel);
                bbox.left = std::min(pObject->m_horizontal_position, bbox.left);
                bbox.top = std::min(pObject->m_vertical_position, bbox.top);
                bbox.right = std::max(pObject->m_horizontal_position + pObject->m_width, bbox.right);
                bbox.bottom = std::max(pObject->m_vertical_position + pObject->m_height, bbox.bottom);

                TRACE_PGSSUB(_T(" --> Object %d (Pos=%dx%d, Res=%dx%d, SPDRes=%dx%d)\n"),
                             pObject->m_object_id_ref, pObject->m_horizontal_position, pObject->m_vertical_position, pObject->m_width, pObject->m_height, spd.w, spd.h);
                pObject->RenderHdmv(spd);

                bRendered = true;
            } else {
                TRACE_PGSSUB(_T(" --> Invalid object %d\n"), pObject->m_object_id_ref);
            }
        }
    }

    if (!bRendered) {
        bbox = { 0, 0, 0, 0 };
    }

    return S_OK;
}
Exemplo n.º 3
0
HRESULT CPGSSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox, bool bRemoveOldSegments)
{
    CAutoLock cAutoLock(&m_csCritSec);

    bool bRendered = false;

    if (bRemoveOldSegments) {
        RemoveOldSegments(rt);
    }

    POSITION posPresentationSegment = FindPresentationSegment(rt);

    if (posPresentationSegment) {
        const auto& pPresentationSegment = m_pPresentationSegments.GetAt(posPresentationSegment);

        m_eSourceMatrix = ColorConvTable::NONE ? (pPresentationSegment->video_descriptor.nVideoWidth > 720) ? ColorConvTable::BT709 : ColorConvTable::BT601 : m_eSourceMatrix;

        TRACE_PGSSUB(_T("CPGSSub:Render Presentation segment %d --> %s - %s\n"), pPresentationSegment->composition_descriptor.nNumber,
                     ReftimeToString(pPresentationSegment->rtStart),
                     (pPresentationSegment->rtStop == UNKNOWN_TIME) ? _T("?") : ReftimeToString(pPresentationSegment->rtStop));

        bbox.left = bbox.top = LONG_MAX;
        bbox.right = bbox.bottom = 0;

        for (const auto& pObject : pPresentationSegment->objects) {
            if (pObject->GetRLEDataSize() && pObject->m_width > 0 && pObject->m_height > 0
                    && spd.w >= (pObject->m_horizontal_position + pObject->m_width) && spd.h >= (pObject->m_vertical_position + pObject->m_height)) {
                pObject->SetPalette(pPresentationSegment->CLUT.size, pPresentationSegment->CLUT.palette.data(), m_eSourceMatrix);
                bbox.left = std::min(pObject->m_horizontal_position, bbox.left);
                bbox.top = std::min(pObject->m_vertical_position, bbox.top);
                bbox.right = std::max(pObject->m_horizontal_position + pObject->m_width, bbox.right);
                bbox.bottom = std::max(pObject->m_vertical_position + pObject->m_height, bbox.bottom);

                TRACE_PGSSUB(_T(" --> Object %d (Pos=%dx%d, Res=%dx%d, SPDRes=%dx%d)\n"),
                             pObject->m_object_id_ref, pObject->m_horizontal_position, pObject->m_vertical_position, pObject->m_width, pObject->m_height, spd.w, spd.h);
                pObject->RenderHdmv(spd);

                bRendered = true;
            } else {
                TRACE_PGSSUB(_T(" --> Invalid object %d\n"), pObject->m_object_id_ref);
            }
        }
    }

    if (!bRendered) {
        bbox = { 0, 0, 0, 0 };
    }

    return S_OK;
}
Exemplo n.º 4
0
void CPGSSub::ParseObject(CGolombBuffer* pGBuffer, size_t nUnitSize)   // #498
{
    short object_id = pGBuffer->ReadShort();
    if (object_id < 0 || size_t(object_id) >= m_compositionObjects.size()) {
        ASSERT(FALSE); // This is not supposed to happen
        return;
    }

    CompositionObject& pObject = m_compositionObjects[object_id];

    pObject.m_version_number = pGBuffer->ReadByte();
    BYTE m_sequence_desc = pGBuffer->ReadByte();

    if (m_sequence_desc & 0x80) {
        int object_data_length = (int)pGBuffer->BitRead(24);

        pObject.m_width = pGBuffer->ReadShort();
        pObject.m_height = pGBuffer->ReadShort();

        pObject.SetRLEData(pGBuffer->GetBufferPos(), nUnitSize - 11, object_data_length - 4);

        TRACE_PGSSUB(_T("CPGSSub:ParseObject %d (size=%ld, %dx%d)\n"), object_id, object_data_length, pObject.m_width, pObject.m_height);
    } else {
        pObject.AppendRLEData(pGBuffer->GetBufferPos(), nUnitSize - 4);
    }
}
Exemplo n.º 5
0
int CPGSSub::ParsePresentationSegment(REFERENCE_TIME rt, CGolombBuffer* pGBuffer)
{
    m_pCurrentPresentationSegment.Free();
    m_pCurrentPresentationSegment.Attach(DEBUG_NEW HDMV_PRESENTATION_SEGMENT());

    m_pCurrentPresentationSegment->rtStart = rt;
    m_pCurrentPresentationSegment->rtStop = UNKNOWN_TIME; // Unknown for now

    ParseVideoDescriptor(pGBuffer, &m_pCurrentPresentationSegment->video_descriptor);
    ParseCompositionDescriptor(pGBuffer, &m_pCurrentPresentationSegment->composition_descriptor);
    m_pCurrentPresentationSegment->palette_update_flag = !!(pGBuffer->ReadByte() & 0x80);
    m_pCurrentPresentationSegment->CLUT.id = pGBuffer->ReadByte();
    m_pCurrentPresentationSegment->objectCount = pGBuffer->ReadByte();

    TRACE_PGSSUB(_T("CPGSSub::ParsePresentationSegment Size = %d, state = %#x, nObjectNumber = %d\n"), pGBuffer->GetSize(),
                 m_pCurrentPresentationSegment->composition_descriptor.bState, m_pCurrentPresentationSegment->objectCount);

    for (int i = 0; i < m_pCurrentPresentationSegment->objectCount; i++) {
        std::unique_ptr<CompositionObject> pCompositionObject(DEBUG_NEW CompositionObject());
        if (ParseCompositionObject(pGBuffer, pCompositionObject)) {
            m_pCurrentPresentationSegment->objects.emplace_back(std::move(pCompositionObject));
        }
    }

    return m_pCurrentPresentationSegment->objectCount;
}
Exemplo n.º 6
0
void CPGSSub::EnqueuePresentationSegment()
{
    if (m_pCurrentPresentationSegment) {
        // TODO: improve the handling of subtitles without known end time in the queue
        //       so that empty segments can be ignored again safely
        /*if (m_pCurrentPresentationSegment->objectCount > 0) */{
            m_pCurrentPresentationSegment->CLUT = m_CLUTs[m_pCurrentPresentationSegment->CLUT.id];

            // Get the objects' data
            POSITION pos = m_pCurrentPresentationSegment->objects.GetHeadPosition();
            while (pos) {
                const auto& pObject = m_pCurrentPresentationSegment->objects.GetNext(pos);

                const CompositionObject& pObjectData = m_compositionObjects[pObject->m_object_id_ref];

                pObject->m_width = pObjectData.m_width;
                pObject->m_height = pObjectData.m_height;

                if (pObjectData.GetRLEData()) {
                    pObject->SetRLEData(pObjectData.GetRLEData(), pObjectData.GetRLEPos(), pObjectData.GetRLEDataSize());
                }
            }

            TRACE_PGSSUB(_T("CPGSSub: Enqueue Presentation Segment %d - %s => ?\n"), m_pCurrentPresentationSegment->composition_descriptor.nNumber,
                         ReftimeToString(m_pCurrentPresentationSegment->rtStart + m_rtCurrentSegmentStart));
            m_pPresentationSegments.AddTail(m_pCurrentPresentationSegment);
        }/* else {
            TRACE_PGSSUB(_T("CPGSSub: Delete empty Presentation Segment %d\n"), m_pCurrentPresentationSegment->composition_descriptor.nNumber);
            m_pCurrentPresentationSegment.Free();
        }*/
    }
}
Exemplo n.º 7
0
void CPGSSub::RemoveOldSegments(REFERENCE_TIME rt)
{
    // Cleanup the old presentation segments. We keep a 2 min buffer to play nice with the queue.
    while (!m_pPresentationSegments.IsEmpty()
            && m_pPresentationSegments.GetHead()->rtStop != UNKNOWN_TIME
            && m_pPresentationSegments.GetHead()->rtStop + 120 * 10000000i64 < rt) {
        TRACE_PGSSUB(_T("CPGSSub::RemoveOldSegments Remove presentation segment %d %s => %s (rt=%s)\n"),
                     m_pPresentationSegments.GetHead()->composition_descriptor.nNumber,
                     ReftimeToString(m_pPresentationSegments.GetHead()->rtStart),
                     ReftimeToString(m_pPresentationSegments.GetHead()->rtStop),
                     ReftimeToString(rt));
        m_pPresentationSegments.RemoveHeadNoReturn();
    }
}
Exemplo n.º 8
0
void CPGSSub::UpdateTimeStamp(REFERENCE_TIME rtStop)
{
    if (!m_pPresentationSegments.IsEmpty()) {
        const auto& pPresentationSegment = m_pPresentationSegments.GetTail();

        // Since we drop empty segments we might be trying to update a segment that isn't
        // in the queue so we update the timestamp only if it was previously unknown.
        if (pPresentationSegment->rtStop == UNKNOWN_TIME) {
            pPresentationSegment->rtStop = rtStop;

            TRACE_PGSSUB(_T("CPGSSub: Update Presentation Segment TimeStamp %d - %s => %s\n"), pPresentationSegment->composition_descriptor.nNumber,
                         ReftimeToString(pPresentationSegment->rtStart),
                         ReftimeToString(pPresentationSegment->rtStop));
        }
    }
}
Exemplo n.º 9
0
void CPGSSubFile::ParseFile(CString fn)
{
    CFile f;
    if (!f.Open(fn, CFile::modeRead | CFile::shareDenyWrite)) {
        return;
    }

    // Header: Sync code | start time | stop time | segment type | segment size
    std::array < BYTE, 2 + 2 * 4 + 1 + 2 > header;
    const int nExtraSize = 1 + 2; // segment type + segment size
    std::vector<BYTE> segBuff;

    while (!m_bStopParsing && f.Read(header.data(), (UINT)header.size()) == header.size()) {
        // Parse the header
        CGolombBuffer headerBuffer(header.data(), (int)header.size());

        if (WORD(headerBuffer.ReadShort()) != PGS_SYNC_CODE) {
            break;
        }

        REFERENCE_TIME rtStart = REFERENCE_TIME(headerBuffer.ReadDword()) * 1000 / 9;
        REFERENCE_TIME rtStop  = REFERENCE_TIME(headerBuffer.ReadDword()) * 1000 / 9;
        headerBuffer.ReadByte(); // segment type
        WORD wLenSegment = (WORD)headerBuffer.ReadShort();

        // Leave some room to add the segment type and size
        int nLenData = nExtraSize + wLenSegment;
        segBuff.resize(nLenData);
        memcpy(segBuff.data(), &header[header.size() - nExtraSize], nExtraSize);

        // Read the segment
        if (wLenSegment && f.Read(&segBuff[nExtraSize], wLenSegment) != wLenSegment) {
            break;
        }

        // Parse the data (even if the segment size is 0 because the header itself is important)
        TRACE_PGSSUB(_T("--------- CPGSSubFile::ParseFile rtStart=%s, rtStop=%s, len=%d ---------\n"),
                     ReftimeToString(rtStart), ReftimeToString(rtStop), nLenData);
        ParseSample(rtStart, rtStop, segBuff.data(), nLenData);
    }
}
Exemplo n.º 10
0
HRESULT CPGSSub::ParseSample(IMediaSample* pSample)
{
    CheckPointer(pSample, E_POINTER);

    BYTE* pData = nullptr;
    HRESULT hr = pSample->GetPointer(&pData);
    if (FAILED(hr) || !pData) {
        return hr;
    }
    int lSampleLen = pSample->GetActualDataLength();

    REFERENCE_TIME rtStart = INVALID_TIME, rtStop = INVALID_TIME;
    hr = pSample->GetTime(&rtStart, &rtStop);
    if (FAILED(hr)) {
        return hr;
    }

    TRACE_PGSSUB(_T("--------- CPGSSub::ParseSample rtStart=%s, rtStop=%s, len=%d ---------\n"),
                 ReftimeToString(rtStart + m_rtCurrentSegmentStart), ReftimeToString(rtStop + m_rtCurrentSegmentStart), lSampleLen);

    return ParseSample(rtStart, rtStop, pData, lSampleLen);
}
Exemplo n.º 11
0
void CPGSSub::ParseObject(CGolombBuffer* pGBuffer, unsigned short nUnitSize)   // #498
{
    short object_id = pGBuffer->ReadShort();
    ASSERT(object_id < _countof(m_compositionObjects));

    CompositionObject& pObject = m_compositionObjects[object_id];

    pObject.m_version_number = pGBuffer->ReadByte();
    BYTE m_sequence_desc = pGBuffer->ReadByte();

    if (m_sequence_desc & 0x80) {
        int object_data_length = (int)pGBuffer->BitRead(24);

        pObject.m_width = pGBuffer->ReadShort();
        pObject.m_height = pGBuffer->ReadShort();

        pObject.SetRLEData(pGBuffer->GetBufferPos(), nUnitSize - 11, object_data_length - 4);

        TRACE_PGSSUB(_T("CPGSSub:ParseObject %d (size=%ld, %dx%d)\n"), object_id, object_data_length, pObject.m_width, pObject.m_height);
    } else {
        pObject.AppendRLEData(pGBuffer->GetBufferPos(), nUnitSize - 4);
    }
}
Exemplo n.º 12
0
int CPGSSub::ParsePresentationSegment(REFERENCE_TIME rt, CGolombBuffer* pGBuffer)
{
    m_pCurrentPresentationSegment = CAutoPtr<HDMV_PRESENTATION_SEGMENT>(DEBUG_NEW HDMV_PRESENTATION_SEGMENT());

    m_pCurrentPresentationSegment->rtStart = rt;
    m_pCurrentPresentationSegment->rtStop = INFINITE_TIME; // Unknown for now

    ParseVideoDescriptor(pGBuffer, &m_pCurrentPresentationSegment->video_descriptor);
    ParseCompositionDescriptor(pGBuffer, &m_pCurrentPresentationSegment->composition_descriptor);
    m_pCurrentPresentationSegment->palette_update_flag = !!(pGBuffer->ReadByte() & 0x80);
    m_pCurrentPresentationSegment->CLUT.id = pGBuffer->ReadByte();
    m_pCurrentPresentationSegment->objectCount = pGBuffer->ReadByte();

    TRACE_PGSSUB(_T("CPGSSub::ParsePresentationSegment Size = %d, state = %#x, nObjectNumber = %d\n"), pGBuffer->GetSize(),
                 m_pCurrentPresentationSegment->composition_descriptor.bState, m_pCurrentPresentationSegment->objectCount);

    for (int i = 0; i < m_pCurrentPresentationSegment->objectCount; i++) {
        CAutoPtr<CompositionObject> pCompositionObject(DEBUG_NEW CompositionObject());
        ParseCompositionObject(pGBuffer, pCompositionObject);
        m_pCurrentPresentationSegment->objects.AddTail(pCompositionObject);
    }

    return m_pCurrentPresentationSegment->objectCount;
}
Exemplo n.º 13
0
HRESULT CPGSSub::ParseSample(REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, BYTE* pData, size_t nLen)
{
    CheckPointer(pData, E_POINTER);

    CAutoLock cAutoLock(&m_csCritSec);

    CGolombBuffer sampleBuffer(pData, nLen);

    while (!sampleBuffer.IsEOF()) {
        if (m_nCurSegment == NO_SEGMENT) {
            HDMV_SEGMENT_TYPE nSegType = (HDMV_SEGMENT_TYPE)sampleBuffer.ReadByte();
            unsigned short nUnitSize = sampleBuffer.ReadShort();
            nLen -= 3;

            switch (nSegType) {
                case PALETTE:
                case OBJECT:
                case PRESENTATION_SEG:
                case END_OF_DISPLAY:
                    m_nCurSegment = nSegType;
                    AllocSegment(nUnitSize);
                    break;

                case WINDOW_DEF:
                case INTERACTIVE_SEG:
                case HDMV_SUB1:
                case HDMV_SUB2:
                    // Ignored stuff...
                    sampleBuffer.SkipBytes(nUnitSize);
                    break;
                default:
                    return VFW_E_SAMPLE_REJECTED;
            }
        }

        if (m_nCurSegment != NO_SEGMENT) {
            if (m_nSegBufferPos < m_nSegSize) {
                size_t nSize = std::min(m_nSegSize - m_nSegBufferPos, nLen);
                sampleBuffer.ReadBuffer(m_pSegBuffer + m_nSegBufferPos, nSize);
                m_nSegBufferPos += nSize;
            }

            if (m_nSegBufferPos >= m_nSegSize) {
                CGolombBuffer SegmentBuffer(m_pSegBuffer, m_nSegSize);

                switch (m_nCurSegment) {
                    case PALETTE:
                        TRACE_PGSSUB(_T("CPGSSub:PALETTE            %s\n"), ReftimeToString(rtStart));
                        ParsePalette(&SegmentBuffer, m_nSegSize);
                        break;
                    case OBJECT:
                        TRACE_PGSSUB(_T("CPGSSub:OBJECT             %s\n"), ReftimeToString(rtStart));
                        ParseObject(&SegmentBuffer, m_nSegSize);
                        break;
                    case PRESENTATION_SEG:
                        TRACE_PGSSUB(_T("CPGSSub:PRESENTATION_SEG   %s (size=%d)\n"), ReftimeToString(rtStart), m_nSegSize);

                        if (rtStart == INVALID_TIME) {
                            break;
                        }

                        // Update the timestamp for the previous segment
                        UpdateTimeStamp(rtStart);

                        // Parse the new presentation segment
                        ParsePresentationSegment(rtStart, &SegmentBuffer);

                        break;
                    case WINDOW_DEF:
                        //TRACE_PGSSUB(_T("CPGSSub:WINDOW_DEF         %s\n"), ReftimeToString(rtStart));
                        break;
                    case END_OF_DISPLAY:
                        TRACE_PGSSUB(_T("CPGSSub:END_OF_DISPLAY     %s\n"), ReftimeToString(rtStart));
                        // Enqueue the current presentation segment if any
                        EnqueuePresentationSegment();
                        break;
                    default:
                        TRACE_PGSSUB(_T("CPGSSub:UNKNOWN Seg %d     %s\n"), m_nCurSegment, ReftimeToString(rtStart));
                }

                m_nCurSegment = NO_SEGMENT;
            }
        }
    }

    return S_OK;
}