/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can
 *  be found in the License.html file in the root of the source tree.
 */
 
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_DVBSUBTITLE_YES)
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Text/File_DvbSubtitle.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
//---------------------------------------------------------------------------
 
namespace MediaInfoLib
{
 
//***************************************************************************
// Info
//***************************************************************************
 
static const int8u DvbSubtitle_region_depth[8]=
{
    0,
    2,
    4,
    8,
    0,
    0,
    0,
    0,
};
 
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
 
//---------------------------------------------------------------------------
File_DvbSubtitle::File_DvbSubtitle()
:File__Analyze()
{
    //Configuration
    ParserName="DVB Subtitle";
    #if MEDIAINFO_EVENTS
        ParserIDs[0]=MediaInfo_Parser_DvbSubtitle;
    #endif //MEDIAINFO_EVENTS
    #if MEDIAINFO_TRACE
        Trace_Layers_Update(8); //Stream
    #endif //MEDIAINFO_TRACE
    PTS_DTS_Needed=true;
    StreamSource=IsStream;
    MustSynchronize=true;
 
    //In
    Frame_Count_Valid=0;
 
    //Temp
    MustFindDvbHeader=true;
}
 
//---------------------------------------------------------------------------
File_DvbSubtitle::~File_DvbSubtitle()
{
}
 
//***************************************************************************
// Streams management
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::Streams_Fill()
{
    Stream_Prepare(Stream_Text);
    Fill(Stream_Text, 0, Text_Format, "DVB Subtitle");
 
    for (std::map<int8u, subtitle_stream_data>::iterator subtitle_stream=subtitle_streams.begin(); subtitle_stream!=subtitle_streams.end(); ++subtitle_stream)
        for (std::map<int16u, page_data>::iterator page=subtitle_stream->second.pages.begin(); page!=subtitle_stream->second.pages.end(); ++page)
            for (std::map<int8u, region_data>::iterator region=page->second.regions.begin(); region!=page->second.regions.end(); ++region)
            {
                Fill(Stream_Text, 0, "subtitle_stream_id", subtitle_stream->first);
                Fill_SetOptions(Stream_Text, 0, "subtitle_stream_id", "N NI");
                Fill(Stream_Text, 0, "page_id", page->first);
                Fill_SetOptions(Stream_Text, 0, "page_id", "N NI");
                Fill(Stream_Text, 0, "region_id", region->first);
                Fill_SetOptions(Stream_Text, 0, "region_id", "N NI");
                Fill(Stream_Text, 0, "region_horizontal_address", region->second.page_composition_segment?Ztring::ToZtring(region->second.region_horizontal_address):Ztring());
                Fill_SetOptions(Stream_Text, 0, "region_horizontal_address", "N NI");
                Fill(Stream_Text, 0, "region_vertical_address", region->second.page_composition_segment?Ztring::ToZtring(region->second.region_vertical_address):Ztring());
                Fill_SetOptions(Stream_Text, 0, "region_vertical_address", "N NI");
                Fill(Stream_Text, 0, "region_width", region->second.region_composition_segment?Ztring::ToZtring(region->second.region_width):Ztring());
                Fill_SetOptions(Stream_Text, 0, "region_width", "N NI");
                Fill(Stream_Text, 0, "region_height", region->second.region_composition_segment?Ztring::ToZtring(region->second.region_height):Ztring());
                Fill_SetOptions(Stream_Text, 0, "region_height", "N NI");
                Fill(Stream_Text, 0, "region_depth", region->second.region_composition_segment?Ztring::ToZtring(DvbSubtitle_region_depth[region->second.region_depth]):Ztring());
                Fill_SetOptions(Stream_Text, 0, "region_depth", "N NI");
            }
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::Streams_Finish()
{
}
 
//***************************************************************************
// Buffer - Synchro
//***************************************************************************
 
//---------------------------------------------------------------------------
bool File_DvbSubtitle::Synchronize()
{
    //Synchronizing
    if (MustFindDvbHeader)
    {
        while(Buffer_Offset+3<=Buffer_Size)
        {
            if (Buffer[Buffer_Offset]==0x20
             && Buffer[Buffer_Offset+1]==0x00
             && (Buffer[Buffer_Offset+2]==0x0F
              || Buffer[Buffer_Offset+1]==0xFF))
                break;
            Buffer_Offset++;
        }
 
        if (Buffer_Offset+3>Buffer_Size)
            return false;
 
        Accept();
    }
    else
    {
        while(Buffer_Offset<Buffer_Size)
        {
            if (Buffer[Buffer_Offset]==0x0F
             || Buffer[Buffer_Offset]==0xFF)
                break;
            Buffer_Offset++;
        }
 
        if (Buffer_Offset>=Buffer_Size)
            return false;
    }
 
    //Synched is OK
    Synched=true;
    if (!Frame_Count_Valid)
        Frame_Count_Valid=Config->ParseSpeed>=0.3?32:2;
    return true;
}
 
//---------------------------------------------------------------------------
bool File_DvbSubtitle::Synched_Test()
{
    if (MustFindDvbHeader)
    {
        //Must have enough buffer for having header
        if (Buffer_Offset+1>Buffer_Size)
            return false;
 
        if (CC2(Buffer+Buffer_Offset)!=0x2000)
        {
            Synched=false;
            return true;
        }
 
        //Displaying it
        Element_Size=2;
        Skip_B1(                                                "data_identifier");
        Get_B1 (subtitle_stream_id,                             "subtitle_stream_id");
        Buffer_Offset+=2;
        MustFindDvbHeader=false;
    }
 
    //Must have enough buffer for having header
    if (Buffer_Offset+1>Buffer_Size)
        return false;
 
    //Quick test of synchro
    if (Buffer[Buffer_Offset]!=0x0F
     && Buffer[Buffer_Offset]!=0xFF)
    {
        Synched=false;
        return true;
    }
 
    //We continue
    return true;
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::Read_Buffer_Unsynched()
{
    MustParseTheHeaderFile=true;
    Synched=false;
}
 
//***************************************************************************
// Buffer - Demux
//***************************************************************************
 
//---------------------------------------------------------------------------
#if MEDIAINFO_DEMUX
bool File_DvbSubtitle::Demux_UnpacketizeContainer_Test()
{
    if (Demux_Offset==0)
    {
        Demux_Offset=Buffer_Offset;
    }
    while (Demux_Offset<Buffer_Size)
    {
        bool MustBreak;
        switch (Buffer[Demux_Offset])
        {
            case 0xFF :
                        MustBreak=true; break; //0xFF is not in the demuxed frame
            default   : MustBreak=false;
        }
        if (MustBreak)
            break; //while() loop
 
        if (Demux_Offset+6>Buffer_Size)
            return false; //No complete frame
 
        int16u segment_length=BigEndian2int16u(Buffer+Demux_Offset+4);
        Demux_Offset+=6+segment_length;
 
        if (Demux_Offset>=Buffer_Size)
            return false; //No complete frame
    }
 
    if (Demux_Offset>=Buffer_Size)
        return false; //No complete frame
 
    Demux_UnpacketizeContainer_Demux();
 
    Demux_TotalBytes++; //0xFF is not demuxed
 
    return true;
}
#endif //MEDIAINFO_DEMUX
 
//***************************************************************************
// Buffer - Per element
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::Header_Parse()
{
    //Parsing
    int8u sync_byte;
    Get_B1 (sync_byte,                                          "sync_byte");
    switch (sync_byte)
    {
        case 0xFF : //Stuffing
                    MustFindDvbHeader=true;
 
                    //Filling
                    Header_Fill_Code(0xFF, "end of PES data field marker");
                    Header_Fill_Size(1);
                    return;
        default   : ; //Normal (0x0F)
    }
 
    int16u segment_length;
    int8u  segment_type;
    Get_B1 (segment_type,                                       "segment_type");
    Get_B2 (page_id,                                            "page_id");
    Get_B2 (segment_length,                                     "segment_length");
 
    //Filling
    Header_Fill_Code(segment_type);
    Header_Fill_Size(Element_Offset+segment_length);
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::Data_Parse()
{
    switch (Element_Code)
    {
        case 0x10 : page_composition_segment(); break;
        case 0x11 : region_composition_segment(); break;
        case 0x12 : CLUT_definition_segment(); break;
        case 0x13 : object_data_segment(); break;
        case 0x14 : display_definition_segment(); break;
        case 0x80 : end_of_display_set_segment(); break;
        case 0xFF : end_of_PES_data_field_marker(); return;
        default   :
                    if (Element_Code>=0x40 && Element_Code<=0x7F)
                        reserved_for_future_use();
                    else if (Element_Code>=0x81 && Element_Code<=0xEF)
                        private_data();
                    else if (Element_Size)
                        Skip_XX(Element_Size,                   "Unknown");
    }
}
 
//***************************************************************************
// Elements
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::page_composition_segment()
{
    Element_Name("page composition segment");
 
    //Parsing
    Skip_B1(                                                    "page_time_out");
    BS_Begin();
    Skip_S1(4,                                                  "page_version_number");
    Skip_S1(2,                                                  "page_state");
    Skip_S1(2,                                                  "reserved");
    BS_End();
    while(Element_Offset<Element_Size)
    {
        Element_Begin1("Region");
        int16u region_horizontal_address, region_vertical_address;
        int8u region_id;
        Get_B1 (region_id,                                      "region_id");
        Skip_B1(                                                "reserved");
        Get_B2 (region_horizontal_address,                      "region_horizontal_address");
        Get_B2 (region_vertical_address,                        "region_vertical_address");
        Element_End0();
 
        FILLING_BEGIN();
            subtitle_streams[subtitle_stream_id].pages[page_id].regions[region_id].page_composition_segment=true;
            subtitle_streams[subtitle_stream_id].pages[page_id].regions[region_id].region_horizontal_address=region_horizontal_address;
            subtitle_streams[subtitle_stream_id].pages[page_id].regions[region_id].region_vertical_address=region_vertical_address;
        FILLING_END();
    }
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::region_composition_segment()
{
    Element_Name("region composition segment");
 
    //Parsing
    int16u region_width, region_height;
    int8u  region_id, region_depth;
    Get_B1 (   region_id,                                       "region_id");
    BS_Begin();
    Skip_S1(4,                                                  "region_version_number");
    Skip_S1(1,                                                  "region_fill_flag");
    Skip_S1(3,                                                  "reserved");
    BS_End();
    Get_B2 (   region_width,                                    "region_width");
    Get_B2 (   region_height,                                   "region_height");
    BS_Begin();
    Skip_S1(3,                                                  "region_level_of_compatibility");
    Get_S1 (3, region_depth,                                    "region_depth"); Param_Info2(DvbSubtitle_region_depth[region_depth], " bits");
    Skip_S1(2,                                                  "reserved");
    BS_End();
    Skip_B1(                                                    "CLUT_id");
    Skip_B1(                                                    "region_8-bit_pixel_code");
    BS_Begin();
    Skip_S1(4,                                                  "region_4-bit_pixel-code");
    Skip_S1(2,                                                  "region_2-bit_pixel-code");
    Skip_S1(2,                                                  "reserved");
    BS_End();
    while(Element_Offset<Element_Size)
    {
        Element_Begin1("Object");
        int8u object_type;
        Skip_B2(                                                "object_id");
        BS_Begin();
        Get_S1 ( 2, object_type,                                "object_type");
        Skip_S1( 2,                                             "object_provider_flag");
        Skip_S1(12,                                             "object_horizontal_position");
        Skip_S1( 4,                                             "reserved");
        Skip_S1(12,                                             "object_vertical_position");
        BS_End();
        switch (object_type)
        {
            case 0x01 :
            case 0x02 :
                        Skip_B2(                                 "foreground_pixel_code");
                        Skip_B2(                                 "background_pixel_code");
                        break;
            default   : ;
        }
        Element_End0();
    }
 
    FILLING_BEGIN();
        region_data& region = subtitle_streams[subtitle_stream_id].pages[page_id].regions[region_id];
        region.region_composition_segment=true;
        region.region_width=region_width;
        region.region_height=region_height;
        region.region_depth=region_depth;
    FILLING_END();
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::CLUT_definition_segment()
{
    Element_Name("CLUT definition segment");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::object_data_segment()
{
    Element_Name("object data segment");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::display_definition_segment()
{
    Element_Name("display definition segment");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::reserved_for_future_use()
{
    Element_Name("reserved for future use");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::end_of_display_set_segment()
{
    Element_Name("end of display set segment");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::private_data()
{
    Element_Name("private data");
 
    Skip_XX(Element_Size,                                       "Data");
}
 
//---------------------------------------------------------------------------
void File_DvbSubtitle::end_of_PES_data_field_marker()
{
    Frame_Count++;
    if (!Status[IsFilled] && Frame_Count>Frame_Count_Valid)
    {
        Fill();
        Finish();
    }
}
 
//***************************************************************************
// C++
//***************************************************************************
 
} //NameSpace
 
#endif //MEDIAINFO_DVBSUBTITLE_YES
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: page_id, subtitle_stream_id.

V525 The code contains the collection of similar blocks. Check items '4', '2', '2' in lines 321, 322, 323.

V525 The code contains the collection of similar blocks. Check items '4', '2', '2' in lines 368, 369, 370.

V525 The code contains the collection of similar blocks. Check items '2', '12', '4', '12' in lines 379, 380, 381, 382.

V807 Decreased performance. Consider creating a reference to avoid using the 'region->second' expression repeatedly.

V807 Decreased performance. Consider creating a reference to avoid using the same expression repeatedly.