0

Looks like it's a pretty common trouble but I still can't see any solution.

The goal is to play some AAC stream in Adobe AIR mobile app.

I've used a transcoder from https://code.google.com/p/project-thunder-snow/. The one adds FLV headers to AAC data so the stream can be played through a standard AS3 NetStream object (as I understand). It works fine for Win and Mac, but the same app launched on Android or iOS produces neither sound nor any error. I've figured out that transcoder works fine, the cause is in the different characteristics of NetStreams.

So, is there any solution or, at least, any documentation describing the difference between the NetStreams on PC and mobile platform?

Vyacheslav Orlovsky
  • 329
  • 1
  • 6
  • 15
  • Hi, are you able to answer this question please? http://stackoverflow.com/questions/25819826/shoutcast-aacplus-streaming-with-as3. If you can give them quick example of their shown url & how / where to play using Thunder-Snow. – VC.One Sep 23 '14 at 22:38
  • FLV is not supported on iOS. On Android you could play saved FLV files from memory card but I don't know about streaming. So when you get this AAC data on mobile you should not wrap in FLV but instead buffer and send to another native app (ie: built-in OS decoder?) using [ [Native Extensions](http://www.adobe.com/devnet/air/native-extensions-for-air.html) ] and see also [ [Google results](https://www.google.co.uk/?gws_rd=cr&ei=nqYkUqj1Osi20QXAhoHoDQ#q=as3%20android%20native%20extensions) ]. Just one thing to consider but I've not tried myself. – VC.One Sep 23 '14 at 22:47
  • Looks like some guys are able to play it on Android somehow - http://stackoverflow.com/questions/20714135/streaming-aac-inside-flv-on-ios-using-air. We have tried to build a proper native extension for Android, but it's hard to make ANE using available C/Java solutions.((( – Vyacheslav Orlovsky Sep 24 '14 at 17:07
  • And unfortunately, I don't know how to ask http://stackoverflow.com/users/1282424/rolf-kaiser about the way he did it on Android.((( – Vyacheslav Orlovsky Sep 24 '14 at 17:13
  • Thanks for answering the other link. Was not my question just that I also was linked then found you & hoped you could help him. Now that @Rolf-Kaiser is linked he might notice and advise here, maybe? Anyway I might have a solution for you.. – VC.One Sep 25 '14 at 07:50
  • Oh, really, it wasn't your question!) – Vyacheslav Orlovsky Sep 25 '14 at 08:06
  • No but your answer worked.. I wanted to play AAC files not radio but they linked me anyway in comments. Thanks to you I can now play AAC radio too if I want in future. – VC.One Sep 25 '14 at 08:10

1 Answers1

0

okay... I might have a clue for you. I tried running the NBAAC code link you posted on the other question from inside Flash CS5 and got this error:

onChannelReady : MetaData Error: Error #2067: The ExternalInterface is not available in this container. ExternalInterface requires Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime.

So it seems the player is designed to be used only inside an html page & uses ExternalInterface (Javascript) for getting metadata from browser into SWF

