m2ts file with TsViewer

Jun 6, 2013 at 3:59 AM
I'm trying to watch a m2ts file ri??ed from the DVD saved by D?GA.
The file can play with Windows Media Player.
Your TsViewer did not implement Open File click handler then I added it.
But I got a exception at opening the file.


BMFF\TsViewer\TsFileSource.cs
private void ReadFile(object data)
{
    //var fileStream = File.Open(Uri.AbsolutePath, FileMode.Open, FileAccess.Read, FileShare.Read);
    var fileStream = File.Open( Uri.LocalPath , FileMode.Open , FileAccess.Read , FileShare.Read );
If URI has non-7bitByte characters, uri.AbsolutePath returns URI-encoded string, like
C:/Project/%E3%83%93%E3%83%87%E3%82%AA%E7%B7%A8%E9%9B%86/%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB/%E3%83%AC%E3%83%B4%E3%82%A3%E3%82%A2%E3%82%BF%E3%83%B3/00001.m2ts

You should use Uri.LocalPath.
"A String that contains the local operating-system representation of a file name."


I still have a trouble.

*** Skipping packet due to prior discontinuity. ***
Remainder is 84 bytes.
Read 1316 bytes from 00001.m2ts with a first byte of 0xC1
Skipping byte 0xC1 at offset 0.
Skipping byte 0x32 at offset 1.
Skipping byte 0x86 at offset 2.
Skipping byte 0x2C at offset 3.
Has Original Program Clock Reference.
Has Splice Countdown.
Has 201 byte of Private Data.
Private data exceeds length by 31 bytes!!!!!!!
Reading 170 bytes of Private Data.
Has 20 bytes of Adaptation Field stuffing.
型 'System.OverflowException' の初回例外が MatrixIO.IO.MPEGTS.dll で発生しました

I'll let you know when I found something.
BR
Coordinator
Jun 6, 2013 at 2:02 PM
Edited Jun 6, 2013 at 2:12 PM
m2ts files aren't standard MPEG-TS, they are BDAV and have 192 byte packets instead of 188 (4-byte timestamp, I believe) and there may be some other changes, I'm not sure.
Coordinator
Jun 6, 2013 at 2:52 PM
Note I just checked in a significant overhaul to the TS library and viewer though the viewer is still just a test platform for development, not a useful tool. It's very much a work in progress. It still doesn't read BDAV or FEC variants but I think that's just a matter of skipping bytes in the reader.

Ideally it should sync on 4 or more packets to determine the packet length and ensure it hasn't found a false start point because TS packets don't have a very robust start code and have no way of signalling the extra packet length in the various modified formats.
Coordinator
Jun 6, 2013 at 3:20 PM
Edited Jun 6, 2013 at 3:20 PM
The updated version still has a problem with BDAV when it crosses read buffer boundaries. I'll look into fixing that. Within a single read buffer it handles skipping the extra 4 bytes between packets but it'll likely have problems if the timecode contains the 0x47 start byte so it still needs to be extended to support syncing multiple packets to determine the packet length.
Coordinator
Jun 6, 2013 at 4:52 PM
Ok, I checked in an initial fix for the read block boundary sync problem. It now does a better job of skipping the 4-byte timecodes. It may or may not handle the 20byte ATSC FEC variant and other modified formats -- it depends on the TsPacket constructor throwing an exception if it finds an 0x47 that isn't actually a sync byte.

Actually doing multi-packet sync to determine non-standard TS formats will be more complicated.
Jun 6, 2013 at 9:16 PM
I've played with m2ts before half a year.
I referrenced http://code.google.com/p/tsdemuxer/ .
It can read both m2ts(192bytes/packet) and ts(188bytes/packet).

ts.cpp
int ts::demuxer::demux_file(const char* name)
{
    prefix.clear();

    char buf[192];

    int buf_len=0;

    ts::file file;

    if(!file.open(file::in,"%s",name))
    {
        fprintf(stderr,"can`t open file %s\n",name);
        return -1;
    }

    get_prefix_name_by_filename(name,prefix);
    if(prefix.length())
        prefix+='.';

    for(u_int64_t pn=1;;pn++)
    {
        if(buf_len)
        {
            if(file.read(buf,buf_len)!=buf_len)
                break;
        }else
        {
            if(file.read(buf,188)!=188)
                break;
            if(buf[0]==0x47 && buf[4]!=0x47)
            {
                buf_len=188;
                fprintf(stderr,"TS stream detected in %s (packet length=%i)\n",name,buf_len);
                hdmv=false;
            }else if(buf[0]!=0x47 && buf[4]==0x47)
            {
                if(file.read(buf+188,4)!=4)
                    break;
                buf_len=192;
                fprintf(stderr,"M2TS stream detected in %s (packet length=%i)\n",name,buf_len);
                hdmv=true;
            }else
            {
                fprintf(stderr,"unknown stream type in %s\n",name);
                return -1;
            }
        }

        int n;
        if((n=demux_ts_packet(buf)))
        {
            fprintf(stderr,"%s: invalid packet %llu (%i)\n",name,pn,n);
            return -1;
        }
    }

    return 0;
}
Jun 6, 2013 at 10:40 PM
I've modified TsFileSource.cs then seems working.
    private void ReadFile(object data)
    {
        using ( var fileStream = File.Open( Uri.LocalPath , FileMode.Open , FileAccess.Read , FileShare.Read ) )
        {
            var buffer = new byte[192];
            int length = 0;
            int packetLen = 0;
            do
            {
                if ( packetLen == 0 )
                {
                    // stream type has not been detected yet

                    // first, read 188bytes (assuming TS stream)
                    length = fileStream.Read( buffer , 0 , 188 );
                    if ( length != 188 )
                    {
                        Debug.WriteLine( "Invalid file (too short)." );
                        break;
                    }
                    if ( buffer[0] == 0x47 && buffer[4] != 0x47 )
                    {
                        // stream must be TS(188bytes/packet)
                        packetLen = 188;
                        Debug.WriteLine( "TS stream detected." );
                    }
                    else if ( buffer[0]!=0x47 && buffer[4]==0x47 )
                    {
                        // check stream chould be m2ts
                        if ( fileStream.Read( buffer , 188 , 4 ) != 4 )
                        {
                            Debug.WriteLine( "Invalid file (too short)." );
                            break;
                        }
                        // stream must be M2TS(192bytes/packet)
                        packetLen=192;
                        Debug.WriteLine( "M2TS stream detected." );
                    }
                    else
                    {
                        Debug.WriteLine( "Unknown stream type." );
                        break;
                    }
                }
                else
                {
                    //if ( (length = fileStream.Read( buffer , 0 , buffer.Length )) > 0 )
                    //{
                    //  Debug.WriteLine( "Read " + length + " bytes from " +
                    //              Uri.Segments[Uri.Segments.Length - 1] + " with a first byte of 0x" +
                    //              buffer[0].ToString( "X2" ) );
                    //  Demuxer.ProcessInput( buffer , 0 , length );
                    //}
                    if ( (length=fileStream.Read( buffer , 0 , packetLen )) != packetLen )
                    {
                        if ( length > 0 )
                        {
                            Debug.WriteLine( "Failed to read a packet." );
                        }
                        break;
                    }
                }

                Demuxer.ProcessInput( buffer , packetLen == 192 ? 4:0 , 188 );
            } while ( length > 0 && _running );
        }
    }

When I open a file, it takes a while there are some information on the tree,
file://tested file path
    Program 0
    Program 1048
        MPEG4_AVC(PID 4113)
        MPEG4_AAC(PID 4129)
That's it. When I click to select a node, it does nothing.
How can I see any informations?


On my application played before, it shows all PID with each number of packes.

PID=0x0000 923packets
PID=0x0100 923packets
PID=0x1001 1packets
PID=0x001f 93packets
PID=0x1011 2,716packets
PID=0x1021 4,211packets
PID=0x1fff 1packets

And it shows packets on the tree.
Also it shows propertys of a packet (not completed).

Your Demo App, TsViewer, shows something on Debug output,
### Completed unit for PID 4113 with 101 packets, 18327 bytes of payload and 2 adaptation fields.
Has PTS: 00:01:23.8111805
First 40 bytes of PES Data: 000000010950000000010601070000380000030050800000000106053317EE8C60F84D11D98CD608
PES Identifier is VideoStream0
### Completed unit for PID 4129 with 3 packets, 494 bytes of payload and 1 adaptation fields.
Has PTS: 00:01:23.0919694
First 40 bytes of PES Data: FFF84C803C01E8A53F249E09D68A442190A4411040000000000DAB6923842FCBE2FC7B112C4D0E9D
PES Identifier is AudioStream0
Has 60 bytes of Adaptation Field stuffing.
### Completed unit for PID ProgramAssociationTable with 1 packets, 184 bytes of payload and 0 adaptation fields.
Received ProgramAssociation Table
SectionInfo: 0xB011 SectionLength: 0x0011
TS Identifier: 0x0001
SectionLength=17 TableIdentifier=ProgramAssociation SectionNumber=0 LastSectionNumber=0
but I want to see in UI on TsViewer.
Jun 6, 2013 at 10:43 PM
Another problem.
When I showed the Log window via menu, then I opened a file, an exception occured.
System.InvalidOperationException はハンドルされませんでした。
  HResult=-2146233079
  Message=指定されたインデックス '0' で、追加された項目が表示されません。
  Source=PresentationFramework
  StackTrace:
       場所 System.Windows.Data.ListCollectionView.AdjustBefore(NotifyCollectionChangedAction action, Object item, Int32 index)
       場所 System.Windows.Data.ListCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
       場所 System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
       場所 TsViewer.DispatchingObservableCollection`1.<>c__DisplayClass3.<OnCollectionChanged>b__0() 場所 xxxxxxxxxxxxxxxxx\BMFF\72283\TsViewer\DispatchingObservableCollection.cs:行 56
       場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       場所 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       場所 System.Windows.Threading.DispatcherOperation.InvokeImpl()
       場所 System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
       場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       場所 System.Windows.Threading.DispatcherOperation.Invoke()
       場所 System.Windows.Threading.Dispatcher.ProcessQueue()
       場所 System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       場所 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       場所 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       場所 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       場所 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       場所 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       場所 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       場所 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       場所 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       場所 System.Windows.Threading.Dispatcher.Run()
       場所 System.Windows.Application.RunDispatcher(Object ignore)
       場所 System.Windows.Application.RunInternal(Window window)
       場所 System.Windows.Application.Run(Window window)
       場所 System.Windows.Application.Run()
       場所 TsViewer.App.Main() 場所 xxxxxxxxxxxxxxxxxxx\BMFF\72283\TsViewer\obj\x86\Debug\App.g.cs:行 0
       場所 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       場所 System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
Coordinator
Jun 7, 2013 at 12:15 AM
Your fix for m2ts will break with other formats and I'll be adding ATSC and DVB extensions soon. I've done a fix for this based on the latest rev but I can't check it in until later this evening. I added an AACS packet class which encapsulates the additional header which includes 2 bits of crypto information and a 30 bit 27Mhz clock.

The current UI is just a test rig. This is a recent addition to the project. It's nowhere near complete. The UI doesn't show the PSI, padding and program clock pids because it's specifically designed to show the program/stream hierarchy. That's what's missing from your list. No further information is visible through the UI at this time though quite a bit can be gotten from the API including a full list of PIDs. I plan to display details for the PSI information for each program/stream in the tree and additional statistics.

The errors you are seeing are binding errors because of the AACS/BDAV specific type codes in the "user defined" space of the MPEG TS standard. That's why it doesn't show the stream types in the UI. I've added some of them in but again I can't check it in now and I'm not sure it's the best way to handle it because there's no guarantee that other formats won't have overlapping definitions.
Coordinator
Jun 7, 2013 at 12:32 AM
Oh wait.. that's a different error than I'm seeing. Your file is using MPEG standard types from the looks of it. No idea what's causing that. It's a COM automation error. Best I can figure is it might be a windows version difference of some sort but I'll look into it.
Jun 7, 2013 at 10:08 AM
I've tested with http://www.dododge.net/roku/hd-test-streams/football.ts then I've understood what you said.
A ts file has garbage before a packet, and/or in-between packets, synchronizing should be performed.
I looked into ffmpeg/libavformat/mpegts.c, and found auto-stream detection (get_packet_size()).

Also I found there are more stream types, then I read Wikipedia. http://en.wikipedia.org/wiki/MPEG_transport_stream#Packet

So, there are several types of stream,
188bytes/packet original TS
192bytes/packet m2ts (ts + 4bytes-timecode)
204bytes/packet DVB (ts + 16bytes-FEC)
208bytes/packet ATSC (ts + 20bytes-reed-solomon)
ts reader is desired detecting them automatically, humm.

I could not get a DVB-ts file(204bytes/packet) ATSC-ts file(208bytes/packet).
Are there any place to get them?
Do they exist as a file?


Anyway I'm trying to make a trans-container app(m2ts -> mp4) with your library.
Your library has such capabilities I believe.

ps.
The exception problem has been occured with http://www.dododge.net/roku/hd-test-streams/football.ts, testing with original TsFileSource.cs.
So this should be another thread?
I'm playing on VS2012, Win7 Ultimate SP1 (32bit).
Jun 7, 2013 at 10:42 AM
I've tested 72288 with
  http://av.watch.impress.co.jp/docs/20081015/h1916.m2ts
  ri??ed from D?GA DR m2ts (MPEG-2 Video+AAC)
                   HX m2ts (H.264+AAC)
It works fine.
Good Job.
Coordinator
Jun 7, 2013 at 6:07 PM
Only m2ts is technically considered a file container format which is why I put the code for it in the file reader. Everything else should go through the arbitrary block reader with sync and resync capability because TS is a broadcast format and may not be aligned in any particular way. You won't always start reading it at the beginning of a packet. I believe the spec says to sync on the first 4-5 packets to confirm you've aligned properly and you'll have to be able to resync on lost packets or stream corruption.

TS over udp, for instance, is often sent with 7 whole TS packets per UDP packet but sometimes they are just stuffed in without regard for where the packets start and end -- it's more bandwidth efficient that way. Serial streams (like capture from ASI or tuners) will rarely come in nice even chunks or start at the beginning of a packet.

Note, football.ts starts in the middle of a packet as if it were recorded from a live serial source. It ends with a packet cut off as well.

I had a typo in the comparison in the new block boundary code that broke plain TS file processing. I've checked in a fix for that. It was causing the demuxer to miss packets at block boundaries for plain TS.

I am not seeing the exception you reported with football.ts with or without that fix. There may be a timing-sensitive edge case failing in DispatchingObservableCollection. Cross-thread UI updates in WPF are always a pain. I don't think it's actually a problem with the library, just the sample app UI, but I'd still like to find and fix it anyway because I use that DispatchingObservableCollection class a lot.

I don't have any good samples of TS streams with Forward Error Correction. I'm looking around for some. My tuner strips off the ATSC FEC before I can get at it and I don't have access to a DVB or DVB-ASI stream at the moment. I know FEC is sometimes used with TS over UDP/RTP so there are probably some samples around somewhere. I'll keep looking.
Jun 7, 2013 at 9:25 PM
So this should be another thread?
I'm playing on VS2012, Win7 Ultimate SP1 (32bit).
Sorry, I mean another new thread OF THIS FORUM.

Now I remember that I've also used dispatching-observable-collections wchich can see many implementations on the internet.
These were working fine with .NET4, but after I upgraded to .NET4.5, they caused error even I did not re-compiled.
Because .NET4.5 is inplace upgrade so executing assemblies is not same as .NET4 even if the application specifies .NET4.
And some informations are on MSDN, existing codes should be re-written due to internal implementing collection or dispatch or notification or... I forget.
Anyway some dispatching-observable-collections have trouble with .NET4.5.

Currently I'm using similar technique ;
http://blogs.microsoft.co.il/blogs/pavely/archive/2012/07/21/wpf-4-5-accessing-bound-collections-on-non-ui-threads.aspx

And lock entire codes touching collection.
I'm still looking for more efficient way...