How to protect your SWF files by loading them from Wowza Media Server

You can stop SWFs and images from being stolen or decompiled by loading them as a byte array instead of using the normal Loader. This simple module loads the .swf and sends it to the Flash client. The Flash client uses the loadBytes method of the Loader class to load the byteArray.

Using this method, the .swf will only exist on the client machine as an object in the memory and not as a file in the browser cache.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;

public class ModuleSwfLoader extends ModuleBase {

	public void loadSwf(IClient client, RequestFunction function, AMFDataList params) {
		getLogger().info("loadSwf");
		String swfName = "";
		byte[] fileBytes = null;

		if (getParam(params, PARAM1).getType() == AMFData.DATA_TYPE_STRING)
			swfName  = params.getString(PARAM1);

		try {
			if (!swfName .isEmpty()) {
				File swf = new File(swfName);
				if (swf.exists() && swf.canRead() && swf.isFile() && swf.length() < Integer.MAX_VALUE) {
						InputStream in = new FileInputStream(swf);
						int length = (int) swf.length();
						fileBytes = new byte[length];

						int offset = 0;
				        int numRead = 0;
				        while (offset < fileBytes.length && (numRead=in.read(fileBytes, offset, fileBytes.length-offset)) >= 0) {
				            offset += numRead;
				        }

				        if (offset < fileBytes.length) {
				            throw new IOException("Could not completely read file "+swf.getName());
				        }

				        in.close();
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		sendResult(client, params, AMFDataByteArray.wrap(fileBytes));
	}

}

On the Flash side, you still need a SWF to put into your page but it just needs to be a simple loader class that makes a net connection to the wowza media server and then loads the page.

AS3 Code:

package {
    import flash.display.Loader;
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.net.NetConnection;
    import flash.net.Responder;
    import flash.utils.ByteArray;

	public class SwfLoader extends Sprite
	{
		public var mc:MovieClip;

		private var nc:NetConnection = new NetConnection();
		private var animationLoader:Loader = new Loader();

		public function SwfLoader()
		{
			nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			nc.connect("rtmp://localhost/myApp");
		}

		private function onNetStatus(event:NetStatusEvent):void {
			trace (event.info.code);
			if (event.info.code == "NetConnection.Connect.Success") {
				nc.call("loadSwf", new Responder(onLoadSwf), "myPrivateFlashApp.swf");
			}
		}
		private function onLoadSwf(swfBytes:ByteArray):void {
			animationLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete_Loader_Section);
            if(swfBytes.length != 0)
            {
                animationLoader.loadBytes(swfBytes);
             }
		}

		private function onComplete_Loader_Section(event:Event):void {
			mc = MovieClip(event.target.content);
			addChild(mc);
			nc.close();

			animationLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete_Loader_Section);
		}

	}
}

The main Flash app time line, etc., can be accessed through the mc var.

Note that some content that is loaded may fail if it requires access to the stage. An example is the simplevideostreaming.swf that comes with Wowza Streaming Engine. In order for this to work, you need to add an event listener to the .swf that you are loading to check that it has been added to the stage before interacting with it.

Modify the as3 code at the end of simplevideostreaming.fla to add this event listener.

if (stage == null)
{
	addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
else
{
	onAddedToStage();
}

function onAddedToStage(event:Event = null):void
{
	// init code that requires stage
	var h264Capable:Boolean = testVersion(9, 0, 115, 0);
	playerVersion.text = (h264Capable?"H.264 Ready (":"No H.264 (")+Capabilities.version+")";
	if (!h264Capable)
		playerVersion.textColor = 0xee0000;
	stage.align = "TL";
	stage.scaleMode = "noScale";

	mainInit();
}

Originally Published: 10-03-2010.

If you're having problems or want to discuss this article, post in our forum.