I removed the ExternalInterface stuff and it played ok in Flash Player & Device Central without browser/HTML. Try this code in your AIR app (I don't have AIR installed anymore to confirm..)

package
{
        import com.thebitstream.flv.CodecFactory;
        import com.thebitstream.flv.Transcoder;
        import com.thebitstream.flv.codec.*;
        import com.thebitstream.ice.*;
        import com.thebitstream.nsv.*;

        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.IOErrorEvent;
        import flash.events.ProgressEvent;
        import flash.events.SecurityErrorEvent;
        //import flash.external.externalInterface;
        import flash.media.SoundTransform;
        import flash.net.NetConnection;
        import flash.net.NetStream;
        import flash.net.NetStreamAppendBytesAction;
        import flash.net.URLRequest;
        import flash.net.URLRequestMethod;
        import flash.net.URLStream;
        import flash.utils.ByteArray;
        import flash.utils.setTimeout;

        [SWF (width="250",height="250")]

        public dynamic class NBAAC_AIR_v2 extends Sprite
        {
            public var request:URLRequest;
            public var transcoder :Transcoder
            public var transport :NetConnection;
            public var transportStream :NetStream;
            public var serverConnection:URLStream;

            //public var host:String=" "; //now not used in line... request=new URLRequest(resource);
            public var resource:String="http://aacplus-ac-64.timlradio.co.uk/;"; //test station

            public function NBAAC_AIR_v2 ()
            {
                super();

                /*
                if(loaderInfo.parameters.host)
                        host=loaderInfo.parameters.host;
                if(loaderInfo.parameters.resource)
                        resource=loaderInfo.parameters.resource;
                 */       

                //CodecFactory.ImportCodec(MetaData);

                CodecFactory.ImportCodec(AAC);
                CodecFactory.ImportCodec(AACP);         

                transcoder = new Transcoder();
                transcoder.addEventListener(CodecEvent.STREAM_READY,onChannelReady);
                transcoder.addEventListener(StreamDataEvent.DATA, onTag);
                transcoder.initiate();

                transcoder.loadCodec("AAC");

                transport = new NetConnection();
                transport.connect(null);

                flash.utils.setTimeout(boot,500);
            }

            public function boot():void
            {
                //externalInterface.addCallback('nbaac.setVolume', onVolume);
                //externalInterface.addCallback('nbaac.togglePause', onTogglePause);
                //externalInterface.addCallback('nbaac.setBuffer', setBufferLength);
                //externalInterface.addCallback('nbaac.getBuffer', getBufferLength);
                //externalInterface.addCallback('nbaac.getTime', getTime);

                /*
                var meta:Object={};
                meta.uri="";
                meta.StreamTitle=""; 
                */

                //externalInterface.call('logit','start up');

                transportStream = new NetStream(transport);
                transportStream.bufferTime=2;
                transportStream.client = this;
                transportStream.soundTransform=new SoundTransform(.5,0);
                transportStream.play(null);                             
                transportStream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);

                var headerTag:ByteArray=transcoder.createHeader(false,true);
                transportStream.appendBytes(headerTag);                         
                headerTag.clear();

                //transcoder.readMetaObject(meta,0);

                serverConnection=new URLStream();
                serverConnection.addEventListener(ProgressEvent.PROGRESS,loaded);
                serverConnection.addEventListener(IOErrorEvent.IO_ERROR, onIo);                                 
                serverConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onNoPolicy);
                serverConnection.addEventListener(Event.CLOSE, onClose);

                request=new URLRequest(resource); //removed "host" from url

                //request.requestHeaders=[new URLRequestHeader("GET",resource+" HTTP/1.0")];
                //request.requestHeaders=[new URLRequestHeader("Icy-MetaData","1")];

                request.method=URLRequestMethod.GET;
                serverConnection.load(request);
            }

            private function getTime() :Number
            {   return transportStream.time;  }

            private function getBufferLength() :Number
            {  return transportStream.bufferLength; }

            private function setBufferLength (val:Number) :void
            {   transportStream.bufferTime = val;  }

            private function onTogglePause():void
            {   transportStream.togglePause(); }

            private function onVolume (val:Number) :void
            {   transportStream.soundTransform = new SoundTransform(val, 0); }

            private function onIo(pe:IOErrorEvent) :void
            {  /* externalInterface.call('logit','IOErrorEvent') */  }

            private function onTag(sde:StreamDataEvent) :void
            {
                    sde.tag.position=0;
                    transportStream.appendBytes(sde.tag);
            }

            private function onChannelReady(ce:CodecEvent) :void
            {  trace('onChannelReady :',ce.codec.type); }

            private function onClose(e:Event):void
            {   /* externalInterface.call('logit','onClose') */ }

            public function onMetaData(e:Object):void
            {  /* externalInterface.call('logit','onMetaData') */ }

            private function loaded(e:ProgressEvent):void
            {                       
                var chunk:ByteArray=new ByteArray();

                while(serverConnection.bytesAvailable)
                {   chunk.writeByte( serverConnection.readByte() ); }

                chunk.position=0;   
                transcoder.addRawData( chunk, 0, "AAC" );                                                        
            }

            private function onNoPolicy(se:SecurityErrorEvent):void
            { /* externalInterface.call('logit','SecurityErrorEvent'+host+resource+'<br />'+se.text); */ }

    }

}
Community
  • 1
  • 1
