Apps foundation

เพิ่มประสิทธิภาพการเล่นสื่อและเพิ่มการเข้าถึงสูงสุดด้วย Jetpack Media3 API

ลำโพง

เกี่ยวกับ

เกี่ยวกับการเสวนา

การใช้งานสื่อคิดเป็นสัดส่วนที่มากของเวลาที่ผู้ใช้ใช้บนอุปกรณ์เคลื่อนที่ ซึ่งต้องอาศัยประสบการณ์การเล่นที่ราบรื่น มีประสิทธิภาพสูง และคำนึงถึงอายุการใช้งานแบตเตอรี่ นอกจากนี้ ผู้ใช้ยังคาดหวังว่าประสบการณ์เหล่านี้จะเปลี่ยนผ่านระหว่างอุปกรณ์ต่างๆ ได้อย่างราบรื่น ตั้งแต่โทรศัพท์ไปจนถึงทีวีใน Living Room และอื่นๆ

สำรวจว่าการอัปเดตสถาปัตยกรรมใน Media3 และ ExoPlayer ช่วยให้การเล่นราบรื่นในอุปกรณ์รูปแบบต่างๆ ได้อย่างไร การพูดคุยจะเน้นไปที่การสร้างอินเทอร์เฟซที่ปรับเปลี่ยนตามอุปกรณ์ด้วยชุดเครื่องมือ Media3 Compose UI, การกำจัดบัฟเฟอร์ในฟีดโดยใช้ PreloadManager ใหม่ และการขยายแอปไปยังหน้าจอ Living Room และ Android XR ด้วย CastPlayer และเสียงรอบทิศทาง

สรุป

  • Media3 UI Compose มีคอมโพสิเบิลเพลเยอร์สำเร็จรูปและองค์ประกอบที่ใช้สร้างสรรค์ที่ปรับแต่งได้ คุณสามารถลบล้างช่องควบคุมที่เฉพาะเจาะจงได้ ขณะเดียวกันก็อนุญาตให้ไลบรารีจัดการสถานะและการเปลี่ยนสถานะของเพลเยอร์
  • CastPlayer ที่เขียนใหม่จะมาแทนที่ Cast SDK แบบสแตนด์อโลน โดยจะใช้อินเทอร์เฟซเพลเยอร์มาตรฐานและมอบสิทธิ์การเล่นระหว่างอินสแตนซ์ ExoPlayer ในเครื่องและอุปกรณ์แคสต์ระยะไกลโดยอัตโนมัติ
  • ไปป์ไลน์การแก้ไขที่สร้างขึ้นใหม่จะแยกออกจาก OpenGL และจัดการบัฟเฟอร์ฮาร์ดแวร์โดยตรง แนวทางแบบ Zero-Copy นี้ช่วยลดภาระในการใช้หน่วยความจำ และการแยกส่วนช่วยให้นักพัฒนาซอฟต์แวร์ผสานรวมเฟรมเวิร์กกราฟิกที่ทันสมัย เช่น WebGPU ได้
  • Media3 เพิ่มการรองรับวิดีโอ Eclipsa และระบบเสียง Eclipsa แบบพร้อมใช้งานทันทีใน Android 17 ขึ้นไป ข้อมูลเมตา HDR ที่กำหนดจะปรับไฮไลต์ให้เข้ากับแสงแวดล้อม ขณะที่เสียงรอบทิศทางจะจัดการการนำเสนอแบบผสมหลายรายการ
  • การเพิ่มประสิทธิภาพในตัว เช่น การลดภาระการประมวลผลเสียง PCM ช่วยลดการใช้พลังงานเมื่อปิดหน้าจอได้สูงสุด 20% CodecDB ที่กำลังจะเปิดตัวใช้เมตริกการเปรียบเทียบเพื่อเลือกการตั้งค่าโปรแกรมเปลี่ยนไฟล์ที่เหมาะสมที่สุดสำหรับอุปกรณ์

แหล่งข้อมูล

Gemini Notebook

หัวข้อ

  • jetpack-media3
  • exoplayer
  • jetpack-compose
  • google-cast
  • การตัดต่อวิดีโอ
  • เสียง
  • ประสิทธิภาพ

ข้อความถอดเสียง

ข้อมูลเบื้องต้นเกี่ยวกับ Jetpack Media3

  • ตกลง

  • ขอต้อนรับทุกคน

  • ฉันชื่อ Toni

  • ฉันเป็นวิศวกรในทีมสื่อของ Android

  • และเราจะพูดคุยกับ Kristina และ Donovan เกี่ยวกับวิธีที่ Jetpack Media3 สามารถช่วยตอบสนองความต้องการด้านสื่อทั้งหมดของคุณบน Android

  • Media3 เป็นไลบรารีสนับสนุนของ Android สำหรับทุกอย่างที่เกี่ยวข้องกับสื่อ ไม่ว่าจะเป็นการเล่นด้วย ExoPlayer, การแชร์สื่อด้วยเซสชันสื่อและ Cast, การแก้ไขสื่อด้วย Transformer หรือการตรวจสอบสื่อ เช่น การแยกเฟรมหรือข้อมูลเมตา

  • Media3 ครอบคลุมพื้นที่ทั้งหมดนี้ในกลุ่มคลังเดียว

  • เนื่องจากเป็นไลบรารีสนับสนุน จึงรวมอยู่ในแอปของคุณ

  • ซึ่งหมายความว่าเราสามารถรับประกันความสอดคล้องได้ เพื่อให้โค้ดเดียวกันทำงานในระบบปฏิบัติการทุกเวอร์ชันและอุปกรณ์ทุกเครื่อง

  • นอกจากนี้ยังช่วยให้สามารถอัปเดตได้

  • การแก้ไขข้อบกพร่องทุกรายการที่เราทำจะครอบคลุมไปถึง API 24 และไม่ได้ผูกกับรอบการเผยแพร่ระบบปฏิบัติการใดๆ

  • และในฐานะไลบรารีสนับสนุน เราสามารถอนุญาตตัวเลือกการปรับแต่งที่หลากหลาย ซึ่งเป็นไปไม่ได้ในเฟรมเวิร์ก Android

  • และในฐานะไลบรารีบน GitHub เราเป็นโอเพนซอร์สเป็นอันดับแรก

  • ซึ่งหมายความว่าการเปลี่ยนแปลงทุกอย่างที่เราทำจะพร้อมใช้งานใน GitHub ทันที

  • เรามี Issue Tracker ที่ใช้งานอยู่ และเรายอมรับ Pull Request

  • สิ่งสำคัญที่ต้องเน้นย้ำใน Use Case ทั้งหมดนี้คือเราให้ความสำคัญกับรายละเอียดทุกระดับ ตั้งแต่องค์ประกอบ UI ระดับสูงไปจนถึง API ที่พร้อมใช้งานทันทีแบบง่ายๆ รวมถึงตัวเลือกการปรับแต่งเชิงลึก การรองรับรูปแบบและมาตรฐานที่หลากหลาย ตลอดจนประสิทธิภาพและประสิทธิผลที่รองรับทั้งหมดนี้

  • Media3 ครอบคลุมพื้นที่ทั้งหมดนี้

  • วันนี้เราจะพยายามเจาะลึกหัวข้อเหล่านี้บางหัวข้อ

  • เราจะพูดถึงโมดูล Compose UI ใหม่, CastPlayer ใหม่ที่ช่วยให้การแชร์กับเซสชันสื่อทำได้ง่ายขึ้น, ไปป์ไลน์การแก้ไขที่ปรับปรุงใหม่พร้อมเอฟเฟกต์ขั้นสูงใหม่, เครื่องมือใหม่ในการดึงข้อมูลเมตาและเฟรม, เทคโนโลยีการสตรีมใหม่ๆ โดยเฉพาะวิดีโอและเสียง Eclipsa และยูทิลิตีคุณภาพของประสบการณ์การใช้งานจำนวนมากสำหรับการโหลดล่วงหน้า การลากแถบเวลา และการประหยัดแบตเตอรี่

