Home » Tutoriel 2: Android WebRadio avec source M3U

Partie 1 : Introduction

 

Pour ce second article, je vais présenter comment réaliser un player de fichier M3U.

La plupart des WebRadios utilisent des liens portants sur des fichiers M3u. Voici la définition de wikipédia «M3U (MPEG version 3.0 URL) est un format de fichier qui a pour but de stocker une liste de fichiers audio ou vidéo.»

On voit donc qu’il fait parti des standards. Son étude sur android est donc intéressante. Malheureusement l’api mediaplayer d’Android n’est pas capable aujourd’hui de lire directement ce format. Voici donc un tutoriel montrant comment contourner ce problème.


 

 

Partie 2 : Architecture Technique

 

En voici son format : (Wikipédia  Razz)

  • défini par le mot clé : #EXTM3U
  • puis #EXTINF: suivi de la durée du titre en secondes (ou de -1 pour ignorer la durée) et d’une virgule.
  • le titre du morceau.
  • Et à la ligne : l’URL ou le simple nom du fichier.

Pour ceux qui recherchent des liens de webradios voici un site regroupant un très grand nombre. Dommage que leur api n’est pas disponible au public.

 

En regardant avec un éditeur de texte, un fichier M3u n’est d’autre que du texte. Si l’on arrive à partir d’un fichier à récupérer l’url, alors notre mediaplayer pourra lire notre radio. (çà devrait pas être compliqué)

 


 

 

Partie 3 : Conception applicative

Avant de rentrer directement dans le code, on va définir la page de notre application ainsi que la classe métier qui réalise notamment le parsing du fichier M3u …

En fin de partie nous aurons notre AndroidManisfest qui représente notre fichier de configuration des applications Android.

Une page Android ou View est appelée Activity. Par la suite je parlerai alors d’activity…

 

3.1 L’activity

 

Pour organiser correctement notre application, commencez par créer un package «com.projet.android». Celui-ci comprendra notre activity que voici :

Nom Layout Associé Description
PlayerActivity main.xml page d’accueil

 

Attention à ne pas mettre de majuscule dans le nom des layouts…

 

3.2 La classe

Cette fois-ci un seul package sera utilisé  «com.projet.android.metier».

ParserM3UToURL Classe métier qui gère la récupération de l’url dans le fichier M3U com.projet.android.metier

 

Comme promis voici le manifest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.radio.reveil"
android:versionCode="1"
android:versionName="1.0" >

android:minSdkVersion="10" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".PlayerActivity" >
<intent-filter >
android:name="android.intent.action.MAIN" />
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
android:name="android.permission.INTERNET">
</manifest>

 

Quelques explications :

On retrouve notre activité sous la baliseandroid ….>

L’application a besoin d’internet :

  • Internet pour faire les requêtes android.permission.INTERNET

 

Voilà on vient de terminer notre conception. On passe maintenant au code…

 

 


 

Partie 4 : Code

On va commencer par l’ activity.

4.1 PlayerActivity.java

Voici la seule activity de notre projet. Je vous l’accorde, ce n’est pas très joli Smile

Le but étant de simplifier afin de ne pas être perdu. Razz


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
package com.radio.reveil;

import java.io.IOException;

import com.radio.reveil.metier.ParserM3UToURL;

import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.DigitalClock;
import android.widget.ProgressBar;
import android.widget.TextView;

public class PlayerActivity extends Activity implements OnInfoListener,OnPreparedListener,OnErrorListener , OnCompletionListener{

    @SuppressWarnings("unused")
    private Button button_play;
    @SuppressWarnings("unused")
    private Button button_quit;
    private ProgressBar progress_main;
    private TextView text_info;
    MediaPlayer mediaPlayer;
    private Activity rootActivity;
    private boolean isPlaying = false;
    @SuppressWarnings("unused")
    private DigitalClock digitalClock;

    String STREAM_URI;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {

        Log.i("WakeUpRadio","Radio Starts");
        // get are self
        rootActivity = this;    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // get the UI elemntns
        button_play = (Button) findViewById(R.id.start);
        button_quit = (Button) findViewById(R.id.stop);
        progress_main = (ProgressBar) findViewById(R.id.progressBar);
        text_info = (TextView) findViewById(R.id.textInfo);
        digitalClock = (DigitalClock)findViewById(R.id.digitalClock);

    }

