How to play YuMe video ads in Adobe Flex/AS3

Problem

Compared to a lot of other Internet video advertising services, YuMe has a pretty low barrier to entry because they don’t limit who can join as a publisher based on your current visitor volume or anything like that: simply sign-up and get access to the SDK. On the other hand, the documentation you receive along with the SDK is so convoluted that it turns what should be a 5 minute job into a goose hunt. What’s a goose hunt? Exactly, but once you understand what’s going on it’s pretty sweet.

Solution

I’ve created a simple Flex wrapper around the YuMe video player to get you up and running quickly. You’ll need to signup with YuMe to get access to the SDK and their required library swfs.

Feel free to add and improve upon the code or post your own wrapper and link to it in the comments!

YuMeVideoAd.mxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
 
	<mx:Metadata>
		[Event(name="ad_completed", type="flash.events.Event")]
		[Event(name="ad_absent", type="flash.events.Event")]
		[Event(name="ad_error", type="flash.events.Event")]
	</mx:Metadata>
	<mx:Script>
		<![CDATA[
			import mx.controls.SWFLoader;
			import mx.core.FlexMovieClip;
 
			// Possible ad types
			public static const BEFORE_CONTENT:String 		= "before_content";
			public static const DURING_CONTENT:String 		= "during_content";
			public static const AFTER_CONTENT:String		= "after_content";
 
			/**
			 * URL to where you are hosting "yume_player_4x3.swf"
			 */
			public static var yume_player_swf_url:String 	= "yume_player_4x3.swf";
 
			/**
			 * URL to where you are hosting "yume_ad_library.swf"
			 */
			public static var yume_library_swf_url:String	= "yume_ad_library.swf";
 
			/**
			 * URL to where you are hosting any specific CSS associated with the YuMe ads you are displaying.
			 */
			public var yume_css_url:String 					= "";
 
			public var ad_width:Number 	= 480;
			public var ad_height:Number = 360;
 
			/**
			 * The type of ad to play in this player the next time startAd() is called,
			 * valid values are: "before_content", "during_content", "after_content"
			 */
			public function set ad_type( value:String ):void {
				// set ad type
				yume_params.ad_type = value;
				regeneratePlaylistURL();
			}
 
			/**
			 * Help YuMe target the video ad by passing tags related to the content this ad will be shown with.
			 */
			public function set tags( value:String ):void {
				_tags = value;
				regeneratePlaylistURL();
			}
 
			/**
			 * Help YuMe target the video ad by passing the title of the content this ad will be shown with.
			 */
			public function set title( value:String ):void {
				_title = value;
				regeneratePlaylistURL();
			}
 
			/**
			 * Help YuMe target the video ad by passing the duration of the content this ad will be shown with.
			 */
			public function set duration( value:Number ):void {
				_duration = value;
				regeneratePlaylistURL();
			}
 
			/**
			 * Help YuMe advertisers target your company's content by giving them a way to identify you.
			 */
			public function set channel( value:String ):void {
				_channel = value;
				regeneratePlaylistURL();
			}
 
			/**
			 * Any YuMe specific errors caught by the YuMe player.
			 * You should check this value if you catch an "ad_error" event.
			 */
			public function get errors():String {
				return _yume ? _yume.error_string : null;
			}
 
			/**
			 * The object the YuMe interface will be loaded into and accessible through.
			 */
			private var _yume:Object;
 
			/**
			 * The actual visual component the YuMe video will be loaded into.
			 */
			private var _yume_mc:MovieClip = new MovieClip();
 
			/**
			 * True if the yume swfs have been successfully loaded and initialized, false otherwise.
			 */
			private var _yume_ready:Boolean = false;
 
			/**
			 * True if there has been a request to play a YuMe ad. This allows the containing application
			 * to initialize and immediately request ad playback without having to worry about asynchronous
			 * load times and have the ad begin playing as soon as it's loaded. 
			 */
			private var _start_requested:Boolean = false;
 
			/**
			 * Your YuMe API key, given to you by YuMe when you're approved. Below is the test key.
			 */
			private var _yume_api_key:String = "88ElOFHxAh";
 
			/**
			 * Any tags associated with the current content being monetized.
			 */
			private var _tags:String = "";
 
			/**
			 * The title of the current content being monetized.
			 */
			private var _title:String = "";
 
			/**
			 * The duration of the current content being monetized.
			 */
			private var _duration:Number = 30;
 
			/**
			 * The name of the YuMe channel you want to create for advertisers to target.
			 */
			private var _channel:String = "your-company";
 
			public var yume_params:Object = {
				parent_mc: 				_yume_mc,
				ad_type: 				BEFORE_CONTENT,
				yume_url:				yume_player_swf_url,
				yume_library_swf_url:	yume_library_swf_url,
				// this is the test playlist from the YuMe setup documentation
				playlist:				"http://shadow01.yumenetworks.com/dynamic_preroll_playlist.fmil?domain=88ElOFHxAh",
				update_interval: 		1000,
				ad_volume:				100,
				normalscreen_x:			0,
				normalscreen_y:			0,
				normalscreen_width:		ad_width,
				normalscreen_height:	ad_height,
				fullscreen_x:			0,
				fullscreen_y:			0,
				fullscreen_width:		ad_width,
				fullscreen_height:		ad_height,
				yume_leader_slot:		true,
				css_url:				yume_css_url
			};
 
			private function regeneratePlaylistURL():void {
				// set playlist url
				var playlist_type:String = "";
				if( yume_params.ad_type == "during_content" ){
					playlist_type = "dynamic_midroll_playlist.fmil";
				}else if( yume_params.ad_type == "after_content" ){
					playlist_type = "dynamic_postroll_playlist.fmil";
				}else{
					playlist_type = "dynamic_preroll_playlist.fmil";	
				}
				yume_params.playlist = "http://shadow01.yumenetworks.com/" + playlist_type 
					+ "?channel=" + encodeURIComponent( _channel )
                                        + "&domain=" + _yume_api_key 
					+ "&title=" + encodeURIComponent( _title ) 
					+ "&tags=" + encodeURIComponent( _tags ) 
					+ "&duration=" + _duration;	
			}
 
			private function init():void {
				Security.allowDomain( "*" );
				Security.allowInsecureDomain( "*" );
				yume_holder.addChild( _yume_mc );
				var loader:SWFLoader = new SWFLoader();
				loader.addEventListener( Event.COMPLETE, handleLoaderComplete );
				loader.load( yume_library_swf_url );
			}
 
			private function handleLoaderComplete( e:Event ):void {
				_yume 			= e.target.content.yume_ad;
				setupListeners( _yume as IEventDispatcher );
				_yume_ready = true;
				if( _start_requested ){
					startAd();
				}
			}
 
			public function startAd():void {
				if( _yume_ready ){
					yume_params.normalscreen_width 	= this.width;
					yume_params.normalscreen_height 	= this.height;
					_yume.start_ad( yume_params );
					_start_requested = false;
				}else{
					_start_requested = true;
				}
			}
 
			// ================= YUME AD LISTENER IMPLEMENTATION ==============
			private function setupListeners( target:IEventDispatcher ):void {
				target.addEventListener( "ad_present", ad_present );
				target.addEventListener( "ad_absent", ad_absent );
				target.addEventListener( "ad_playing", ad_playing );
				target.addEventListener( "ad_completed", ad_completed );
				target.addEventListener( "ad_closed", ad_closed );
				target.addEventListener( "image_closed", image_closed );
				target.addEventListener( "ad_reopened", ad_reopened );
				target.addEventListener( "ad_clicked2site", ad_clicked2site );
				target.addEventListener( "ad_clicked2video", ad_clicked2video );
				target.addEventListener( "pip_video_playing", pip_video_playing );
				target.addEventListener( "pip_video_completed", pip_video_completed ); 
				target.addEventListener( "ad_error", ad_error );
			}
 
			private function ad_absent( e:Event ):void {
				// bubble
				dispatchEvent( e );
			}
 
			private function ad_error( e:Event ):void {
				// bubble
				dispatchEvent( e );
			}
 
			private function ad_completed( e:Event ):void {
				// bubble
				dispatchEvent( e );
			}
 
			private function ad_playing( e:Event ):void {
				trace( e );
				// displays the number of seconds left in the ad for the user if it can be determined
				if( _yume.get_duration() > 0 ){
					var seconds_left:int = Math.round( _yume.get_duration() - _yume.get_time() );
					info.text = "Sponsor: " + ( seconds_left >= 0 ? seconds_left : '' ) 
						+ " second" + ( seconds_left != 1 ? 's' : '' ) + " remaining";
				}
			}
 
			private function ad_present( e:Event ):void {
				trace( e );
			}
 
			private function image_closed( e:Event ):void {
				trace( e );
			}
 
			private function ad_closed( e:Event ):void {
				trace( e );
			}
 
			private function ad_reopened( e:Event ):void {
				trace( e );
			}
 
			private function ad_clicked2site( e:Event ):void {
				trace( e );
			}
 
			private function ad_clicked2video( e:Event ):void {
				trace( e );
			}
 
			private function pip_video_playing( e:Event ):void {
				trace( e );
			}
 
			private function pip_video_completed( e:Event ):void {
				trace( e );
			}		
 
		]]>
	</mx:Script>
 
	<!-- just a holder for the video ad -->
	<mx:UIComponent id="yume_holder" width="100%" height="100%" creationComplete="init()"/>
 
	<!-- displays the seconds countdown -->
	<mx:HBox backgroundColor="#323232" backgroundAlpha="0.5" width="100%" top="0" paddingLeft="10">
		<mx:Label id="info" fontSize="10" color="#FFFFFF"/>
	</mx:HBox>
 