UI ของสื่อด้วย Jetpack Compose

  • ตอนนี้เรามาเจาะลึกหัวข้อแรกกันเลย นั่นคือวิธีที่ Media3 ช่วยสร้างองค์ประกอบ UI สำหรับสื่อ

  • เมื่อเร็วๆ นี้เราได้เผยแพร่โมดูลใหม่ 2 โมดูล ได้แก่ Media3 UI Compose และ Media3 UI Compose Material 3

  • ที่นี่ คุณจะเห็นคอมโพสิเบิลเพลเยอร์ระดับสูง เนื่องจากเปิดตัวใน Media3 1.10 โดยไม่มีการแก้ไขเพิ่มเติม

    @Composable
    fun videoScreen() {
      Player(viewModel.exoPlayer)
    }
  • พร้อมใช้งานทันที

  • โดยใช้เพียงเพลเยอร์ Media3 เท่านั้น

  • ในตัวอย่างนี้คือ ExoPlayer ซึ่งปกติแล้วคุณจะมีอยู่ในโมเดลมุมมอง

  • แต่ก็อาจเป็นอย่างอื่นได้ เช่น CastPlayer, ตัวควบคุมสื่อ หรือเพลเยอร์อื่นๆ ที่คุณมีในแอป

  • โปรดทราบว่า ExoPlayer เป็นแหล่งข้อมูลที่เชื่อถือได้

  • ซึ่งมีข้อมูลทั้งหมด และคอมโพสิเบิลเพลเยอร์จะดูแลการแสดงผล UI และเชื่อมต่อทุกอย่างเข้าด้วยกัน

  • แต่จะเกิดอะไรขึ้นหากคุณต้องการปรับแต่ง UI นี้ ปุ่มใหม่ ตำแหน่งที่แตกต่างกัน สไตล์ที่แตกต่างกัน การวางซ้อนข้อความ

  • Composable ของโปรแกรมเล่นสร้างขึ้นจากองค์ประกอบที่ใช้สร้างสรรค์

  • คุณสามารถเปลี่ยนแบตเตอรี่ได้

  • คุณปรับแต่งแต่ละบล็อกได้ หรือจะใช้บล็อกแบบสแตนด์อโลนที่อื่นในแอปก็ได้

  • เช่น คุณสามารถปรับแต่งด้านบน ตรงกลาง และbottomControlsแยกกันได้

  • นอกจากนี้ คุณยังปรับแต่งลักษณะของชัตเตอร์ซึ่งจะแสดงเมื่อวิดีโอไม่เล่นหรือการวางซ้อนข้อผิดพลาดได้ด้วย

  • แต่นี่เป็นเพียงชั้นแรกเท่านั้น

  • นอกจากนี้ หน้าปัดแบบ Default Center และ bottomControls ยังมีช่องที่ละเอียดมากขึ้นซึ่งคุณปรับแต่งได้

  • เช่น คุณสามารถสลับปุ่มตรงกลางออกโดยปล่อยให้องค์ประกอบอื่นๆ ทั้งหมดเหมือนเดิม หรือเพิ่มองค์ประกอบด้านบนหรือด้านล่างแถบเลื่อน

  • Media3 Compose มีองค์ประกอบทั้งหมดที่คุณต้องใช้ในการดำเนินการนี้

  • ตัวอย่างเช่น เรามีปุ่มต่างๆ มากมาย เช่น ปุ่มเล่น/หยุดชั่วคราว โหมดเล่นซ้ำ โหมดสุ่มเพลง ปุ่มปิดเสียง และอื่นๆ

  • จากนั้นจะมีตัวบ่งชี้ความคืบหน้า เช่น แถบเลื่อนและข้อความตำแหน่งที่แสดงบนหน้าจอ

  • นอกจากนี้ยังมีคอนเทนเนอร์และภาพซ้อนทับจำนวนมากด้วย

  • คอมโพสิเบิลเพลเยอร์ระดับสูงที่ฉันแสดงให้คุณเห็นเป็นเพียงตัวอย่างหนึ่ง แต่ยังมีตัวอย่างอื่นๆ อีก เช่น การวางซ้อนข้อผิดพลาดหรือคำบรรยาย และอื่นๆ อีกมากมาย

  • นอกจากนี้ เรายังมีคอมโพเนนต์อื่นๆ ที่มีประโยชน์ เช่น ตัวควบคุมขนาดเล็กนี้

  • คุณสามารถวางไว้ใน Bottom Sheet ขณะที่ผู้ใช้ไปยังส่วนต่างๆ ของแอป

  • และเราจะไม่หยุดเพียงเท่านี้

  • เรากำลังขยายคลังนี้เพิ่มเติมอย่างต่อเนื่อง เช่น การเพิ่มการควบคุมการแก้ไขใหม่ๆ อย่างแถบเลื่อนการตัดคลิปที่จะเปิดตัวเร็วๆ นี้

  • ตอนนี้เรามาดูการปรับแต่งนี้ในการใช้งานจริงเพื่อดูวิธีใช้องค์ประกอบที่ใช้สร้างสรรค์เหล่านี้กัน

    Player(viewModel.exoPlayer)
  • สมมติว่าเราต้องการย้ายข้อความตำแหน่งและระยะเวลาซึ่งปัจจุบันอยู่ข้างแถบเลื่อนไปไว้ด้านบน

    Player(
      player = viewModel.exoPlayer,
      bottomControls = {
        PlayerDefaults.BottomControls(
          player,
          left = {}, right = {},
          above = {
            PositionAndDurationText(
              player, separator = " • ")
          },
        )
      },
    )
  • สิ่งที่เราทำได้คือลบล้างbottomControlsเหล่านี้

  • เรายังคงใช้คอมโพเนนต์เริ่มต้นได้ แต่เราตั้งค่าช่องด้านซ้ายและขวาให้ว่างเปล่าเพื่อให้หายไป

  • แต่เราจะวางข้อความตำแหน่งและระยะเวลาไว้ด้านบนในช่องด้านบนพร้อมตัวคั่นที่กำหนดเองเพื่อให้ดูน่าสนใจยิ่งขึ้น

  • และตอนนี้ สมมติว่าเราต้องการนำภาพซ้อนทับ centerControls ออกทั้งหมด และวางปุ่มเล่นไว้เหนือแถบเลื่อนพร้อมตัวควบคุมความเร็วและปุ่มปิดเสียงแทน

    Player(
      player = viewModel.exoPlayer,
      centerControls = {},
      bottomControls = { ...
          above = {
            Row(Modifier.fillMaxWidth()) {
              PositionAndDurationText(...)
              Spacer(Modifier.weight(1f))
              PlaybackSpeedToggleButton(player)
              MuteButton(player)
              PlayPauseButton(player)
            }
          },
        )
      }
  • ซึ่งเป็นแนวคิดเดียวกันกับที่กล่าวไปข้างต้น

  • เราสามารถตั้งค่า centerControls ให้ว่างเปล่าเพื่อให้หายไป และเราสามารถขยายช่องเหนือแถบเลื่อนด้วยพื้นที่เพิ่มเติมและปุ่ม 3 ปุ่มที่เราต้องการแสดง

  • โปรดทราบว่าปุ่มเหล่านี้มาจากไลบรารีเอง คุณจึงไม่ต้องใส่โค้ดเพิ่มเติมลงในปุ่ม

  • เพียงเท่านี้ก็เรียบร้อย

  • และคุณสามารถดำเนินการต่อตามเส้นทางนี้เพื่อจัดรูปแบบองค์ประกอบเหล่านี้ได้เช่นกัน

  • หากต้องการเปลี่ยนไอคอนปุ่มปิดเสียง แต่ยังคงฟังก์ชันการทำงานไว้ ก็ทำได้ในลักษณะเดียวกัน

    Player(
      ...
      bottomControls = { ...
          above = {
              ...
              MuteButton(player,
                painter = { painterResource(
                  if (showMuted)
                    R.drawable.muted
                  else
                    R.drawable.unmuted )})
          }
      },
    )
  • อย่างที่เห็นตรงนี้ คุณสามารถกำหนด Painter ที่กำหนดเองสำหรับปุ่มและเลือกไอคอนด้วยตนเองได้

  • คอมโพเนนต์จะให้ข้อมูลเมตาบางอย่างแก่คุณ ดังนั้นสิ่งที่คุณต้องทำก็คือดูสถานะ เช่น showMuted ในตัวอย่างนี้ เพื่อเลือกไอคอนที่ถูกต้อง

  • และนี่คือประโยชน์หลักของ Media3 Compose

  • คุณสามารถมุ่งเน้นที่รูปลักษณ์ของแอป และ Media3 จะจัดการส่วนที่เหลือให้เอง

  • โดยจะอ่านสถานะจากโปรแกรมเล่น โต้ตอบกับโปรแกรมเล่น และจัดการการเปลี่ยนภาพและภาพเคลื่อนไหวทั้งหมดในระหว่างนั้น

