public PacketizedElementaryStream(BufferExtractor extractor, TsPacket first)
        {
            if (extractor == null || first == null)
            {
                throw new ArgumentNullException("extractor or first", "no ctor parameters may be null");
            }

            myExtractor = extractor;

            IsComplete = true;//we assume it is complete until we prove it is not

            packets = new Queue <TsPacket>(4);
            packets.Enqueue(first);

            lastCC = first.ContinuityCounter;

            payloadIndex = first.PayloadStart;
        }
Example #2
0
        override public async void Run()
        {
            //Android.Media.MediaExtractor extractor;

            Android.Media.MediaCodec decoder = null;

            using (var extractor = new Android.Media.MediaExtractor())
            //using (Android.Media.MediaCodec decoder = null)
            {
                //extractor = new Android.Media.MediaExtractor();
                try
                {
                    await extractor.SetDataSourceAsync(SAMPLE).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    var s = ex.ToString();
                    return;
                }

                for (int i = 0; i < extractor.TrackCount; i++)
                {
                    var format = extractor.GetTrackFormat(i);

                    Log.Debug("Format info: ", format.ToString());

                    String mime = format.GetString(Android.Media.MediaFormat.KeyMime);
                    if (mime.StartsWith("video/"))
                    {
                        Log.Debug("Format mime: ", mime);
                        //Log.Debug("Format " + MediaFormat.KeyMaxInputSize + ": ",
                        //            format.GetInteger(MediaFormat.KeyMaxInputSize).ToString());
                        Log.Debug("Format " + MediaFormat.KeyWidth + ": ",
                                    format.GetInteger(MediaFormat.KeyWidth).ToString());
                        Log.Debug("Format " + MediaFormat.KeyHeight + ": ",
                                    format.GetInteger(MediaFormat.KeyHeight).ToString());

                        PrintFormatInfo(format);

                        extractor.SelectTrack(i);
                        //decoder = Android.Media.MediaCodec.CreateDecoderByType(mime);
                        //this is where the Xamarin Android VM dies.
                        //decoder.Configure(format, surface, null, 0);
                        break;
                    }
                }

                //if (decoder == null)
                //{
                //    Android.Util.Log.Error("DecodeActivity", "Can't find video info!");
                //    return;//can't continue...
                //}
                var f = new Java.IO.File(dir+"decode.out");
                if (f.Exists())
                    f.Delete();

                f.CreateNewFile();

                var f2 = new Java.IO.File(dir + "decode2.out");
                if (f2.Exists())
                    f2.Delete();

                f2.CreateNewFile();

                //open the file for our custom extractor
                var inInfo = new System.IO.FileInfo(SAMPLE);
                if (!inInfo.Exists)
                {
                    Log.Error("input file not found!", inInfo.FullName);
                    return;
                }

                using (var inStream = inInfo.OpenRead())
                using (var fs2 = new Java.IO.FileOutputStream(f2))//get an output stream
                using (var fs = new Java.IO.FileOutputStream(f))//get an output stream
                {

                    //var inputBuffers = decoder.GetInputBuffers();
                    //var outputBuffers = decoder.GetOutputBuffers();
                    var info = new Android.Media.MediaCodec.BufferInfo();
                    bool started = false, isEOS = false;
                    var sw = new System.Diagnostics.Stopwatch();
                    long startMs = sw.ElapsedMilliseconds;
                    sw.Start();
                    byte[] peekBuf = new byte[188];
                    //for dumping the sample into instead of the decoder.
                    var buffer = Java.Nio.ByteBuffer.Allocate(165000);// decoder.GetInputBuffer(inIndex);
                    var buffEx = new BufferExtractor();
                    var tmpB = new byte[20000];


                    while (!interrupted)
                    {
                        //sw.Restart();

                        if (!isEOS)
                        {
                            int inIndex = 1;// decoder.DequeueInputBuffer(10000);
                            if (inIndex >= 0)
                            {
                                buffer.Position(0);//reset the buffer
                                if (buffer.Position() != 0)
                                    Log.Debug("inBuff.Position: ", buffer.Position().ToString());
                                Log.Debug("inBuff: ", buffer.ToString());

                                int sampleSize = extractor.ReadSampleData(buffer, 0);
                                if (sampleSize < 0)
                                {
                                    // We shouldn't stop the playback at this point, just pass the EOS
                                    // flag to decoder, we will get it again from the
                                    // dequeueOutputBuffer
                                    Log.Debug("DecodeActivity", MediaCodecBufferFlags.EndOfStream.ToString());
                                    //decoder.QueueInputBuffer(inIndex, 0, 0, 0, MediaCodecBufferFlags.EndOfStream);
                                    isEOS = true;
                                }
                                else
                                {
                                    if (peekBuf.Length < sampleSize)
                                        peekBuf = new byte[sampleSize];
                                    peekBuf.Initialize();//clear old data.
                                    buffer.Get(peekBuf);
                                    buffer.Position(0);//reset for the decoder

                                    for (int i = 4; i < peekBuf.Length; ++i)
                                    {
                                        if (peekBuf[i] == 0x01
                                            && peekBuf[i - 1] == 0x00
                                            && peekBuf[i - 2] == 0x00
                                            && peekBuf[i - 3] == 0x00)
                                            Log.Debug("Found h264 start code: ",
                                                        string.Format("i={0} of {1}", i, sampleSize));
                                    }

                                    Log.Debug("ExtractorActivity, sampleSize: ", sampleSize.ToString());

                                    if (!started)//get your parser synced with theirs
                                    {
                                        do
                                        {
                                            peekBuf = new byte[188];
                                            await inStream.ReadAsync(peekBuf, 0, peekBuf.Length)
                                                            .ConfigureAwait(false);

                                            buffEx.AddRaw(peekBuf);

                                            if (buffEx.outBuffers.Count > 0
                                                && buffEx.outBuffers.Peek().GetPayload().Length != sampleSize)
                                            {
                                                buffEx.outBuffers.Dequeue();//throw this one away
                                            }

                                        } while (buffEx.outBuffers.Count == 0);
                                        started = true;
                                    }
                                    else
                                    {
                                        do
                                        {
                                            peekBuf = new byte[188];
                                            await inStream.ReadAsync(peekBuf, 0, peekBuf.Length)
                                                            .ConfigureAwait(false);

                                            buffEx.AddRaw(peekBuf);

                                        } while (buffEx.outBuffers.Count == 0);
                                        started = true;
                                    }

                                    //write out the vid data.
                                    buffer.Limit(sampleSize);
                                    buffer.Position(0);
                                    //if (tmpB.Length < sampleSize)
                                        tmpB = new byte[sampleSize];

                                    buffer.Get(tmpB);
                                    fs.Write(tmpB);

                                    buffer.Limit(buffer.Capacity());//reset the limit for next sample
                                    buffer.Position(0);

                                    fs2.Write(buffEx.outBuffers.Dequeue().GetPayload());

                                    if (!inStream.CanRead)
                                        isEOS = true;//end of stream.

                                    //decoder.QueueInputBuffer(inIndex, 0, sampleSize, extractor.SampleTime, 0);
                                    await extractor.AdvanceAsync().ConfigureAwait(false);
                                    //extractor.AdvanceAsync();
                                }
                            }
                        }


                        // All decoded frames have been rendered, we can stop playing now
                        if ((info.Flags & MediaCodecBufferFlags.EndOfStream) != 0)
                        {
                            Android.Util.Log.Debug("DecodeActivity",
                                                    MediaCodecBufferFlags.EndOfStream.ToString());
                            break;
                        }
                    }

                    //decoder.Stop();
                }
            }
        }