</mx:Canvas>

Edit [2010/01/05]
If you are monetizing live streaming content and plan to play midrolls you can add a playlist_midroll property to the YuMe parameters to pre-fetch midrolls:

yume_params.playlist_midroll = "http://shadow01.yumenetworks.com/dynamic_midroll_playlist.fmil"
                                       + "?domain=" + _yume_api_key
                                       + "&title=" + encodeURIComponent( _title )
                                       + "&tags=" + encodeURIComponent( _tags )
                                       + "&duration=" + _duration;

Then when you’re ready to play a midroll set the ad_type to DURING_CONTENT and call startAd() again to immediately play the cached midroll advertisement.

Gotchas

I only need in-stream video playback and don’t support accompanying banners so I only bubble “ad_completed”, “ad_absent” and “ad_error” events, catching any other YuMe events you are interested in should be pretty self-explanatory.

Also, YuMe notes that there are some ad units that do not broadcast their duration. An example of this might be a preroll that a user can interact with (pause, switch ad flv’s, etc). Because of this, they recommend that instead of a countdown timer, customers display a message like “Your content will resume after this advertisement.”

If you are running the example via the Flash/Flex Builder IDE and YuMe happens to send you back a powerroll to display you might see a variety of Security Errors thrown, according to YuMe support this apparently doesn’t happen in production.

Special thanks to David Lea of YuMe support for his quick, clear responses.

Tagged : , ,

3 Responses to “How to play YuMe video ads in Adobe Flex/AS3”

  1. Saying that trying to do anything useful with the YuMe SDK documentation is a wild goose hunt is like saying that getting your face mauled off by a bear is a minor cosmetic issue.

    The problem isn’t simply isolated to the documentation, either. There’s also the matter of the completely inconsistent and un-ActionScript-like coding conventions, the broken normalscreen_x and normalscreen_y attributes, the lack of an swc (which means no debug symbols, no compiling it in using the Flash IDE, and extra function calls to get information that should be part of the dispatched events)…the list goes on.

    I’ve written a more complete wrapper to abstract away most of the stupidity of the YuMe API and plan to release it soon. In the meantime, I hope that they fire their entire development staff, because this SDK is a joke.

    Regards,

  2. Hi Ian,

    Released my wrapper & documentation today. Check it out and send over some feedback if you have time. YuMeWrapper.

    Regards,

  3. Great stuff, thanks! but I need a stop() or unload() function to stop the ad in the middle of play.

Leave a Reply