การแชร์การเล่นด้วย MediaSession

  • ตอนนี้เราจะเปลี่ยนจาก UI ไปเป็นการแชร์สื่อ

  • คุณสามารถแชร์สถานะการเล่นกับแอปอื่นๆ ในอุปกรณ์เดียวกันหรือเชื่อมต่อกับอุปกรณ์ระยะไกลได้

  • วิธีหลักในการเผยแพร่การเล่นที่ดำเนินอยู่คือการใช้ MediaSession

  • ซึ่งจะทำให้ผู้อื่นเห็นสถานะและข้อมูลเมตาของการเล่นของคุณ และควบคุมการเล่นของคุณได้

  • ซึ่งเป็นวิธีที่ Gemini, การแจ้งเตือนสื่อของ UI ระบบ, Cast, Wear OS หรือแอปที่ใช้ร่วมกันของ Android Auto สื่อสารกับแอปของคุณ

  • MediaSession จะรวมเพลเยอร์ Media3 ไว้ และการสร้างเพลเยอร์นั้นทำได้ง่ายมากเมื่อคุณมีเพลเยอร์แล้ว

    MediaSession.Builder(context, player).build()
  • ซึ่งอาจเป็น ExoPlayer หรือเพลเยอร์อื่นๆ ที่คุณมีในแอป เช่นเดียวกับ Compose UI

  • หากคุณเล่นในเบื้องหลัง โดยปกติแล้วคุณจะโฮสต์สิ่งนี้ไว้ใน MediaSessionService หรือ MediaLibraryService ซึ่งจะจัดการวงจรทั้งหมดให้คุณโดยอัตโนมัติ รวมถึงสิ่งต่างๆ เช่น การแจ้งเตือน

    class PlaybackService : MediaSessionService() {
    
      private lateinit var mediaSession:
            MediaSession
    
      override fun onGetSession(
            info: ControllerInfo) : MediaSession?
        = mediaSession
    }
  • แต่หากคุณมีแอปสื่อ คุณก็น่าจะทราบเรื่องนี้อยู่แล้ว

  • สิ่งใหม่ที่น่าตื่นเต้นที่เราอยากแสดงให้คุณเห็นคือ CastPlayer ที่เขียนขึ้นใหม่

  • Google Cast ช่วยให้คุณโอนการเล่นในเครื่องไปยังอุปกรณ์ระยะไกล เช่น Android TV, Chromecast หรือลำโพงอัจฉริยะได้

  • ก่อนหน้านี้เราเคยเผยแพร่ Cast SDK แยกต่างหาก ซึ่งตอนนี้อยู่ในโหมดการบำรุงรักษา

  • แต่ Media3 Cast จะมาแทนที่โดยสมบูรณ์เนื่องจากมีการผสานรวมที่ง่ายและมีประสิทธิภาพมากขึ้น

  • องค์ประกอบที่สำคัญที่สุดคือ CastPlayer ซึ่งใช้อินเทอร์เฟซเพลเยอร์มาตรฐานเดียวกันกับ ExoPlayer

  • แม้ว่า ExoPlayer จะใช้สำหรับการเล่นในเครื่อง แต่ CastPlayer เป็นอินสแตนซ์การมอบสิทธิ์ที่สามารถเล่นในเครื่องผ่าน ExoPlayer หรือเล่นจากระยะไกลผ่านโปรโตคอล Cast ก็ได้

  • และจะจัดการทุกอย่างให้คุณ

  • โดยจะคอยฟังการเปลี่ยนแปลงของอุปกรณ์เอาต์พุต สร้างการเชื่อมต่อ ซิงค์สถานะ และโอนการเล่นให้คุณเมื่อจำเป็น

  • และด้วยเหตุนี้ การผสานรวม CastPlayer เข้ากับแอปของคุณจึงเป็นเรื่องง่ายมาก

    val player = ExoPlayer.Builder(context).build()
    
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • แทนที่จะสร้าง ExoPlayer และแชร์กับ MediaSession แบบนี้ คุณจะใช้ CastPlayer แทน และนั่นก็คือทั้งหมด

    Cast.getSingletonInstance(context).initialize()
    
    val player = CastPlayer.Builder(context).build()
    
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • สิ่งเดียวที่คุณต้องทำคือเริ่มต้นระบบ Cast ก่อนที่จะใช้ระบบดังกล่าวในแอป

  • โปรดทราบว่า CastPlayer นี้จะสร้าง ExoPlayer ภายในสําหรับการเล่นในเครื่อง ดังนั้นหากต้องการปรับแต่งเพลเยอร์ในเครื่องเพิ่มเติม คุณสามารถสร้าง ExoPlayer ของคุณเองและส่งเป็นเพลเยอร์ในเครื่องไปยัง CastPlayer นี้ได้

    Cast.getSingletonInstance(context).initialize()
    val exoPlayer = ExoPlayer.Builder(context).build()
    val player = CastPlayer.Builder(context)
                    .setLocalPlayer(exoPlayer).build()
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • และนี่ก็เป็นหลักการเดียวกันกับ Media3 อีกครั้ง

  • เรามีองค์ประกอบพื้นฐานของ API ที่ใช้งานง่ายและพร้อมใช้งานทันที รวมถึงตัวเลือกการปรับแต่งทั้งหมดที่คุณต้องการเพื่อปรับแต่งเพิ่มเติม

  • และคุณอาจสงสัยว่าผู้ใช้จะสลับระหว่างการเล่นในเครื่องและการเล่นระยะไกลในการตั้งค่านี้ได้อย่างไร

  • โดยสามารถใช้งานได้ทันทีด้วยกล่องโต้ตอบตัวสลับเอาต์พุตของระบบในการแจ้งเตือนของสื่อ

  • ซึ่งเป็นปุ่มเล็กๆ ที่มุมนั้น

  • แต่จะทำอย่างไรหากต้องการแสดงปุ่มแคสต์ในหน้าจอการเล่นเพื่อให้ค้นพบได้ง่ายขึ้น

  • จำ UI ของ Compose ที่ปรับแต่งเองซึ่งเราเพิ่งสร้างเมื่อไม่กี่นาทีก่อนได้ไหม

    Player(
      exoPlayer,
      centerControls = {}
      bottomControls = { /* customized */ },
    )
  • นอกจากนี้ Media3 Cast ยังมีปุ่มที่พร้อมใช้งานทันทีซึ่งคุณสามารถวางไว้ที่นั่นได้

  • ซึ่งทำได้ง่ายๆ เพียงเพิ่ม MediaRouteButton สำเร็จรูปที่มีอยู่แล้วลงใน topControls เช่น เราไม่จำเป็นต้องตั้งค่าพารามิเตอร์เพิ่มเติมใดๆ อีก

    Player(
      castPlayer,
      centerControls = {}
      bottomControls = { /* customized */ },
      topControls = {
        PlayerDefaults.TopControls(castPlayer) {
          MediaRouteButton(
            Modifier.align(Alignment.TopEnd))
        }
      },
    )
  • และการคลิกปุ่มนี้จะเปิดกล่องโต้ตอบตัวสลับเอาต์พุตของระบบเดียวกัน จากนั้น CastPlayer จะโอนการเล่นให้คุณ

  • และคุณจะเห็นว่าฉันเชื่อมต่อ CastPlayer กับ UI ที่นี่เพื่อให้แน่ใจว่าสิ่งที่เกิดขึ้นจากระยะไกลจะแสดงใน UI ในเครื่องของคุณ

  • และในส่วนนี้ ฉันขอส่งต่อให้ Kristina มาพูดถึงกรณีการใช้งานการแก้ไข

