MPEG TS Как собрать секцию EIT

С целью разобраться со стандартом Mpeg Ts пишу собственную библиотеку для парсинга ts файлов на C#. Сделал и протестировал для таблиц PAT, CAT, PMT, NIT, SDT, BAT и для кучи дескрипторов. И для части EIT. Но попался мне один нестандартный поток в котором pointer_field (ISO/IEC 13818-1 п.п 2.4.4.1) не равен нулю. EIT поток с транспондера №27 спутника Eutelsat 36B. Стандарт такое допускает, но алгоритмов в которое это поддерживают я так и не нашёл. Собственно загвоздка вот в чем: Ранее (когда Pointer_field = 0) я находил значение section_length, создавал массив длинной section_length нужной мне таблицы и если это значение больше 184 байт ставил флаг, что таблица собрана не вся. Ждал следующую таблицу с этим же пидом и добавлял остаток в конец массива. Если же pointer_field !=0 , то бывает что смещение равно 179 байтам и само по себе значение section_length находиться в другой таблице. В этом месте у меня и получился затык. Может кто-то работает с библиотеками для парсинга mpeg ts, подскажите как собрать секцию EIT в таком случае. Ps потоки выложить могу.

Вот пример таблицы со смещением: введите сюда описание изображения
Здесь 0xB0 смещение относительно начала payload.


Ответы (1 шт):

Автор решения: Deim

Если кто-то будет интересоваться, то черновой, но зато рабочий вариант сбора таблицы у меня получился следующим:

internal void AddRawData(TsPacket tsPacket)
    {            

        if (tsPacket.Payload_unit_start_indicator)
        {
            Pointer = tsPacket.Payload[0];

            if (Pointer > 184) // 184 - длина пакета
            {
                throw new Exception($"Pointer field greater than packet length for pid: {tsPacket.Pid}");
            }

            if (isInProgresTable)
            {
                if (TableBytes < 3) // если таблица не завершена и мы ещё не можем вытащить длину секции, то обрабатываем этот случай здесь.
                {
                    Buffer.BlockCopy(tsPacket.Payload, 1, tempBuffer, TableBytes, 3 - TableBytes);
                    CurrentTableSectionLength = (((tempBuffer[1] & 0x0F) << 8) + tempBuffer[2]);
                    if (CurrentTableSectionLength > Pointer)
                    {
                        throw new Exception(" section length greater than pointer!");
                    }
                    TableData = new byte[CurrentTableSectionLength + 3];
                    Buffer.BlockCopy(tempBuffer, 0, TableData, 0, 3);
                    var offsetInPayload = 1 + 3 - TableBytes; // 1 байт сам Pointer,  3 - TableBytes это сколько байт мы считали из предыдущего пакета.
                    var bytesToCopyCount = Pointer - (3 - TableBytes); // сколько нужно скопировать байт.
                    Buffer.BlockCopy(tsPacket.Payload, offsetInPayload, TableData, 3, bytesToCopyCount);

                    // table ready
                    isInProgresTable = false;                        
                    return;
                }
                else // если длина известна, но конец текущей таблицы находиться перед заголовком следующей, то это случай обрабатываем здесь.
                {
                    Buffer.BlockCopy(tsPacket.Payload, 1, TableData, TableBytes, Pointer);
                    TableBytes += Pointer;

                    //table ready
                    isInProgresTable = false;                        
                    return;
                }
            }

            isInProgresTable = true;
            CurrentTablePointerField = Pointer;

            if (Pointer >= 181) // если заголовок таблицы располагается в самом конце payload, причём таким образом, что нельзя сразу считать длину секции, то копируем 1 или 2 байта ( если 3. то уже можно считать длину) во временный буфер.
            {
                TableBytes = 184 - Pointer - 1;
                tempBuffer = new byte[3];
                Buffer.BlockCopy(tsPacket.Payload, Pointer + 1, tempBuffer, 0, TableBytes);
                return;
            }
            else // здесь обрабатываем случай, когда длину можно считать сразу
            {
                CurrentTableSectionLength = (((tsPacket.Payload[Pointer + 2] & 0x0F) << 8) + tsPacket.Payload[Pointer + 3]);
                TableData = new byte[CurrentTableSectionLength + 3];

                if(CurrentTableSectionLength + 3 < 184 - Pointer)// здесь случай когда длина секции не превышает длину payload. Иными словами вся таблица здесь.
                {
                    Buffer.BlockCopy(tsPacket.Payload, Pointer + 1, TableData, 0, TableData.Length);


                    // table ready

                    isInProgresTable = false;                        
                    return;
                }
                else // если таблица большая, то копируем данные и ждём следующих входных данных.
                {
                    Buffer.BlockCopy(tsPacket.Payload, Pointer + 1, TableData, 0, 184 - Pointer - 1);
                    TableBytes = 184 - Pointer - 1;
                    return;
                }
            }
        }

        if (isInProgresTable) // если следующий пакет не имеет флага Payload start indicator, то нужно опять же рассмотреть несколько вариантов
        {
            if (TableBytes < 3) // если начало таблицы было в самом конце payload предыдущего пакета и мы не смогли сразу узнать длину секции
            {
                Buffer.BlockCopy(tsPacket.Payload, 0, tempBuffer, TableBytes, 3 - TableBytes);
                CurrentTableSectionLength = (((tempBuffer[1] & 0x0F) << 8) + tempBuffer[2]);
                TableData = new byte[CurrentTableSectionLength + 3];

                if(CurrentTableSectionLength + 3 <= 184) // проверяем заканчивается ли таблица в этом пакета и если да, то обрабатываем этот случай тут
                {
                    Buffer.BlockCopy(tempBuffer, 0, TableData, 0, 3);
                    CurrentTableSectionLength = 3;
                    Buffer.BlockCopy(tsPacket.Payload, 0, TableData, 3, CurrentTableSectionLength);
                    TableBytes = CurrentTableSectionLength + 3;

                    // table ready TableBytes  = CurrentTableSectionLength + 3;
                    isInProgresTable = false;                        
                    return;
                }
                else // таблица большая и конец не в этом пакете.
                {
                    Buffer.BlockCopy(tempBuffer, 0, TableData, 0, 3);
                    Buffer.BlockCopy(tsPacket.Payload, 3 - TableBytes, TableData, 3, 184 - (3 - TableBytes));
                    TableBytes += 184;
                    return;
                }
            }
            else if(CurrentTableSectionLength + 3 - TableBytes < 184) // если пришла таблица с заранее известной длиной, то проверяем заканчивается ли она в этом пакете, если да, то обрабатываем это здесь
            {
                Buffer.BlockCopy(tsPacket.Payload, 0, TableData, TableBytes, CurrentTableSectionLength + 3 - TableBytes);
                TableBytes = CurrentTableSectionLength + 3;

                //table ready
                isInProgresTable = false;                    
                return;
            }
            else // если таблица пришла с заранее известной длиной, но конец не в этом пакете.
            {
                Buffer.BlockCopy(tsPacket.Payload, 0, TableData, TableBytes, 184);
                TableBytes += 184;
                return;
            }
        }
        else
        {
            return;
        }
    }

В итоге полная таблица будет в массиве TableData, TableBytes - это количество байт которые мы считали из пакетов. tempBuffer - массив длинной 3 байта для хранения данных, чтобы можно было извлечь длину секции.

→ Ссылка