Delayed stream: Using FMS for semi-live broadcasting
4/25/2010 10:46:00 PM
Posted by RIAlog
When you watch a live show on TV, its never really live. Its not only the foregone latency made by the different machines - but a very intended delay designed to let the editor a way to stop the transmitting if they fill such need.
You can use your imagination to complete the possible scenario.
Unfortunately, Adobe's Encoder or Flash Media Server don't have live stream delay feature out of the box. So, if you want to have such delay with Adobe FMS, you will need some server-side scripting. (Go ahead, fill a request to adobe's wish list)
On the other hand, this will be quite simple.
[NOTE: The code here is not real tested code.]
Step 1: Record your stream.
- Method 1:
You can use SSAS (Server Side ActionScript) to the live stream. Adobe's documentation is fairly descriptove: Stream.record()
What you actually do is adding the recording functionality to the Application.onPublish event. Your final code will be similar to:
application.onAppStart = function() { trace("Starting recoding application"); }; application.onConnect = function(clientObj) { this.acceptConnection(clientObj); }; application.onPublish = function (clientObj , stream) { trace("Publish: "+stream.name); stream.record(); }; application.onUnpublish = function(clientObj, stream) { trace("Unpublish: "+stream.name); stream.record(false); };
NOTE: Recording using this method will save the file with its default name:
"When you record a stream, the server creates a file with the
name you passed to the Stream.get() method. The
server automatically creates a “streams” directory and subdirectories
for each application instance name. If a stream isn’t associated
with an application instance, it is stored in a subdirectory called “_definst_”
(default instance). For example, a stream from the default lecture application
instance would be stored here: applications\lectures\streams\_definst_.
A stream from the monday lectures application instance would be
stored here: applications\lectures\streams\monday. "
- Method 2:
If you are using Adobe's Flash Media Live Encoder - you can simply tick the record checkbox.
Now, when you start your broadcasting - use the Stream.play() method to stream the recoded file to everyone.
The play function can link one file after another.. so we can add a preliminary video file as a place holder for the delayed time and than add the recorded file right afterward.
Doing that - we can easily use the "onPublish" event to make it all work so our code will be similar to this one:
application.onPublish = function (clientObj , stream) { trace("Publish: "+stream.name); stream.record() stream.play("placeHolder.flv", -1, -1); stream.play("recorded.flv", -1, -1); }; application.onUnpublish = function(clientObj, stream) { trace("Unpublish: "+stream.name); stream.record(false); stream.play(false); };
Enjoy!
WebResources - Watch the creation time
4/24/2010 11:26:00 PM
Posted by RIAlog
One day I had a simple task to do: take one site, fully working with no issues and load it on another server.
It sounds simple. Well, just like any real time-stealing task everything went fine till I tested it and from some odd reason some of the menus and visual components went crasy.
The problem we saw was bad request or denied access to the .axd compiled resources on page.
After hours of investigation I found it: The servers TIME & DATE were wrong. When you upload a compiled resource - the server use the creation date to create a key for that resource. That key probably being computed using the current date and file's creation date. When the creation date is in the future - the calculation returns some invalid value and that resource is being denied.
I hope I saved someone's time..
Better thumbnail creation then "GetThumbnailImage" in C sharp
2/21/2010 03:03:00 PM
Posted by RIAlog
In many cases you may want to create a smaller version of your images. maybe for mobile devices or for images view on your page.
One known way is to use the "ready-made" GetThumbnailImage function. That will generally do the job (another way is below):
int Width; int Height; int ThumbWidth; int ThumbHeight; string SavePath = "[Some save path"]; string FilenameWOext = "[]"; string FileExtention; Bitmap btmp1 = new Bitmap(SavePath + FilenameWOext + FileExtention); Width = btmp1.Width; Height = btmp1.Height; System.Drawing.Image.GetThumbnailImageAbort myCallBack = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback); if (ThumbWidth > Width && ThumbHeight > Height) { ThumbWidth = Width; ThumbHeight = Height; } System.Drawing.Image myThumbnail1 = btmp1.GetThumbnailImage(ThumbWidth, ThumbHeight, myCallBack, IntPtr.Zero); myThumbnail1.Save(SavePath + ThumbFile); btmp1.Dispose();
HOWEVER, this will sometimes wont work. when using images from some digital cameras, there is addotional EXIF data that makes the generated image to be blur.
Fortunatly, there is another way using Graphics.DrawImage
Bitmap resized = new Bitmap(ThumbWidth, ThumbHeight); Graphics g = Graphics.FromImage(resized); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.DrawImage(btmp1, new Rectangle(0, 0, resized.Width, resized.Height)); g.Dispose();
This way will work with EXIF images or any other image with high quality too!
Enjoy!
Adobe P2P VideoPhone with asp.net registration
2/14/2010 11:44:00 PM
Posted by RIAlog
The Adobe p2p and Stratus are not that new. its sample application is cool and shows more than just textual p2p connection like other tutorials, but actually shows a good enough voice and image transfer.
BUT, as a .Net programmer, the registration Python code was a bit wierd to work with. Moreover, the application is literally poor in user management, so I needed a good start point to have that registration use real DB.
So, you want to use ASP.NET with Adobe Stratus VideoPhone ? here you go: ( I used Linq to SQL for this one. DB name is simply "P2P")
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; namespace P2PRegistration { ////// Summary description for $codebehindclassname$ /// [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class reg : IHttpHandler { public void ProcessRequest(HttpContext context) { RegistrationDBDataContext db = new RegistrationDBDataContext(); string user = context.Request.QueryString["username"]; string identity = context.Request.QueryString["identity"]; string friendsString = context.Request.QueryString["friends"]; List friends = new List(); if (friendsString != null && friendsString != "") { friends = friendsString.Split(',').ToList(); } context.Response.ContentType = "text/plain"; context.Response.Write(@""); context.Response.Write(@""); if (user != null && user != "" && identity != null && identity != "") { context.Response.Write(@" "); } public bool IsReusable { get { return false; } } } }"); try { var rUser = from p in db.registrations where (p.m_username == user) select p; if (rUser.Count() > 0) { //update rUser.First().m_username = user; rUser.First().m_identity = identity; rUser.First().m_updatetime = DateTime.Now; } else { registration newReg = new registration(); newReg.m_username = user; newReg.m_identity = identity; newReg.m_updatetime = DateTime.Now; db.registrations.InsertOnSubmit(newReg); } db.SubmitChanges(); context.Response.Write("true"); } catch { context.Response.Write("false"); } context.Response.Write(@" "); } foreach (string f in friends) { context.Response.Write(@""); context.Response.Write(@" "); } context.Response.Write(@""); context.Response.Write(context.Server.UrlEncode(f)); context.Response.Write(@" "); var rFriends = from p in db.registrations where p.m_username == f && p.m_updatetime > DateTime.Now.AddHours(-1) select p; foreach (registration r in rFriends) { string ident = r.m_identity; if (ident == null) ident = ""; context.Response.Write(@""+context.Server.UrlEncode(ident)+@" "); if (f != r.m_username) { context.Response.Write(@""+r.m_username+" "); } } context.Response.Write(@"
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[registrations]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[registrations]( [m_username] [varchar](100) NOT NULL, [m_identity] [varchar](100) NULL, [m_updatetime] [datetime] NULL, CONSTRAINT [PK__registrations__7C8480AE] PRIMARY KEY CLUSTERED ( [m_username] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] END
... // please insert your webservice URL here for exchanging private const WebServiceUrl:String = "http://[your domain here]/reg.ashx"; ...
allowFullScreen is not working in IE but do work in FF
2/09/2010 12:45:00 PM
Posted by RIAlog