API การแก้ไข Media3

  • ขอขอบคุณ

  • สวัสดี ตอนนี้เรามาดูเรื่องการตัดต่อและเอฟเฟกต์ขั้นสูงกัน

  • Media3 API ประกอบด้วยคอมโพเนนต์หลายรายการ

  • ก่อนอื่น Transformer เป็นจุดแรกเข้าหลักสำหรับ API การแก้ไขของเรา

  • Transformer จะประมวลผล แปลง และส่งออกสื่อไปยังไฟล์

  • ถัดไปคือ CompositionPlayer ซึ่งแสดงตัวอย่างองค์ประกอบสื่อแบบเรียลไทม์

  • ซึ่งจะช่วยให้คุณดูตัวอย่างและปรับแต่งการแก้ไขก่อนส่งออกขั้นสุดท้ายได้

  • จากนั้นก็มีเอฟเฟกต์

  • ซึ่งจะช่วยให้คุณควบคุมการประมวลผลแบบเฟรมต่อเฟรมได้อย่างเต็มที่เมื่อใช้เอฟเฟกต์ภาพแบบง่ายหรือเพิ่มการวางซ้อนกราฟิก

  • สุดท้ายคือ Muxer

  • โดยทำหน้าที่เป็นขั้นตอนเอาต์พุตสุดท้ายในไปป์ไลน์ของเรา ซึ่งจะรับแทร็กเสียง วิดีโอ และข้อมูลเมตาที่ประมวลผลแล้ว และเขียนลงในคอนเทนเนอร์ MP4 ที่ได้มาตรฐาน

  • เราใช้เวลาตลอดปีที่ผ่านมาในการคิดใหม่และสร้างไปป์ไลน์การตัดต่อวิดีโอขึ้นมาใหม่ตั้งแต่ต้น และวันนี้เราตื่นเต้นที่จะได้แชร์การปรับปรุงที่สำคัญ

  • เหตุใดเราจึงสร้างไปป์ไลน์ใหม่

  • สถาปัตยกรรมเดิมจึงทำให้เกิดความท้าทายหลายประการ

  • ประการแรก ระบบไม่สามารถเผยแพร่ข้อมูลเมตาในทุกขั้นตอนการประมวลผล

  • โดยเชื่อมโยงกับ OpenGL อย่างลึกซึ้ง ซึ่งจำกัดความยืดหยุ่นสำหรับเครื่องมือกราฟิกสมัยใหม่

  • นอกจากนี้ API ที่ซับซ้อนยังทำให้การปรับแต่งและการบำรุงรักษาเป็นอุปสรรคที่ยากลำบากสำหรับนักพัฒนาซอฟต์แวร์

  • เราจึงสร้างไปป์ไลน์การประมวลผลใหม่ทั้งหมดเพื่อจัดการกับความท้าทายเหล่านี้

  • อย่างแรก การจัดทีมที่ซับซ้อนซึ่งประกอบด้วยหลายชิ้นงานทำได้ง่ายขึ้นมากแล้วในตอนนี้

  • ประการที่ 2 การแยกออกจาก OpenGL ช่วยให้คุณมีอิสระอย่างเต็มที่ในการใช้เฟรมเวิร์กกราฟิกสมัยใหม่ เช่น WebGPU เป็นต้น

  • สุดท้ายนี้ Media3 จะจัดการตัวถอดรหัสและตัวเข้ารหัสโดยตรง เพื่อให้มั่นใจว่าอุปกรณ์ทั้งหมดจะมีความสอดคล้องและมีประสิทธิภาพมากขึ้น

  • มาเจาะลึกวิธีการทำงานของไปป์ไลน์นี้กัน

  • ตัวถอดรหัสจะแสดงผลเฟรมไปยังบัฟเฟอร์ฮาร์ดแวร์ที่จัดการโดย Media3 โดยตรง

  • ตัวรวมเฟรมจะประสานงานเฟรมในหลายลำดับ เพื่อให้โปรเซสเซอร์เฟรมที่กำหนดเองมีสิทธิ์เข้าถึงโดยตรงในการอ่านและประมวลผลบัฟเฟอร์เหล่านั้นจากการส่งไปยังโปรแกรมเปลี่ยนไฟล์

  • อินเทอร์เฟซ FrameProcessor ทำหน้าที่เป็นจุดขยายหลักสำหรับการประมวลผลระดับแอป

    public interface FrameProcessor
        extends AutoCloseable {
    
      interface Listener {
        void onWakeup();
        void onError(
             VideoFrameProcessingException exception);
        void onFrameProcessed(
             Frame frame,
             @Nullable SyncFenceWrapper onCompleteFence);
      }
    
      interface Factory {
        FrameProcessor create(
             FrameWriter output,
             Executor listenerExecutor,
             Listener listener);
      }
    
      boolean queue(List<AsyncFrame> frames);
    
      void signalEndOfStream();
    }
  • แอปพลิเคชันจะแจ้งเตือน Media3 ผ่านอินเทอร์เฟซ Listener ทุกครั้งที่มีการประมวลผลเฟรม โดยส่งผ่านรั้วการซิงค์ข้อมูล เพื่อให้ Media3 ทราบอย่างแน่ชัดว่าสามารถนำบัฟเฟอร์กลับมาใช้ใหม่ได้อย่างปลอดภัยเมื่อใด

  • หมายเหตุเกี่ยวกับหน่วยความจำแบบรวม: การให้ Media3 เป็นเจ้าของบัฟเฟอร์ฮาร์ดแวร์จะช่วยให้แอปอ่านและแก้ไขข้อมูลได้โดยตรงในตำแหน่งนั้น

  • แนวทางแบบ Zero-Copy นี้ช่วยลดภาระการใช้หน่วยความจำและทำให้การประมวลผลรวดเร็วเป็นพิเศษ

  • Adobe เป็นหนึ่งในนักพัฒนาแอปที่ใช้ประโยชน์จาก API การแก้ไขใหม่

  • ทีม Adobe Premiere ใช้ API การตัดต่อ Media3 เป็นรากฐานของโครงสร้างพื้นฐานการตัดต่อสื่อเพื่อให้มั่นใจว่ามีความเข้ากันได้มากขึ้นในระบบนิเวศของ Android ขณะเดียวกันก็ยังคงความยืดหยุ่นที่จำเป็นในการใช้ไปป์ไลน์ที่กำหนดเองและ WebGPU ของตนเอง

  • ต่อไป เรายินดีที่จะประกาศว่าเรากำลังเปิดตัวเอฟเฟกต์ใหม่ซึ่งเป็นส่วนหนึ่งของ Media3 เวอร์ชัน 1.12

  • เริ่มจากการเพิ่มประสิทธิภาพวิดีโอ

  • สถาปัตยกรรมของเราอาศัยการตั้งค่าแบบโมดูลาร์

  • แอปพลิเคชันของคุณผสานรวมกับบริการ Play Services สำหรับเอฟเฟกต์ Media3 ซึ่งเชื่อมต่อกับโมดูลการเพิ่มประสิทธิภาพบริการ Play Services

  • โมดูลการปรับปรุงวิดีโอประกอบด้วยความสามารถในการแมปโทน การลดนอยส์ การปรับให้คมชัด และความละเอียดสูง

  • การผสานรวม Video Enhance เข้ากับไปป์ไลน์ของคุณใช้โค้ดเพียงไม่กี่บรรทัด

    val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
        .setTonemappingEnabled(true)
        .setDeblurAndDenoiseEnabled(true)
        .setUpscaleVideoEnabled(true)
        .build()
    
    val transformerBuilder = Transformer.Builder(/* context = */ this)
        .setFrameProcessorFactory(frameProcessorFactory)
  • เมื่อใช้ EnhancementSessionDecorator.Builder คุณจะเลือกเปิดใช้ความสามารถต่างๆ เช่น การแมปโทน การปรับให้คมชัด หรือการเพิ่มความละเอียดของรูปภาพ และส่ง frameProcessorFactory และตัวช่วยบัฟเฟอร์ฮาร์ดแวร์ที่ได้ไปยัง Transformer.Builder ได้

  • อีกหนึ่งเอฟเฟกต์ที่น่าตื่นเต้นซึ่งจะมาใน Media3 1.12 คือเสียงระดับสตูดิโอ

  • ฟีเจอร์เสียงระดับสตูดิโอจะวิเคราะห์แทร็กเสียงของวิดีโอโดยอัตโนมัติและแยกออกเป็นองค์ประกอบต่างๆ เช่น เสียงพูด เสียงแวดล้อม และเพลง

  • ครีเอเตอร์สามารถลดเสียงรบกวนจากลมหรือฝูงชนได้อย่างง่ายดาย พร้อมกับทำให้เสียงพูดและเพลงชัดเจนขึ้น

  • หากต้องการผสานรวมเสียงระดับสตูดิโอ ให้ส่งตัวประมวลผลเสียงระดับสตูดิโอภายในออบเจ็กต์เอฟเฟกต์

    val editedMediaItem =
         EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
           .setEffects(
             Effects(
               listOf(
                 soundSeparationAudioProcessor
               ),
               emptyList(), // video effects
             )
           )
           .build()
  • ซึ่งจะแนบโปรเซสเซอร์เสียงกับรายการสื่อของคุณ

  • เมื่อสร้างโปรเซสเซอร์เสียง Studio Sound ให้แนบ Callback ของ Listener เพื่อรับการแมปช่องเสียงที่วิเคราะห์แล้วผ่าน onResult

    val soundSeparationAudioProcessor =
          SoundSeparationAudioProcessor(
            getApplication(),
            mainExecutor,
            object : SoundSeparationAudioProcessor.Listener {
              override fun onResult(channelToAudioType: SparseIntArray) {
                for (i in 0 until channelToAudioType.size()) {
                  val channelIndex = channelToAudioType.keyAt(i)
                  val audioType = channelToAudioType.valueAt(i)
                  val label = toAudioTypeName(audioType)
                  detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
                }
              }
            },
          )
  • ทำซ้ำในแต่ละช่องทางเพื่อดึงดัชนีช่องทาง ประเภทเสียงที่ระบุ และป้ายกำกับที่อ่านได้เมื่อผลลัพธ์ของการดำเนินการพร้อมใช้งาน

  • มาฟังเสียงระดับสตูดิโอในการใช้งานจริงกัน

  • ผลกระทบแรก ที่รัก ได้โปรดอย่าร้องไห้เลย

  • เรากำลังตัดเพื่อเตรียมอาหารเย็นให้คุณ

  • คุณจะได้ยินเสียงทารกร้องไห้เป็นพื้นหลัง และตอนนี้ก็ใช้เอฟเฟกต์แล้ว

  • ใช่ อย่างที่คุณได้ยิน ไม่มีเสียงเด็กร้องไห้ในพื้นหลังเลย ต้องขอบคุณเอฟเฟกต์เสียงสตูดิโอ

  • ตอนนี้เรามาเจาะลึกเรื่องการดึงข้อมูลเมตากัน

  • เราจะมาดูวิธีที่ Media3 ช่วยให้คุณอ่านข้อมูลเมตาของคอนเทนเนอร์และดึงเฟรมออกมาได้อย่างง่ายดาย

  • โดยเริ่มจาก MetadataRetriever ซึ่งผสานรวมกับโครูทีน Kotlin ได้อย่างราบรื่นเพื่อให้โค้ดสะอาดและไม่บล็อก

  • โดยมีการออกแบบที่สะอาดตาและมีแนวโน้มที่จะเกิดข้อผิดพลาดน้อยลง โดยใช้ตัวดึงข้อมูลที่เปลี่ยนแปลงไม่ได้ 1 ตัวต่อรายการสื่อ

  • ซึ่งทำงานได้อย่างสอดคล้องกันใน API ทุกเวอร์ชันและอุปกรณ์ทุกเครื่อง

  • และรองรับรูปแบบสื่อที่หลากหลายตั้งแต่แกะกล่อง

  • ต่อไปนี้เป็นตัวอย่างการใช้งานจริงโดยใช้ MetadataRetriever

    MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
        // 1. Duration (in microseconds)
        val durationUs = retriever.retrieveDurationUs().await()
        // 2. Track formats & container metadata (reuses the same preparation pass)
        val trackGroups = retriever.retrieveTrackGroups().await()
        for (i in 0 until trackGroups.length) {
            val format = trackGroups[i].getFormat(0)
            // Video/Audio properties: format.width, format.height, format.colorInfo …
            // Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
            val metadata = format.metadata
        }
        // 3. Stream structure & manifest info
        val timeline = retriever.retrieveTimeline().await()
    }
  • เมื่อใช้โครูทีน Kotlin คุณจะดึงข้อมูลระยะเวลา ตรวจสอบรูปแบบแทร็กและข้อมูลเมตาแบบอะซิงโครนัสได้ทั้งหมดภายในบล็อกเดียวที่สะอาดตา

  • ตอนนี้เรามาพูดถึง FrameExtractor กัน

  • ซึ่งช่วยให้ดึงเฟรมวิดีโอที่ถอดรหัสแล้วจากรายการสื่อได้อย่างรวดเร็วและมีประสิทธิภาพ

  • โดยจะจัดการทรัพยากรที่สำคัญ เช่น ตัวถอดรหัสโดยอัตโนมัติ ช่วยให้คุณใช้เอฟเฟกต์ภาพหรือการแมปโทนสี HDR ได้ และให้คุณควบคุมการค้นหาแบบละเอียดเพื่อให้ได้เฟรมที่ต้องการอย่างแม่นยำ

  • ต่อไปนี้คือลักษณะของการดึงเฟรมในโค้ด

    FrameExtractor.Builder(context, mediaItem)
        .setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
        .setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
        .setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
        .build()
        .use { extractor ->
            // Representative thumbnail
            val thumbnail = extractor.getThumbnail().await()
            // Frame at specific timestamp (bitmap + actual presentationTimeMs)
            val frame = extractor.getFrame(positionMs = 5_000L).await()
        }
  • คุณสร้างอินสแตนซ์ที่มีตัวเลือกต่างๆ เช่น พารามิเตอร์การกรอวิดีโอ การลดขนาด GPU เอฟเฟกต์ และการรองรับ HDR จากนั้นรอภาพปกและเฟรมที่เฉพาะเจาะจงแบบอะซิงโครนัสโดยใช้การเรียกใช้แบบระงับที่สะอาด

  • และตอนนี้ขอส่งต่อให้ Donovan

