All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scodec.protocols.mpeg.transport.psi.ProgramAssociationTable.scala Maven / Gradle / Ivy

The newest version!
package scodec.protocols.mpeg
package transport
package psi

import scodec.Codec
import scodec.codecs._

case class ProgramAssociationTable(
  tsid: TransportStreamId,
  version: Int,
  current: Boolean,
  programByPid: Map[ProgramNumber, Pid]
) extends Table {
  def tableId = ProgramAssociationSection.TableId
  def toSections: GroupedSections[ProgramAssociationSection] = ProgramAssociationTable.toSections(this)
}

object ProgramAssociationTable {

  val MaxProgramsPerSection = 253

  def toSections(pat: ProgramAssociationTable): GroupedSections[ProgramAssociationSection] = {
    val entries = pat.programByPid.toVector.sortBy { case (ProgramNumber(n), _) => n }
    val groupedEntries = entries.grouped(MaxProgramsPerSection).toVector
    val lastSection = groupedEntries.size - 1
    val sections = groupedEntries.zipWithIndex.map { case (es, idx) =>
      ProgramAssociationSection(SectionExtension(pat.tsid.value, pat.version, pat.current, idx, lastSection), es)
    }
    if (sections.isEmpty)
      GroupedSections(ProgramAssociationSection(SectionExtension(pat.tsid.value, pat.version, pat.current, 0, 0), Vector.empty))
    else
      GroupedSections(sections.head, sections.tail.toList)
  }

  def fromSections(sections: GroupedSections[ProgramAssociationSection]): Either[String, ProgramAssociationTable] = {
    def extract[A](name: String, f: ProgramAssociationSection => A): Either[String, A] = {
      val extracted = sections.list.map(f).distinct
      if (extracted.size == 1) Right(extracted.head)
      else Left(s"sections have diferring $name: " + extracted.mkString(", "))
    }
    for {
      tsid <- extract("TSIDs", _.tsid).right
      version <- extract("versions", _.extension.version).right
    } yield {
      val current = sections.list.foldLeft(false) { (acc, s) => acc || s.extension.current }
      ProgramAssociationTable(
        tsid,
        version,
        current,
        (for {
          section <- sections.list
          pidMapping <- section.pidMappings
        } yield pidMapping).toMap)
    }
  }

  implicit val tableSupport: TableSupport[ProgramAssociationTable] = new TableSupport[ProgramAssociationTable] {
    def tableId = ProgramAssociationSection.TableId
    def toTable(gs: GroupedSections[Section]) =
      gs.narrow[ProgramAssociationSection].toRight("Not PAT sections").right.flatMap { sections => fromSections(sections) }
    def toSections(pat: ProgramAssociationTable) = ProgramAssociationTable.toSections(pat)
  }
}

case class ProgramAssociationSection(
  extension: SectionExtension,
  pidMappings: Vector[(ProgramNumber, Pid)]
) extends ExtendedSection {
  def tableId = ProgramAssociationSection.TableId
  def tsid: TransportStreamId = TransportStreamId(extension.tableIdExtension)
}

object ProgramAssociationSection {
  val TableId = 0

  private type Fragment = Vector[(ProgramNumber, Pid)]

  private val fragmentCodec: Codec[Fragment] = {
    vector {
      ("program_number" | Codec[ProgramNumber]) ~
      (reserved(3) ~>
      ("pid" | Codec[Pid]))
    }
  }

  implicit val sectionFragmentCodec: SectionFragmentCodec[ProgramAssociationSection] =
    SectionFragmentCodec.psi[ProgramAssociationSection, Vector[(ProgramNumber, Pid)]](
      TableId,
      (ext, mappings) => ProgramAssociationSection(ext, mappings),
      pat => (pat.extension, pat.pidMappings)
    )(fragmentCodec)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy