using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Windows.Forms; using BroccoliProducts; namespace RtmpDownloader { ///////////////////////////////////////////////////////////////// // declaration of Program class class Program { ///////////////////////////////////////////////////////////// // constants and enumerations // store the audio and video in these files private static readonly String AudioFilepath = "c:\\rtmp_audio_data.dat"; private static readonly String Mp3Filepath = "c:\\rtmp_audio_data.mp3"; private static readonly String VideoFilepath = "c:\\rtmp_video_data.dat"; ///////////////////////////////////////////////////////////// // the rtmp connection parameter // comment and un-comment the sets of RTMP connection parameters. // To test the localhost, you will need to install your own RTMP server, // such as Wowza. The other RTMP servers listed here are streamed media // that are made available on the Internet by others. private static readonly String RtmpServer = "localhost"; private static readonly UInt32 RtmpPort = RtmpClient.DefaultRtmpPort; private static readonly String RtmpApp = "video"; private static readonly String RtmpTcUrl = ""; private static readonly String RtmpStreamName = "mp3:smallfile.mp3"; /* private static readonly String RtmpServer = "cyzy7r959.rtmphost.com"; private static readonly UInt32 RtmpPort = RtmpClient.DefaultRtmpPort; private static readonly String RtmpApp = "flowplayer"; private static readonly String RtmpTcUrl = "rtmp://cyzy7r959.rtmphost.com/flowplayer"; private static readonly String RtmpStreamName = "metacafe"; */ /* private static readonly String RtmpServer = "cyzy7r959.rtmphost.com"; private static readonly UInt32 RtmpPort = RtmpClient.DefaultRtmpPort; private static readonly String RtmpApp = "flowplayer"; private static readonly String RtmpTcUrl = "rtmp://cyzy7r959.rtmphost.com/flowplayer"; private static readonly String RtmpStreamName = "mp3:fake_empire"; */ /* private static readonly String RtmpServer = "b0jukk1m.rtmphost.com"; private static readonly UInt32 RtmpPort = RtmpClient.DefaultRtmpPort; private static readonly String RtmpApp = "songs"; private static readonly String RtmpTcUrl = "rtmp://b0jukk1m.rtmphost.com/songs/mad.mp3"; private static readonly String RtmpStreamName = "mp3:mad"; */ /* private static readonly String RtmpServer = "flv.world.mii-streaming.net"; private static readonly UInt32 RtmpPort = RtmpClient.DefaultRtmpPort; private static readonly String RtmpApp = "nytimes/flash/t_assets/20080604"; private static readonly String RtmpTcUrl = "rtmp://flv.world.mii-streaming.net/nytimes/flash/t_assets/20080604/1501990898518641fe6848d57a58d26bcfb07c11.flv"; private static readonly String RtmpStreamName = "1501990898518641fe6848d57a58d26bcfb07c11"; */ ///////////////////////////////////////////////////////////////// // main application function static void Main(string[] args) { // declaration of local variables bool bOk = true; // false when application cannot continue FileStream fsAudioTarget = null; // target for the audio data FileStream fsVideoTarget = null; // target for the video data // display message - app has started Console.WriteLine("RtmpDownloader Started."); // start by opening the target files try { // A - open target files - audio Console.WriteLine("Opening target audio file."); fsAudioTarget = new FileStream(AudioFilepath, FileMode.Create, FileAccess.Write); // A - open target files - video Console.WriteLine("Opening target video file."); fsVideoTarget = new FileStream(VideoFilepath, FileMode.Create, FileAccess.Write); } catch (Exception x) { // display message and update flag Console.WriteLine(x.Message); bOk = false; } // if that worked... if (bOk) { // B - create a client object Console.WriteLine("Creating Rtmp client."); using (RtmpClient rtmpClient = new RtmpClient(null)) { // declare rtmp connection and stream objects RtmpConnection rtmpConn = null; RtmpStream rtmpStream = null; try { // C - connect to the rtmp server Console.WriteLine("Creating Rtmp connection to {0}:{1}.", RtmpServer, RtmpPort); rtmpConn = rtmpClient.CreateConnection(RtmpServer, RtmpPort, RtmpClient.eProtocol.RTMP); // D- setup the connection - if any aggregate messages arrive, chop them up // (some RTMP servers bunch messages togather to reduce processing overhead) rtmpConn.AutoChopUpAggregateMessages = true; // D- setup the connection - auto response to the SetPeerBW-AckWindowSize handshake rtmpConn.AutoRespondToSetPeerBW = true; // D- setup the connection - auto respond to a ping message with a pong rtmpConn.AutoPingPong = true; // D- setup the connection - auto send received-data-ack when ack-window reached rtmpConn.AutoAckReceivedData = true; // E - invoke a connection to the streaming application Console.WriteLine("Invoking application."); rtmpConn.ConnectToApplication( RtmpApp, // app name RtmpClient.DummyFlashVersion, null, RtmpTcUrl, // tc url true, RtmpClient.eCaps.DefaultCaps, RtmpClient.eAudioCodec.SUPPORT_SND_ALL, RtmpClient.eVideoCodec.SUPPORT_VID_ALL, RtmpClient.eVideoFunction.SUPPORT_VID_CLIENT_SEEK, null ); // F - open a stream Console.WriteLine("Creating stream."); rtmpStream = rtmpConn.CreateStream(); rtmpStream.SetBufferSize(2000); // show stream id provided by the RTMP server Console.WriteLine("Stream id = {0}.", rtmpStream.StreamId); // G - get the stream to collect the audio and video data // in thread-safe buffers rtmpStream.CollectAudioDataFlag = true; rtmpStream.CollectVideoDataFlag = true; // H - pre-process rtmp messages for our stream // ( specify a pre-processing function to pick out tge meta-data // message, so they can be displayed. rtmpStream.RegisterForPreProcessing(RtmpMessageCallback); // I - "play" the stream Console.WriteLine("Playing stream."); rtmpStream.Play(RtmpStreamName, 0, RtmpStream.PlayAll); // display cancel instruction for the user Console.WriteLine("(When download starts, press \"C\" to cancel.)"); // J - wait for the stream to complete while (rtmpStream.State == RtmpStream.eState.Streaming) { // as audio and video data is collected by the RTMPStream object, // grab it and write it ot the target files. _WriteAudioAndVideoDataToFiles(false,rtmpStream,fsAudioTarget,fsVideoTarget); // sleep to prevent processor spinning Thread.Sleep(250); // check for cancel if (Console.KeyAvailable) { // get key details ConsoleKeyInfo key = Console.ReadKey(true); if (key.Key == ConsoleKey.C) { // display error and break out of loop _writeError("Operation cancelled by user."); break; } } } // while-loop // write the remaining collected data to the files _WriteAudioAndVideoDataToFiles(true, rtmpStream, fsAudioTarget, fsVideoTarget); } catch (Exception x) { // dump exception Console.WriteLine(); _writeError(x.Message); Console.WriteLine(); // update error flag bOk = false; } finally { // K - close down stream if ((rtmpStream != null) && (rtmpStream.IsValid() == null)) rtmpStream.Close(); rtmpStream = null; // K - close the connection if ((rtmpConn != null) && (rtmpConn.IsValid() == null)) rtmpConn.Close(); rtmpConn = null; } } // using RtmpClient } // L - close down files Console.WriteLine("Closing target files."); if (fsAudioTarget != null) { fsAudioTarget.Close(); fsAudioTarget.Dispose(); } if (fsVideoTarget != null) { fsVideoTarget.Close(); fsVideoTarget.Dispose(); } // if that worked... if (bOk) { // M - try to convert the audio data to mp3 (it could be ACC or any other // format, but it's worth a try) String strResult = RtmpClient.RebuildMP3File(AudioFilepath, Mp3Filepath); Console.WriteLine(); if (strResult == null) Console.WriteLine("Converted audio data to mp3 file \"{0}\".", Mp3Filepath); else _writeError(String.Format("Cannot convert audio data to mp3 file. May not be mp3 packets. {0}", strResult)); } // app has finished Console.WriteLine("\nRtmpDownloader Finished."); Console.WriteLine("Click any key to close..."); while (!Console.KeyAvailable) { } } private static void _WriteAudioAndVideoDataToFiles(bool bFlush, RtmpStream rtmpStream, FileStream fsAudioTarget, FileStream fsVideoTarget) { ///////////////////////////////////////////////////////// // write the audio and video data collected // by the Rtml stream to the files // declaration of local variables Int32 iBytesGrabbed = 0; byte[] buffer = new byte[64 * 1024]; // unless flushing, only write to file when there is a good sized // chunk of data waiting, otherwise we waste processor-time Int32 iGrabMinLimit = bFlush ? 0 : buffer.Length; // audio-data while (rtmpStream.AvailableAudioDataSize > iGrabMinLimit) { // grab buffer full of data and save to file iBytesGrabbed = rtmpStream.GetCollectedAudioData(buffer); fsAudioTarget.Write(buffer, 0, iBytesGrabbed); // display message Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine("Added {0} bytes of audio to file ({1} bytes).", iBytesGrabbed.ToString("#,#"), fsAudioTarget.Length.ToString("#,#") ); Console.ForegroundColor = ConsoleColor.Gray; } // while-loop // video-data while (rtmpStream.AvailableVideoDataSize > iGrabMinLimit) { // grab buffer full of data and save to file iBytesGrabbed = rtmpStream.GetCollectedVideoData(buffer); fsVideoTarget.Write(buffer, 0, iBytesGrabbed); // display message Console.ForegroundColor = ConsoleColor.DarkRed; Console.WriteLine("Added {0} bytes of video to file ({1} bytes).", iBytesGrabbed.ToString("#,#"), fsVideoTarget.Length.ToString("#,#") ); Console.ForegroundColor = ConsoleColor.Gray; } // while-loop } private static void RtmpMessageCallback(RtmpMessage msg) { ///////////////////////////////////////////////////////// // all RTMP message for our stream will pac through this // function before the RTMPClient processes them. // DO NOT do anything processor intensive in this function. // if this is a meta-data... if (msg.IsMetaDataAmf0) { // show the content of the meta-data message Console.WriteLine("Incoming meta-data, details follow:"); Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(msg.ToString(RtmpMessage.eToStringFormat.MulitLine)); Console.ForegroundColor = ConsoleColor.Gray; } } private static void _writeError(String strMsg) { ///////////////////////////////////////////////////////// // write errors to the display in red // write error in red Console.ForegroundColor = ConsoleColor.Red; { // write message Console.WriteLine(String.Format("!!! {0}", strMsg)); } Console.ForegroundColor = ConsoleColor.Gray; } } }