    //Bouton PLay
    public void Start(View v){
        // Perform action on click

        Log.v("FreshAir", "Play button pressed");

        STREAM_URI = ParserM3UToURL.parse("http://streams.frequence3.net/mp3-192.m3u");

        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setOnCompletionListener((OnCompletionListener) rootActivity);
            mediaPlayer.setOnErrorListener((OnErrorListener) rootActivity);
            mediaPlayer.setOnInfoListener((OnInfoListener) rootActivity);
            mediaPlayer.setOnPreparedListener((OnPreparedListener) rootActivity);
            try {
                mediaPlayer.setDataSource(STREAM_URI);
            } catch (IllegalArgumentException e) {
                onError(mediaPlayer, -1, -1);
                return;
            } catch (IllegalStateException e) {
                onError(mediaPlayer, -1, -1);
                return;
            } catch (IOException e) {
                onError(mediaPlayer, -1, -1);
                return;
            }  

            try {
                progress_main.setVisibility(View.VISIBLE);
                mediaPlayer.prepareAsync();
                text_info.setText("Loading ...");
                isPlaying = true;
            } catch (IllegalStateException e) {
                onError(mediaPlayer, -1, -1);
            }
        }

    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        Log.v("FreshAir", "onError (" + what + ")");

        if(mp != null){
            mp.release();
            mp = null;
        }
        isPlaying = false;

        text_info.setText("Error: Please Retry");

        progress_main.setVisibility(View.INVISIBLE);

        return true; // return true to stop OS calling 'onCompletion'
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        try {
            mediaPlayer.start();
        } catch (IllegalStateException e) {
            onError(mp, -1, -1);
        }
        text_info.setText("Playing ...");
        progress_main.setVisibility(View.INVISIBLE);
    }

    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {
        // TODO: currently the getMetadata method is not present

        Log.v("FreshAir", "onInfo (" + what + " - " + extra + ")");

        switch (what) {
        case 701: //MediaPlayer.MEDIA_INFO_BUFFERING_START:
            text_info.setText("Buffering ...");
            progress_main.setVisibility(View.VISIBLE);
            isPlaying = true; // ie you cant re set the data sourse without
            // stoping first
            break;

        case 702: //MediaPlayer.MEDIA_INFO_BUFFERING_END:
            text_info.setText("Playing ...");
            progress_main.setVisibility(View.INVISIBLE);
            isPlaying = true;

        default:
            break;
        }

        return true; // return true to stop the OS calling 'onError'
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        if(mp == null)
            return;
        mp.release();
        mp = null;
        isPlaying = false;
        progress_main.setVisibility(View.INVISIBLE);
    }

    // Stop button
    public void Stop(View v){
        if(isPlaying || (mediaPlayer != null &amp;&amp; mediaPlayer.isPlaying())){
            if(mediaPlayer.isPlaying()){
                mediaPlayer.stop();
            }
            mediaPlayer.release();
            mediaPlayer = null;

            text_info.setText("");
            isPlaying = false;
            progress_main.setVisibility(View.INVISIBLE);
        }
    }

}

Maitenant le layout associé

4.2 main.xml

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
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"

android:orientation="vertical" >

<ProgressBar
android:id="@+id/progressBar"
style="@android:style/Widget.ProgressBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="8dip"
android:visibility="invisible" />

<TextView
android:id="@+id/textInfo"
android:text="info"
android:layout_width="90dip"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/progressBar"
android:padding="8dip" />

<DigitalClock
android:id="@+id/digitalClock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textInfo"
android:layout_centerHorizontal="true"
android:paddingTop="10dip"
android:text="DigitalClock"
android:textColor="#CC00FF"
android:textSize="70dip" />

<Button
android:id="@+id/start"
android:layout_below="@+id/digitalClock"
android:layout_width="90dip"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="12dp"
android:text="Start"
android:onClick="Start" />

<Button
android:id="@+id/stop"
android:layout_below="@+id/start"
android:layout_width="90dip"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/start"
android:layout_marginTop="12dp"
android:text="Stop"
android:onClick="Stop" />

</RelativeLayout>

 