มาตรฐานการสตรีมและรูปแบบสื่อใหม่ๆ

  • ขอบคุณ Kristina

  • ต่อไป เราจะพูดถึงการรองรับเทคโนโลยีสตรีมมิงพื้นฐานบางอย่างที่เราเพิ่มเข้ามาใน Media3 ซึ่งรวมถึงการรองรับรูปแบบต่างๆ เช่น วิดีโอ Eclipsa และระบบเสียง Eclipsa

  • ดังนั้น ความพยายามหลักใน Media3 คือการก้าวนำมาตรฐานการสตรีม เพื่อให้คุณไม่ต้องเขียนไปป์ไลน์สื่อใหม่ทุกครั้งที่มีข้อกำหนดใหม่

  • ตัวอย่างสิ่งที่เราเปิดตัวเมื่อเร็วๆ นี้มีดังนี้

  • เรามีฟีเจอร์การควบคุมเนื้อหา ซึ่งช่วยให้สลับเส้นทาง CDN แบ็กเอนด์แบบไดนามิกได้ระหว่างสตรีม

  • เรามีการรองรับการแทรกโฆษณาตามคำแนะนำของเซิร์ฟเวอร์เพื่อการต่อโฆษณาที่ปรับตามโปรไฟล์ของผู้ใช้อย่างราบรื่น

  • นอกจากนี้ เรายังได้เปิดตัวการรองรับ AV1 ในจุดเข้าใช้งานหลายจุด ซึ่งรวมถึงการรองรับ AV1 HDR อย่างเต็มรูปแบบ

  • เรามีวิดีโอระดับมืออาชีพสำหรับ Android เพื่อการจับภาพและตัดต่อที่มีความแม่นยำสูง

  • นอกจากนี้ เรายังเพิ่มการรองรับรูปภาพเคลื่อนไหวทั้งในรูปแบบ JPEG และ HEIC ด้วย

  • และแน่นอนว่าเรากำลังเพิ่มการรองรับวิดีโอ Eclipsa และระบบเสียง Eclipsa ซึ่งเราจะพูดถึงเพิ่มเติมในเร็วๆ นี้

  • ข่าวดีก็คือระบบจะรองรับฟีเจอร์ทั้งหมดนี้โดยอัตโนมัติเมื่อเปิดตัวการรองรับอุปกรณ์

  • ตอนนี้เรามาดูวิวัฒนาการขั้นถัดไปของ HDR, Eclipsa Video และปัญหาหลักที่พยายามแก้ไขกัน

  • ดังนั้น รูปแบบ HDR ก่อนหน้านี้จึงให้ข้อมูลเมตาเชิงอธิบาย ซึ่งจะกำหนดลักษณะที่ฉากควรจะเป็น แต่ไม่ได้บอกจอแสดงผลจริงๆ ว่าจะแสดงผลอย่างไร

  • เรามาเริ่มจากพื้นฐานกันก่อน

  • สำหรับสภาพแวดล้อมที่แตกต่างกัน เรามีคำแนะนำที่แตกต่างกันสำหรับความสว่างหน้าจอโดยเฉลี่ย เพื่อให้คุณดูเนื้อหาได้อย่างชัดเจนและสบายตา

  • ดังนั้นในห้องที่มีแสงสลัว ช่วงดังกล่าวจะอยู่ระหว่าง 100 ถึง 300 นิต

  • และแน่นอนว่าเมื่อคุณอยู่ในสภาพแวดล้อมที่สว่างขึ้นเรื่อยๆ หน้าจอจะต้องปรับตามและสว่างขึ้นเพื่อให้คุณเห็นเนื้อหาได้อย่างชัดเจน จนถึงเมื่อคุณดูโทรศัพท์ในที่ที่มีแสงแดดส่องโดยตรง หน้าจออาจต้องสว่างถึง 1,000, 2,000 หรือมากกว่านั้นเพื่อให้คุณดูเนื้อหาได้

  • SDR แบบดั้งเดิมจะทำงานในช่วง 100 ถึง 300 นิต

  • และแน่นอนว่า HDR ไม่ได้มีไว้เพื่อเพิ่มความสว่างของหน้าจอโดยรวม

  • คุณต้องการขยายช่วงไดนามิกเพื่อให้มีไฮไลต์ที่ปรับให้เหมาะสมกับสิ่งต่างๆ เช่น ไฟและแสงแดด

  • อย่างไรก็ตาม สมาร์ทโฟนทำให้เกิดความซับซ้อนเล็กน้อย เนื่องจากเราพกติดตัวไปทุกที่ ไม่ว่าจะเป็นห้องมืดๆ ในบ้านหรือชายหาดที่สว่างสดใส

  • ซึ่งเมื่อรวมกับข้อเท็จจริงที่ว่าแอปจำนวนมากผสมผสานองค์ประกอบ UI แบบ SDR เข้ากับเนื้อหา SDR และเนื้อหา HDR ในฟีดแบบผสม ก็อาจทำให้เกิดเอฟเฟกต์ที่สว่างจ้าจนแสบตาซึ่งคุณอาจพบได้ในบางครั้ง โดยเฉพาะอย่างยิ่งหากคุณเลื่อนดูในช่วงดึก

  • เราจึงเห็นได้ว่าทำไม HDR จึงคุ้มค่าที่จะใช้

  • ซึ่งจะช่วยให้เนื้อหาดูดีในสภาพแวดล้อมที่หลากหลายมากขึ้น

  • และเรายังพบว่าการมีส่วนร่วมของผู้ใช้เพิ่มขึ้นเมื่อเปิด HDR

  • แต่ปัญหาคือรูปแบบ HDR ที่มีอยู่ไม่ได้คำนึงถึงสภาพแวดล้อมเหล่านี้

  • นี่คือจุดที่ Eclipsa Video โดดเด่นอย่างแท้จริง

  • หยุดรอเสียงหัวเราะ

  • ขอขอบคุณ

  • ขอขอบคุณ

  • ดังนั้น Eclipsa Video จึงให้ข้อมูลเมตาที่กำหนดไว้แทนข้อมูลเมตาที่สื่อความหมาย

  • ซึ่งสร้างขึ้นจาก 3 เสาหลัก

  • เสาแรกคือหมุดยึดสีขาวสำหรับอ้างอิง

  • ดังนั้น การดำเนินการนี้จะล็อกความสว่างมาตรฐานไว้ที่จุดสีขาว SDR ที่ปรับตามสภาพแวดล้อมของจอแสดงผล ซึ่งจะช่วยขจัดความสว่างที่เพิ่มขึ้นอย่างฉับพลัน

  • เสาหลักที่ 2 คือเกณฑ์พื้นฐาน SDR สำหรับครีเอเตอร์

  • ซึ่งจะช่วยคงเส้นโค้งการแมปโทนสีที่แน่นอนของครีเอเตอร์กลับไปเป็น SDR

  • ประการที่ 3 เรามีเส้นโค้งอัตราขยายแบบปรับได้ของเฮดรูม ซึ่งจะปรับขนาดไฮไลต์ตามที่กำหนดเพื่อให้ตรงกับเฮดรูม HDR แบบเรียลไทม์ของจอแสดงผล เพื่อให้เนื้อหาตรงกับสภาพแวดล้อมที่ดู

  • และฟีเจอร์นี้พร้อมใช้งานใน ExoPlayer แบบสำเร็จรูปใน Media3 1.11 บนอุปกรณ์ที่ใช้ Android 17 ขึ้นไป

  • นอกจากนี้ Eclipsa Video ยังสร้างขึ้นตามมาตรฐานแบบเปิดเพื่อให้ใช้งานร่วมกันได้ในอุตสาหกรรมต่างๆ

  • จึงไม่มีตอนไหนเหมาะจะรองรับ HDR เท่าตอนนี้อีกแล้ว

  • ในด้านเสียง เรามีระบบเสียง Eclipsa ซึ่งเป็นรูปแบบเสียงรอบทิศทาง 3 มิติแบบโอเพนซอร์สและไม่มีค่าสิทธิ

  • ซึ่งอิงตามข้อกำหนด IAMF ของ Alliance for Open Media

  • โดยจะรวม Ambisonics แบบอิงตามฉากเข้ากับเสียงแบบอิงตามช่องเป็นสตรีมเดียวที่มีการนำเสนอแบบผสมหลายรายการ

  • ซึ่งช่วยให้สามารถจัดการเสียงแบบออบเจ็กต์ได้ เช่น การปรับปรุงบทสนทนา และจะทำงานได้กับทุกอย่างตั้งแต่หูฟังสเตอริโอไปจนถึงการตั้งค่าเสียงเซอร์ราวด์

  • จริงๆ แล้ว Media3 มีเส้นทางการเล่น 2 เส้นทาง

  • สำหรับ Android 17 ขึ้นไป ExoPlayer และเฟรมเวิร์กเสียงของ Android จะแสดงผลระบบเสียง Eclipsa โดยอัตโนมัติ

  • และสำหรับ Android 16 และเวอร์ชันก่อนหน้า คุณยังมีตัวเลือกอยู่

  • คุณเปิดใช้ส่วนขยาย IAMF ได้ผ่านไลบรารีส่วนเสริม Media3 ที่ไม่บังคับ

  • และในด้านการสร้าง คุณสามารถเขียนแทร็กเสียง IAMF โดยใช้ Muxer ของ Media3

