How do I stream .flv files from SQL database
Asked Answered
A

3

12

I want to store .flv files in the database and not in the file system.

This is what I can do right now:
Successfully convert .wmv and .mpeg to .flv with ffmpeg.
Store images in SQL Server and show them on my page with an httphandler.
Same with .avi and .mpeg videos. (It's up to the user's software if he can view it though)
Play .flv files in the browser if the file is located in the file system and not in the database.

What I can't do is:
Stream .flv videos to JW Player directly from the database. (Stored as binary data)

I've searched the internet for two days now but I can't get it to work. It feels as if I'm almost there though. The JW Player opens up and starts to "buffer", but nothing happens.

I know there's no easy answer but if anyone has done this before, or something similar, I'd like to know how you did. I feel I've got too much code to post it all here.

Thanks in advance!

Adumbral answered 9/12, 2010 at 15:14 Comment(3)
+1 wicked interesting questionMoulton
Would you post a snip or discuss how you're streaming the .flv when it's on the file system?Silvana
Just use the code in my answer but with FileStream() instead of SqlDataReader()Adumbral
A
4

I got it to work but I have no idea as to how efficient it is. Is it better to stream from the file system than from the database in terms of connections, efficency, load etc. I could use some pointers on this!

I'm using JW Player here, hence "swfobject.js" and "player.swf"

httpHandler:

public class ViewFilm : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        try
        {
            // Check if id was given
            if (context.Request.QueryString["id"] != null)
            {
                string movId = context.Request.QueryString["id"];

                // Connect to DB and get the item id
                using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
                using (SqlCommand cmd = new SqlCommand("GetItem", con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    SqlParameter sqlParam = cmd.Parameters.Add("@itemId", SqlDbType.Int);
                    sqlParam.Value = movId;

                    con.Open();
                    using (SqlDataReader dr = cmd.ExecuteReader())
                    {
                        if (dr.HasRows)
                        {
                            dr.Read();
                            // Add HTTP header stuff: cache, content type and length
                            context.Response.Cache.SetCacheability(HttpCacheability.Public);
                            context.Response.Cache.SetLastModified(DateTime.Now);
                            context.Response.AppendHeader("Content-Type", "video/x-flv");
                            context.Response.AppendHeader("Content-Length", ((byte[])dr["data"]).Length.ToString());
                            context.Response.BinaryWrite((byte[])dr["data"]);
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.ToString());
        }
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

javascript
The function adds a player to <div id="video1"> and can be called e.g when a user clicks a button.

<script type='text/javascript' src='swfobject.js'></script>
<script type="text/javascript" language="javascript">
function vid() {
  var s1 = new SWFObject('player.swf', 'player1', '480', '270', '9');
  s1.addParam('allowfullscreen', 'true');
  s1.addParam('allowscriptaccess', 'always');
  s1.addVariable('file', encodeURIComponent('ViewFilm.ashx?id=10'));
  s1.addVariable('type', 'video');
  s1.write(document.getElementById("video1"));
}
</script>
Adumbral answered 10/12, 2010 at 15:57 Comment(3)
A few tips on your handler: 1. The try ... catch is currently only throwing away information and should be removed since allowing the error to propagate would provide more info. 2. The using statement should be used for your SqlConnection, SqlCommand, SqlDataReader instances which ensure everything is always disposed (closed) even if exceptions are thrown. 3. You're currently reading the entire file into memory, this won't scale; in order to scale you could use SequentialAccess on the command then repeatedly GetBytes from the reader to a buffer and BinaryWrite to the response.Silvana
I could not get this to work so I posted it as another question: #4469444Adumbral
Updated on point one and two. Sequential access is still a mystery though.Adumbral
L
2

Not sure exactly how literally to take "stream directly from the database", but would it work to set the source "file" for the JW Player to "ServeFLV.aspx?id=123", and have ServeFLV.aspx retrieve the bytes from the database, and write them out to the response with no markup?

Logbook answered 9/12, 2010 at 19:35 Comment(1)
I think you're close to the right answer. Instead of using an aspx file which has all of the associated page lifecycle, use an ashx handler instead. It's much simpler, faster, and built for exactly this type of thing.Pod
S
1

If you're using SQL Server 2008 you could use varbinary(MAX) FILESTREAM which would allow the files to be managed by the database but still give you access to a FileStream from .NET.

Silvana answered 9/12, 2010 at 18:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.