11 Replies Latest reply on Jun 12, 2011 11:19 PM by shamik_shah_1

    Made a free web MP3 player with OSMF

    cayennecode Level 1

      I used iTunes as a skin, and it was fairly quick & easy to do, thanks guys!

       

      http://blog.leefernandes.com/?p=251

       

      iWebTunes.jpg

        • 2. Re: Made a free web MP3 player with OSMF
          cayennecode Level 1

          Can someone please show how to get a reference to an actual Sound object with OSMF?  For example, I would expect the AudioElement to have a getter method for the flash.media.Sound object, but I cannot find this.

          Or, can someone point me to the equivalent of the flash.media.Sound.length property for an AudioElement?  MediaPlayer.duration is not the same.  Thanks.

          • 3. Re: Made a free web MP3 player with OSMF
            bringrags Level 4

            The Sound class isn't publicly accessible, as so far we've been able to map the specific properties into our more abstract media APIs.  Sound.length should map to MediaPlayer.duration -- if it doesn't, then please file a bug.  (Glancing at the code, it looks like Sound.length is coupled to the download length, which seems like an inappropriate thing for us to do.  Probably a bug.)

             

            Assuming we can get MediaPlayer.duration to map to Sound.length, is there any other reason why you would need access to the Sound object?

            • 4. Re: Made a free web MP3 player with OSMF
              cayennecode Level 1

              Hey Brian,

               

              I see, the duration property works with some mp3's, but not all.  The mp3 below consistently does not work with the duration property.  I assume the headers are formed differently, or something, and the MediaPlayer is not defaulting to the SoundAdapter's estimatedDuration method.  What I needed was access to the estimatedDuration property in the SoundAdapter class, and added a getter method in AudioElement for the adapter.

               

              http://blog.leefernandes.com/audio/party2nyte.mp3

               

              I suppose the only other reason I would want access to the Sound object is because I currently do not see how else to access an mp3's ID3 data.  Thanks.

              • 5. Re: Made a free web MP3 player with OSMF
                cayennecode Level 1

                To clarify, the duration property currently returns a greatly inaccurate number while loading.  Once load is completed, it is correct. I also notice that seek does not work with the above mp3 while loading.

                • 6. Re: Made a free web MP3 player with OSMF
                  bringrags Level 4

                  I'm curious, do you see accurate numbers when using Sound.length?  I changed the code locally to have duration map to Sound.length, but I see Sound.length growing as the file downloads (i.e. it seems to return the length of the currently downloaded portion of the file).  For example, it starts at 0.15, grows incrementally as the file continues its download, then jumps up to 224 when the download finishes.  Is this what you would expect?

                  • 7. Re: Made a free web MP3 player with OSMF
                    cayennecode Level 1

                    Hey Brian,

                     

                    Yes, I was going to use Sound.length to estimate the duration using bytesLoaded, and found a function inside SoundAdapter that already does this.  I only had to add a public get method in AudioElement to access the SoundAdapter object.  It's reliable when dealing with mp3's like the one above where Player.duration does not return an accurate duration.

                     

                     

                    public function get estimatedDuration():Number

                    {

                         return sound.length / (1000 * sound.bytesLoaded / sound.bytesTotal);

                    }

                    • 8. Re: Made a free web MP3 player with OSMF
                      bringrags Level 4

                      I'm a bit confused now.  SoundAdapter.estimatedDuration should be identical to MediaPlayer.duration.  Are you seeing cases where the numbers differ?

                       

                      Looking at the code more closely, it's possible that the numbers would differ due to the removal of the "progress" event listener in AudioTimeTrait.onDownloadProgress.  If you comment out the "soundAdapter.removeEventListener(...)" line in AudioTimeTrait, does MediaPlayer.duration reflect what you'd expect?

                      • 9. Re: Made a free web MP3 player with OSMF
                        cayennecode Level 1

                        Yes, I'm definitely seeing a difference with the mp3 above (but not all, which is strange).  During the load process, Player.duration returns something much shorter (usually around 2-50 seconds) whereas the mp3 is actually over 2 minutes.  I'll try commenting out that removeEventListener.

                        • 10. Re: Made a free web MP3 player with OSMF
                          shamik_shah_1

                          Hey Guys,

                           

                          Wanted to quickly check if this issue was resolved in a later version of OSMF ? I'm using the latest Strobe Media Playback and compiling it against OSMF 1.5.1 and continue to see this issue.

                           

                          If this issue isn't resolved, is there a workaround specifically for SMP ?

                           

                           

                          Thanks!

                          • 11. Re: Made a free web MP3 player with OSMF
                            shamik_shah_1 Level 1

                            Some digging around and looking at the OSMF code answered the question for me. For the case of an MP3 file, OSMF doesn't use the traditional netstream/netconnect mechanism, instead it uses the Sound object to download and play the file. By default, the Sound object in flash has issues with determining the length of the mp3 and that cascades up the stack to create issues with the seekbar. I found a couple of work-arounds for this issue,

                             

                            1) use mp3infoutil - i integrated mp3infoutil into the SoundAdapter class in OSMF, the disadvantage here is that it only works with MP3's that have ID3v1 tags, if the mp3 has an ID3v2 tag, this doesn't work at all. The modified code for SoundAdapter is (you will need to download the mp3infoutil swc and include it as a part of your compile library list)

                             

                            /*****************************************************

                            *  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.

                            *****************************************************
                            *  The contents of this file are subject to the Mozilla Public License
                            *  Version 1.1 (the "License"); you may not use this file except in
                            *  compliance with the License. You may obtain a copy of the License at
                            http://www.mozilla.org/MPL/
                            *  
                            *  Software distributed under the License is distributed on an "AS IS"
                            *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
                            *  License for the specific language governing rights and limitations
                            *  under the License.
                            *  

                            *  The Initial Developer of the Original Code is Adobe Systems Incorporated.
                            *  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
                            *  Incorporated. All Rights Reserved.

                            *****************************************************/
                            package org.osmf.elements.audioClasses
                            {
                                import flash.events.Event;
                                import flash.events.EventDispatcher;
                                import flash.events.IOErrorEvent;
                                import flash.events.ProgressEvent;
                                import flash.media.Sound;
                                import flash.media.SoundChannel;
                                import flash.media.SoundTransform;
                               
                                import org.osmf.events.MediaError;
                                import org.osmf.events.MediaErrorCodes;
                                import org.osmf.events.MediaErrorEvent;
                                import org.osmf.media.MediaElement;
                                import org.osmf.media.URLResource;
                               
                               
                                import com.fastforwardthinker.util.mp3.events.MP3InfoEvent;
                                import com.fastforwardthinker.util.mp3.MP3InfoUtil;
                               
                                [ExcludeClass]
                               
                                /**
                                 * Dispatched when playback of the Sound completes.
                                 *
                                 * @eventType flash.events.Event.COMPLETE
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="complete", type="flash.events.Event")]
                               
                                /**
                                 * Dispatched when download of the Sound completes.
                                 *
                                 * @eventType downloadComplete
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="downloadComplete", type="flash.events.Event")]
                               
                                /**
                                 * Dispatched periodically as the download of the Sound progresses.
                                 *
                                 * @eventType flash.events.ProgressEvent.PROGRESS
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="progress", type="flash.events.ProgressEvent")]
                                       
                                /**
                                * @private
                                *
                                * Utility class to make working with the Sound class a bit easier.
                                * 
                                *  @langversion 3.0
                                *  @playerversion Flash 10
                                *  @playerversion AIR 1.5
                                *  @productversion OSMF 1.0
                                */
                                public class SoundAdapter extends EventDispatcher
                                {
                                    public static const DOWNLOAD_COMPLETE:String = "downloadComplete";   
                                   
                                    public function SoundAdapter(owner:MediaElement, sound:Sound)
                                    {
                                        super();
                                       
                                        this.owner = owner;
                                        trace("sound adapter init and resource is " + (owner.resource as URLResource).url.toString());
                                        this.sound = sound;
                                        _soundTransform = new SoundTransform();
                                       
                                        sound.addEventListener(Event.COMPLETE, onDownloadComplete, false, 0, true);
                                        sound.addEventListener(ProgressEvent.PROGRESS, onProgress, false, 0, true);
                                        sound.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
                                        //sound.addEventListener(Event.ID3, id3Handler);
                                        trace("input into mp3infoutil " + (owner.resource as URLResource).url.toString());
                                        MP3InfoUtil.getInfo( (owner.resource as URLResource).url.toString() );
                                        MP3InfoUtil.addEventListener(MP3InfoEvent.COMPLETE, onMP3InfoComplete );
                                        MP3InfoUtil.addEventListener(MP3InfoEvent.ERROR, onMP3InfoError );
                                       
                                    }
                                   
                                    public function onMP3InfoComplete(e:MP3InfoEvent):void{
                                            //output.text = '';
                                            /*trace("*********** mp3 info ********");
                                            for( var key:String in e.info ){
                                                trace(key + ": " + e.info[key]);
                                            }*/
                                           
                                            var durationFromMp3Info:Number = new Number(e.info["lengthSeconds"]);
                                            trace("********** the duration of the file is = " + durationFromMp3Info);  
                                           
                                            this.customDuration = durationFromMp3Info;
                                           
                                            dispatchEvent(new Event(DOWNLOAD_COMPLETE));
                                        }
                                       
                                    public function onMP3InfoError(e:MP3InfoEvent):void{
                                            trace(e.info.message);
                                        }
                                   
                                    /*public function id3Handler(e:Event):void{
                                        trace("id3Handler: " + e);
                                        trace("**************length according to id3 tag is " + e.target.id3.songName);
                                    }*/
                                   
                                    public function get currentTime():Number
                                    {           
                                        return channel != null ? channel.position / 1000 : lastStartTime / 1000;
                                    }

                             

                                    /**
                                     * Returns an estimate of the duration of the partially downloaded
                                     * audio file, in seconds.
                                     * 
                                     *  @langversion 3.0
                                     *  @playerversion Flash 10
                                     *  @playerversion AIR 1.5
                                     *  @productversion OSMF 1.0
                                     */
                                    public function get estimatedDuration():Number
                                    {
                                            /*trace("sound length = " + sound.length);
                                            trace("bytesLoaded = " + sound.bytesLoaded);
                                            trace("bytesTotal = " + sound.bytesTotal);*/
                                            //return sound.length / (1000 * sound.bytesLoaded / sound.bytesTotal);
                                            return this.customDuration;           
                                    }   
                                   
                                    /*public function setCustomDuration(customDuration:Number):void{
                                        this.customDuration = customDuration;
                                    }*/
                                   
                                    public function get soundTransform():SoundTransform
                                    {
                                        return _soundTransform;
                                    }
                                   
                                    public function set soundTransform(value:SoundTransform):void
                                    {
                                        _soundTransform = value;
                                        if (channel != null)
                                        {
                                            channel.soundTransform = value;   
                                        }       
                                    }

                             

                                    /**
                                     * Play the sound.  If the given time is -1, starts from the
                                     * beginning.  Otherwise, attempts to play from that point.
                                     *
                                     * @returns True if playing the file was successful, false if
                                     * playback failed for some reason.
                                     * 
                                     *  @langversion 3.0
                                     *  @playerversion Flash 10
                                     *  @playerversion AIR 1.5
                                     *  @productversion OSMF 1.0
                                     */
                                    public function play(time:Number=-1):Boolean
                                    {
                                        var success:Boolean = false;
                                       
                                        if (channel == null)
                                        {
                                            // HTTPS urls can throw errors here.
                                            try
                                            {
                                                channel = sound.play(time != -1 ? time : lastStartTime);
                                            }
                                            catch (error:ArgumentError)
                                            {
                                                // Do nothing, just send the playback error (see below).
                                                channel = null;                   
                                            }
                                           
                                            if (channel != null)
                                            {
                                                playing = true;
                                               
                                                // Apply any previously-set SoundTransform on the new channel.
                                                channel.soundTransform = _soundTransform;
                                               
                                                channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
                                               
                                                success = true;
                                            }
                                            else
                                            {
                                                // When channel is null, we either have no sound card or no
                                                // sound channels available.
                                                owner.dispatchEvent
                                                    ( new MediaErrorEvent
                                                        ( MediaErrorEvent.MEDIA_ERROR
                                                        , false
                                                        , false
                                                        , new MediaError(MediaErrorCodes.SOUND_PLAY_FAILED)
                                                        )
                                                    );
                                            }
                                        }
                                       
                                        return success;
                                    }
                                               
                                    public function pause():void
                                    {
                                        if (channel != null)
                                        {
                                            lastStartTime = channel.position;
                                           
                                            clearChannel();
                                            playing = false;
                                        }
                                    }
                                   
                                    public function stop():void
                                    {
                                        if (channel != null)
                                        {
                                            lastStartTime = 0;
                                           
                                            clearChannel();
                                            playing = false;
                                        }
                                    }
                                   
                                    public function seek(time:Number):void
                                    {
                                        var wasPlaying:Boolean = playing;
                                       
                                        if (channel != null)
                                        {
                                            clearChannel();
                                        }

                             

                                        play(time*1000);

                             

                                        if (wasPlaying == false)
                                        {
                                            pause();
                                        }
                                    }   
                                                   
                                    // Internals
                                    //
                                   
                                    private function clearChannel():void
                                    {
                                        if (channel != null)
                                        {
                                            channel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete);
                                            channel.stop();
                                            channel = null;
                                        }
                                    }
                                   
                                    private function onSoundComplete(event:Event):void
                                    {
                                        lastStartTime = channel.position;
                                       
                                        clearChannel();
                                        playing = false;
                                       
                                        // Signal playback has completed.
                                        dispatchEvent(new Event(Event.COMPLETE));
                                    }
                                           
                                    private function onDownloadComplete(event:Event):void
                                    {
                                        dispatchEvent(new Event(DOWNLOAD_COMPLETE));
                                    }
                                   
                                    private function onProgress(event:ProgressEvent):void
                                    {
                                        dispatchEvent(event.clone());
                                    }

                             

                                    private function onIOError(event:IOErrorEvent):void
                                    {
                                        owner.dispatchEvent
                                            ( new MediaErrorEvent
                                                ( MediaErrorEvent.MEDIA_ERROR
                                                , false
                                                , false
                                                , new MediaError(MediaErrorCodes.IO_ERROR)
                                                )
                                            );
                                    }
                                   
                                   
                                    private var customDuration:Number;
                                    private var owner:MediaElement;
                                    private var _soundTransform:SoundTransform;   
                                    private var sound:Sound;   
                                    private var playing:Boolean = false;       
                                    private var channel:SoundChannel;
                                    private var lastStartTime:Number = 0;
                                }
                            }

                             

                             

                            2) the second work around is to actuall pass in the duration of the mp3 as a flashvar into the player (not elegant at all, but works!!), you will also need to modify the AudioTrait.as file to add the setCustomDimension() method

                             

                            /*****************************************************

                            *  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.

                            *****************************************************
                            *  The contents of this file are subject to the Mozilla Public License
                            *  Version 1.1 (the "License"); you may not use this file except in
                            *  compliance with the License. You may obtain a copy of the License at
                            http://www.mozilla.org/MPL/
                            *  
                            *  Software distributed under the License is distributed on an "AS IS"
                            *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
                            *  License for the specific language governing rights and limitations
                            *  under the License.
                            *  

                            *  The Initial Developer of the Original Code is Adobe Systems Incorporated.
                            *  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
                            *  Incorporated. All Rights Reserved.

                            *****************************************************/
                            package org.osmf.elements.audioClasses
                            {
                                import flash.events.Event;
                                import flash.events.EventDispatcher;
                                import flash.events.IOErrorEvent;
                                import flash.events.ProgressEvent;
                                import flash.media.Sound;
                                import flash.media.SoundChannel;
                                import flash.media.SoundTransform;
                               
                                import org.osmf.events.MediaError;
                                import org.osmf.events.MediaErrorCodes;
                                import org.osmf.events.MediaErrorEvent;
                                import org.osmf.media.MediaElement;
                               
                               
                                [ExcludeClass]
                               
                                /**
                                 * Dispatched when playback of the Sound completes.
                                 *
                                 * @eventType flash.events.Event.COMPLETE
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="complete", type="flash.events.Event")]
                               
                                /**
                                 * Dispatched when download of the Sound completes.
                                 *
                                 * @eventType downloadComplete
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="downloadComplete", type="flash.events.Event")]
                               
                                /**
                                 * Dispatched periodically as the download of the Sound progresses.
                                 *
                                 * @eventType flash.events.ProgressEvent.PROGRESS
                                 * 
                                 *  @langversion 3.0
                                 *  @playerversion Flash 10
                                 *  @playerversion AIR 1.5
                                 *  @productversion OSMF 1.0
                                 */
                                [Event(name="progress", type="flash.events.ProgressEvent")]
                                       
                                /**
                                * @private
                                *
                                * Utility class to make working with the Sound class a bit easier.
                                * 
                                *  @langversion 3.0
                                *  @playerversion Flash 10
                                *  @playerversion AIR 1.5
                                *  @productversion OSMF 1.0
                                */
                                public class SoundAdapter extends EventDispatcher
                                {
                                    public static const DOWNLOAD_COMPLETE:String = "downloadComplete";   
                                   
                                    public function SoundAdapter(owner:MediaElement, sound:Sound)
                                    {
                                        super();
                                       
                                        this.owner = owner;
                                        this.sound = sound;
                                        _soundTransform = new SoundTransform();
                                       
                                        sound.addEventListener(Event.COMPLETE, onDownloadComplete, false, 0, true);
                                        sound.addEventListener(ProgressEvent.PROGRESS, onProgress, false, 0, true);
                                        sound.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
                                        //sound.addEventListener(Event.ID3, id3Handler);
                                    }
                                   
                                    /*public function id3Handler(e:Event):void{
                                        trace("id3Handler: " + e);
                                        trace("**************length according to id3 tag is " + sound.id3);
                                    }*/
                                   
                                    public function get currentTime():Number
                                    {           
                                        return channel != null ? channel.position / 1000 : lastStartTime / 1000;
                                    }

                             

                                    /**
                                     * Returns an estimate of the duration of the partially downloaded
                                     * audio file, in seconds.
                                     * 
                                     *  @langversion 3.0
                                     *  @playerversion Flash 10
                                     *  @playerversion AIR 1.5
                                     *  @productversion OSMF 1.0
                                     */
                                    public function get estimatedDuration():Number
                                    {
                                            /*trace("sound length = " + sound.length);
                                            trace("bytesLoaded = " + sound.bytesLoaded);
                                            trace("bytesTotal = " + sound.bytesTotal);*/
                                            //return sound.length / (1000 * sound.bytesLoaded / sound.bytesTotal);
                                            return this.customDuration;           
                                    }   
                                   
                                    public function setCustomDuration(customDuration:Number):void{
                                        this.customDuration = customDuration;
                                    }
                                   
                                    public function get soundTransform():SoundTransform
                                    {
                                        return _soundTransform;
                                    }
                                   
                                    public function set soundTransform(value:SoundTransform):void
                                    {
                                        _soundTransform = value;
                                        if (channel != null)
                                        {
                                            channel.soundTransform = value;   
                                        }       
                                    }

                             

                                    /**
                                     * Play the sound.  If the given time is -1, starts from the
                                     * beginning.  Otherwise, attempts to play from that point.
                                     *
                                     * @returns True if playing the file was successful, false if
                                     * playback failed for some reason.
                                     * 
                                     *  @langversion 3.0
                                     *  @playerversion Flash 10
                                     *  @playerversion AIR 1.5
                                     *  @productversion OSMF 1.0
                                     */
                                    public function play(time:Number=-1):Boolean
                                    {
                                        var success:Boolean = false;
                                       
                                        if (channel == null)
                                        {
                                            // HTTPS urls can throw errors here.
                                            try
                                            {
                                                channel = sound.play(time != -1 ? time : lastStartTime);
                                            }
                                            catch (error:ArgumentError)
                                            {
                                                // Do nothing, just send the playback error (see below).
                                                channel = null;                   
                                            }
                                           
                                            if (channel != null)
                                            {
                                                playing = true;
                                               
                                                // Apply any previously-set SoundTransform on the new channel.
                                                channel.soundTransform = _soundTransform;
                                               
                                                channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
                                               
                                                success = true;
                                            }
                                            else
                                            {
                                                // When channel is null, we either have no sound card or no
                                                // sound channels available.
                                                owner.dispatchEvent
                                                    ( new MediaErrorEvent
                                                        ( MediaErrorEvent.MEDIA_ERROR
                                                        , false
                                                        , false
                                                        , new MediaError(MediaErrorCodes.SOUND_PLAY_FAILED)
                                                        )
                                                    );
                                            }
                                        }
                                       
                                        return success;
                                    }
                                               
                                    public function pause():void
                                    {
                                        if (channel != null)
                                        {
                                            lastStartTime = channel.position;
                                           
                                            clearChannel();
                                            playing = false;
                                        }
                                    }
                                   
                                    public function stop():void
                                    {
                                        if (channel != null)
                                        {
                                            lastStartTime = 0;
                                           
                                            clearChannel();
                                            playing = false;
                                        }
                                    }
                                   
                                    public function seek(time:Number):void
                                    {
                                        var wasPlaying:Boolean = playing;
                                       
                                        if (channel != null)
                                        {
                                            clearChannel();
                                        }

                             

                                        play(time*1000);

                             

                                        if (wasPlaying == false)
                                        {
                                            pause();
                                        }
                                    }   
                                                   
                                    // Internals
                                    //
                                   
                                    private function clearChannel():void
                                    {
                                        if (channel != null)
                                        {
                                            channel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete);
                                            channel.stop();
                                            channel = null;
                                        }
                                    }
                                   
                                    private function onSoundComplete(event:Event):void
                                    {
                                        lastStartTime = channel.position;
                                       
                                        clearChannel();
                                        playing = false;
                                       
                                        // Signal playback has completed.
                                        dispatchEvent(new Event(Event.COMPLETE));
                                    }
                                           
                                    private function onDownloadComplete(event:Event):void
                                    {
                                        dispatchEvent(new Event(DOWNLOAD_COMPLETE));
                                    }
                                   
                                    private function onProgress(event:ProgressEvent):void
                                    {
                                        dispatchEvent(event.clone());
                                    }

                             

                                    private function onIOError(event:IOErrorEvent):void
                                    {
                                        owner.dispatchEvent
                                            ( new MediaErrorEvent
                                                ( MediaErrorEvent.MEDIA_ERROR
                                                , false
                                                , false
                                                , new MediaError(MediaErrorCodes.IO_ERROR)
                                                )
                                            );
                                    }
                                   
                                   
                                    private var customDuration:Number;
                                    private var owner:MediaElement;
                                    private var _soundTransform:SoundTransform;   
                                    private var sound:Sound;   
                                    private var playing:Boolean = false;       
                                    private var channel:SoundChannel;
                                    private var lastStartTime:Number = 0;
                                }
                            }

                             

                             

                             

                            hope this helps!!!