Example #3
0
        protected override async void Run()
        {
            try
            {
            running = true;
            var finfo = new System.IO.FileInfo(FilePlayer.SAMPLE);//FilePlayer.dir + "decode.out");
            var fs = finfo.OpenRead();
            int bytes, count, inIndex =0;
            int buffSize = TsPacket.PacketLength*4;//simulate multiple TS packets grouped into a single UDP packet
            var buff = new byte[buffSize];
            var ts = new MpegTS.TsPacket(buff);
            buffEx = new BufferExtractor();
            //buffEx.SampleReady += BuffEx_SampleReady;
            bool eof = false;


            using(info)
            using(decoder)
            using (fs)
            {
                count = 0;
                Log.Debug(TAG, "looking for format info");

                ////look for the format info.
                //do
                //{
                //    bytes = await fs.ReadAsync(buff, 0, buff.Length).ConfigureAwait(false);

                //    Log.Debug(TAG, "PID: " + string.Format("{0}", ts.PID));

                //} while (BitConverter.ToInt32(buff, formatStartI) != formatStartVal);

                //Log.Debug(TAG, "found format info");

                //var tmpB = new byte[23 + 8];
                //System.Buffer.BlockCopy(buff, formatStartI - 4, tmpB, 0, tmpB.Length);

                InitializeDecoder();

                fs.Position = 0;//reset

                sw.Restart();
                //bool started = false;
                do
                {
                    ++count;
                    try
                    {
                        while (fs.CanRead && buffEx.SampleCount == 0)
                        {
                            if (fs.Length - fs.Position < buffSize)
                            {
                                eof = true;
                                break;//we're @ EOF
                            }

                            //we need a new buffer every loop!
                            buff = new byte[buffSize];
                            bytes = await fs.ReadAsync(buff, 0, buff.Length)
                                            .ConfigureAwait(false);

                            //push the raw data to our custom extractor
                            if (!buffEx.AddRaw(buff))
                            {
                                Log.Debug("ExtractorActivity,   ", " ----------bad TS packet!");

                                //find next sync byte and try again
                                fs.Position -= buff.Length
                                              - buff.ToList().IndexOf(MpegTS.TsPacket.SyncByte);
                            }
                        }

                        if (!fs.CanRead || eof)
                            break;
                    }
                    catch (Exception ex)
                    {
                        Log.Error("ExtractorActivity error: ", ex.ToString());
                    }

                    //get the raw video stream, stripped of Mpeg TS headers
                    var sample = buffEx.DequeueNextSample();
                    Log.Debug("ExtractorActivity, sampleSize: ", sample.Length.ToString());

                    //var outputBuffers = decoder.GetOutputBuffers();

                    //get a input buffer index from the decoder for input
                    inIndex = decoder.DequeueInputBuffer(10000);


                    if (inIndex >= 0)
                    {
                        //get the re-assembled video data from the extractor
                        using (var b = Java.Nio.ByteBuffer.Wrap(sample.Buffer))
                        {

                            var inB = inputBuffers[inIndex];
                            //*************
                            //THE BUFFER *******MUST********* be CLEARED before each write,
                            //else when the buffers start getting recycled, the decoder will
                            //read past the end of the current data into old data!
                            //This may cause tearing of the picture, or even a complete 
                            //crash of the app from internal errors in native decoder code!!!!!
                            inB.Clear();
                            inB.Put(b);//put data into the decoder's native buffer

                            //tell the decoder about the new data in the buffer
                            decoder.QueueInputBuffer(inIndex, 0, b.Limit(), 0, MediaCodecBufferFlags.None);

                        }//  b.Dispose();//clean up
                    }
                    //else
                    //    continue;//we don't have a full video frame, look for more.

                    //check decoder output/state
                    int outIndex = decoder.DequeueOutputBuffer(info, 10000);

                    switch ((Android.Media.MediaCodecInfoState)outIndex)
                    {
                        case MediaCodecInfoState.OutputBuffersChanged:
                            Android.Util.Log.Debug("DecodeActivity",
                                                    MediaCodecInfoState.OutputBuffersChanged.ToString());
                            outputBuffers = decoder.GetOutputBuffers();
                            break;
                        case MediaCodecInfoState.OutputFormatChanged:

                            Android.Util.Log.Debug("DecodeActivity", "New format " + decoder.OutputFormat);//.GetOutputFormat(outIndex));
                            break;
                        case MediaCodecInfoState.TryAgainLater:
                            Android.Util.Log.Debug("DecodeActivity", "dequeueOutputBuffer timed out!");
                            break;
                        default:
                            var buffer = outputBuffers[outIndex];// decoder.GetOutputBuffer(outIndex);
                            Android.Util.Log.Verbose("DecodeActivity", "render the buffer, " + buffer);

                            //bool gcDone = false;
                            // We use a very simple clock to keep the video FPS, or the video
                            // playback will be too fast
                            //This causes the next frame to not be rendered too quickly.
                            while (info.PresentationTimeUs / 1000 > sw.ElapsedMilliseconds)
                            {
                                await Task.Delay(10).ConfigureAwait(false);
                            }
                            //the decoder won't advance without this...
                            //must be called before the next decoder.dequeue call
                            decoder.ReleaseOutputBuffer(outIndex, true);
                            break;
                    }

                } while (fs.CanRead && running);

                Log.Debug("DecodeActivity", MediaCodecBufferFlags.EndOfStream.ToString());
                try
                {
                    decoder.QueueInputBuffer(inIndex, 0, 0, 0, MediaCodecBufferFlags.EndOfStream);
                }
                catch (Exception ex)
                {
                    Log.Debug("DecodeActivity", "error closing decoder!");
                }
                }//dispose filestream,decoder, info
            }
            catch (Exception ex)
            {
                Log.Debug(this.GetType().Name, ex.ToString());
            }
            info = null;
            decoder = null;
        }