เราจะเพิ่ม Visualizer ใน ExoPlayer ยังไงดีนะ?

Minseo Chayabanjonglerd
Black Lens
Published in
3 min readOct 22, 2019

--

มันไม่ได้ใช้ง่ายเท่าตอนใส่ใน MediaPlayer อ่ะดิ

อันนี้เราลองเขียนขึ้นมาเล่นๆ ว่าถ้าเราเล่นไฟล์เสียงใน ExoPlayer แล้ว มีเจ้า Visualizer มาด้วย เอ้อออ ว่าแต่ ทำยังไงนะ

ก่อนอื่นมาลองอ่านมาลองทำตามบล็อกนี้ก่อนนะ เดี๋ยวจะอธิบายให้ฟัง

ส่วนประกอบหลักๆมี 2 ส่วน คือ Visualizer View และ Player บน Activity/Fragment

Visualizer View ขอเล่าคร่าวๆว่ามันเป็น View Class ตัวนึง ที่แสดง Visualizer ของไฟล์เสียงของเรา ซึ่งอาจจะเขียนเองแบบในบล็อกอ้างอิงด้านบน จะเป็นแบบเส้นๆ หรือใช้ library ก็ได้นะ เลือกได้ว่าอยากออกมาแบบไหน ไม่ต้องวาดเอง เช่น

ส่วน Player ในหน้า Activity/Fragment ที่เราต้องการ ก็ต้องมีเจ้า ExoPlayer อยู่ในนี้ก่อนเนอะ ซึ่งเจ้า ExoPlayer จะแยกเป็น 2 ส่วน คือ View และ Player

ก่อนอื่นแจ้งกันก่อน เราใช้ ExoPlayer version นี้นะ

implementation 'com.google.android.exoplayer:exoplayer:2.10.4'

ซึ่งส่วนของ ExoPlayer View นั้นเราใส่ลงไปใน layout ซึ่งใส่แบบ basic ก่อนค่อยมา custom ทีหลังแล้วกัน โดยเราจะให้เจ้า Visualizer View ไว้ด้านบน Player เพื่อ UX ที่ดี เป็นธรรมชาติ

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.gauravk.audiovisualizer.visualizer.BlastVisualizer
android:id="@+id/playerVisualizer"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:avDensity="0.8"
app:avType="fill"
app:avColor="@color/av_dark_blue"
app:avSpeed="normal"/>

<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="100dp" />

</LinearLayout>

เรามาสร้างเจ้า player ของ ExoPlayer กันเถอะ พร้อมกำหนดค่าต่างๆใน player ของเรา

และก็สร้าง Visualizer ขึ้นมา และนำมาเล่น

ใน listener ของ Visualizer.OnDataCaptureListener เราจะต้อง override 2 functions ด้วยกัน

  • onWaveFormDataCapture อันนี้เราจะได้เจ้า waveform ออกมา
  • onFftDataCapture อันนี้เราจะได้ fft (ใน doc บอกว่า frequency capture) ออกมา

ทั้ง waveform และ fft นั้น จะเป็น ByteArray ทั้งคู่

จริงๆเจ้า visualizer.setDataCaptureListener นั้น parameter แรกนั้นใส่เจ้า object ของ Visualizer.OnDataCaptureListener เพื่อ implement data capture ว่าเอาไปทำอะไรต่อ ถ้าให้แสดงแบบ waveform หรือ fft จากนั้น parameter ต่อมาเป็นค่า rate ส่วนสอง parameter หลังนั้น เป็นการ enable ว่าจะใช้แบบ waveform หรือ fft ดี ซึ่งเราขอแนะนำควรใส่ค่า true แค่อันไหนอันนึงพอจ้า ถ้า false ทั้งคู่มันจะไม่ขึ้นนะเตง ถ้าใส่ true สองอันเราจะเห็น fft เป็นหลักอ่ะ การฟมันใหญ่บัง waveform มิดเชียว

อันแรกเป็น waveform อันต่อมา ไฟไม่ไหม้สงบเน้อ เป็น fft ตัวอย่างใช้ ExoPlayer นะ เอากราฟให้ดูเฉยๆ

แต่แล้วความแตกต่างจาก MediaPlayer คือ exoPlayer.audioSessionId ดันเป็น 0 ซะได้ และ crash ตลอดเลย

ลองมา google กันหน่อย ทำยังไงให้ได้ค่านี้มามากกว่า 0 จนมาเจออันนี้ เราต้อง get audioSessionId จาก AnalyticsListener

exoPlayer.addAnalyticsListener(object : AnalyticsListener {
override fun onAudioSessionId(
eventTime: AnalyticsListener.EventTime?,
audioSessionId: Int
) {
startPlayerVisualizer(audioSessionId)
}
})

เรารับ audioSessionId จากในนี้ ค่ามันก็มากกว่า 0 แล้วหล่ะถ้าลอง debug ดูอะนะ และพอเอาไปรันสุดท้ายได้ผลเหมือน MediaPlayer แล้ว เย้ๆๆๆๆ

สรุปโค้ดทั้งหมดเลยดีกว่า

แต่อัดวิดีโอเดโม่ลำบากมาก เพราะอัดจากโทรศัพท์ก็ไม่ได้ เหมือนอัดเสียงในอัดเสียงอีกที โอ้ยยยย สับสน

อันนี้ใช้ Visualizer ตามบล็อกตัวอย่างเน้อ มันจะเป็นเส้นๆ

สุดท้ายเราปรับให้เจ้า ExoPlayer ของเรา เป็นเนื้อเดียวกับ Visualizer ให้มากกว่านี้หน่อย ซึ่งเราก็ custom player มาอีกทีนึง

เดี๋ยวหาว่าจบไวไป ขอเล่าเรื่อง custom player สั้นๆ จ้า

ก่อนอื่นเพิ่มเจ้า ExoPlayer เข้าไปใน layout ของเราก่อน

<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:controller_layout_id="@layout/layout_player"
app:use_controller="true"
app:auto_show="true"
app:show_timeout="0"
app:hide_on_touch="false" />
  • เราใส่ layout player ที่เรา custom ลงไปใน app:controller_layout_id
  • ส่วนอันนี้เราใส่ไปเพื่อไม่ให้ player มัน hide ตัวเอง
app:use_controller="true"
app:auto_show="true"
app:show_timeout="0"
  • และใส่อันนี้ไปอีกที เพื่อบอกว่า ถ้ามือเราไปโดน player แล้วไม่ต้อง hide เน้อ
app:hide_on_touch="false"

จากนั้นเราก็สร้าง layout custom player ขึ้นมา นามว่า layout_player.xml

ซึ่งเราต้อง override ปุ่ม โดยเรา override id ของปุ่มต่างๆ เช่น ปุ่มเล่น override @+id/exo_play ปุ่มหยุด override @+id/exo_pause ประมาณนี้

หรืออ่านเพิ่มเติมที่นี่

สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

--

--

Minseo Chayabanjonglerd
Black Lens

Android Developer | Content Creator AKA. MikkiPastel | Web2 & Web3 Contributor