4.3 ParserM3UToURL


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
package com.radio.reveil.metier;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class ParserM3UToURL {

    /*
     * Classe qui permet de retrouver l'url d'un flux
     * à partir d'un fichier M3U
     */

    public static String parse(String urlM3u) {

        String ligne=null;

        try {
            URL urlPage = new URL(" http://streams.frequence3.net/mp3-192.m3u");
            HttpURLConnection connection = (HttpURLConnection) urlPage.openConnection();
            InputStream inputStream = connection.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuffer stringBuffer = new StringBuffer();

            while((ligne = bufferedReader.readLine()) != null) {
                if (ligne.contains("http")){
                    connection.disconnect();
                    bufferedReader.close();
                    inputStream.close();
                    return ligne;
                }
                stringBuffer.append(ligne);
            }

        connection.disconnect();
        bufferedReader.close();
        inputStream.close();
        }catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 



Partie 5 : Conclusion

 

Voilà je pense que çà été pour ce tutoriel. Il n’est pas très long à reproduire.

Bon courage…

 

5 Responses

  1. attar oussama

    si on veut utiliser shoutcast pour le radio qu’est qu’on doit faire . merci

  2. BAH

    Je trouve très intéressant ce tutoriel!!!! mais ma question est comment connaitre l’URL streaming de n’importe quelle radio? Merci d’avance

  3. el-kosovar

    bonjour, très bon tuto je voulait savoir si il est possible de faire un copier coler a partir de ton tuto ???
    merci de me répondre stp, et comment recopier les plyliste m3u ( pour tv français et kosovar ) au dossier android pour un démodulateur sogno smart tv 300s android.
    svp aidez moi je vien de l’achter il y a 1 semaines..
    et apparemment sa marche bien un amie a mon cousin lui a fait et sa marche méme les chaines payants…
    autre question svp et si je fait un backup de l’appareille de mon cousin et que je la mes dans mon appareille en sachent que sais les méme sa peut marcher ??? merci de me repondre svp et 1001 excuse pour mes fautes d’orthographe.
    si vous souhaiter je peux vous laisser mon mail mes svp aidez moi a creer 1er playlis m3u pour chaines albanais kosoar et 2em playlist m3u chaines français 1001 merci pour votre aide

    a enfait sa ne marche pas comme la dreambox dm500s car la je conneser impeux mes la je suis larguer il y a pas un style dcc qui marche avec dm500s en ftp ooou de se style merci de me dire le quelle logiciel et le meilleur et en francais si possible merci a bientot

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Click to Insert Smiley

SmileBig SmileGrinLaughFrownBig FrownCryNeutralWinkKissRazzChicCoolAngryReally AngryConfusedQuestionThinkingPainShockYesNoLOLSillyBeautyLashesCuteShyBlushKissedIn LoveDroolGiggleSnickerHeh!SmirkWiltWeepIDKStruggleSide FrownDazedHypnotizedSweatEek!Roll EyesSarcasmDisdainSmugMoney MouthFoot in MouthShut MouthQuietShameBeat UpMeanEvil GrinGrit TeethShoutPissed OffReally PissedMad RazzDrunken RazzSickYawnSleepyDanceClapJumpHandshakeHigh FiveHug LeftHug RightKiss BlowKissingByeGo AwayCall MeOn the PhoneSecretMeetingWavingStopTime OutTalk to the HandLoserLyingDOH!Fingers CrossedWaitingSuspenseTremblePrayWorshipStarvingEatVictoryCurseAlienAngelClownCowboyCyclopsDevilDoctorFemale FighterMale FighterMohawkMusicNerdPartyPirateSkywalkerSnowmanSoldierVampireZombie KillerGhostSkeletonBunnyCatCat 2ChickChickenChicken 2CowCow 2DogDog 2DuckGoatHippoKoalaLionMonkeyMonkey 2MousePandaPigPig 2SheepSheep 2ReindeerSnailTigerTurtleBeerDrinkLiquorCoffeeCakePizzaWatermelonBowlPlateCanFemaleMaleHeartBroken HeartRoseDead RosePeaceYin YangUS FlagMoonStarSunCloudyRainThunderUmbrellaRainbowMusic NoteAirplaneCarIslandAnnouncebrbMailCellPhoneCameraFilmTVClockLampSearchCoinsComputerConsolePresentSoccerCloverPumpkinBombHammerKnifeHandcuffsPillPoopCigarette