การเพิ่มประสิทธิภาพและแบตเตอรี่

  • แต่การรองรับรูปแบบคุณภาพสูงเป็นเพียงส่วนหนึ่งเท่านั้น

  • เราต้องนำเสนอคุณภาพนั้นอย่างมีประสิทธิภาพ

  • ตอนนี้เราจะพูดถึงวิธีที่ Media3 ช่วยให้ไปป์ไลน์ของเราทำงานได้เร็วขึ้นและมีประสิทธิภาพมากขึ้น

  • เช่นเดียวกับหลายๆ สิ่งที่เราพูดถึงในวันนี้ การปรับปรุงประสิทธิภาพส่วนใหญ่เหล่านี้พร้อมใช้งานทันทีเพียงแค่อัปเดต Media3 ให้เป็นเวอร์ชันล่าสุด

  • ตัวอย่างหนึ่งของฟีเจอร์ที่เรามีคือการลดภาระเสียง PCM ซึ่งสามารถกำหนดเส้นทางการเล่นเสียงไปยังหน่วยประมวลผลที่ใช้พลังงานต่ำได้

  • จากการวิเคราะห์เบื้องต้น เราพบว่าการเล่นเสียงแบบปิดหน้าจอช่วยประหยัดพลังงานได้ถึง 20%

  • นอกจากการลดภาระการประมวลผลเสียง PCM แล้ว เรายังมีการกำหนดเวลาแบบไดนามิก ซึ่งจะปรับการปลุกการวนซ้ำการแสดงผลภายในของ ExoPlayer ให้สอดคล้องกับ VSYNC และความพร้อมใช้งานของบัฟเฟอร์ตัวถอดรหัส

  • นอกจากนี้ เรายังมีฟีเจอร์การเตรียมพร้อมโปรแกรมแสดงผล ซึ่งช่วยให้คุณเตรียมพร้อมโปรแกรมแสดงผลวิดีโอรองได้ ซึ่งจะช่วยให้เปลี่ยนเพลย์ลิสต์ได้ทันทีและต่อโฆษณาได้ง่ายขึ้น

  • นอกจากนี้ เรายังมีฟีเจอร์การตรวจจับเพลเยอร์ที่ค้างและการแคชล่วงหน้า ซึ่งจะตรวจหาบัฟเฟอร์และโปรแกรมแสดงผลที่หยุดทำงานโดยอัตโนมัติ และจำกัดหน่วยความจำที่แคชล่วงหน้าอย่างเป็นระเบียบ

  • โหมดที่เหมาะสมอีกโหมดหนึ่งที่เรามีคือโหมดการลากแถบเวลา

  • และโฆษณานี้เหมาะสำหรับแอปที่ผู้ใช้มักจะเลื่อนดูเนื้อหา

  • ดังนั้นเมื่อเปิดใช้โหมดการลากแถบเวลา การลากแถบค้นหาจะตอบสนองได้ดีและราบรื่นขึ้นมาก ดังที่เห็นที่นี่

  • ทั้งนี้ การดำเนินการนี้จะเกิดขึ้นเมื่อปิดโหมดการลากแถบเวลา

  • และทางด้านขวา เราจะเห็นว่าโหมดการลากแถบเวลาเปิดอยู่

  • ขอขอบคุณ

  • ขอขอบคุณ

  • โอเค

  • ทีนี้เรามาพูดถึงประสิทธิภาพของระบบ Android กัน

  • เราต้องคำนึงถึงทรัพยากร RAM, CPU และแบตเตอรี่ที่ใช้ร่วมกัน ดังนั้นเมื่อ Android เข้มงวดกับกฎเกี่ยวกับการเพิ่มความปลอดภัยของเสียงและบริการที่ทำงานอยู่เบื้องหน้า แอปที่จัดการบริการดิบและบิตแมปของตนเองจึงมักพบข้อผิดพลาดบางอย่าง

  • สิ่งที่คุณอาจเคยเห็นคือข้อยกเว้นที่ไม่อนุญาตให้เริ่มบริการที่ทำงานอยู่เบื้องหน้าหรือข้อยกเว้น Binder หน่วยความจำเต็ม

  • เราจึงได้ปรับปรุงเซสชันบริการสื่อและเครื่องมือจัดการการแจ้งเตือนสื่อ รวมถึงไปป์ไลน์บิตแมปในหลายๆ ด้าน เช่น ป้องกัน Intent ของบริการที่ทำงานอยู่เบื้องหน้าที่ล้าสมัย และจำกัดหน่วยความจำของอาร์ตเวิร์ก เป็นต้น

  • แต่สิ่งสำคัญสำหรับคุณคือ หากคุณใช้ Media3 และปฏิบัติตามคำแนะนำมาตรฐาน ระบบจะจัดการข้อจำกัดด้านประสิทธิภาพของระบบทั้งหมดให้คุณโดยไม่มีผลกระทบใดๆ

  • สุดท้ายนี้ เราอยากพูดถึงฟีเจอร์ที่จะพร้อมใช้งานใน Media3 เร็วๆ นี้

  • เมื่อถึงเวลาเข้ารหัสหรือส่งออกวิดีโอ ความหลากหลายของฮาร์ดแวร์อาจเป็นหนึ่งในความท้าทายที่ยิ่งใหญ่ที่สุด

  • แอปจำนวนมากใช้การกำหนดค่าตัวแปลงรหัสแบบครอบจักรวาล ซึ่งคุณก็ทราบดีว่าทำให้คุณภาพและประสิทธิภาพไม่ดีเท่าที่ควร

  • เราจึงสร้าง CodecDB ขึ้นมา

  • CodecDB สร้างขึ้นจากการวัดผลวิดีโอในชีวิตจริงอย่างครอบคลุมโดยใช้เมตริกคุณภาพที่ได้มาตรฐานและการเปรียบเทียบฮาร์ดแวร์ในอุปกรณ์ Android

  • มาดูการทำงานจริงกันเลย

  • ทางด้านซ้าย เรามีเฟรมของวิดีโอที่ถ่ายด้วยโทรศัพท์ซึ่งเราใช้การกำหนดค่าวิดีโอทั่วไปที่อาจใช้ในอุปกรณ์หลายเครื่อง

  • และทางด้านขวา เราใช้การกำหนดค่าที่เหมาะสมที่สุดในระดับอุปกรณ์ซึ่งได้มาจาก CodecDB

  • แล้วความแตกต่างอยู่ตรงไหน

  • หากดูในพื้นที่ที่มีรายละเอียดสูง คุณจะเห็นความแตกต่างด้านคุณภาพที่ชัดเจนที่สุด

  • ดังนั้นให้ดูความชัดเจนของกิ่งก้านต้นไม้และพุ่มไม้ หรือความคมชัดของขอบอาคาร

  • ดังนั้นสิ่งที่เราได้รับจากที่นี่คือประสบการณ์การรับชมที่ดีขึ้นมาก ซึ่งใช้ประโยชน์จากความสามารถของอุปกรณ์ที่เฉพาะเจาะจงโดยใช้ประโยชน์จาก CodecDB

  • และการใช้ CodecDB ในโค้ดก็ค่อนข้างง่าย

    val config = CodecDb.onDevice(context)
       .forEncodingFallingBackTo {
               constraints -> createFallbackConfig(constraints)
       }
       .findOptimalSettings()
       .forEditing()
       .withResolution(1920, 1080, /* frameRate= */ 30.0)
       .withAnyCodec()
       .targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
       .get()
  • คุณจะใช้รูปแบบเครื่องมือสร้างสเตจที่คล่องแคล่วซึ่งจะแนะนำคุณตลอดพารามิเตอร์ที่จำเป็นทั้งหมด

  • ดังนั้นคุณจะต้องระบุกรณีการใช้งาน

  • ในที่นี้เราจะใช้เพื่อแก้ไข

  • ซึ่งจะบอก CodecDB ว่าคุณกำลังใช้ไปป์ไลน์การส่งออกหรือการแปลงของ Media3

  • กรณีการใช้งานอื่นๆ ที่คุณตั้งค่าได้ เช่น การจับภาพด้วยกล้อง หากคุณใช้เพื่อการนั้น

  • นอกจากนี้ คุณยังส่งความละเอียดและอัตราเฟรมเป้าหมายได้ด้วย

  • จากนั้น แทนที่จะเดาว่าวิดีโอ 1080p นั้นต้องการความเร็ว 8 เมกะบิตต่อวินาทีหรือ 15 เมกะบิตต่อวินาที คุณก็บอก CodecDB ว่าต้องการใช้ตัวแปลงรหัสใดและขอเป้าหมายคุณภาพการรับรู้

  • ในกรณีนี้ เราจะใช้ระดับคุณภาพ "ดีมาก"

  • เบื้องหลังการทำงาน CodecDB จะค้นหาเส้นโค้งการเปรียบเทียบเชิงประจักษ์สำหรับ SOC นั้นๆ และแสดงผลพารามิเตอร์ตัวแปลงรหัสและโปรแกรมเปลี่ยนไฟล์ที่เหมาะสมที่สุด

  • เราคาดว่า CodecDB จะเปิดตัวใน Media3 เร็วๆ นี้ และเมื่อเปิดตัวแล้ว จะใช้ประเภท Media3 แบบเนทีฟ

  • ดังนั้นการผสานรวมกับโค้ด Media3 ที่มีอยู่จึงเป็นเรื่องง่าย