VC.One
  • 14,790
  • 4
  • 25
  • 57
  • Hm... Your absolutely right about the basic version of the example, it targets HTML or any other app container that implements external interface. Your updated version just seems to have calls to container removed. It's good for launching app in a standalone player, but will not help on mobile platform.( – Vyacheslav Orlovsky Sep 25 '14 at 08:22
  • We need the Kaiser on this one..?? Whats your testing link? I noticed some streams did not work in Chrome but were okay in I.Explorer and Firefox even with this updated code. It might be fussy about wether Shoutcast OR Icecast system? Also try your url without `...;` or `.../;`since that's a browser related trick in Flash – VC.One Sep 25 '14 at 08:44
  • `String="http://aacplus-ac-64.timlradio.co.uk/;";` becomes `String="http://aacplus-ac-64.timlradio.co.uk";` but anyway I just found my link might not like non-UK connections.. – VC.One Sep 25 '14 at 08:55
  • Yes, Kiser is for sure desired to tell about his algorithm of transcoding. My test link is http://listen.classicrocklounge.com:8000/aac64. – Vyacheslav Orlovsky Sep 25 '14 at 12:54
  • In Device Central debugger I now noticed that the D.C settings were "Embedded in HTML" so it works on mobile only inside an HTML file (`FP 10.3`?). Anyways only solution from this clue is to **try embedding the HTML plus SWF inside your AIR app**. The idea is to make your AIR app become the browser. I cannot try this myself so let me know: See this [ [Embed SWF+HTML in AIR](http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS4B441C24-BAE3-4110-91FD-A4E5EEFB2467.html) ] also this [ [HTML-based AIR App](http://help.adobe.com/en_US/air/build/WS5b3ccc516d4fbf351e63e3d118666ade46-7ecc.html) ]. – VC.One Sep 28 '14 at 15:54
  • This looks useful too.. (not necessary if you do the above but still good to know).. [ [Using ActionScript libraries within an HTML page](http://help.adobe.com/en_US/air/html/dev/WS5b3ccc516d4fbf351e63e3d118666ade46-7ed9.html) ]. All this info and above is from here.. [ [AIR and HTML Programming Guide](http://help.adobe.com/en_US/air/html/dev/WS5b3ccc516d4fbf351e63e3d118666ade46-7fa7.html)] – VC.One Sep 28 '14 at 16:03
  • Thank you for the good links, but I'm sure that this one is working in DC just because it's simulator is not so precise (e.g. in Flash Builder it is not - it's just a PC version of Flash player running sized for a certain device's screen). Also, as you know, there is no Flash player in iOS at all and it's not supported on Android. – Vyacheslav Orlovsky Sep 29 '14 at 12:23
  • In iOS it seems you can embed SWF inside AIR app but the AS code inside cannot work, only animations allowed. In android no such limit. What I mean is use the radio SWF just like you use jpeg image/logo inside your app. As an on-screen asset that when clicked plays radio. AIR sends same user agent like Safari so servers should still accept as though the request is coming inside a Safari browser. There is no loading any actual HTML page that needs Flash Player plugins since AIR _is_ the FP plugin for SWF now.. – VC.One Sep 29 '14 at 19:02
  • If all the above fails then my final hope is these links: **[[RadioPlyr](http://rdioplyr.co/)]** and the **[[Source Code](https://www.assembla.com/code/radioplayer-mobile/subversion/nodes)]**. There is also this one I bookmarked long ago **[[Live Radio in AIR](http://www.overdigital.com/2012/01/18/tune-in-to-live-radio-with-adobe-air/)]** It says RTMP but in the comments someone used for non-RTMP stream so worth trying... I've been asked again to make Android radio player (previously I declined) so now I really need a solution too. – VC.One Sep 29 '14 at 19:17
  • According to [Adobe reference](http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-8000.html) building HTML-based mobile app is not possible. The second your approach seems to be based on OSMF and you have a big chance to make your hopes come true :) Unfortunately, I'm not an expert in it and looks like it's too high-level for me cause I need the way to manipulate stream bytes in order to cache data. – Vyacheslav Orlovsky Sep 29 '14 at 21:18
  • RTMP is for the real last chance - setting up an additional server for transcoding can be inacceptable. – Vyacheslav Orlovsky Sep 29 '14 at 21:23