diff --git a/CUETools.Codecs.BDLPCM/BDLPCMReader.cs b/CUETools.Codecs.MPEG/BDLPCM/AudioDecoder.cs similarity index 97% rename from CUETools.Codecs.BDLPCM/BDLPCMReader.cs rename to CUETools.Codecs.MPEG/BDLPCM/AudioDecoder.cs index 0e378a3..09d1a03 100644 --- a/CUETools.Codecs.BDLPCM/BDLPCMReader.cs +++ b/CUETools.Codecs.MPEG/BDLPCM/AudioDecoder.cs @@ -1,739 +1,740 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace CUETools.Codecs.BDLPCM -{ - public class AudioDecoder : IAudioSource - { - public unsafe AudioDecoder(string path, Stream IO, int pid) - : this(new DecoderSettings() { StreamId = pid }, path, IO) - { - } - - public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO) - { - _path = path; - _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); - streams = new Dictionary(); - frameBuffer = new byte[192]; - demuxer_channel = 0; - _samplePos = 0; - _sampleLen = -1; - demux_ts_packets(null, 0); - m_settings = settings; - } - - public IAudioDecoderSettings Settings => m_settings; - - public void Close() - { - //if (_br != null) - //{ - // _br.Close(); - // _br = null; - //} - _IO = null; - } - - public long Length - { - get - { - return _sampleLen; - } - } - - public long Remaining - { - get - { - return _sampleLen - _samplePos; - } - } - - public unsafe int StreamIds - { - get - { - int res = 0; - foreach (var s in streams) - if (s.Value.is_opened) - res++; - return res; - } - } - - public long Position - { - get - { - return _samplePos; - } - set - { - if (_samplePos == value) - { - return; - } - - throw new NotSupportedException(); - } - } - - public unsafe AudioPCMConfig PCM - { - get { - if (chosenStream == null) - { - if (m_settings.StreamId.HasValue) - { - if (streams.ContainsKey((ushort)m_settings.StreamId.Value)) - { - var s = streams[(ushort)m_settings.StreamId.Value]; - if (s.is_opened) - { - chosenStream = s; - if (chosenStream.pcm == null) - { - demux_ts_packets(null, 0); - } - return chosenStream.pcm; - } - } - throw new Exception("StreamId can be " + - string.Join(", ", (new List(streams.Keys)).FindAll(pid => streams[pid].is_opened).ConvertAll(pid => pid.ToString()).ToArray())); - } - if (m_settings.Stream.HasValue) - { - foreach (var s in streams) - { - if (s.Value.is_opened && s.Value.streamId == m_settings.Stream.Value) - { - chosenStream = s.Value; - if (chosenStream.pcm == null) - { - demux_ts_packets(null, 0); - } - return chosenStream.pcm; - } - } - throw new Exception("Stream can be " + - string.Join(", ", (new List(streams.Values)).FindAll(s => s.is_opened).ConvertAll(s => s.streamId.ToString()).ToArray())); - } - - throw new Exception("multiple streams present, please specify StreamId or Stream"); - } - return chosenStream.pcm; - } - } - - public string Path { get { return _path; } } - - public unsafe int Read(AudioBuffer buff, int maxLength) - { - buff.Prepare(this, maxLength); - int sampleCount; - fixed (int* dest = &buff.Samples[0,0]) - sampleCount = demux_ts_packets(dest, buff.Length); - buff.Length = sampleCount; - _samplePos += sampleCount; - return sampleCount; - } - - unsafe int demux_ts_packets(int* dest, int maxSamples) - { - int samplesOffset = 0; - while (true) - { - if (chosenStream != null && chosenStream.pcm != null) - { - int samplesInBuffer = chosenStream.savedBufferSize / chosenStream.pcm.BlockAlign; - if (samplesInBuffer > 0) - { - int chunkSamples = Math.Min(samplesInBuffer, maxSamples - samplesOffset); - int chunkLen = chunkSamples * chosenStream.pcm.BlockAlign; - fixed (byte* psrc_start = &chosenStream.savedBuffer[0]) - BDBytesToFLACSamples( - dest, - psrc_start + chosenStream.savedBufferOffset, - chunkSamples, chosenStream.pcm); - samplesOffset += chunkSamples; - dest += chunkSamples * chosenStream.pcm.ChannelCount; - chosenStream.savedBufferOffset += chunkLen; - chosenStream.savedBufferSize -= chunkLen; - } - - if (chosenStream.savedBufferSize > 0 && chosenStream.savedBufferOffset + chosenStream.pcm.BlockAlign > chosenStream.savedBuffer.Length) - { - Buffer.BlockCopy(chosenStream.savedBuffer, chosenStream.savedBufferOffset, chosenStream.savedBuffer, 0, chosenStream.savedBufferSize); - chosenStream.savedBufferOffset = 0; - } - - if (samplesOffset >= maxSamples) return samplesOffset; - } - - int read = _IO.Read(frameBuffer, 0, 192); - if (read != 192) - break; - //if (frameBuffer[0] == 0x47 && frameBuffer[4] != 0x47) - // throw new FormatException("TS, not M2TS"); - //if (frameBuffer[0] == 0x47 || frameBuffer[4] != 0x47) - // throw new FormatException("unknown stream type"); - - fixed (byte* ptr = &frameBuffer[0]) - { - var fr = new FrameReader(ptr, ptr + 192); - TsStream s; - demux_ts_packet(fr, out s); - int dataLen = (int) fr.Length; - if (dataLen > 0 && s != null && (chosenStream == null || s == chosenStream)) - { - int dataOffset = (int)(fr.Ptr - ptr); - if (s.savedBufferSize > 0) - { - int blockLen = s.pcm.BlockAlign; - int chunkLen = Math.Min(dataLen, blockLen - s.savedBufferSize); - // fr.read_bytes(svptr + s.savedBufferOffset + s.savedBufferSize, chunkLen); - Buffer.BlockCopy(frameBuffer, dataOffset, s.savedBuffer, s.savedBufferOffset + s.savedBufferSize, chunkLen); - dataOffset += chunkLen; - dataLen -= chunkLen; - chosenStream.savedBufferSize += chunkLen; - if (chosenStream.savedBufferSize == blockLen) - { - fixed (byte* psrc = &s.savedBuffer[s.savedBufferOffset]) - BDBytesToFLACSamples(dest, psrc, 1, s.pcm); - samplesOffset += 1; - dest += chosenStream.pcm.ChannelCount; - chosenStream.savedBufferOffset = 0; - chosenStream.savedBufferSize = 0; - } - } - if (dataLen > 0) - { - var tmp = s.savedBuffer; - s.savedBuffer = frameBuffer; - s.savedBufferOffset = dataOffset; - s.savedBufferSize = dataLen; - frameBuffer = tmp; - if (dest == null) return 0; - } - } - } - } - - return samplesOffset; - } - - unsafe static void BDBytesToFLACSamples_24bit_6ch(int* pdest, byte* psrc, int samples, int wastedBits) - { - for (int i = 0; i < samples; i++) - { - // front left - *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - // front right - *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - // front center - *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - // surround left - pdest[1] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - // surround right - pdest[2] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - // LFE - pdest[0] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - pdest += 3; - } - } - - unsafe static void BDBytesToFLACSamples_24bit_noremap(int* pdest, byte* psrc, int samples, int channels, int wastedBits) - { - for (int i = 0; i < samples * channels; i++) - { - *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); - } - // if (0 != (pcm.ChannelCount & 1)) channels are padded with one extra unused channel! is it the same for wav? - } - - unsafe static void BDBytesToFLACSamples_24bit(int* pdest, byte* psrc, int samples, int channels, int wastedBits) - { - switch (channels) - { - case 2: - BDBytesToFLACSamples_24bit_noremap(pdest, psrc, samples, channels, wastedBits); - break; - case 4: - BDBytesToFLACSamples_24bit_noremap(pdest, psrc, samples, channels, wastedBits); - break; - case 6: - BDBytesToFLACSamples_24bit_6ch(pdest, psrc, samples, wastedBits); - break; - default: - throw new NotSupportedException(); - } - } - - unsafe static void BDBytesToFLACSamples(int* pdest, byte* psrc, int samples, AudioPCMConfig pcm) - { - switch (pcm.BitsPerSample) - { - case 24: - BDBytesToFLACSamples_24bit(pdest, psrc, samples, pcm.ChannelCount, 0); - break; - case 20: - BDBytesToFLACSamples_24bit(pdest, psrc, samples, pcm.ChannelCount, 4); - break; - default: - throw new NotSupportedException(); - } - } - - unsafe void process_psi_pat(TsStream s, FrameReader fr) - { - long len = fr.Length - 4; - long n = len / 4; - if (len < 0 || 0 != (len % 4)) - throw new NotSupportedException(); - - for (int i = 0; i < n; i++) - { - ushort channel = fr.read_ushort(); - ushort pid = fr.read_ushort(); - if ((pid & 0xe000) != 0xe000) - throw new NotSupportedException(); - - pid &= 0x1fff; - - if (demuxer_channel == 0 || demuxer_channel == channel) - { - if (!streams.ContainsKey(pid)) - streams.Add(pid, new TsStream()); - TsStream ss = streams[pid]; - ss.channel = channel; - ss.type = 0xff; - } - } - } - - unsafe void process_psi_pmt(TsStream s, FrameReader fr) - { - // PMT (Program Map Table) - ushort pcr_pid = fr.read_ushort(); - ushort info_len = (ushort)(fr.read_ushort() & 0x0fff); - - fr.skip(info_len); - - // CRC - fr.Length -= 4; - - while (fr.Length > 0) - { - byte type = fr.read_byte(); - ushort es_pid = fr.read_ushort(); - - if ((es_pid & 0xe000) != 0xe000) - throw new IndexOutOfRangeException(); - - es_pid &= 0x1fff; - - info_len = (ushort)(fr.read_ushort() & 0x0fff); - - while (info_len > 0) - { - byte tag = fr.read_byte(); - switch (tag) - { - case 0x05: // registration descriptor - { - byte len = fr.read_byte(); - uint rid = fr.read_uint(); - if (rid == 0x48444D56 /* "HDMV" */ && type == 0x80 /* LPCM */) - { - if (!streams.ContainsKey(es_pid)) - { - streams.Add(es_pid, new TsStream()); - TsStream ss = streams[es_pid]; - if (ss.channel != s.channel || ss.type != type) - { - ss.channel = s.channel; - ss.type = type; - ss.streamId = s.streamId; - ss.is_opened = true; - s.streamId++; - } - } - } - fr.skip(len - 4); - info_len -= (ushort)(len + 2); - break; - } - default: - { - fr.skip(info_len - 1); - info_len = 0; - break; - } - } - } - } - - if (fr.Length > 0) - throw new IndexOutOfRangeException(); - } - - unsafe void process_psi(TsStream s, FrameReader fr, UInt16 pid, int table) - { - if (0 == pid) - { - process_psi_pat(s, fr); // Program Association Table - return; - } - //if (1 == pid) - //{ - // process_cat(s, ptr, end_ptr); // Conditional Access Table - // return; - //} - //if (0x11 == pid) - //{ - // process_sdt(s, ptr, end_ptr); - // return; - //} - - if (table == 0x02) - { - process_psi_pmt(s, fr); // Program Map Table - return; - } - } - - unsafe void process_pes_header(TsStream s, FrameReader fr) - { - // Packet start code prefix - if (fr.read_byte() != 0 || fr.read_byte() != 0 || fr.read_byte() != 1) - throw new NotSupportedException(); - - s.ts_stream_id = fr.read_byte(); - - int pes_len = (int)fr.read_ushort(); - int pes_type = (fr.read_byte() >> 6) & 3; - - // pes_type == 2; /* mpeg2 PES */ - - byte flags1 = fr.read_byte(); - s.frame_size = fr.read_byte(); - s.frame_num++; - s.at_packet_header = true; - - switch (flags1 & 0xc0) - { - case 0x80: // PTS only - { - ulong pts = fr.read_pts(); - - if (s.dts > 0 && pts > s.dts) - s.frame_length = (uint)(pts - s.dts); - s.dts = pts; - - if (pts > s.last_pts) - s.last_pts = pts; - - if (s.first_pts == 0) - s.first_pts = pts; - } - break; - case 0xc0: // PTS,DTS - { - ulong pts = fr.read_pts(); - ulong dts = fr.read_pts(); - - if (s.dts > 0 && dts > s.dts) - s.frame_length = (uint)(dts - s.dts); - s.dts = dts; - - if (pts > s.last_pts) - s.last_pts = pts; - - if (s.first_dts == 0) - s.first_dts = dts; - } - break; - } - } - - unsafe void BdHeader(TsStream s, FrameReader fr) - { - uint h = fr.read_uint(); - int pi_channels; - int pi_channels_padding; - int pi_bits; - int pi_rate; - - switch( ( h & 0xf000) >> 12 ) - { - case 1: - pi_channels = 1; - break; - case 3: - pi_channels = 2; - break; - case 4: - pi_channels = 3; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, - // AOUT_CHAN_CENTER, 0 }; - break; - case 5: - pi_channels = 3; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, - // AOUT_CHAN_REARCENTER, 0 }; - break; - case 6: - pi_channels = 4; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, - // AOUT_CHAN_CENTER, AOUT_CHAN_REARCENTER, 0 }; - break; - case 7: - pi_channels = 4; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, - // AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }; - break; - case 8: - pi_channels = 5; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, - // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 }; - break; - case 9: - pi_channels = 6; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, - // AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_LFE, 0 }; - break; - case 10: - pi_channels = 7; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, - // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, - // AOUT_CHAN_MIDDLERIGHT, 0 }; - break; - case 11: - pi_channels = 8; - //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, - // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, - // AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LFE, 0 }; - break; - - default: - throw new NotSupportedException(); - } - pi_channels_padding = pi_channels & 1; - - switch( (h >> 6) & 0x03 ) - { - case 1: - pi_bits = 16; - break; - case 2: - pi_bits = 20; - break; - case 3: - pi_bits = 24; - break; - default: - throw new NotSupportedException(); - } - - switch( (h >> 8) & 0x0f ) - { - case 1: - pi_rate = 48000; - break; - case 4: - pi_rate = 96000; - break; - case 5: - pi_rate = 192000; - break; - default: - throw new NotSupportedException(); - } - - if (s.pcm == null) - s.pcm = new AudioPCMConfig(pi_bits, pi_channels, pi_rate); - } - - unsafe void demux_ts_packet(FrameReader fr, out TsStream dataStream) - { - dataStream = null; - - uint timecode = fr.read_uint() & 0x3fffffff; - - byte sync = fr.read_byte(); // ts sync byte - if (sync != 0x47) throw new FormatException("invalid packet"); - - ushort pid = fr.read_ushort(); - bool transport_error = (pid & 0x8000) != 0; - bool payload_unit_start_indicator = (pid & 0x4000) != 0; - pid &= 0x1fff; - if (transport_error) - throw new FormatException("invalid packet "); - - byte flags = fr.read_byte(); - bool adaptation_field_exist = (flags & 0x20) != 0; - bool payload_data_exist = (flags & 0x10) != 0; - byte continuity_counter = (byte)(flags & 0x0f); - - if (pid == 0x1fff || !payload_data_exist) - { - return; - } - - // skip adaptation field - if (adaptation_field_exist) - { - fr.skip(fr.read_byte()); - } - - if (!streams.ContainsKey(pid)) - streams.Add(pid, new TsStream()); - TsStream s = streams[pid]; - - if (0 == pid || (s.channel != 0xffff && s.type == 0xff)) - { - // PSI (Program Specific Information) - if (payload_unit_start_indicator) - { - // begin of PSI table - byte pointerLen = fr.read_byte(); - fr.skip(pointerLen); - byte tableId = fr.read_byte(); - ushort sectionHeader = fr.read_ushort(); - bool syntaxFlag = ((sectionHeader >> 15) & 1) != 0; - bool privateFlag = ((sectionHeader >> 14) & 1) != 0; - int reservedBits = (sectionHeader >> 12) & 3; - if (reservedBits != 3) throw new NotSupportedException(); - int l = sectionHeader & 0x0fff; - if (syntaxFlag) - { - ushort extId = fr.read_ushort(); - byte tmp8 = fr.read_byte(); - int reserved3 = (tmp8 >> 6); - int version = (tmp8 >> 1) & 31; - bool current_flag = (tmp8 & 1) != 0; - byte section_num = fr.read_byte(); - byte last_section_num = fr.read_byte(); - l -= 5; - } - - int len = (int)fr.Length; - if (l <= len) - { - fr.Length = l; - process_psi(s, fr, pid, tableId); - return; - } - - if (l > s.psi.Length) - throw new FormatException("invalid packet "); - fixed (byte* psip = &s.psi[0]) - fr.read_bytes(psip, len); - s.psi_offset = len; - s.psi_table = tableId; - s.psi_len = l; - return; - } - - // next part of PSI - if (0 == s.psi_offset) - throw new FormatException("invalid packet "); - - { - int len = (int)fr.Length; - if (len > s.psi.Length - s.psi_offset) - throw new FormatException("invalid packet "); - fixed (byte* psip = &s.psi[s.psi_offset]) - fr.read_bytes(psip, len); - s.psi_offset += len; - } - - if (s.psi_offset < s.psi_len) - return; - fixed (byte* psip = &s.psi[0]) - { - var psiFr = new FrameReader(psip, psip + s.psi_len); - process_psi(s, psiFr, pid, s.psi_table); - } - return; - } - - if (s.type != 0xff) - { - // PES - - if (payload_unit_start_indicator) - { - s.psi_offset = 0; - s.psi_len = 9; - } - - while (s.psi_offset < s.psi_len) - { - int len = Math.Min((int)fr.Length, s.psi_len - s.psi_offset); - if (len <= 0) return; - fixed (byte* psip = &s.psi[s.psi_offset]) - fr.read_bytes(psip, len); - s.psi_offset += len; - if (s.psi_len == 9) - s.psi_len += s.psi[8]; - } - - if (s.psi_len != 0) - { - fixed (byte* psip = &s.psi[0]) - { - var pesFr = new FrameReader(psip, psip + s.psi_len); - process_pes_header(s, pesFr); - } - - s.psi_len = 0; - s.psi_offset = 0; - } - - if (s.frame_num == 0) - return; - - //if(es_parse) - //{ - // switch(s.type) - // { - // case 0x1b: - // s.frame_num_h264.parse(fr); - // break; - // case 0x06: - // case 0x81: - // case 0x83: - // s.frame_num_ac3.parse(fr); - // break; - // } - //} - - if (s.is_opened) - { - if (s.at_packet_header) - { - BdHeader(s, fr); - s.at_packet_header = false; - } - - dataStream = s; - } - } - } - - string _path; - Stream _IO; - Dictionary streams; - byte[] frameBuffer; - - int demuxer_channel; - TsStream chosenStream; - long _samplePos, _sampleLen; - DecoderSettings m_settings; - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using CUETools.Codecs.MPEG; + +namespace CUETools.Codecs.MPEG.BDLPCM +{ + public class AudioDecoder : IAudioSource + { + public unsafe AudioDecoder(string path, Stream IO, int pid) + : this(new DecoderSettings() { StreamId = pid }, path, IO) + { + } + + public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO) + { + _path = path; + _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); + streams = new Dictionary(); + frameBuffer = new byte[192]; + demuxer_channel = 0; + _samplePos = 0; + _sampleLen = -1; + demux_ts_packets(null, 0); + m_settings = settings; + } + + public IAudioDecoderSettings Settings => m_settings; + + public void Close() + { + //if (_br != null) + //{ + // _br.Close(); + // _br = null; + //} + _IO = null; + } + + public long Length + { + get + { + return _sampleLen; + } + } + + public long Remaining + { + get + { + return _sampleLen - _samplePos; + } + } + + public unsafe int StreamIds + { + get + { + int res = 0; + foreach (var s in streams) + if (s.Value.is_opened) + res++; + return res; + } + } + + public long Position + { + get + { + return _samplePos; + } + set + { + if (_samplePos == value) + { + return; + } + + throw new NotSupportedException(); + } + } + + public unsafe AudioPCMConfig PCM + { + get { + if (chosenStream == null) + { + if (m_settings.StreamId.HasValue) + { + if (streams.ContainsKey((ushort)m_settings.StreamId.Value)) + { + var s = streams[(ushort)m_settings.StreamId.Value]; + if (s.is_opened) + { + chosenStream = s; + if (chosenStream.pcm == null) + { + demux_ts_packets(null, 0); + } + return chosenStream.pcm; + } + } + throw new Exception("StreamId can be " + + string.Join(", ", (new List(streams.Keys)).FindAll(pid => streams[pid].is_opened).ConvertAll(pid => pid.ToString()).ToArray())); + } + if (m_settings.Stream.HasValue) + { + foreach (var s in streams) + { + if (s.Value.is_opened && s.Value.streamId == m_settings.Stream.Value) + { + chosenStream = s.Value; + if (chosenStream.pcm == null) + { + demux_ts_packets(null, 0); + } + return chosenStream.pcm; + } + } + throw new Exception("Stream can be " + + string.Join(", ", (new List(streams.Values)).FindAll(s => s.is_opened).ConvertAll(s => s.streamId.ToString()).ToArray())); + } + + throw new Exception("multiple streams present, please specify StreamId or Stream"); + } + return chosenStream.pcm; + } + } + + public string Path { get { return _path; } } + + public unsafe int Read(AudioBuffer buff, int maxLength) + { + buff.Prepare(this, maxLength); + int sampleCount; + fixed (int* dest = &buff.Samples[0,0]) + sampleCount = demux_ts_packets(dest, buff.Length); + buff.Length = sampleCount; + _samplePos += sampleCount; + return sampleCount; + } + + unsafe int demux_ts_packets(int* dest, int maxSamples) + { + int samplesOffset = 0; + while (true) + { + if (chosenStream != null && chosenStream.pcm != null) + { + int samplesInBuffer = chosenStream.savedBufferSize / chosenStream.pcm.BlockAlign; + if (samplesInBuffer > 0) + { + int chunkSamples = Math.Min(samplesInBuffer, maxSamples - samplesOffset); + int chunkLen = chunkSamples * chosenStream.pcm.BlockAlign; + fixed (byte* psrc_start = &chosenStream.savedBuffer[0]) + BDBytesToFLACSamples( + dest, + psrc_start + chosenStream.savedBufferOffset, + chunkSamples, chosenStream.pcm); + samplesOffset += chunkSamples; + dest += chunkSamples * chosenStream.pcm.ChannelCount; + chosenStream.savedBufferOffset += chunkLen; + chosenStream.savedBufferSize -= chunkLen; + } + + if (chosenStream.savedBufferSize > 0 && chosenStream.savedBufferOffset + chosenStream.pcm.BlockAlign > chosenStream.savedBuffer.Length) + { + Buffer.BlockCopy(chosenStream.savedBuffer, chosenStream.savedBufferOffset, chosenStream.savedBuffer, 0, chosenStream.savedBufferSize); + chosenStream.savedBufferOffset = 0; + } + + if (samplesOffset >= maxSamples) return samplesOffset; + } + + int read = _IO.Read(frameBuffer, 0, 192); + if (read != 192) + break; + //if (frameBuffer[0] == 0x47 && frameBuffer[4] != 0x47) + // throw new FormatException("TS, not M2TS"); + //if (frameBuffer[0] == 0x47 || frameBuffer[4] != 0x47) + // throw new FormatException("unknown stream type"); + + fixed (byte* ptr = &frameBuffer[0]) + { + var fr = new FrameReader(ptr, ptr + 192); + TsStream s; + demux_ts_packet(fr, out s); + int dataLen = (int) fr.Length; + if (dataLen > 0 && s != null && (chosenStream == null || s == chosenStream)) + { + int dataOffset = (int)(fr.Ptr - ptr); + if (s.savedBufferSize > 0) + { + int blockLen = s.pcm.BlockAlign; + int chunkLen = Math.Min(dataLen, blockLen - s.savedBufferSize); + // fr.read_bytes(svptr + s.savedBufferOffset + s.savedBufferSize, chunkLen); + Buffer.BlockCopy(frameBuffer, dataOffset, s.savedBuffer, s.savedBufferOffset + s.savedBufferSize, chunkLen); + dataOffset += chunkLen; + dataLen -= chunkLen; + chosenStream.savedBufferSize += chunkLen; + if (chosenStream.savedBufferSize == blockLen) + { + fixed (byte* psrc = &s.savedBuffer[s.savedBufferOffset]) + BDBytesToFLACSamples(dest, psrc, 1, s.pcm); + samplesOffset += 1; + dest += chosenStream.pcm.ChannelCount; + chosenStream.savedBufferOffset = 0; + chosenStream.savedBufferSize = 0; + } + } + if (dataLen > 0) + { + var tmp = s.savedBuffer; + s.savedBuffer = frameBuffer; + s.savedBufferOffset = dataOffset; + s.savedBufferSize = dataLen; + frameBuffer = tmp; + if (dest == null) return 0; + } + } + } + } + + return samplesOffset; + } + + unsafe static void BDBytesToFLACSamples_24bit_6ch(int* pdest, byte* psrc, int samples, int wastedBits) + { + for (int i = 0; i < samples; i++) + { + // front left + *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + // front right + *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + // front center + *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + // surround left + pdest[1] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + // surround right + pdest[2] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + // LFE + pdest[0] = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + pdest += 3; + } + } + + unsafe static void BDBytesToFLACSamples_24bit_noremap(int* pdest, byte* psrc, int samples, int channels, int wastedBits) + { + for (int i = 0; i < samples * channels; i++) + { + *(pdest++) = ((((((int)*(psrc++) << 8) + (int)*(psrc++)) << 8) + (int)*(psrc++)) << 8) >> (8 + wastedBits); + } + // if (0 != (pcm.ChannelCount & 1)) channels are padded with one extra unused channel! is it the same for wav? + } + + unsafe static void BDBytesToFLACSamples_24bit(int* pdest, byte* psrc, int samples, int channels, int wastedBits) + { + switch (channels) + { + case 2: + BDBytesToFLACSamples_24bit_noremap(pdest, psrc, samples, channels, wastedBits); + break; + case 4: + BDBytesToFLACSamples_24bit_noremap(pdest, psrc, samples, channels, wastedBits); + break; + case 6: + BDBytesToFLACSamples_24bit_6ch(pdest, psrc, samples, wastedBits); + break; + default: + throw new NotSupportedException(); + } + } + + unsafe static void BDBytesToFLACSamples(int* pdest, byte* psrc, int samples, AudioPCMConfig pcm) + { + switch (pcm.BitsPerSample) + { + case 24: + BDBytesToFLACSamples_24bit(pdest, psrc, samples, pcm.ChannelCount, 0); + break; + case 20: + BDBytesToFLACSamples_24bit(pdest, psrc, samples, pcm.ChannelCount, 4); + break; + default: + throw new NotSupportedException(); + } + } + + unsafe void process_psi_pat(TsStream s, FrameReader fr) + { + long len = fr.Length - 4; + long n = len / 4; + if (len < 0 || 0 != (len % 4)) + throw new NotSupportedException(); + + for (int i = 0; i < n; i++) + { + ushort channel = fr.read_ushort(); + ushort pid = fr.read_ushort(); + if ((pid & 0xe000) != 0xe000) + throw new NotSupportedException(); + + pid &= 0x1fff; + + if (demuxer_channel == 0 || demuxer_channel == channel) + { + if (!streams.ContainsKey(pid)) + streams.Add(pid, new TsStream()); + TsStream ss = streams[pid]; + ss.channel = channel; + ss.type = 0xff; + } + } + } + + unsafe void process_psi_pmt(TsStream s, FrameReader fr) + { + // PMT (Program Map Table) + ushort pcr_pid = fr.read_ushort(); + ushort info_len = (ushort)(fr.read_ushort() & 0x0fff); + + fr.skip(info_len); + + // CRC + fr.Length -= 4; + + while (fr.Length > 0) + { + byte type = fr.read_byte(); + ushort es_pid = fr.read_ushort(); + + if ((es_pid & 0xe000) != 0xe000) + throw new IndexOutOfRangeException(); + + es_pid &= 0x1fff; + + info_len = (ushort)(fr.read_ushort() & 0x0fff); + + while (info_len > 0) + { + byte tag = fr.read_byte(); + switch (tag) + { + case 0x05: // registration descriptor + { + byte len = fr.read_byte(); + uint rid = fr.read_uint(); + if (rid == 0x48444D56 /* "HDMV" */ && type == 0x80 /* LPCM */) + { + if (!streams.ContainsKey(es_pid)) + { + streams.Add(es_pid, new TsStream()); + TsStream ss = streams[es_pid]; + if (ss.channel != s.channel || ss.type != type) + { + ss.channel = s.channel; + ss.type = type; + ss.streamId = s.streamId; + ss.is_opened = true; + s.streamId++; + } + } + } + fr.skip(len - 4); + info_len -= (ushort)(len + 2); + break; + } + default: + { + fr.skip(info_len - 1); + info_len = 0; + break; + } + } + } + } + + if (fr.Length > 0) + throw new IndexOutOfRangeException(); + } + + unsafe void process_psi(TsStream s, FrameReader fr, UInt16 pid, int table) + { + if (0 == pid) + { + process_psi_pat(s, fr); // Program Association Table + return; + } + //if (1 == pid) + //{ + // process_cat(s, ptr, end_ptr); // Conditional Access Table + // return; + //} + //if (0x11 == pid) + //{ + // process_sdt(s, ptr, end_ptr); + // return; + //} + + if (table == 0x02) + { + process_psi_pmt(s, fr); // Program Map Table + return; + } + } + + unsafe void process_pes_header(TsStream s, FrameReader fr) + { + // Packet start code prefix + if (fr.read_byte() != 0 || fr.read_byte() != 0 || fr.read_byte() != 1) + throw new NotSupportedException(); + + s.ts_stream_id = fr.read_byte(); + + int pes_len = (int)fr.read_ushort(); + int pes_type = (fr.read_byte() >> 6) & 3; + + // pes_type == 2; /* mpeg2 PES */ + + byte flags1 = fr.read_byte(); + s.frame_size = fr.read_byte(); + s.frame_num++; + s.at_packet_header = true; + + switch (flags1 & 0xc0) + { + case 0x80: // PTS only + { + ulong pts = fr.read_pts(); + + if (s.dts > 0 && pts > s.dts) + s.frame_length = (uint)(pts - s.dts); + s.dts = pts; + + if (pts > s.last_pts) + s.last_pts = pts; + + if (s.first_pts == 0) + s.first_pts = pts; + } + break; + case 0xc0: // PTS,DTS + { + ulong pts = fr.read_pts(); + ulong dts = fr.read_pts(); + + if (s.dts > 0 && dts > s.dts) + s.frame_length = (uint)(dts - s.dts); + s.dts = dts; + + if (pts > s.last_pts) + s.last_pts = pts; + + if (s.first_dts == 0) + s.first_dts = dts; + } + break; + } + } + + unsafe void BdHeader(TsStream s, FrameReader fr) + { + uint h = fr.read_uint(); + int pi_channels; + int pi_channels_padding; + int pi_bits; + int pi_rate; + + switch( ( h & 0xf000) >> 12 ) + { + case 1: + pi_channels = 1; + break; + case 3: + pi_channels = 2; + break; + case 4: + pi_channels = 3; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + // AOUT_CHAN_CENTER, 0 }; + break; + case 5: + pi_channels = 3; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + // AOUT_CHAN_REARCENTER, 0 }; + break; + case 6: + pi_channels = 4; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + // AOUT_CHAN_CENTER, AOUT_CHAN_REARCENTER, 0 }; + break; + case 7: + pi_channels = 4; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + // AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }; + break; + case 8: + pi_channels = 5; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, + // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 }; + break; + case 9: + pi_channels = 6; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, + // AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_LFE, 0 }; + break; + case 10: + pi_channels = 7; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, + // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, + // AOUT_CHAN_MIDDLERIGHT, 0 }; + break; + case 11: + pi_channels = 8; + //{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, + // AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, + // AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LFE, 0 }; + break; + + default: + throw new NotSupportedException(); + } + pi_channels_padding = pi_channels & 1; + + switch( (h >> 6) & 0x03 ) + { + case 1: + pi_bits = 16; + break; + case 2: + pi_bits = 20; + break; + case 3: + pi_bits = 24; + break; + default: + throw new NotSupportedException(); + } + + switch( (h >> 8) & 0x0f ) + { + case 1: + pi_rate = 48000; + break; + case 4: + pi_rate = 96000; + break; + case 5: + pi_rate = 192000; + break; + default: + throw new NotSupportedException(); + } + + if (s.pcm == null) + s.pcm = new AudioPCMConfig(pi_bits, pi_channels, pi_rate); + } + + unsafe void demux_ts_packet(FrameReader fr, out TsStream dataStream) + { + dataStream = null; + + uint timecode = fr.read_uint() & 0x3fffffff; + + byte sync = fr.read_byte(); // ts sync byte + if (sync != 0x47) throw new FormatException("invalid packet"); + + ushort pid = fr.read_ushort(); + bool transport_error = (pid & 0x8000) != 0; + bool payload_unit_start_indicator = (pid & 0x4000) != 0; + pid &= 0x1fff; + if (transport_error) + throw new FormatException("invalid packet "); + + byte flags = fr.read_byte(); + bool adaptation_field_exist = (flags & 0x20) != 0; + bool payload_data_exist = (flags & 0x10) != 0; + byte continuity_counter = (byte)(flags & 0x0f); + + if (pid == 0x1fff || !payload_data_exist) + { + return; + } + + // skip adaptation field + if (adaptation_field_exist) + { + fr.skip(fr.read_byte()); + } + + if (!streams.ContainsKey(pid)) + streams.Add(pid, new TsStream()); + TsStream s = streams[pid]; + + if (0 == pid || (s.channel != 0xffff && s.type == 0xff)) + { + // PSI (Program Specific Information) + if (payload_unit_start_indicator) + { + // begin of PSI table + byte pointerLen = fr.read_byte(); + fr.skip(pointerLen); + byte tableId = fr.read_byte(); + ushort sectionHeader = fr.read_ushort(); + bool syntaxFlag = ((sectionHeader >> 15) & 1) != 0; + bool privateFlag = ((sectionHeader >> 14) & 1) != 0; + int reservedBits = (sectionHeader >> 12) & 3; + if (reservedBits != 3) throw new NotSupportedException(); + int l = sectionHeader & 0x0fff; + if (syntaxFlag) + { + ushort extId = fr.read_ushort(); + byte tmp8 = fr.read_byte(); + int reserved3 = (tmp8 >> 6); + int version = (tmp8 >> 1) & 31; + bool current_flag = (tmp8 & 1) != 0; + byte section_num = fr.read_byte(); + byte last_section_num = fr.read_byte(); + l -= 5; + } + + int len = (int)fr.Length; + if (l <= len) + { + fr.Length = l; + process_psi(s, fr, pid, tableId); + return; + } + + if (l > s.psi.Length) + throw new FormatException("invalid packet "); + fixed (byte* psip = &s.psi[0]) + fr.read_bytes(psip, len); + s.psi_offset = len; + s.psi_table = tableId; + s.psi_len = l; + return; + } + + // next part of PSI + if (0 == s.psi_offset) + throw new FormatException("invalid packet "); + + { + int len = (int)fr.Length; + if (len > s.psi.Length - s.psi_offset) + throw new FormatException("invalid packet "); + fixed (byte* psip = &s.psi[s.psi_offset]) + fr.read_bytes(psip, len); + s.psi_offset += len; + } + + if (s.psi_offset < s.psi_len) + return; + fixed (byte* psip = &s.psi[0]) + { + var psiFr = new FrameReader(psip, psip + s.psi_len); + process_psi(s, psiFr, pid, s.psi_table); + } + return; + } + + if (s.type != 0xff) + { + // PES + + if (payload_unit_start_indicator) + { + s.psi_offset = 0; + s.psi_len = 9; + } + + while (s.psi_offset < s.psi_len) + { + int len = Math.Min((int)fr.Length, s.psi_len - s.psi_offset); + if (len <= 0) return; + fixed (byte* psip = &s.psi[s.psi_offset]) + fr.read_bytes(psip, len); + s.psi_offset += len; + if (s.psi_len == 9) + s.psi_len += s.psi[8]; + } + + if (s.psi_len != 0) + { + fixed (byte* psip = &s.psi[0]) + { + var pesFr = new FrameReader(psip, psip + s.psi_len); + process_pes_header(s, pesFr); + } + + s.psi_len = 0; + s.psi_offset = 0; + } + + if (s.frame_num == 0) + return; + + //if(es_parse) + //{ + // switch(s.type) + // { + // case 0x1b: + // s.frame_num_h264.parse(fr); + // break; + // case 0x06: + // case 0x81: + // case 0x83: + // s.frame_num_ac3.parse(fr); + // break; + // } + //} + + if (s.is_opened) + { + if (s.at_packet_header) + { + BdHeader(s, fr); + s.at_packet_header = false; + } + + dataStream = s; + } + } + } + + string _path; + Stream _IO; + Dictionary streams; + byte[] frameBuffer; + + int demuxer_channel; + TsStream chosenStream; + long _samplePos, _sampleLen; + DecoderSettings m_settings; + } +} diff --git a/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs b/CUETools.Codecs.MPEG/BDLPCM/DecoderSettings.cs similarity index 92% rename from CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs rename to CUETools.Codecs.MPEG/BDLPCM/DecoderSettings.cs index 8d51dcd..a651702 100644 --- a/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs +++ b/CUETools.Codecs.MPEG/BDLPCM/DecoderSettings.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Text; -using Newtonsoft.Json; - -namespace CUETools.Codecs.BDLPCM -{ - [JsonObject(MemberSerialization.OptIn)] - public class DecoderSettings : IAudioDecoderSettings - { - #region IAudioDecoderSettings implementation - [Browsable(false)] - public string Extension => "m2ts"; - - [Browsable(false)] - public string Name => "cuetools"; - - [Browsable(false)] - public Type DecoderType => typeof(AudioDecoder); - - [Browsable(false)] - public int Priority => 2; - - public IAudioDecoderSettings Clone() - { - return MemberwiseClone() as IAudioDecoderSettings; - } - #endregion - - public DecoderSettings() - { - this.Init(); - } - - [DefaultValue(true)] - public bool IgnoreShortItems { get; set; } - - [Browsable(false)] - public int? Stream { get; set; } - - [Browsable(false)] - public int? StreamId { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using Newtonsoft.Json; + +namespace CUETools.Codecs.MPEG.BDLPCM +{ + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings + { + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "m2ts"; + + [Browsable(false)] + public string Name => "cuetools"; + + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); + + [Browsable(false)] + public int Priority => 2; + + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } + + [DefaultValue(true)] + public bool IgnoreShortItems { get; set; } + + [Browsable(false)] + public int? Stream { get; set; } + + [Browsable(false)] + public int? StreamId { get; set; } + } +} diff --git a/CUETools.Codecs.BDLPCM/CUETools.Codecs.BDLPCM.csproj b/CUETools.Codecs.MPEG/CUETools.Codecs.MPEG.csproj similarity index 86% rename from CUETools.Codecs.BDLPCM/CUETools.Codecs.BDLPCM.csproj rename to CUETools.Codecs.MPEG/CUETools.Codecs.MPEG.csproj index 457b3cb..1dda590 100644 --- a/CUETools.Codecs.BDLPCM/CUETools.Codecs.BDLPCM.csproj +++ b/CUETools.Codecs.MPEG/CUETools.Codecs.MPEG.csproj @@ -1,29 +1,29 @@ - - - - net40;net20;netstandard2.0 - 2.1.7.0 - CUETools.Codecs.BDLPCM - CUETools.Codecs - CUETools - A library for encoding and decoding BluRay Disc LPCM audio. - Copyright (c) 2008-2018 Grigory Chudov - Grigory Chudov - true - ..\bin\$(Configuration)\plugins - https://github.com/gchudov/cuetools.net - git - - - - - - False - - - - - - - - + + + + net40;net20;netstandard2.0 + 2.1.7.0 + CUETools.Codecs.MPEG + CUETools.Codecs.MPEG + CUETools + A library for encoding and decoding BluRay Disc LPCM audio. + Copyright (c) 2008-2018 Grigory Chudov + Grigory Chudov + true + ..\bin\$(Configuration)\plugins + https://github.com/gchudov/cuetools.net + git + + + + + + False + + + + + + + + diff --git a/CUETools.Codecs.BDLPCM/FrameReader.cs b/CUETools.Codecs.MPEG/FrameReader.cs similarity index 95% rename from CUETools.Codecs.BDLPCM/FrameReader.cs rename to CUETools.Codecs.MPEG/FrameReader.cs index 935cbce..9da2ce2 100644 --- a/CUETools.Codecs.BDLPCM/FrameReader.cs +++ b/CUETools.Codecs.MPEG/FrameReader.cs @@ -1,116 +1,116 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CUETools.Codecs.BDLPCM -{ - internal unsafe class FrameReader - { - internal FrameReader(byte* ptr, byte* end) - { - ptr_m = ptr; - end_m = end; - } - - internal FrameReader(byte* ptr, long len) - { - ptr_m = ptr; - end_m = ptr + len; - } - - internal FrameReader(FrameReader src, long len) - { - if (src.ptr_m + len > src.end_m) throw new IndexOutOfRangeException(); - ptr_m = src.ptr_m; - end_m = src.ptr_m + len; - } - - internal void read_bytes(byte* dst, int len) - { - if (ptr_m + len > end_m) throw new IndexOutOfRangeException(); - AudioSamples.MemCpy(dst, ptr_m, len); - ptr_m += len; - } - - internal byte[] read_bytes(int len) - { - var res = new byte[len]; - fixed (byte* ptr = &res[0]) - read_bytes(ptr, len); - return res; - } - - internal string read_string(int len) - { - var res = new byte[len]; - fixed (byte* ptr = &res[0]) - read_bytes(ptr, len); - return Encoding.UTF8.GetString(res, 0, res.Length);; - } - - internal byte read_byte() - { - if (ptr_m + 1 > end_m) throw new IndexOutOfRangeException(); - return *(ptr_m++); - } - - internal ushort read_ushort() - { - if (ptr_m + 2 > end_m) throw new IndexOutOfRangeException(); - ushort n = (ushort)(*(ptr_m++)); - n <<= 8; n += (ushort)(*(ptr_m++)); - return n; - } - - internal uint read_uint() - { - if (ptr_m + 4 > end_m) throw new IndexOutOfRangeException(); - uint n = (uint)(*(ptr_m++)); - n <<= 8; n += (uint)(*(ptr_m++)); - n <<= 8; n += (uint)(*(ptr_m++)); - n <<= 8; n += (uint)(*(ptr_m++)); - return n; - } - - internal ulong read_pts() - { - if (ptr_m + 5 > end_m) throw new IndexOutOfRangeException(); - ulong pts - = ((ulong)(*(ptr_m++) & 0x0e) << 29); - pts |= ((ulong)(*(ptr_m++) & 0xff) << 22); - pts |= ((ulong)(*(ptr_m++) & 0xfe) << 14); - pts |= ((ulong)(*(ptr_m++) & 0xff) << 7); - pts |= ((ulong)(*(ptr_m++) & 0xfe) >> 1); - return pts; - } - - internal void skip(long bytes) - { - if (ptr_m + bytes > end_m) throw new IndexOutOfRangeException(); - ptr_m += bytes; - } - - internal long Length - { - get - { - return end_m - ptr_m; - } - set - { - end_m = ptr_m + value; - } - } - - internal byte* Ptr - { - get - { - return ptr_m; - } - } - - byte* ptr_m; - byte* end_m; - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace CUETools.Codecs.MPEG +{ + internal unsafe class FrameReader + { + internal FrameReader(byte* ptr, byte* end) + { + ptr_m = ptr; + end_m = end; + } + + internal FrameReader(byte* ptr, long len) + { + ptr_m = ptr; + end_m = ptr + len; + } + + internal FrameReader(FrameReader src, long len) + { + if (src.ptr_m + len > src.end_m) throw new IndexOutOfRangeException(); + ptr_m = src.ptr_m; + end_m = src.ptr_m + len; + } + + internal void read_bytes(byte* dst, int len) + { + if (ptr_m + len > end_m) throw new IndexOutOfRangeException(); + AudioSamples.MemCpy(dst, ptr_m, len); + ptr_m += len; + } + + internal byte[] read_bytes(int len) + { + var res = new byte[len]; + fixed (byte* ptr = &res[0]) + read_bytes(ptr, len); + return res; + } + + internal string read_string(int len) + { + var res = new byte[len]; + fixed (byte* ptr = &res[0]) + read_bytes(ptr, len); + return Encoding.UTF8.GetString(res, 0, res.Length);; + } + + internal byte read_byte() + { + if (ptr_m + 1 > end_m) throw new IndexOutOfRangeException(); + return *(ptr_m++); + } + + internal ushort read_ushort() + { + if (ptr_m + 2 > end_m) throw new IndexOutOfRangeException(); + ushort n = (ushort)(*(ptr_m++)); + n <<= 8; n += (ushort)(*(ptr_m++)); + return n; + } + + internal uint read_uint() + { + if (ptr_m + 4 > end_m) throw new IndexOutOfRangeException(); + uint n = (uint)(*(ptr_m++)); + n <<= 8; n += (uint)(*(ptr_m++)); + n <<= 8; n += (uint)(*(ptr_m++)); + n <<= 8; n += (uint)(*(ptr_m++)); + return n; + } + + internal ulong read_pts() + { + if (ptr_m + 5 > end_m) throw new IndexOutOfRangeException(); + ulong pts + = ((ulong)(*(ptr_m++) & 0x0e) << 29); + pts |= ((ulong)(*(ptr_m++) & 0xff) << 22); + pts |= ((ulong)(*(ptr_m++) & 0xfe) << 14); + pts |= ((ulong)(*(ptr_m++) & 0xff) << 7); + pts |= ((ulong)(*(ptr_m++) & 0xfe) >> 1); + return pts; + } + + internal void skip(long bytes) + { + if (ptr_m + bytes > end_m) throw new IndexOutOfRangeException(); + ptr_m += bytes; + } + + internal long Length + { + get + { + return end_m - ptr_m; + } + set + { + end_m = ptr_m + value; + } + } + + internal byte* Ptr + { + get + { + return ptr_m; + } + } + + byte* ptr_m; + byte* end_m; + } +} diff --git a/CUETools.Codecs.BDLPCM/MPLSReader.cs b/CUETools.Codecs.MPEG/MPLS/AudioDecoder.cs similarity index 93% rename from CUETools.Codecs.BDLPCM/MPLSReader.cs rename to CUETools.Codecs.MPEG/MPLS/AudioDecoder.cs index e08e303..6b8301c 100644 --- a/CUETools.Codecs.BDLPCM/MPLSReader.cs +++ b/CUETools.Codecs.MPEG/MPLS/AudioDecoder.cs @@ -1,662 +1,661 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Globalization; - -namespace CUETools.Codecs.BDLPCM -{ - public class MPLSDecoder : IAudioSource - { - public unsafe MPLSDecoder(string path, Stream IO, ushort pid) - : this(new MPLS.DecoderSettings() { StreamId = pid }, path, IO) - { - } - - public unsafe MPLSDecoder(MPLS.DecoderSettings settings, string path, Stream IO) - { - m_settings = settings; - _path = path; - _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); - int length = (int)_IO.Length; - contents = new byte[length]; - if (_IO.Read(contents, 0, length) != length) throw new Exception(""); - fixed (byte* ptr = &contents[0]) - { - FrameReader fr = new FrameReader(ptr, length); - hdr_m = parseHeader(fr); - fr = new FrameReader(ptr + hdr_m.list_pos, length - hdr_m.list_pos); - parsePlaylist(fr); - fr = new FrameReader(ptr + hdr_m.mark_pos, length - hdr_m.mark_pos); - parsePlaylistMarks(fr); - } - } - - void openEntries() - { - readers = new List(); - var pids = new List(); - foreach (var item in hdr_m.play_item) - foreach (var audio in item.audio) - { - if (audio.coding_type != 0x80 /* LPCM */) continue; - pids.Add(audio.pid); - } - int chosenPid; - if (m_settings.StreamId.HasValue) - { - if (!pids.Contains(m_settings.StreamId.Value)) - throw new Exception("StreamId can be " + - string.Join(", ", pids.ConvertAll(pid => pid.ToString()).ToArray())); - chosenPid = m_settings.StreamId.Value; - } - else if (m_settings.Stream.HasValue) - { - if (m_settings.Stream.Value < 0 || m_settings.Stream.Value >= pids.Count) - throw new Exception("Stream can be 0.." + (pids.Count - 1).ToString()); - chosenPid = pids[m_settings.Stream.Value]; - } - else throw new Exception("multiple streams present, please specify StreamId or Stream"); - foreach (var item in hdr_m.play_item) - foreach (var audio in item.audio) - { - if (audio.coding_type != 0x80 /* LPCM */) continue; - if (m_settings.IgnoreShortItems && item.out_time - item.in_time < shortItemDuration) continue; - if (audio.pid == chosenPid) - { - var parent = Directory.GetParent(System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(_path))); - var m2ts = System.IO.Path.Combine( - System.IO.Path.Combine(parent.FullName, "STREAM"), - item.clip_id + ".m2ts"); - var entry = new AudioDecoder(m2ts, null, chosenPid); - readers.Add(entry); - break; - } - } - currentReader = readers[0]; - pcm = currentReader.PCM; - } - - MPLSHeader parseHeader(FrameReader fr) - { - var hdr = new MPLSHeader(); - hdr.play_item = new List(); - hdr.play_mark = new List(); - long length = fr.Length; - hdr.type_indicator = fr.read_uint(); - hdr.type_indicator2 = fr.read_uint(); - hdr.list_pos = fr.read_uint(); - hdr.mark_pos = fr.read_uint(); - hdr.ext_pos = fr.read_uint(); - if (hdr.type_indicator != 0x4d504c53 /*MPLS*/) throw new NotSupportedException(); - if (hdr.type_indicator2 != 0x30313030 && hdr.type_indicator2 != 0x30323030) throw new NotSupportedException(); - if (hdr.list_pos > length || hdr.mark_pos > length || hdr.ext_pos > length) throw new NotSupportedException(); - return hdr; - } - - void parsePlaylist(FrameReader parentFr) - { - uint len = parentFr.read_uint(); - FrameReader fr = new FrameReader(parentFr, len); - parentFr.skip(len); - ushort reserved = fr.read_ushort(); - hdr_m.list_count = fr.read_ushort(); - hdr_m.sub_count = fr.read_ushort(); - for (int i = 0; i < hdr_m.list_count; i++) - hdr_m.play_item.Add(parsePlaylistItem(fr)); - } - - void parsePlaylistMarks(FrameReader parentFr) - { - uint len = parentFr.read_uint(); - FrameReader fr = new FrameReader(parentFr, len); - parentFr.skip(len); - hdr_m.mark_count = fr.read_ushort(); - for (int ii = 0; ii < hdr_m.mark_count; ii++) - hdr_m.play_mark.Add(parsePlaylistMark(fr)); - } - - MPLSPlaylistItem parsePlaylistItem(FrameReader parentFr) - { - var item = new MPLSPlaylistItem(); - item.video = new List(); - item.audio = new List(); - item.pg = new List(); - - // PlayItem Length - ushort len = parentFr.read_ushort(); - FrameReader fr = new FrameReader(parentFr, len); - parentFr.skip(len); - - // Primary Clip identifer - item.clip_id = fr.read_string(5); - - // skip the redundant "M2TS" CodecIdentifier - uint codecId = fr.read_uint(); - if (codecId != 0x4D325453) throw new NotSupportedException("Incorrect CodecIdentifier"); - - ushort flags = fr.read_ushort(); - bool is_multi_angle = ((flags >> 4) & 1) != 0; - item.connection_condition = (byte)(flags & 15); - if (item.connection_condition != 0x01 && - item.connection_condition != 0x05 && - item.connection_condition != 0x06) - throw new NotSupportedException("Unexpected connection condition"); - - item.stc_id = fr.read_byte(); - item.in_time = fr.read_uint(); - item.out_time = fr.read_uint(); - - // Skip UO_mask_table, random_access_flag, reserved, still_mode - // and still_time - fr.skip(12); - - if (is_multi_angle) - { - byte num_angles = fr.read_byte(); - // skip reserved, is_different_audio, is_seamless_angle_change - fr.skip(1); - for (int ii = 1; ii < num_angles; ii++) - { - // Drop clip_id, clip_codec_id, stc_id - fr.skip(10); - } - } - - // Skip STN len - fr.skip(2); - - // Skip 2 reserved bytes - fr.skip(2); - - item.num_video = fr.read_byte(); - item.num_audio = fr.read_byte(); - item.num_pg = fr.read_byte(); - item.num_ig = fr.read_byte(); - item.num_secondary_audio = fr.read_byte(); - item.num_secondary_video = fr.read_byte(); - item.num_pip_pg = fr.read_byte(); - - // 5 reserve bytes - fr.skip(5); - - for (int ii = 0; ii < item.num_video; ii++) - item.video.Add(parseStream(fr)); - for (int ii = 0; ii < item.num_audio; ii++) - item.audio.Add(parseStream(fr)); - for ( int ii = 0; ii < item.num_pg; ii++) - item.pg.Add(parseStream(fr)); - - return item; - } - - MPLSStream parseStream(FrameReader parentFr) - { - MPLSStream s = new MPLSStream(); - - byte len = parentFr.read_byte(); - FrameReader fr = new FrameReader(parentFr, len); - parentFr.skip(len); - - s.stream_type = fr.read_byte(); - switch (s.stream_type) - { - case 1: - s.pid = fr.read_ushort(); - break; - case 2: - case 4: - s.subpath_id = fr.read_byte(); - s.subclip_id = fr.read_byte(); - s.pid = fr.read_ushort(); - break; - case 3: - s.subpath_id = fr.read_byte(); - s.pid = fr.read_ushort(); - break; - default: - throw new Exception("unrecognized stream type"); - }; - - len = parentFr.read_byte(); - fr = new FrameReader(parentFr, len); - parentFr.skip(len); - - s.coding_type = fr.read_byte(); - if (s.coding_type == 0x01 - || s.coding_type == 0x02 - || s.coding_type == 0xea - || s.coding_type == 0x1b) - { - // Video - byte fmt = fr.read_byte(); - s.format = (byte)(fmt >> 4); - s.rate = (byte)(fmt & 15); - } - else if (s.coding_type == 0x03 - || s.coding_type == 0x04 - || s.coding_type == 0x80 - || s.coding_type == 0x81 - || s.coding_type == 0x82 - || s.coding_type == 0x83 - || s.coding_type == 0x84 - || s.coding_type == 0x85 - || s.coding_type == 0x86) - { - // Audio - byte fmt = fr.read_byte(); - s.format = (byte)(fmt >> 4); - s.rate = (byte)(fmt & 15); - s.lang = fr.read_string(3); - } - else if (s.coding_type == 0x90 - || s.coding_type == 0x91) - { - s.lang = fr.read_string(3); - } - else if (s.coding_type == 0x92) - { - s.char_code = fr.read_byte(); - s.lang = fr.read_string(3); - } - else throw new Exception("unrecognized stream type"); - - return s; - } - - MPLSPlaylistMark parsePlaylistMark(FrameReader fr) - { - var mark = new MPLSPlaylistMark(); - mark.mark_id = fr.read_byte(); - mark.mark_type = fr.read_byte(); - mark.play_item_ref = fr.read_ushort(); - mark.time = fr.read_uint(); - mark.entry_es_pid = fr.read_ushort(); - mark.duration = fr.read_uint(); - return mark; - } - - public IAudioDecoderSettings Settings => m_settings; - - public void Close() - { - if (readers != null) - foreach (var rdr in readers) - { - rdr.Close(); - } - readers = null; - currentReader = null; - _IO = null; - } - - public long Length - { - get - { - return -1; - } - } - - public TimeSpan Duration - { - get - { - uint totalLength = 0; - foreach (var item in hdr_m.play_item) - { - if (item.num_audio == 0) continue; - uint item_duration = item.out_time - item.in_time; - if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; - totalLength += item_duration; - } - - return TimeSpan.FromSeconds(totalLength / 45000.0); - } - } - - public long Remaining - { - get - { - return -1; - } - } - - public long Position - { - get - { - long res = 0; - foreach (var rdr in readers) - { - res += rdr.Position; - if (rdr == currentReader) break; - } - return res; - } - set - { - throw new NotSupportedException(); - } - } - - public unsafe AudioPCMConfig PCM - { - get { - if (readers == null) openEntries(); - return pcm; - } - } - - public string Path { get { return _path; } } - - public unsafe int Read(AudioBuffer buff, int maxLength) - { - if (readers == null) openEntries(); - int res = currentReader.Read(buff, maxLength); - if (res == 0) - { - bool nextOne = false; - foreach (var rdr in readers) - { - if (nextOne) - { - currentReader = rdr; - return currentReader.Read(buff, maxLength); - } - nextOne = (rdr == currentReader); - } - currentReader = null; - } - return res; - } - - public string FileName - { - get - { - return System.IO.Path.GetFileName(_path); - } - } - - public MPLSHeader MPLSHeader - { - get - { - return hdr_m; - } - } - - public List Chapters - { - get - { - //settings.IgnoreShortItems - var res = new List(); - if (hdr_m.play_mark.Count < 1) return res; - if (hdr_m.play_item.Count < 1) return res; - res.Add(0); - for (int i = 0; i < hdr_m.mark_count; i++) - { - ushort mark_item = hdr_m.play_mark[i].play_item_ref; - uint item_in_time = hdr_m.play_item[mark_item].in_time; - uint item_out_time = hdr_m.play_item[mark_item].out_time; - if (m_settings.IgnoreShortItems && item_out_time - item_in_time < shortItemDuration) continue; - uint item_offset = 0; - for (int j = 0; j < mark_item; j++) - { - if (hdr_m.play_item[j].num_audio == 0) continue; - uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; - if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; - item_offset += item_duration; - } - res.Add(hdr_m.play_mark[i].time - item_in_time + item_offset); - } - uint end_offset = 0; - for (int j = 0; j < hdr_m.play_item.Count; j++) - { - if (hdr_m.play_item[j].num_audio == 0) continue; - uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; - if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; - end_offset += hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; - } - res.Add(end_offset); - while (res.Count > 1 && res[1] - res[0] < 45000) res.RemoveAt(1); - while (res.Count > 1 && res[res.Count - 1] - res[res.Count - 2] < 45000) res.RemoveAt(res.Count - 2); - return res; - } - } - - readonly static int shortItemDuration = 45000 * 30; - - string _path; - Stream _IO; - byte[] contents; - - AudioPCMConfig pcm; - List readers; - AudioDecoder currentReader; - MPLSHeader hdr_m; - MPLS.DecoderSettings m_settings; - } - - public struct MPLSPlaylistMark - { - public byte mark_id; - public byte mark_type; - public ushort play_item_ref; - public uint time; - public ushort entry_es_pid; - public uint duration; - } - - public struct MPLSStream - { - public byte stream_type; - public byte coding_type; - public ushort pid; - public byte subpath_id; - public byte subclip_id; - public byte format; - public byte rate; - public byte char_code; - public string lang; - - public string FormatString - { - get - { - if (coding_type == 0x01 - || coding_type == 0x02 - || coding_type == 0xea - || coding_type == 0x1b) - switch (format) - { - case 0: return "reserved0"; - case 1: return "480i"; - case 2: return "576i"; - case 3: return "480p"; - case 4: return "1080i"; - case 5: return "720p"; - case 6: return "1080p"; - case 7: return "576p"; - default: return format.ToString(); - } - switch (format) - { - case 0: return "reserved0"; - case 1: return "mono"; - case 2: return "reserved2"; - case 3: return "stereo"; - case 4: return "reserved4"; - case 5: return "reserved5"; - case 6: return "multi-channel"; - case 12: return "combo"; - default: return format.ToString(); - } - } - } - - public int FrameRate - { - get - { - switch (rate) - { - case 1: return 24; - case 2: return 24; - case 3: return 25; - case 4: return 30; - case 6: return 50; - case 7: return 60; - default: throw new NotSupportedException(); - } - } - } - - public bool Interlaced - { - get - { - return format == 1 || format == 2 || format == 4; - } - } - - public string RateString - { - get - { - if (coding_type == 0x01 - || coding_type == 0x02 - || coding_type == 0xea - || coding_type == 0x1b) - switch (rate) - { - case 0: return "reserved0"; - case 1: return "23.976"; - case 2: return "24"; - case 3: return "25"; - case 4: return "29.97"; - case 5: return "reserved5"; - case 6: return "50"; - case 7: return "59.94"; - default: return rate.ToString(); - } - switch (rate) - { - case 0: return "reserved0"; - case 1: return "48KHz"; - case 2: return "reserved2"; - case 3: return "reserved3"; - case 4: return "96KHz"; - case 5: return "192KHz"; - //case 12: return "48/192KHz"; (core/hd) - case 12: return "192KHz"; - //case 14: return "48/96KHz"; (core/hd) - case 14: return "96KHz"; - default: return rate.ToString(); - } - } - } - - public string CodecString - { - get - { - switch (coding_type) - { - case 0x01: return "MPEG-1 Video"; - case 0x02: return "MPEG-2 Video"; - case 0x03: return "MPEG-1 Audio"; - case 0x04: return "MPEG-2 Audio"; - //case 0x80: return "LPCM"; - case 0x80: return "RAW/PCM"; - case 0x81: return "AC-3"; - case 0x82: return "DTS"; - case 0x83: return "TrueHD"; - case 0x84: return "AC-3 Plus"; - case 0x85: return "DTS-HD"; - //case 0x86: return "DTS-HD Master"; - case 0x86: return "DTS Master Audio"; - case 0xea: return "VC-1"; - case 0x1b: return "h264/AVC"; - case 0x90: return "Presentation Graphics"; - case 0x91: return "Interactive Graphics"; - case 0x92: return "Text Subtitle"; - default: return coding_type.ToString(); - } - } - } - - public byte CodingType - { - get - { - return coding_type; - } - } - - public byte FormatType - { - get - { - return format; - } - } - - public string LanguageString - { - get - { - CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - // Exclude custom cultures. - if ((culture.CultureTypes & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture) - continue; - - if (culture.ThreeLetterISOLanguageName == lang) - return culture.EnglishName; - } - return lang; - } - } - } - - public struct MPLSPlaylistItem - { - public string clip_id; - public byte connection_condition; - public byte stc_id; - public uint in_time; - public uint out_time; - - public byte num_video; - public byte num_audio; - public byte num_pg; - public byte num_ig; - public byte num_secondary_audio; - public byte num_secondary_video; - public byte num_pip_pg; - public List video; - public List audio; - public List pg; - } - - public struct MPLSHeader - { - public uint type_indicator; - public uint type_indicator2; - public uint list_pos; - public uint mark_pos; - public uint ext_pos; - - public ushort list_count; - public ushort sub_count; - public ushort mark_count; - - public List play_item; - public List play_mark; - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace CUETools.Codecs.MPEG.MPLS +{ + public class AudioDecoder : IAudioSource + { + public unsafe AudioDecoder(string path, Stream IO, ushort pid) + : this(new DecoderSettings() { StreamId = pid }, path, IO) + { + } + + public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO) + { + m_settings = settings; + _path = path; + _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); + int length = (int)_IO.Length; + contents = new byte[length]; + if (_IO.Read(contents, 0, length) != length) throw new Exception(""); + fixed (byte* ptr = &contents[0]) + { + var fr = new FrameReader(ptr, length); + hdr_m = parseHeader(fr); + fr = new FrameReader(ptr + hdr_m.list_pos, length - hdr_m.list_pos); + parsePlaylist(fr); + fr = new FrameReader(ptr + hdr_m.mark_pos, length - hdr_m.mark_pos); + parsePlaylistMarks(fr); + } + } + + void openEntries() + { + readers = new List(); + var pids = new List(); + foreach (var item in hdr_m.play_item) + foreach (var audio in item.audio) + { + if (audio.coding_type != 0x80 /* LPCM */) continue; + pids.Add(audio.pid); + } + int chosenPid; + if (m_settings.StreamId.HasValue) + { + if (!pids.Contains(m_settings.StreamId.Value)) + throw new Exception("StreamId can be " + + string.Join(", ", pids.ConvertAll(pid => pid.ToString()).ToArray())); + chosenPid = m_settings.StreamId.Value; + } + else if (m_settings.Stream.HasValue) + { + if (m_settings.Stream.Value < 0 || m_settings.Stream.Value >= pids.Count) + throw new Exception("Stream can be 0.." + (pids.Count - 1).ToString()); + chosenPid = pids[m_settings.Stream.Value]; + } + else throw new Exception("multiple streams present, please specify StreamId or Stream"); + foreach (var item in hdr_m.play_item) + foreach (var audio in item.audio) + { + if (audio.coding_type != 0x80 /* LPCM */) continue; + if (m_settings.IgnoreShortItems && item.out_time - item.in_time < shortItemDuration) continue; + if (audio.pid == chosenPid) + { + var parent = Directory.GetParent(System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(_path))); + var m2ts = System.IO.Path.Combine( + System.IO.Path.Combine(parent.FullName, "STREAM"), + item.clip_id + ".m2ts"); + var entry = new BDLPCM.AudioDecoder(m2ts, null, chosenPid); + readers.Add(entry); + break; + } + } + currentReader = readers[0]; + pcm = currentReader.PCM; + } + + MPLSHeader parseHeader(FrameReader fr) + { + var hdr = new MPLSHeader(); + hdr.play_item = new List(); + hdr.play_mark = new List(); + long length = fr.Length; + hdr.type_indicator = fr.read_uint(); + hdr.type_indicator2 = fr.read_uint(); + hdr.list_pos = fr.read_uint(); + hdr.mark_pos = fr.read_uint(); + hdr.ext_pos = fr.read_uint(); + if (hdr.type_indicator != 0x4d504c53 /*MPLS*/) throw new NotSupportedException(); + if (hdr.type_indicator2 != 0x30313030 && hdr.type_indicator2 != 0x30323030) throw new NotSupportedException(); + if (hdr.list_pos > length || hdr.mark_pos > length || hdr.ext_pos > length) throw new NotSupportedException(); + return hdr; + } + + void parsePlaylist(FrameReader parentFr) + { + uint len = parentFr.read_uint(); + var fr = new FrameReader(parentFr, len); + parentFr.skip(len); + ushort reserved = fr.read_ushort(); + hdr_m.list_count = fr.read_ushort(); + hdr_m.sub_count = fr.read_ushort(); + for (int i = 0; i < hdr_m.list_count; i++) + hdr_m.play_item.Add(parsePlaylistItem(fr)); + } + + void parsePlaylistMarks(FrameReader parentFr) + { + uint len = parentFr.read_uint(); + var fr = new FrameReader(parentFr, len); + parentFr.skip(len); + hdr_m.mark_count = fr.read_ushort(); + for (int ii = 0; ii < hdr_m.mark_count; ii++) + hdr_m.play_mark.Add(parsePlaylistMark(fr)); + } + + MPLSPlaylistItem parsePlaylistItem(FrameReader parentFr) + { + var item = new MPLSPlaylistItem(); + item.video = new List(); + item.audio = new List(); + item.pg = new List(); + + // PlayItem Length + ushort len = parentFr.read_ushort(); + var fr = new FrameReader(parentFr, len); + parentFr.skip(len); + + // Primary Clip identifer + item.clip_id = fr.read_string(5); + + // skip the redundant "M2TS" CodecIdentifier + uint codecId = fr.read_uint(); + if (codecId != 0x4D325453) throw new NotSupportedException("Incorrect CodecIdentifier"); + + ushort flags = fr.read_ushort(); + bool is_multi_angle = ((flags >> 4) & 1) != 0; + item.connection_condition = (byte)(flags & 15); + if (item.connection_condition != 0x01 && + item.connection_condition != 0x05 && + item.connection_condition != 0x06) + throw new NotSupportedException("Unexpected connection condition"); + + item.stc_id = fr.read_byte(); + item.in_time = fr.read_uint(); + item.out_time = fr.read_uint(); + + // Skip UO_mask_table, random_access_flag, reserved, still_mode + // and still_time + fr.skip(12); + + if (is_multi_angle) + { + byte num_angles = fr.read_byte(); + // skip reserved, is_different_audio, is_seamless_angle_change + fr.skip(1); + for (int ii = 1; ii < num_angles; ii++) + { + // Drop clip_id, clip_codec_id, stc_id + fr.skip(10); + } + } + + // Skip STN len + fr.skip(2); + + // Skip 2 reserved bytes + fr.skip(2); + + item.num_video = fr.read_byte(); + item.num_audio = fr.read_byte(); + item.num_pg = fr.read_byte(); + item.num_ig = fr.read_byte(); + item.num_secondary_audio = fr.read_byte(); + item.num_secondary_video = fr.read_byte(); + item.num_pip_pg = fr.read_byte(); + + // 5 reserve bytes + fr.skip(5); + + for (int ii = 0; ii < item.num_video; ii++) + item.video.Add(parseStream(fr)); + for (int ii = 0; ii < item.num_audio; ii++) + item.audio.Add(parseStream(fr)); + for ( int ii = 0; ii < item.num_pg; ii++) + item.pg.Add(parseStream(fr)); + + return item; + } + + MPLSStream parseStream(FrameReader parentFr) + { + MPLSStream s = new MPLSStream(); + + byte len = parentFr.read_byte(); + var fr = new FrameReader(parentFr, len); + parentFr.skip(len); + + s.stream_type = fr.read_byte(); + switch (s.stream_type) + { + case 1: + s.pid = fr.read_ushort(); + break; + case 2: + case 4: + s.subpath_id = fr.read_byte(); + s.subclip_id = fr.read_byte(); + s.pid = fr.read_ushort(); + break; + case 3: + s.subpath_id = fr.read_byte(); + s.pid = fr.read_ushort(); + break; + default: + throw new Exception("unrecognized stream type"); + }; + + len = parentFr.read_byte(); + fr = new FrameReader(parentFr, len); + parentFr.skip(len); + + s.coding_type = fr.read_byte(); + if (s.coding_type == 0x01 + || s.coding_type == 0x02 + || s.coding_type == 0xea + || s.coding_type == 0x1b) + { + // Video + byte fmt = fr.read_byte(); + s.format = (byte)(fmt >> 4); + s.rate = (byte)(fmt & 15); + } + else if (s.coding_type == 0x03 + || s.coding_type == 0x04 + || s.coding_type == 0x80 + || s.coding_type == 0x81 + || s.coding_type == 0x82 + || s.coding_type == 0x83 + || s.coding_type == 0x84 + || s.coding_type == 0x85 + || s.coding_type == 0x86) + { + // Audio + byte fmt = fr.read_byte(); + s.format = (byte)(fmt >> 4); + s.rate = (byte)(fmt & 15); + s.lang = fr.read_string(3); + } + else if (s.coding_type == 0x90 + || s.coding_type == 0x91) + { + s.lang = fr.read_string(3); + } + else if (s.coding_type == 0x92) + { + s.char_code = fr.read_byte(); + s.lang = fr.read_string(3); + } + else throw new Exception("unrecognized stream type"); + + return s; + } + + MPLSPlaylistMark parsePlaylistMark(FrameReader fr) + { + var mark = new MPLSPlaylistMark(); + mark.mark_id = fr.read_byte(); + mark.mark_type = fr.read_byte(); + mark.play_item_ref = fr.read_ushort(); + mark.time = fr.read_uint(); + mark.entry_es_pid = fr.read_ushort(); + mark.duration = fr.read_uint(); + return mark; + } + + public IAudioDecoderSettings Settings => m_settings; + + public void Close() + { + if (readers != null) + foreach (var rdr in readers) + { + rdr.Close(); + } + readers = null; + currentReader = null; + _IO = null; + } + + public long Length + { + get + { + return -1; + } + } + + public TimeSpan Duration + { + get + { + uint totalLength = 0; + foreach (var item in hdr_m.play_item) + { + if (item.num_audio == 0) continue; + uint item_duration = item.out_time - item.in_time; + if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; + totalLength += item_duration; + } + + return TimeSpan.FromSeconds(totalLength / 45000.0); + } + } + + public long Remaining + { + get + { + return -1; + } + } + + public long Position + { + get + { + long res = 0; + foreach (var rdr in readers) + { + res += rdr.Position; + if (rdr == currentReader) break; + } + return res; + } + set + { + throw new NotSupportedException(); + } + } + + public unsafe AudioPCMConfig PCM + { + get { + if (readers == null) openEntries(); + return pcm; + } + } + + public string Path { get { return _path; } } + + public unsafe int Read(AudioBuffer buff, int maxLength) + { + if (readers == null) openEntries(); + int res = currentReader.Read(buff, maxLength); + if (res == 0) + { + bool nextOne = false; + foreach (var rdr in readers) + { + if (nextOne) + { + currentReader = rdr; + return currentReader.Read(buff, maxLength); + } + nextOne = (rdr == currentReader); + } + currentReader = null; + } + return res; + } + + public string FileName + { + get + { + return System.IO.Path.GetFileName(_path); + } + } + + public MPLSHeader MPLSHeader + { + get + { + return hdr_m; + } + } + + public List Chapters + { + get + { + //settings.IgnoreShortItems + var res = new List(); + if (hdr_m.play_mark.Count < 1) return res; + if (hdr_m.play_item.Count < 1) return res; + res.Add(0); + for (int i = 0; i < hdr_m.mark_count; i++) + { + ushort mark_item = hdr_m.play_mark[i].play_item_ref; + uint item_in_time = hdr_m.play_item[mark_item].in_time; + uint item_out_time = hdr_m.play_item[mark_item].out_time; + if (m_settings.IgnoreShortItems && item_out_time - item_in_time < shortItemDuration) continue; + uint item_offset = 0; + for (int j = 0; j < mark_item; j++) + { + if (hdr_m.play_item[j].num_audio == 0) continue; + uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; + if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; + item_offset += item_duration; + } + res.Add(hdr_m.play_mark[i].time - item_in_time + item_offset); + } + uint end_offset = 0; + for (int j = 0; j < hdr_m.play_item.Count; j++) + { + if (hdr_m.play_item[j].num_audio == 0) continue; + uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; + if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue; + end_offset += hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time; + } + res.Add(end_offset); + while (res.Count > 1 && res[1] - res[0] < 45000) res.RemoveAt(1); + while (res.Count > 1 && res[res.Count - 1] - res[res.Count - 2] < 45000) res.RemoveAt(res.Count - 2); + return res; + } + } + + readonly static int shortItemDuration = 45000 * 30; + + string _path; + Stream _IO; + byte[] contents; + + AudioPCMConfig pcm; + List readers; + BDLPCM.AudioDecoder currentReader; + MPLSHeader hdr_m; + DecoderSettings m_settings; + } + + public struct MPLSPlaylistMark + { + public byte mark_id; + public byte mark_type; + public ushort play_item_ref; + public uint time; + public ushort entry_es_pid; + public uint duration; + } + + public struct MPLSStream + { + public byte stream_type; + public byte coding_type; + public ushort pid; + public byte subpath_id; + public byte subclip_id; + public byte format; + public byte rate; + public byte char_code; + public string lang; + + public string FormatString + { + get + { + if (coding_type == 0x01 + || coding_type == 0x02 + || coding_type == 0xea + || coding_type == 0x1b) + switch (format) + { + case 0: return "reserved0"; + case 1: return "480i"; + case 2: return "576i"; + case 3: return "480p"; + case 4: return "1080i"; + case 5: return "720p"; + case 6: return "1080p"; + case 7: return "576p"; + default: return format.ToString(); + } + switch (format) + { + case 0: return "reserved0"; + case 1: return "mono"; + case 2: return "reserved2"; + case 3: return "stereo"; + case 4: return "reserved4"; + case 5: return "reserved5"; + case 6: return "multi-channel"; + case 12: return "combo"; + default: return format.ToString(); + } + } + } + + public int FrameRate + { + get + { + switch (rate) + { + case 1: return 24; + case 2: return 24; + case 3: return 25; + case 4: return 30; + case 6: return 50; + case 7: return 60; + default: throw new NotSupportedException(); + } + } + } + + public bool Interlaced + { + get + { + return format == 1 || format == 2 || format == 4; + } + } + + public string RateString + { + get + { + if (coding_type == 0x01 + || coding_type == 0x02 + || coding_type == 0xea + || coding_type == 0x1b) + switch (rate) + { + case 0: return "reserved0"; + case 1: return "23.976"; + case 2: return "24"; + case 3: return "25"; + case 4: return "29.97"; + case 5: return "reserved5"; + case 6: return "50"; + case 7: return "59.94"; + default: return rate.ToString(); + } + switch (rate) + { + case 0: return "reserved0"; + case 1: return "48KHz"; + case 2: return "reserved2"; + case 3: return "reserved3"; + case 4: return "96KHz"; + case 5: return "192KHz"; + //case 12: return "48/192KHz"; (core/hd) + case 12: return "192KHz"; + //case 14: return "48/96KHz"; (core/hd) + case 14: return "96KHz"; + default: return rate.ToString(); + } + } + } + + public string CodecString + { + get + { + switch (coding_type) + { + case 0x01: return "MPEG-1 Video"; + case 0x02: return "MPEG-2 Video"; + case 0x03: return "MPEG-1 Audio"; + case 0x04: return "MPEG-2 Audio"; + //case 0x80: return "LPCM"; + case 0x80: return "RAW/PCM"; + case 0x81: return "AC-3"; + case 0x82: return "DTS"; + case 0x83: return "TrueHD"; + case 0x84: return "AC-3 Plus"; + case 0x85: return "DTS-HD"; + //case 0x86: return "DTS-HD Master"; + case 0x86: return "DTS Master Audio"; + case 0xea: return "VC-1"; + case 0x1b: return "h264/AVC"; + case 0x90: return "Presentation Graphics"; + case 0x91: return "Interactive Graphics"; + case 0x92: return "Text Subtitle"; + default: return coding_type.ToString(); + } + } + } + + public byte CodingType + { + get + { + return coding_type; + } + } + + public byte FormatType + { + get + { + return format; + } + } + + public string LanguageString + { + get + { + CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); + foreach (var culture in cultures) + { + // Exclude custom cultures. + if ((culture.CultureTypes & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture) + continue; + + if (culture.ThreeLetterISOLanguageName == lang) + return culture.EnglishName; + } + return lang; + } + } + } + + public struct MPLSPlaylistItem + { + public string clip_id; + public byte connection_condition; + public byte stc_id; + public uint in_time; + public uint out_time; + + public byte num_video; + public byte num_audio; + public byte num_pg; + public byte num_ig; + public byte num_secondary_audio; + public byte num_secondary_video; + public byte num_pip_pg; + public List video; + public List audio; + public List pg; + } + + public struct MPLSHeader + { + public uint type_indicator; + public uint type_indicator2; + public uint list_pos; + public uint mark_pos; + public uint ext_pos; + + public ushort list_count; + public ushort sub_count; + public ushort mark_count; + + public List play_item; + public List play_mark; + } +} diff --git a/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs b/CUETools.Codecs.MPEG/MPLS/DecoderSettings.cs similarity index 91% rename from CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs rename to CUETools.Codecs.MPEG/MPLS/DecoderSettings.cs index 5e6e935..6ca9125 100644 --- a/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs +++ b/CUETools.Codecs.MPEG/MPLS/DecoderSettings.cs @@ -4,7 +4,7 @@ using System.ComponentModel; using System.Text; using Newtonsoft.Json; -namespace CUETools.Codecs.MPLS +namespace CUETools.Codecs.MPEG.MPLS { [JsonObject(MemberSerialization.OptIn)] public class DecoderSettings : IAudioDecoderSettings @@ -17,7 +17,7 @@ namespace CUETools.Codecs.MPLS public string Name => "cuetools"; [Browsable(false)] - public Type DecoderType => typeof(BDLPCM.MPLSDecoder); + public Type DecoderType => typeof(AudioDecoder); [Browsable(false)] public int Priority => 2; diff --git a/CUETools.Codecs.BDLPCM/TsStream.cs b/CUETools.Codecs.MPEG/TsStream.cs similarity index 95% rename from CUETools.Codecs.BDLPCM/TsStream.cs rename to CUETools.Codecs.MPEG/TsStream.cs index 6fcce7b..7c95f50 100644 --- a/CUETools.Codecs.BDLPCM/TsStream.cs +++ b/CUETools.Codecs.MPEG/TsStream.cs @@ -1,59 +1,59 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CUETools.Codecs.BDLPCM -{ - internal class TsStream - { - internal UInt16 channel; // channel number (1,2 ...) - internal int streamId; // stream number in channel - internal byte type; // 0xff - not ES - internal AudioPCMConfig pcm; - internal byte[] savedBuffer; - internal int savedBufferOffset; - internal int savedBufferSize; - - internal byte[] psi; // PAT,PMT cache (only for PSI streams) - internal int psi_len; - internal int psi_offset; - internal int psi_table; - internal bool at_packet_header; - - internal byte ts_stream_id; // MPEG stream id - - internal bool is_opened; - - internal UInt64 dts; // current MPEG stream DTS (presentation time for audio, decode time for video) - internal UInt64 first_dts; - internal UInt64 first_pts; - internal UInt64 last_pts; - internal UInt32 frame_length; // frame length in ticks (90 ticks = 1 ms, 90000/frame_length=fps) - internal UInt32 frame_size; // frame size in bytes - internal UInt64 frame_num; // frame counter - - internal TsStream() - { - is_opened = false; - psi = new byte[512]; - psi_len = 0; - psi_offset = 0; - psi_table = 0; - channel = 0xffff; - streamId = 0; - type = 0xff; - ts_stream_id = 0; - dts = 0; - first_dts = 0; - first_pts = 0; - last_pts = 0; - frame_length = 0; - frame_size = 0; - frame_num = 0; - pcm = null; - savedBuffer = new byte[192]; - savedBufferOffset = 0; - savedBufferSize = 0; - } - }; -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace CUETools.Codecs.MPEG +{ + internal class TsStream + { + internal UInt16 channel; // channel number (1,2 ...) + internal int streamId; // stream number in channel + internal byte type; // 0xff - not ES + internal AudioPCMConfig pcm; + internal byte[] savedBuffer; + internal int savedBufferOffset; + internal int savedBufferSize; + + internal byte[] psi; // PAT,PMT cache (only for PSI streams) + internal int psi_len; + internal int psi_offset; + internal int psi_table; + internal bool at_packet_header; + + internal byte ts_stream_id; // MPEG stream id + + internal bool is_opened; + + internal UInt64 dts; // current MPEG stream DTS (presentation time for audio, decode time for video) + internal UInt64 first_dts; + internal UInt64 first_pts; + internal UInt64 last_pts; + internal UInt32 frame_length; // frame length in ticks (90 ticks = 1 ms, 90000/frame_length=fps) + internal UInt32 frame_size; // frame size in bytes + internal UInt64 frame_num; // frame counter + + internal TsStream() + { + is_opened = false; + psi = new byte[512]; + psi_len = 0; + psi_offset = 0; + psi_table = 0; + channel = 0xffff; + streamId = 0; + type = 0xff; + ts_stream_id = 0; + dts = 0; + first_dts = 0; + first_pts = 0; + last_pts = 0; + frame_length = 0; + frame_size = 0; + frame_num = 0; + pcm = null; + savedBuffer = new byte[192]; + savedBufferOffset = 0; + savedBufferSize = 0; + } + }; +} diff --git a/CUETools.Codecs.ffmpeg/DecoderSettings.cs b/CUETools.Codecs.ffmpeg/DecoderSettings.cs index bd11989..23b0b6b 100644 --- a/CUETools.Codecs.ffmpeg/DecoderSettings.cs +++ b/CUETools.Codecs.ffmpeg/DecoderSettings.cs @@ -111,4 +111,13 @@ namespace CUETools.Codecs.ffmpegdll this.Init(); } } + public class MpegDecoderSettings : DecoderSettings, IAudioDecoderSettings + { + public override string Extension => "aob"; + public override string Format => "mpeg"; + public MpegDecoderSettings() + { + this.Init(); + } + } } \ No newline at end of file diff --git a/CUETools.Codecs/CUEToolsCodecsConfig.cs b/CUETools.Codecs/CUEToolsCodecsConfig.cs index 40f4d32..e2e883d 100644 --- a/CUETools.Codecs/CUEToolsCodecsConfig.cs +++ b/CUETools.Codecs/CUEToolsCodecsConfig.cs @@ -88,6 +88,7 @@ namespace CUETools.Codecs formats.Add("ogg", new CUEToolsFormat("ogg", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("ogg", false), null)); formats.Add("opus", new CUEToolsFormat("opus", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("opus", false), null)); formats.Add("mlp", new CUEToolsFormat("mlp", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("mlp"))); + formats.Add("aob", new CUEToolsFormat("aob", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("aob"))); } } } diff --git a/CUETools.eac3to/CUETools.eac3to.csproj b/CUETools.eac3to/CUETools.eac3to.csproj index 777279e..ea3f2f7 100644 --- a/CUETools.eac3to/CUETools.eac3to.csproj +++ b/CUETools.eac3to/CUETools.eac3to.csproj @@ -27,7 +27,7 @@ - + diff --git a/CUETools.eac3to/Program.cs b/CUETools.eac3to/Program.cs index 64438c2..9f34b5d 100644 --- a/CUETools.eac3to/Program.cs +++ b/CUETools.eac3to/Program.cs @@ -1,6 +1,5 @@ using CUETools.CDImage; using CUETools.Codecs; -using CUETools.Codecs.BDLPCM; using CUETools.CTDB; using CUETools.Processor; using System; @@ -124,8 +123,8 @@ namespace CUETools.eac3to { IAudioSource audioSource = null; IAudioDest audioDest = null; - var videos = new List(); - var audios = new List(); + var videos = new List(); + var audios = new List(); List chapters; TimeSpan duration; TagLib.UserDefined.AdditionalFileTypes.Config = config; @@ -136,7 +135,7 @@ namespace CUETools.eac3to { if (true) { - var mpls = new MPLSDecoder(new Codecs.MPLS.DecoderSettings(), sourceFile, null); + var mpls = new Codecs.MPEG.MPLS.AudioDecoder(new Codecs.MPEG.MPLS.DecoderSettings(), sourceFile, null); audioSource = mpls; Console.ForegroundColor = ConsoleColor.White; int frameRate = 0; @@ -310,14 +309,14 @@ namespace CUETools.eac3to throw new Exception("Unknown encoder format: " + destFile); } - if (audioSource is MPLSDecoder) + if (audioSource is Codecs.MPEG.MPLS.AudioDecoder) { if (stream - chapterStreams <= videos.Count) throw new Exception("Video extraction not supported."); if (stream - chapterStreams - videos.Count > audios.Count) throw new Exception(string.Format("The source file doesn't contain a track with the number {0}.", stream)); int pid = audios[stream - chapterStreams - videos.Count - 1].pid; - (audioSource.Settings as Codecs.MPLS.DecoderSettings).StreamId = pid; + (audioSource.Settings as Codecs.MPEG.MPLS.DecoderSettings).StreamId = pid; } } diff --git a/CUETools.eac3ui/BLUTools.csproj b/CUETools.eac3ui/BLUTools.csproj index 659beed..16a5a59 100644 --- a/CUETools.eac3ui/BLUTools.csproj +++ b/CUETools.eac3ui/BLUTools.csproj @@ -105,9 +105,9 @@ {1dd41038-d885-46c5-8dde-e0b82f066584} CUETools.CDImage - + {e75f7ccd-4266-42e1-a039-dc7eb5edd8f6} - CUETools.Codecs.BDLPCM + CUETools.Codecs.MPEG False diff --git a/CUETools.eac3ui/MainWindow.xaml.cs b/CUETools.eac3ui/MainWindow.xaml.cs index 73af51f..26e1c38 100644 --- a/CUETools.eac3ui/MainWindow.xaml.cs +++ b/CUETools.eac3ui/MainWindow.xaml.cs @@ -1,28 +1,17 @@ -using System; -using System.IO; +using CUETools.CDImage; +using CUETools.Codecs; +using CUETools.CTDB; +using CUETools.Processor; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -//using System.Windows.Shapes; -using CUETools.Codecs.BDLPCM; -using CUETools.CDImage; -using CUETools.CTDB; -using System.ComponentModel; -using Krystalware.UploadHelper; -using CUETools.Codecs; -//using CUETools.Codecs.Flake; -using CUETools.Processor; -using System.Collections.ObjectModel; -//using Microsoft.Win32; namespace BluTools { @@ -75,7 +64,7 @@ namespace BluTools private void textBoxSource_TextChanged(object sender, TextChangedEventArgs e) { - var titleSets = new List(); + var titleSets = new List(); IEnumerable playlists = null; try { @@ -87,7 +76,7 @@ namespace BluTools if (playlists != null) foreach (var playlist in playlists) { - var title = new MPLSDecoder(new CUETools.Codecs.MPLS.DecoderSettings(), playlist, null); + var title = new CUETools.Codecs.MPEG.MPLS.AudioDecoder(new CUETools.Codecs.MPEG.MPLS.DecoderSettings(), playlist, null); if (filterDups) { if (titleSets.Exists(title2 => @@ -125,10 +114,10 @@ namespace BluTools cmbMetadata.ItemsSource = new List(); ctdb = null; - var audios = new List(); + var audios = new List(); if (e.AddedItems.Count == 1) { - MPLSDecoder rdr = e.AddedItems[0] as MPLSDecoder; + var rdr = e.AddedItems[0] as CUETools.Codecs.MPEG.MPLS.AudioDecoder; rdr.MPLSHeader.play_item.ForEach(i => i.audio.ForEach(v => { if (!audios.Exists(v1 => v1.pid == v.pid)) audios.Add(v); })); var chapters = rdr.Chapters; @@ -190,7 +179,7 @@ namespace BluTools { if (e.AddedItems.Count == 1) { - MPLSStream stream = (MPLSStream)(e.AddedItems[0]); + CUETools.Codecs.MPEG.MPLS.MPLSStream stream = (CUETools.Codecs.MPEG.MPLS.MPLSStream)(e.AddedItems[0]); } } @@ -200,7 +189,7 @@ namespace BluTools BackgroundWorker workerExtract; CUEMetadataEntry metaresult; ObservableCollection metaresults; - MPLSDecoder chosenReader; + CUETools.Codecs.MPEG.MPLS.AudioDecoder chosenReader; ushort pid; string outputFolderPath; string outputAudioPath; @@ -213,8 +202,8 @@ namespace BluTools private void buttonExtract_Click(object sender, RoutedEventArgs e) { if (cmbTitleSet.SelectedItem == null) return; - pid = ((MPLSStream)cmbAudioTrack.SelectedItem).pid; - chosenReader = cmbTitleSet.SelectedItem as MPLSDecoder; + pid = ((CUETools.Codecs.MPEG.MPLS.MPLSStream)cmbAudioTrack.SelectedItem).pid; + chosenReader = cmbTitleSet.SelectedItem as CUETools.Codecs.MPEG.MPLS.AudioDecoder; metaresult = cmbMetadata.SelectedItem as CUEMetadataEntry; outputFolderPath = Path.Combine(textBoxDestination.Text, metaresult != null ? metaresult.metadata.Artist + " - " + metaresult.metadata.Year + " - " + metaresult.metadata.Title : @@ -239,10 +228,10 @@ namespace BluTools void workerExtract_DoWork(object sender, DoWorkEventArgs e) { - MPLSDecoder reader = null; + CUETools.Codecs.MPEG.MPLS.AudioDecoder reader = null; try { - reader = new MPLSDecoder(chosenReader.Path, null, pid); + reader = new CUETools.Codecs.MPEG.MPLS.AudioDecoder(chosenReader.Path, null, pid); Directory.CreateDirectory(outputFolderPath); if (File.Exists(outputCuePath)) throw new Exception(string.Format("File \"{0}\" already exists", outputCuePath)); if (File.Exists(outputAudioPath)) throw new Exception(string.Format("File \"{0}\" already exists", outputAudioPath)); diff --git a/CUETools/CUETools.sln b/CUETools/CUETools.sln index 3f648ef..4cf4f8a 100644 --- a/CUETools/CUETools.sln +++ b/CUETools/CUETools.sln @@ -163,7 +163,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsMediaLib", "..\Third EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.WMA", "..\CUETools.Codecs.WMA\CUETools.Codecs.WMA.csproj", "{082D6B9E-326E-4D15-9798-DE70A6EDAE9E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.BDLPCM", "..\CUETools.Codecs.BDLPCM\CUETools.Codecs.BDLPCM.csproj", "{E75F7CCD-4266-42E1-A039-DC7EB5EDD8F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.BDLPCM", "..\CUETools.Codecs.MPEG\CUETools.Codecs.MPEG.csproj", "{E75F7CCD-4266-42E1-A039-DC7EB5EDD8F6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.eac3to", "..\CUETools.eac3to\CUETools.eac3to.csproj", "{E3FF7539-6B22-4922-8FEF-6D26F2C2E3CE}" EndProject