GithubHelp home page GithubHelp logo

danielr2001 / flutter_exoplayer Goto Github PK

View Code? Open in Web Editor NEW
48.0 9.0 24.0 507 KB

Amazing Flutter package for playing audio with a lot of features to offer.

Home Page: https://pub.dev/packages/flutter_exoplayer

License: MIT License

Java 67.74% Objective-C 1.29% Dart 30.31% Ruby 0.65%
flutter audio audio-player player foreground-service exoplayer

flutter_exoplayer's Introduction

Flutter_exoplayer

A Flutter plugin that let's you play multiple audio files simultaneously with an option to choose if to play in background or as a forground service, for now works only for Android.

Why pick us

Flutter_exoplayer uses the Java ExoPlayer library, which unlike Android's MediaPlayer offers fast audio buffering, especially when using playlists. All thanks to the ExoPlayer's ConcatenatingMediaSource that let's you use an audio list that allways buffers the next audios. This feature of the ExoPlayer let's you play playlists very smoothly.

Moreover Flutter_exoplayer offers many features such as:

  • Providing realtime player states (PLAYING, PAUSED, STOPPED, RELEASED etc').
  • Run unlimited count of audios simultaneously.
  • Providing audio Session ID for visualizers.
  • It has 2 options for audio playing:
    • Foreground - plays the audio in foreground service so Android won't kill the service when app is in background.
    • Background - plays the audio in background (Android can easily kill the service when app is in background), the main use of this option is when app is in foreground.
  • Providing streams such as: current player position, player duration, current player index, player state etc`.

In addition this library is only in it's first steps, any new feature suggestions or bug reports are allways welcome (just submit an issue/PR in my repository), only in this way we can make this library better!

Install

just add this dependency in your pubsec.yaml file:

  dependencies:
    flutter_exoplayer: ^0.6.1

Support us

If you find a bug or a feature you want to be added to this library go to the github repository Github, there you can add a new issue and tell us about the bug/feature, and if you think you can fix/add by yourself I would love to get a PR from you.

Usage

An AudioPlayer instance can play a single audio at a time. To create it, simply call the constructor:

  AudioPlayer audioPlayer = AudioPlayer();

You can create multiple instances to play audio simultaneously, but only if you choose playerMode: PlayerMode.BACKGROUND, because android can't run two similar services.

For all methods that return a Future<Result>: that's the status of the operation (Result is an enum which contains 3 options: SUCCESS, FAIL and ERROR). If SUCCESS, the operation was successful, If FAIL, you tried to call audio conrolling methods on released audio player (this status is never returned when calling play or playAll). Otherwise it's the platform native ERROR code.

Logs are disable by default! To debug, run:

  AudioPlayer.logEnabled = true;

Playing Audio

To play audio you have two options:

  1. play single audio.
  2. play playlist.
  • play single audio.
  String url = "URL";
  audioPlayer.play(url);
  • play playlist.
  List<String> urls = ["URL1","URL2","URL3"];
  audioPlayer.playAll(urls);

The url you pass can be either local direction or network url.

By default the player is set to play in background (Android system can easily kill the Audio player when app is in background), if Player mode is set to FOREGROUND then you need to also pass audioObject instance for the foreground notification, respectAudioFocus is set to false (if your app is respectiong audio focus it will pause when other app get's audio focus and duck if other app getting temporary access of audio focus), repeatMode is also set by default to false (every audio source will play only once), by default the volume is set to max (1.0), the index of the audio that you you want to start with by default is set to 0. To change one or more of this parameters you need to just pass them to play method.

  final Result result = await audioPlayer.play(url,
      repeatMode: true,
      respectAudioFocus: true,
      playerMode: PlayerMode.FOREGROUND,
      audioObject: audioObject);
  if (result == Result.ERROR) {
    print("something went wrong in play method :(");
  }
  final Result result = await audioPlayer.playAll(urls,
      repeatMode: true,
      respectAudioFocus: true,
      playerMode: PlayerMode.FOREGROUND,
      audioObjects: audioObjects);
  if (result == Result.ERROR) {
    print("something went wrong in playAll method :(");
  }

Controlling

After you call play you can control you audio with pause, resume, stop, release, next, previous and seek methods.

  • Pause: Will pause your audio and keep the position.
  final Result result = await audioPlayer.pause();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in pause :(");
  }
  • Resume: Will resume your audio from the exact position it was paused on.
  final Result result = await audioPlayer.resume();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in resume :(");
  }
  • Stop: Will stop your audio and restart it position.
  final Result result = await audioPlayer.stop();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in stop :(");
  } 
  • Release: Will release your audio source from the player (you need to call play again).
  final Result result = await audioPlayer.release();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in release :(");
  }
  • Next: Will play the next song in the playlist or if playing single audio it will restart the current.
  final Result result = await audioPlayer.next();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in next :(");
  }
  • Previous: Will play the previous song in the playlist or if playing single audio it will restart the current.
  final Result result = await audioPlayer.previous();
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in previous :(");
  }
  • seekPosition: Will seek to the position you set.
  final Result result = await audioPlayer.seekPosition(_duration));
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in seek :(");
  }
  • seekIndex: Will seek to the index in the playlist you set.
  final Result result = await audioPlayer.seekIndex(index));
  if (result == Result.FAIL) {
    print(
        "you tried to call audio conrolling methods on released audio player :(");
  } else if (result == Result.ERROR) {
    print("something went wrong in seekIndex :(");
  }

Notification Customization

When playing in PlayerMode.FOREGROUND then the player will show foreground notification, You can customize it in the AudioObject thing like priority/ background color / what actions to show and etc'.

NotificationDefaultActions represents the actions you want to show with your notification (previous, play/pause, next), you have the option to choose between: NONE - only play/pause, PREVIOUS - previous and play/pause, NEXT - next and play/pause, and ALL - that include all actions.

NotificationCustomActions represents the custom actions you want to show with your notification (like, download etc'), you have the option to choose between: DISABLED - show no custom icons, ONE - show one custom icon, TWO - show two custom icons. The callback of this actions is returned via onNotificationActionCallback (CUSTOM1 is the left action, as CUSTOM2 is the right action). If you chose ONE\TWO you have to provide the resource for the icon inside APP_NAME\android\app\src\main\res\drawable, the resource needs to be vector image. The names of the files need to be: "ic_custom1" for the left custom action and "ic_custom2" for the right action.

Attention! If you choose to show the custom actions you have to follow the instructions above! the file names needs to be as the instructions say and their location too.

NotificationActionCallbackMode is a mode that lets you choose between two options: DEFAULT or CUSTOM, this parameter decides if you will recieve action callback (CUSTOM) or not (DEFAULT) when user taps on the action via onNotificationActionCallback stream, and then you can make custom action for your taste. If set to DEFAULT then the action will do only as the action name says (PLAY -> play, PREVIOUS -> play previous etc`).