สรุปและบทสรุป

  • ตกลง

  • เราได้เห็นฟังก์ชันการทำงานที่น่าทึ่งมากมายใน Media3 วันนี้ มาสรุปกันดีกว่า

  • วันนี้คุณได้เรียนรู้เกี่ยวกับโมดูล Compose UI ใหม่, CastPlayer ใหม่, ไปป์ไลน์การแก้ไขที่ปรับปรุงใหม่ และเอฟเฟกต์ใหม่ๆ มากมายที่คุณใช้ใน Media3 ได้

  • นอกจากนี้ เรายังเห็นเครื่องมือใหม่ๆ ที่ใช้ดึงข้อมูลเมตาและเฟรมด้วย

  • นอกจากนี้ เรายังได้พิจารณาการรองรับเทคโนโลยีการสตรีมใหม่ๆ รวมถึงการเพิ่มประสิทธิภาพและยูทิลิตี

  • Media3 เป็นไลบรารีโอเพนซอร์ส คุณจึงดูวิธีที่เราใช้ฟีเจอร์ทั้งหมดเหล่านี้ได้ในที่เก็บ GitHub

  • ณ วันนี้ Media3 เวอร์ชันเสถียรล่าสุดคือ 1.11.1

  • นอกจากนี้ คุณยังดูเวอร์ชัน 1.12.0 ซึ่งปัจจุบันอยู่ในเวอร์ชันอัลฟ่าได้ด้วย

  • และไปที่ลิงก์สั้นๆ นี้ ซึ่งจะนำคุณไปยังเอกสารประกอบสำหรับนักพัฒนาซอฟต์แวร์เกี่ยวกับ Media3

  • ขอขอบคุณเป็นอย่างยิ่ง และขอให้สนุกกับช่วงเวลาที่เหลือของ ADS