Attention! You need to place your app icon or the icon you want to show in the APP_NAME\android\app\src\main\res\drawable folder (you can drop multiple icons there), if you won`t do so your app will crash because android require a small icon for notification.

  AudioObject audioObject = AudioObject(
      smallIconFileName: "your icon file name",
      title: "title",
      subTitle: "artist",
      largeIconUrl: "local or network image url",
      isLocal: false,
      notificationDefaultActions: NotificationDefaultActions.ALL,
      notificationCustomActions: NotificationCustomActions.TWO,
      );

Streams

The AudioPlayer supports subscribing to events like so:

Duration Event

This event returns the duration of the file, when it's available (it might take a while because it's being downloaded or buffered).

  audioPlayer.onDurationChanged.listen((Duration d) {
    print('Max duration: $d');
    setState(() => duration = d);
  });

Position Event

This Event updates the current position of the audio. You can use it to make a progress bar, for instance.

  audioPlayer.onAudioPositionChanged.listen((Duration  p) {
    print('Current position: $p');
    setState(() => position = p);
  });

State Event

This Event returns the current player state. You can use it to show if player playing, or stopped, or paused.

  audioPlayer.onPlayerStateChanged.listen((PlayerState s) {
    print('Current player state: $s');
    setState(() => plaVyerState = s);
  });

Completion Event

This Event is called when the audio finishes playing (in playAll mode it fires only when all playlist finishes playing). If repeatMode is set yo true then this event is never fired. It does not fire when you interrupt the audio with pause or stop. COMPLETED state acts just like PAUSED state with seek to the first audio and position 0, and can restart the audio player with resume/play/playAll.

  audioPlayer.onPlayerCompletion.listen((event) {
    print('Current player is completed');
  });

Audio Session ID Event

This Event is called when audio session id is changed.

  audioPlayer.onAudioSessionIdChange.listen((audioSessionId) {
      print("audio Session Id: $audioSessionId");
  });

Notification Action Event

This Event is called when the user taps on one of the notification actions, then the stream will return the action name of the action that the user has clicked on.

  audioPlayer.onNotificationActionCallback.listen((notificationActionName) {
    //do something
  });

Audio Index Event

This Event is called when the current player audio index is changed (new audio is being played).

  audioPlayer.onCurrentAudioIndexChanged.listen((index) {
      setState(() {
        currentIndex = index;
      });
    });

Error Event

This is called when an unexpected ERROR is thrown in the native code.

  audioPlayer.onPlayerError.listen((msg) {
    print('audioPlayer ERROR : $msg');
    setState(() {
      playerState = PlayerState.stopped;
      duration = Duration(seconds: 0);
      position = Duration(seconds: 0);
    });
  });

Supported Formats

You can check a list of supported formats below:

IOS implementation

If you have the time and want to implement this library on IOS, i would love to get PR, and hopefully add your PR to my library.

Credits

This project was originally a fork of luanpotter's audioplayers that was also originally a fork of rxlabz's audioplayer, but since we have diverged and added more features.

Thanks for @rxlabz and @luanpotter for the amazing work!

flutter_exoplayer's People

Contributors

bta24 avatar danielr2001 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_exoplayer's Issues

Some features and questions

Thanks Daniel for your nice work .
the notification area is very awesome .
also I have some questions.
1 - can we controll it with headphone ?
2 - if other apps play audio , does it pause the song for example handle phone call interruptions ?
3 - does it support ios completely ?
4 - does it support lock screen controls ?

Crashes after close playing app in foreground mode

`Fatal Exception: java.lang.RuntimeException: Unable to start service danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer@67be8e6 with Intent { act=com.daniel.exoPlayer.action.pause flg=0x10000000 cmp=com.example.app/danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer bnds=[564,1137][708,1281] }: java.lang.NullPointerException: Attempt to invoke virtual method 'd.a.c.a d.a.e.a.d()' on a null object reference
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3698)
       at android.app.ActivityThread.access$1600(ActivityThread.java:205)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1687)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:280)
       at android.app.ActivityThread.main(ActivityThread.java:6706)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)`

Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'd.a.c.a d.a.e.a.d()' on a null object reference
       at danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer.onStartCommand(ForegroundAudioPlayer.java:151)
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3679)
       at android.app.ActivityThread.access$1600(ActivityThread.java:205)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1687)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:280)
       at android.app.ActivityThread.main(ActivityThread.java:6706)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

If close playing app in foreground mode, the app crashed and show a message "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: danielr2001/audioplayer. Response ID: 0"

And when start again app not play.
The message "Can't start more than 1 service at a time, to stop service call release method"

how to play youtube video

need some more feature

  1. youtube video play
  2. with quality option
  3. double tic (fast forward and backward)
  4. speedo meter

Null when getURL() invoked

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String danielr2001.audioplayer.models.AudioObject.getUrl()' on a null object reference
E/AndroidRuntime(13097): at danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer.initExoPlayer(ForegroundAudioPlayer.java:215)
E/AndroidRuntime(13097): at danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer.play(ForegroundAudioPlayer.java:238)

That's the error that show up everytime i want to play a song, thank you!
It's foreground mode

notification title

hi,
please i need to pass a dynamic title to audio notification on foreground mode

IllegalSeekPositionException

Since today this error occurs sometimes and the app crashes.


FATAL EXCEPTION: main
E/AndroidRuntime(11422): com.google.android.exoplayer2.IllegalSeekPositionException
E/AndroidRuntime(11422): 	at com.google.android.exoplayer2.ExoPlayerImpl.seekTo(ExoPlayerImpl.java:300)
E/AndroidRuntime(11422): 	at com.google.android.exoplayer2.SimpleExoPlayer.seekTo(SimpleExoPlayer.java:964)
E/AndroidRuntime(11422): 	at danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer.initExoPlayer(ForegroundAudioPlayer.java:169)
E/AndroidRuntime(11422): 	at danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer.playAll(ForegroundAudioPlayer.java:210)
E/AndroidRuntime(11422): 	at danielr2001.audioplayer.AudioPlayerPlugin$1.onServiceConnected(AudioPlayerPlugin.java:74)
E/AndroidRuntime(11422): 	at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1948)
E/AndroidRuntime(11422): 	at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1980)
E/AndroidRuntime(11422): 	at android.os.Handler.handleCallback(Handler.java:883)
E/AndroidRuntime(11422): 	at android.os.Handler.dispatchMessage(Handler.java:100)
E/AndroidRuntime(11422): 	at android.os.Looper.loop(Looper.java:214)
E/AndroidRuntime(11422): 	at android.app.ActivityThread.main(ActivityThread.java:7356)
E/AndroidRuntime(11422): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(11422): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
E/AndroidRuntime(11422): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)


Feature request: playback rate

I have used this library and like it a lot. An additional feature it could have is the ability to control the playback rate of the audio file.

Supporting downloads

It would be nice if the library could support downloads and offline playback with ExoPlayer.

Invalid Notification icon

Hello,

I tried to use your plugin to show a notification while playing audiofiles but the app crashes with the following stacktrace:

Couldn't find a unique registered media button receiver in the given context.
I/art     (10118): Do partial code cache collection, code=22KB, data=28KB
I/art     (10118): After code cache collection, code=20KB, data=27KB
I/art     (10118): Increasing code cache capacity to 128KB
I/ExoPlayerImpl(10118): Init e14189 [ExoPlayerLib/2.10.3] [hammerhead, Nexus 5, LGE, 25]
W/System  (10118): ClassLoader referenced unknown path: /system/framework/tcmclient.jar
I/flutter (10118): PlayerState.BUFFERING
W/VideoCapabilities(10118): Unrecognized profile 2130706433 for video/avc
I/VideoCapabilities(10118): Unsupported profile 4 for video/mp4v-es
W/AudioCapabilities(10118): Unsupported mime audio/ac3
W/AudioCapabilities(10118): Unsupported mime audio/alac
W/AudioCapabilities(10118): Unsupported mime audio/x-ape
W/AudioCapabilities(10118): Unsupported mime audio/ffmpeg
W/AudioCapabilities(10118): Unsupported mime audio/dts
W/AudioCapabilities(10118): Unsupported mime audio/mpeg-L2
W/AudioCapabilities(10118): Unsupported mime audio/vnd.rn-realaudio
W/AudioCapabilities(10118): Unsupported mime audio/x-ms-wma
W/VideoCapabilities(10118): Unsupported mime video/divx
W/VideoCapabilities(10118): Unsupported mime video/x-flv
W/VideoCapabilities(10118): Unsupported mime video/vnd.rn-realvideo
W/VideoCapabilities(10118): Unsupported mime video/vc1
W/VideoCapabilities(10118): Unsupported mime video/ffmpeg
W/VideoCapabilities(10118): Unsupported mime video/x-ms-wmv
E/ExoPlayerPlugin(10118): **Failed loading image!**
I/OMXClient(10118): MuxOMX ctor
I/ACodec  (10118): codec does not support config priority (err -2147483648)
D/AndroidRuntime(10118): Shutting down VM
I/flutter (10118): track changed to: 0
E/AndroidRuntime(10118): FATAL EXCEPTION: main
E/AndroidRuntime(10118): Process: myAppPackageName, PID: 10118
E/AndroidRuntime(10118): java.lang.IllegalArgumentException: **Invalid notification** (no valid small icon): Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 actions=3 vis=PRIVATE)
E/AndroidRuntime(10118): 	at android.app.NotificationManager.notifyAsUser(NotificationManager.java:313)
E/AndroidRuntime(10118): 	at android.app.NotificationManager.notify(NotificationManager.java:291)
E/AndroidRuntime(10118): 	at android.app.NotificationManager.notify(NotificationManager.java:275)
E/AndroidRuntime(10118): 	at danielr2001.audioplayer.notifications.MediaNotificationManager.showNotification(MediaNotificationManager.java:141)
E/AndroidRuntime(10118): 	at danielr2001.audioplayer.notifications.MediaNotificationManager.access$100(MediaNotificationManager.java:26)
E/AndroidRuntime(10118): 	at danielr2001.audioplayer.notifications.MediaNotificationManager$1.processFinish(MediaNotificationManager.java:213)
E/AndroidRuntime(10118): 	at danielr2001.audioplayer.notifications.LoadImageFromUrl.onPostExecute(LoadImageFromUrl.java:72)
E/AndroidRuntime(10118): 	at danielr2001.audioplayer.notifications.LoadImageFromUrl.onPostExecute(LoadImageFromUrl.java:19)
E/AndroidRuntime(10118): 	at android.os.AsyncTask.finish(AsyncTask.java:667)
E/AndroidRuntime(10118): 	at android.os.AsyncTask.-wrap1(AsyncTask.java)
E/AndroidRuntime(10118): 	at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:684)
E/AndroidRuntime(10118): 	at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(10118): 	at android.os.Looper.loop(Looper.java:154)
E/AndroidRuntime(10118): 	at android.app.ActivityThread.main(ActivityThread.java:6186)
E/AndroidRuntime(10118): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10118): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
E/AndroidRuntime(10118): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
D/AudioTrack(10118): Client defaulted notificationFrames to 3675 for frameCount 11025

This is the AudioNotification like in your example:

final AudioNotification audioNotification = new AudioNotification(
    smallIconFileName: "ic_launcher",
    title: "audioTitle",
    subTitle: "artist1",
    largeIconUrl: 'graphics/testimage.png',
    isLocal: false,
    notificationActionMode: NotificationActionMode.ALL,
  );

audio is playing and was playing with the audioplaers plugin which is the base of this so there is only the problem with the notification itself.

Releasing the player

Hi, how is going? I have an problem with my app and i did the test also on the project of example and happens the same error.

If you start to play some audio (Android device) and press the back button (the right arrow on the navigation bar) the app will be killed but the audio keeps playing, and when you open the app again the audio is still running but you can't recover the status of the playing audio. I'm trying to kill the player on onStop or onDestroy but the release, dispose or stop does not work. And i am also trying to recover some data to stop the audio or in the best case load again the seek bar and keep the thinks rolling.

And the second think is there is any plan for impl the audio media buttons? I did see the comment on the code telling to impl haha.

Thanks a lot.

Android 11 issue

player notification not showing in android 11 and showing only till android 10

One AudioPlayer instance can play multiple audio files (readme says otherwise)

Hi, I have a problem with my AudioPlayer instance being able to play multiple sound files simultaneously. Plugin docs say the following:

An AudioPlayer instance can play a single audio at a time. To create it, simply call the constructor:

AudioPlayer audioPlayer = AudioPlayer();

You can create multiple instances to play audio simultaneously, but only if you choose playerMode: PlayerMode.BACKGROUND, because android can't run two similar services.


I tried playing a song on an instance that is already playing, and instead of stopping the current song and starting another it keeps playing and just starts another one. Is this a bug? Shouldn't one instance stop the current playback and start another one, and if I want simultaneous playback I use a second instance?

MediaNotification custom event/method

Can we have the button on notification have custom event? I want the user to select what to play, so I can't use playAll() method to enable next prev.
my method is something like this

 play(index) async {
    final Result result = await exoPlayer.play(
      radioList[index].streamUrl,
      repeatMode: true,
      respectAudioFocus: false,
      playerMode: PlayerMode.FOREGROUND,
      audioNotification: AudioNotification(        
            smallIconFileName: "ic_launcher",
            title: radioList[index].title,
            subTitle: "RADIOSUKA",
            largeIconUrl: radioList[index].logo,
            isLocal: false,
            notificationMode: NotificationMode.BOTH),
    );
    if (result == Result.error) {
      print("something went wrong in play method :(");
    }
  }

  playNext() {
    ++currentIndex;
    if (isPlaying) stop();
    play(currentIndex);
  }

  playPrev() {
    if (isPlaying) stop();
    --currentIndex;
    play(currentIndex);
  }

thanks

After the broadcast Notification of no completion of broadcast

播放完成后收不到完成的通知
After the broadcast Notification of no completion of broadcast

 // 监听是否播放完成事件
    _playerCompleteSubscription =
        audioPlayer.onPlayerCompletion.listen((event) {
      print("complete");
    });

请问我的播放列表不是一个播放地址集合,而是ID的集合,然后通过ID获取播放地址,我应该如何处理
Please ask my playlist is not a collection of playback addresses, but a collection of IDs, and then get the playback address through the ID, how should I handle it

example does not play

Init 71a6f1d [ExoPlayerLib/2.10.3] [generic_x86_arm, AOSP on IA Emulator, Google, 28]
I/flutter ( 7054): PlayerState.BUFFERING
E/ExoPlayerImplInternal( 7054): Source error.
E/ExoPlayerImplInternal( 7054): com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect to http://dls.tabanmusic.com/music/1398/09/13/Reza-Bahram-Del-128.mp3
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:287)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:257)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:83)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:934)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:381)
E/ExoPlayerImplInternal( 7054): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal( 7054): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal( 7054): 	at java.lang.Thread.run(Thread.java:764)
E/ExoPlayerImplInternal( 7054): Caused by: java.io.IOException: Cleartext HTTP traffic to dls.tabanmusic.com not permitted
E/ExoPlayerImplInternal( 7054): 	at com.android.okhttp.HttpHandler$CleartextURLFilter.checkURLPermitted(HttpHandler.java:115)
E/ExoPlayerImplInternal( 7054): 	at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:458)
E/ExoPlayerImplInternal( 7054): 	at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:556)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:443)
E/ExoPlayerImplInternal( 7054): 	at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:285)
E/ExoPlayerImplInternal( 7054): 	... 7 more
I/flutter ( 7054): PlayerState.STOPPED

this is the error .

Plugin stuck on PlayerState.RELEASED

Hi, I'm using your plugin and it works great but I'm having a bug and I do not know if is the plugin or my flutter code.
When I open the app the first time the plugin work great.
Then I close the app and reopen it but the plugin get stuck on PlayerState.RELEASED.
Then I close the app and reopen it again and the plugin works great.
The opening process goes like this:
1: works
2: doesn't work
3: works
4: doesn't work
etc...
Also the plugin give me Result.SUCCESS all the times.
Sorry if Im not clear because english is not my first language.
Edit: This is the logcat
AudioPlayerPlugin: Can't start more than 1 service at a time, to stop service call release method

No Such Method 'play'

when I run this code:
Future<void> play(String localFiles) async { await _player.play( "https://www.bensound.org/bensound-music/bensound-onceagain.mp3" ); }

I get this error:

Tried calling: play("https://www.bensound.org/bensound-music/bensound-onceagain.mp3") E/flutter ( 7512): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5) E/flutter ( 7512): #1 QuickSongAudioPlayer.play (package:quick_song/audio_player.dart:101:21) E/flutter ( 7512): <asynchronous suspension> E/flutter ( 7512): #2 _HomePageState.build.<anonymous closure>.<anonymous closure> (package:quick_song/home_widget.dart:134:46) E/flutter ( 7512): #3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:654:14) E/flutter ( 7512): #4 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:729:32) E/flutter ( 7512): #5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24) E/flutter ( 7512): #6 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:365:11) E/flutter ( 7512): #7 TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:312:7) E/flutter ( 7512): #8 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27) E/flutter ( 7512): #9 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:222:20) E/flutter ( 7512): #10 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22) E/flutter ( 7512): #11 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7) E/flutter ( 7512): #12 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7) E/flutter ( 7512): #13 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7) E/flutter ( 7512): #14 _rootRunUnary (dart:async/zone.dart:1136:13) E/flutter ( 7512): #15 _CustomZone.runUnary (dart:async/zone.dart:1029:19) E/flutter ( 7512): #16 _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7) E/flutter ( 7512): #17 _invoke1 (dart:ui/hooks.dart:263:10) E/flutter ( 7512): #18 _dispatchPointerDataPacket (dart:ui/hooks.dart:172:5) E/flutter ( 7512):

Any help appreciated

Support custom HTTP headers.

Hey!
I've been searching for audio player that supports custom HTTP headers in Flutter. I haven't found one yet. But this project seems promising. The idea of ​​buffering audio files for playlists bought me! But the repositories from which this project is forked suffer from this problem. The implementation of this feature would certainly bring a group of new users. Token authorization is very common in mobile apps.

m3u8 Stream on Android

Radio Stream URL: https://r3.rocketcdn.com/slowturk/abr/slowturk/256/chunks.m3u8

I'm getting this error after play button clicked.

This error occured also on your example app.

I/ExoPlayerImpl(19182): Init 331b4222 [ExoPlayerLib/2.10.3] [generic_x86, Android SDK built for x86, unknown, 22]
I/flutter (19182): PlayerState.BUFFERING
E/ExoPlayerImplInternal(19182): Source error.
E/ExoPlayerImplInternal(19182): com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (MatroskaExtractor, FragmentedMp4Extractor, Mp4Extractor, Mp3Extractor, AdtsExtractor, Ac3Extractor, TsExtractor, FlvExtractor, OggExtractor, PsExtractor, WavExtractor, AmrExtractor, Ac4Extractor) could read the stream.
E/ExoPlayerImplInternal(19182): at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractorHolder.selectExtractor(ProgressiveMediaPeriod.java:1059)
E/ExoPlayerImplInternal(19182): at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:947)
E/ExoPlayerImplInternal(19182): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:381)
E/ExoPlayerImplInternal(19182): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E/ExoPlayerImplInternal(19182): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E/ExoPlayerImplInternal(19182): at java.lang.Thread.run(Thread.java:818)
I/flutter (19182): PlayerState.STOPPED

Tested on Android Simulator API 21
Tested on Huawei P20 Pro API 28

When I use flutter_exoplayer with flutter-downloader plugin will cause the application to crash directly

当我使用flutter_exoplayer与flutter-downloader插件一起使用时会导致应用直接闪退

When I use flutter_exoplayer with flutter-downloader plugin will cause the application to crash directly

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.app.Activity.getApplicationContext()' on a null object reference
	at danielr2001.audioplayer.AudioPlayerPlugin.<init>(AudioPlayerPlugin.java:93)
	at danielr2001.audioplayer.AudioPlayerPlugin.registerWith(AudioPlayerPlugin.java:87)
	at io.flutter.plugins.GeneratedPluginRegistrant.registerWith(GeneratedPluginRegistrant.java:33)
	at xyz.a5in.a5yin.MusicApplication.registerWith(MusicApplication.java:26)
	at vn.hunghd.flutterdownloader.DownloadWorker.startBackgroundIsolate(DownloadWorker.java:124)
	at vn.hunghd.flutterdownloader.DownloadWorker.access$000(DownloadWorker.java:59)
	at vn.hunghd.flutterdownloader.DownloadWorker$1.run(DownloadWorker.java:97)
	at android.os.Handler.handleCallback(Handler.java:883)
	at android.os.Handler.dispatchMessage(Handler.java:100)
	at android.os.Looper.loop(Looper.java:227)
	at android.app.ActivityThread.main(ActivityThread.java:7523)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)


Back button stops audio player

Thanks for all the hard work. After start playing audio player, hitting back button closes the music. How can make it to run in the background?

Cache Audio for offline use

Is it possible to cache remotefiles on the user device to play them offline if the connection with the internet fails. Something like spotify does without downloading the whole file everytime ?

Update notification adhoc?

I was looking through the code, but cannot find a way to update the Notification dynamically.
This would be useful for looking up cover art, and/or update title/artist on the fly.

Is it possible to update the Notification metadata dynamically (while the player is playing)?

No reproduce nada

No obtengo error pero tampoco ninguna reproduccion !
Estoy en Windows trabajando alguan idea ?

Foreground

hello how to put in the foreground if we click on sample 1.
I would like to have only one button

Issue with Playing Foreground with playlist and specific index

@danielR2001

I am trying to play, play list. I am trying to store the last played audio and position and trying to seek with the following :

      final Result result = await _audioPlayer.playAll(
        urls,
        index: index,
        position: duration,
        repeatMode: false,
        respectAudioFocus: true,
        playerMode: PlayerMode.BACKGROUND,
        audioNotifications: audioNotifications,
      );

it works on the debug mode. but on release mode(apk), it fails with the following error. Please let me know how to fix this issue. any insight will be very helpful.

17336
    Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ad023f4 u0 com.xyz.pyx/danielr2001.audioplayer.audioplayers.ForegroundAudioPlayer}
    Load: 1.82 / 1.82 / 2.19
    CPU usage from 0ms to 6715ms later (2020-03-04 19:25:40.086 to 2020-03-04 19:25:46.801) with 99% awake:

Notification Customization

Hi I would like to ask if its possible to implement the following customizations.

  • A way to show the current progress of the audio in the notification or hide the progressbar and show a text with the current time.
  • A way to show seekPosition buttons to skip for example 5 seconds in the notification.
  • Customize the Icons for example outline icons or fontawesome icons etc.
  • Dismiss the notification if the audio was stopped with _audioPlayer.stop()
    It is dismissed but restarts after some milliseconds and displays the notification again and if I try to play again it crashes because the player was released after the stop methode.
  • A way to decide between expanded or collapsed notification style if possible.
  • If subtitle is empty -> show the title centered vertical on the collapsed notification

OnPlayerCompletion unavailable

When the playback status changes to Completed, the OnPlayerCompletion method should be called back at the same time

https://github.com/danielR2001/flutter_exoplayer/blob/master/android/src/main/java/danielr2001/audioplayer/AudioPlayerPlugin.java#L555-L555

channel.invokeMethod("audio.onPlayerCompletion", buildArguments(audioplayer.getPlayerId(), 4));

https://github.com/danielR2001/flutter_exoplayer/blob/master/lib/audioplayer.dart#L476-L476

case 'audio.onPlayerCompletion':
  player._completionController.add(player._playerState);
  break;

seekIndex fails silently without error message

When I call seekIndex,after a playlist has been loaded into the AudioPlayer, the seekIndex function fails silently without error message.
try{ seekIndex(index); }on ErrorHint catch(e){ player.seekIndex(index); }finally{ play(localSongsPlaylist); }
Is there a reason why it always skips the try clause?
Any help appreciated.

RemoteService Exception

Hi, crashlytics is showing a remote service exception. Roughly 6-7% users are affected. How can I resolve this?

Screenshot 2020-04-28 at 8 14 27 PM

Support URL Streaming

I have a query when I try to put a streaming url the app closes

LOG
`
D/CCodec (16783): client requested max input size 4096, which is smaller than what component recommended (8192); overriding with component recommendation.
W/CCodec (16783): This behavior is subject to change. It is recommended that app developers double check whether the requested max input size is in reasonable range.
D/CCodec (16783): setup formats input: AMessage(what = 0x00000000) = {
D/CCodec (16783): int32_t channel-count = 2
D/CCodec (16783): int32_t max-input-size = 8192
D/CCodec (16783): string mime = "audio/mpeg"
D/CCodec (16783): int32_t sample-rate = 44100
D/CCodec (16783): int64_t durationUs = 0
D/CCodec (16783): } and output: AMessage(what = 0x00000000) = {
D/CCodec (16783): int32_t channel-count = 2
D/CCodec (16783): string mime = "audio/raw"
D/CCodec (16783): int32_t sample-rate = 44100
D/CCodec (16783): }
I/CCodec (16783): state->set(STARTING)
W/Codec2Client(16783): query -- param skipped: index = 1342179345.
W/Codec2Client(16783): query -- param skipped: index = 2415921170.
2
E/FMQ (16783): grantorIdx must be less than 3
D/CCodecBufferChannel(16783): [c2.sec.mp3.decoder#519] Created input block pool with allocatorID 16 => poolID 17 - OK (0)
I/CCodecBufferChannel(16783): [c2.sec.mp3.decoder#519] Created output block pool with allocatorID 16 => poolID 49 - OK
D/CCodecBufferChannel(16783): [c2.sec.mp3.decoder#519] Configured output block pool ids 49 => OK
I/CCodec (16783): state->set(RUNNING)
E/ion (16783): ioctl c0044901 failed with code -1: Inappropriate ioctl for device
2
E/FMQ (16783): grantorIdx must be less than 3
D/AudioTrack(16783): setVolume(1.000000, 1.000000) pid : 16783
D/AndroidRuntime(16783): Shutting down VM
E/AndroidRuntime(16783): FATAL EXCEPTION: main
E/AndroidRuntime(16783): Process: com.nextweb.fmplay101uno, PID: 16783
E/AndroidRuntime(16783): java.lang.IllegalArgumentException: Invalid notification (no valid small icon): Notification(channel=Playback shortcut=null contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 category=transport actions=5 vis=PRIVATE semFlags=0x0 semPriority=0 semMissedCount=0)
E/AndroidRuntime(16783): at android.app.NotificationManager.fixNotification(NotificationManager.java:622)
E/AndroidRuntime(16783): at android.app.NotificationManager.notifyAsUser(NotificationManager.java:601)
E/AndroidRuntime(16783): at android.app.NotificationManager.notify(NotificationManager.java:535)
E/AndroidRuntime(16783): at android.app.NotificationManager.notify(NotificationManager.java:511)
E/AndroidRuntime(16783): at danielr2001.audioplayer.notifications.MediaNotificationManager.showNotification(MediaNotificationManager.java:174)
E/AndroidRuntime(16783): at danielr2001.audioplayer.notifications.MediaNotificationManager.access$100(MediaNotificationManager.java:27)
E/AndroidRuntime(16783): at danielr2001.audioplayer.notifications.MediaNotificationManager$1.processFinish(MediaNotificationManager.java:253)
E/AndroidRuntime(16783): at danielr2001.audioplayer.notifications.LoadImageFromUrl.onPostExecute(LoadImageFromUrl.java:72)
E/AndroidRuntime(16783): at danielr2001.audioplayer.notifications.LoadImageFromUrl.onPostExecute(LoadImageFromUrl.java:19)
E/AndroidRuntime(16783): at android.os.AsyncTask.finish(AsyncTask.java:771)
E/AndroidRuntime(16783): at android.os.AsyncTask.access$900(AsyncTask.java:199)
E/AndroidRuntime(16783): at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
E/AndroidRuntime(16783): at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime(16783): at android.os.Looper.loop(Looper.java:246)
E/AndroidRuntime(16783): at android.app.ActivityThread.main(ActivityThread.java:8506)
E/AndroidRuntime(16783): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(16783): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
E/AndroidRuntime(16783): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
I/Process (16783): Sending signal. PID: 16783 SIG: 9
Lost connection to device.

`

The following dependencies do not satisfy the required version: project ':exoplayer' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.71

I just included dependency into my project and following error encountered:-

The android gradle plugin supports only kotlin gradle plugin version 1.3.10 and higher.
The following dependencies do not satisfy the required version:
project ':exoplayer' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.71

I searched this issue on google, one of them said, "downgrade the android gradle plugin" i.e. downgrade my current android studio, which does not seems to be a good idea.

My specs are:-
Android 3.4 ,
Flutter v1.9.1+hotfix.6
Kotlin version 1.3.50
Gradle version 5.3.6 with gradle build tools version 3.5.0

Would you like to help me out with this ?
Hope to listen from you soon.

AudioPlayer accessed from different pages

Hi @danielR2001 , this is a great plugin you made, it works perfectly. However, I was thinking if the following problem was solvable:
I have an AudioPlayer() object initialized in my Home class to play some audio files but I don't want to dispose of it and create another one when I go to another page (Favourites) to play a different set of audio files because I want to let the user to return to the home page the way they left it. I tried creating another AudioPlayer object in the Favourites page but it gives me this error which doesn't go away when i call the release or dispose method on the Home page AudioPlayer object,
E/AudioPlayer:Can't start more than 1 service at a time, to stop service call release method

Therefore, is it possible to access the same AudioPlayer() object from a different part of my app and play a different set of songs? Do I have to somehow make the AudioPlayer() global?

Any help is very much appreciated.

Unable to trap Errors Contitions

Every single request is giving a Result.Success.

flutter_exoplayer: ^0.6.1

final Result result = await _audioPlayer.play(
url,
repeatMode: true,
respectAudioFocus: false,
playerMode: PlayerMode.BACKGROUND,
);

I can put in a bad URL but it still gives Result.Success.

I've used this library in the past without issues so not sure what is going on now.
Using this
AudioPlayer.logEnabled = true;
doesn't provide anything useful.

I do see exoplayer exceptions thrown and caught in the logs so I can't even put a try catch around the code to catch errors.

I added a listener in initaudioplayer() but this is not even trapped errors.
void _initAudioPlayer() {
_audioPlayer = AudioPlayer();

_playerErrorSubscription = _audioPlayer.onPlayerError.listen((msg) {
  print('audioPlayer ERROR : $msg');
  playerState.value = PlayerState.STOPPED;
  _position = Duration(seconds: 0);
})

Can't see how I will be able to use this library without any way to trap error conditions.

Work with other background isolate plugins

FATAL EXCEPTION: main
E/AndroidRuntime( 6079): Process: com.gerama, PID: 6079
E/AndroidRuntime( 6079): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.app.Activity.getApplicationContext()' on a null object reference
E/AndroidRuntime( 6079): 	at danielr2001.audioplayer.AudioPlayerPlugin.<init>(AudioPlayerPlugin.java:93)
E/AndroidRuntime( 6079): 	at danielr2001.audioplayer.AudioPlayerPlugin.registerWith(AudioPlayerPlugin.java:87)
E/AndroidRuntime( 6079): 	at io.flutter.plugins.GeneratedPluginRegistrant.registerWith(GeneratedPluginRegistrant.java:24)
E/AndroidRuntime( 6079): 	at com.gerama.MyApplication.registerWith(MyApplication.kt:15)
E/AndroidRuntime( 6079): 	at vn.hunghd.flutterdownloader.DownloadWorker.startBackgroundIsolate(DownloadWorker.java:124)
E/AndroidRuntime( 6079): 	at vn.hunghd.flutterdownloader.DownloadWorker.access$000(DownloadWorker.java:59)
E/AndroidRuntime( 6079): 	at vn.hunghd.flutterdownloader.DownloadWorker$1.run(DownloadWorker.java:97)
E/AndroidRuntime( 6079): 	at android.os.Handler.handleCallback(Handler.java:739)
E/AndroidRuntime( 6079): 	at android.os.Handler.dispatchMessage(Handler.java:95)
E/AndroidRuntime( 6079): 	at android.os.Looper.loop(Looper.java:135)
E/AndroidRuntime( 6079): 	at android.app.ActivityThread.main(ActivityThread.java:5254)
E/AndroidRuntime( 6079): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 6079): 	at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime( 6079): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
E/AndroidRuntime( 6079): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
I/Process ( 6079): Sending signal. PID: 6079 SIG: 9
Lost connection to device.

I am using flutter downloader and fireBaseMasseging

Notification with a single audio

Hi

How do I use notification with a single audio

I'm doing it this way but don't display the notification and Android kills the audio

AudioNotification audioObject = AudioNotification(
smallIconFileName: "ic_launcher",
title: "Live",
subTitle: "Live running",
isLocal: false,
notificationDefaultActions: NotificationDefaultActions.ALL,
notificationCustomActions: NotificationCustomActions.TWO,
);

AudioPlayer _audioPlayer = AudioPlayer();

final Result result = await _audioPlayer.play(
"http://stm.painelfoxcasthd.online:7102/;",
repeatMode: true,
respectAudioFocus: true,
audioNotification: audioObject,
playerMode: PlayerMode.FOREGROUND,
);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.