รหัส

ข้อมูลโค้ด

ข้อมูลโค้ด Kotlin ที่นาที 02:31 ใน "Media UI with Jetpack Compose"
@Composable
fun videoScreen() {
  Player(viewModel.exoPlayer)
}
ข้อมูลโค้ด Kotlin ที่ 04:44 ใน "Media UI with Jetpack Compose"
Player(viewModel.exoPlayer)
ข้อมูลโค้ด Kotlin ที่ 04:48 ใน "Media UI with Jetpack Compose"
Player(
  player = viewModel.exoPlayer,
  bottomControls = {
    PlayerDefaults.BottomControls(
      player,
      left = {}, right = {},
      above = {
        PositionAndDurationText(
          player, separator = " • ")
      },
    )
  },
)
ข้อมูลโค้ด Kotlin ที่นาที 05:13 ใน "Media UI with Jetpack Compose"
Player(
  player = viewModel.exoPlayer,
  centerControls = {},
  bottomControls = { ...
      above = {
        Row(Modifier.fillMaxWidth()) {
          PositionAndDurationText(...)
          Spacer(Modifier.weight(1f))
          PlaybackSpeedToggleButton(player)
          MuteButton(player)
          PlayPauseButton(player)
        }
      },
    )
  }
ข้อมูลโค้ด Kotlin ที่นาที 05:51 ใน "Media UI with Jetpack Compose"
Player(
  ...
  bottomControls = { ...
      above = {
          ...
          MuteButton(player,
            painter = { painterResource(
              if (showMuted)
                R.drawable.muted
              else
                R.drawable.unmuted )})
      }
  },
)
ข้อมูลโค้ด Kotlin ที่นาที 07:01 ใน "การแชร์การเล่นด้วย MediaSession"
MediaSession.Builder(context, player).build()
ข้อมูลโค้ด Kotlin ที่นาที 07:17 ใน "การแชร์การเล่นด้วย MediaSession"
class PlaybackService : MediaSessionService() {

  private lateinit var mediaSession:
        MediaSession

  override fun onGetSession(
        info: ControllerInfo) : MediaSession?
    = mediaSession
}
ข้อมูลโค้ด Kotlin ที่ 08:25 ใน "การแชร์การเล่นด้วย MediaSession"
val player = ExoPlayer.Builder(context).build()

mediaSession = MediaSession.Builder(context,player)
.build()
ข้อมูลโค้ด Kotlin ที่ 08:29 ใน "การแชร์การเล่นด้วย MediaSession"
Cast.getSingletonInstance(context).initialize()

val player = CastPlayer.Builder(context).build()

mediaSession = MediaSession.Builder(context,player)
.build()
ข้อมูลโค้ด Kotlin ที่นาที 08:45 ใน "การแชร์การเล่นด้วย MediaSession"
Cast.getSingletonInstance(context).initialize()
val exoPlayer = ExoPlayer.Builder(context).build()
val player = CastPlayer.Builder(context)
                .setLocalPlayer(exoPlayer).build()
mediaSession = MediaSession.Builder(context,player)
.build()
ข้อมูลโค้ด Kotlin ที่นาที 09:32 ใน "การแชร์การเล่นด้วย MediaSession"
Player(
  exoPlayer,
  centerControls = {}
  bottomControls = { /* customized */ },
)
ข้อมูลโค้ด Kotlin ที่นาที 09:40 ใน "การแชร์การเล่นด้วย MediaSession"
Player(
  castPlayer,
  centerControls = {}
  bottomControls = { /* customized */ },
  topControls = {
    PlayerDefaults.TopControls(castPlayer) {
      MediaRouteButton(
        Modifier.align(Alignment.TopEnd))
    }
  },
)
ข้อมูลโค้ด Java ที่ 13:02 ใน "Media3 editing APIs"
public interface FrameProcessor
    extends AutoCloseable {

  interface Listener {
    void onWakeup();
    void onError(
         VideoFrameProcessingException exception);
    void onFrameProcessed(
         Frame frame,
         @Nullable SyncFenceWrapper onCompleteFence);
  }

  interface Factory {
    FrameProcessor create(
         FrameWriter output,
         Executor listenerExecutor,
         Listener listener);
  }

  boolean queue(List<AsyncFrame> frames);

  void signalEndOfStream();
}
ข้อมูลโค้ด Kotlin ที่นาที 14:45 ใน "Media3 editing APIs"
val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
    .setTonemappingEnabled(true)
    .setDeblurAndDenoiseEnabled(true)
    .setUpscaleVideoEnabled(true)
    .build()

val transformerBuilder = Transformer.Builder(/* context = */ this)
    .setFrameProcessorFactory(frameProcessorFactory)
ข้อมูลโค้ด Kotlin ที่นาที 15:36 ใน "Media3 editing APIs"
val editedMediaItem =
     EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
       .setEffects(
         Effects(
           listOf(
             soundSeparationAudioProcessor
           ),
           emptyList(), // video effects
         )
       )
       .build()
ข้อมูลโค้ด Kotlin ที่ 15:50 ใน "Media3 editing APIs"
val soundSeparationAudioProcessor =
      SoundSeparationAudioProcessor(
        getApplication(),
        mainExecutor,
        object : SoundSeparationAudioProcessor.Listener {
          override fun onResult(channelToAudioType: SparseIntArray) {
            for (i in 0 until channelToAudioType.size()) {
              val channelIndex = channelToAudioType.keyAt(i)
              val audioType = channelToAudioType.valueAt(i)
              val label = toAudioTypeName(audioType)
              detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
            }
          }
        },
      )
ข้อมูลโค้ด Kotlin ที่นาที 17:29 ใน "Media3 editing APIs"
MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
    // 1. Duration (in microseconds)
    val durationUs = retriever.retrieveDurationUs().await()
    // 2. Track formats & container metadata (reuses the same preparation pass)
    val trackGroups = retriever.retrieveTrackGroups().await()
    for (i in 0 until trackGroups.length) {
        val format = trackGroups[i].getFormat(0)
        // Video/Audio properties: format.width, format.height, format.colorInfo …
        // Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
        val metadata = format.metadata
    }
    // 3. Stream structure & manifest info
    val timeline = retriever.retrieveTimeline().await()
}
ข้อมูลโค้ด Kotlin ที่นาที 18:06 ใน "Media3 editing APIs"
FrameExtractor.Builder(context, mediaItem)
    .setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
    .setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
    .setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
    .build()
    .use { extractor ->
        // Representative thumbnail
        val thumbnail = extractor.getThumbnail().await()
        // Frame at specific timestamp (bitmap + actual presentationTimeMs)
        val frame = extractor.getFrame(positionMs = 5_000L).await()
    }
ข้อมูลโค้ด Kotlin ที่ 28:26 ใน "การเพิ่มประสิทธิภาพและแบตเตอรี่"
val config = CodecDb.onDevice(context)
   .forEncodingFallingBackTo {
           constraints -> createFallbackConfig(constraints)
   }
   .findOptimalSettings()
   .forEditing()
   .withResolution(1920, 1080, /* frameRate= */ 30.0)
   .withAnyCodec()
   .targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
   .get()