อุโมงค์มัลติมีเดีย

คุณสามารถใช้ช่องสัญญาณมัลติมีเดียในกรอบงาน Android 5.0 ขึ้นไปได้ แม้ว่า Android TV จะไม่จำเป็นต้องใช้อุโมงค์ข้อมูลมัลติมีเดีย แต่ก็ให้ประสบการณ์ที่ดีที่สุดสำหรับเนื้อหาที่มีความคมชัดสูงพิเศษ (4K)

สำหรับ Android 11 หรือสูงกว่า คุณสามารถใช้ช่องสัญญาณมัลติมีเดียกับเนื้อหาเสียงและวิดีโอที่ป้อนโดยตรงจากเครื่องรับสัญญาณ Codec2 และ AudioTrack สามารถใช้ ID ซิงค์ HW จากจูนเนอร์ซึ่งอาจจะตรงกับการอ้างอิงนาฬิกาโปรแกรม (PCR) หรือเวลานาฬิการะบบ (STC) ช่อง

พื้นหลัง

กรอบงานสื่อ Android จัดการเนื้อหาเสียง/วิดีโอในสี่วิธี:

  • ซอฟแวร์เพียว (ถอดรหัสท้องถิ่น): หน่วยประมวลผลแอพลิเคชัน (AP) ในประเทศถอดรหัสเสียงชีพจรรหัสเอฟเอ็ม (PCM) โดยเร่งพิเศษ ใช้สำหรับ Ogg Vorbis เสมอ และใช้สำหรับ MP3 และ AAC เมื่อไม่มีการรองรับการบีบอัดข้อมูล
  • อัด Offload เสียง ส่งการบีบอัดข้อมูลเสียงโดยตรงกับการประมวลผลสัญญาณดิจิตอล (DSP) และช่วยให้ AP ออกมากที่สุดเท่าที่เป็นไปได้ ใช้สำหรับเล่นไฟล์เพลงโดยปิดหน้าจอ
  • อัด passthrough เสียงส่งสัญญาณเสียงบีบอัด (เฉพาะ AC3 และ E-AC3) โดยตรงผ่าน HDMI เข้ากับทีวีภายนอกหรือรับสัญญาณเสียงโดยไม่ต้องถอดรหัสบนอุปกรณ์ Android ทีวี ส่วนวิดีโอได้รับการจัดการแยกต่างหาก
  • มัลติมีเดียอุโมงค์ส่งการบีบอัดข้อมูลเสียงและวิดีโอร่วมกัน เมื่อตัวถอดรหัสวิดีโอและเสียงได้รับสตรีมที่เข้ารหัส จะไม่กลับไปที่เฟรมเวิร์ก ตามหลักการแล้วสตรีมจะไม่ขัดจังหวะ AP
  • มัลติมีเดีย passthrough ส่งการบีบอัดข้อมูลเสียงและวิดีโอด้วยกันจากการจูนเนอร์วิดีโอและเสียงถอดรหัสโดยไม่เกี่ยวข้องกับกรอบ
แผนภาพการไหลของอุโมงค์มัลติมีเดีย
การไหลเวียนของอุโมงค์รูปที่ 1 มัลติมีเดีย

วิธีการเปรียบเทียบ

ซอฟต์แวร์บริสุทธิ์ บีบอัดไฟล์เสียง การส่งผ่านเสียงที่บีบอัด อุโมงค์มัลติมีเดีย การส่งผ่านมัลติมีเดีย
ถอดรหัสตำแหน่ง AP DSP เครื่องรับโทรทัศน์หรือเสียง/วิดีโอ (AVR) ทีวีหรือ AVR ทีวีหรือ AVR
จัดการเสียง ใช่ ใช่ ใช่ ใช่ ไม่
จัดการวิดีโอ ใช่ ไม่ ไม่ ใช่ ไม่

สำหรับนักพัฒนาแอพ

สร้าง SurfaceView เช่นได้รับรหัสเซสชันเสียงแล้วสร้าง AudioTrack และ MediaCodec กรณีที่จะให้ระยะเวลาที่จำเป็นและการกำหนดค่าสำหรับการเล่นและกรอบถอดรหัสวิดีโอ

สำหรับ Android 11 หรือสูงกว่าเป็นทางเลือกสำหรับ ID เซสชั่นเสียง app ที่จะได้รับรหัส HW ซิงค์จากจูนเนอร์และให้มันให้ AudioTrack และ MediaCodec กรณีสำหรับการซิงค์ A / V

ซิงค์ A/V

ในโหมดทันเนลมัลติมีเดีย เสียงและวิดีโอจะซิงโครไนซ์กับนาฬิกาหลัก

  • สำหรับ Android 11 หรือสูงกว่า PCR หรือ STC จาก Tuner อาจเป็นนาฬิกาหลักสำหรับการซิงค์ A/V
  • สำหรับ Android 10 หรือต่ำกว่า นาฬิกาเสียงคือนาฬิกาหลักที่ใช้สำหรับการเล่น A/V

หากวิดีโอเจาะ MediaCodec อินสแตนซ์และ AudioTrack กรณีจะเชื่อมโยงกับ HW_AV_SYNC เช่นใน AudioTrack นาฬิกานัยมาจาก HW_AV_SYNC จำกัด เมื่อเฟรมวิดีโอแต่ละคนและตัวอย่างเสียงจะถูกนำเสนอบนพื้นฐานของเสียงที่เกิดขึ้นจริงหรือเฟรมวิดีโอประทับเวลาการนำเสนอ (PTS)

โฟลว์การเรียก API

สำหรับ Android 11 หรือสูงกว่า ไคลเอ็นต์สามารถใช้ HW sync ID จาก Tuner

  1. สร้าง SurfaceView อินสแตนซ์
    SurfaceView sv = new SurfaceView(mContext);
  2. รับ ID เซสชันเสียง นี้ ID ที่ไม่ซ้ำกันถูกนำมาใช้ในการสร้างแทร็กเสียง ( AudioTrack ) มันส่งผ่านไปยังตัวแปลงสัญญาณสื่อ ( MediaCodec ) และใช้งานโดยกรอบการทำงานของสื่อในการเชื่อมโยงเส้นทางเสียงและวิดีโอ
    AudioManager am = mContext.getSystemService(AUDIO_SERVICE);
    int audioSessionId = am.generateAudioSessionId()
    // or, for Android 11 or higher
    int avSyncId = tuner.getAvSyncHwId();
  3. สร้าง AudioTrack กับ HW ซิงค์ A / V AudioAttributes

    ผู้จัดการนโยบายเสียงถามฮาร์ดแวร์ Abstraction Layer (HAL) สำหรับการส่งออกอุปกรณ์ที่สนับสนุน FLAG_HW_AV_SYNC และสร้างแทร็กเสียงเชื่อมต่อโดยตรงกับการแสดงผลนี้โดยไม่มีการผสมกลาง

    AudioAttributes.Builder aab = new AudioAttributes.Builder();
    aab.setUsage(AudioAttributes.USAGE_MEDIA);
    aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);
    aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
    
    // or, for Android 11 or higher
    new tunerConfig = TunerConfiguration(0, avSyncId);
    aab.setTunerConfiguration(tunerConfig);
    
    AudioAttributes aa = aab.build();
    AudioTrack at = new AudioTrack(aa);
    
  4. สร้างวิดีโอ MediaCodec อินสแตนซ์และกำหนดค่าสำหรับการเล่นวิดีโอเจาะ
    // retrieve codec with tunneled video playback feature
    MediaFormat mf = MediaFormat.createVideoFormat(“video/hevc”, 3840, 2160);
    mf.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
    MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
    String codecName = mcl.findDecoderForFormat(mf);
    if (codecName == null) {
      return FAILURE;
    }
    // create codec and configure it
    mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
    
    // or, for Android 11 or higher
    mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);
    
    MediaCodec mc = MediaCodec.createCodecByName(codecName);
    mc.configure(mf, sv.getSurfaceHolder().getSurface(), null, 0);
    
  5. ถอดรหัสเฟรมวิดีโอ
    mc.start();
     for (;;) {
       int ibi = mc.dequeueInputBuffer(timeoutUs);
       if (ibi >= 0) {
         ByteBuffer ib = mc.getInputBuffer(ibi);
         // fill input buffer (ib) with valid data
         ...
         mc.queueInputBuffer(ibi, ...);
       }
       // no need to dequeue explicitly output buffers. The codec
       // does this directly to the sideband layer.
     }
     mc.stop();
     mc.release();
     mc = null;

หมายเหตุ: คุณสามารถสลับลำดับขั้นตอนที่ 3 และ 4 ในขั้นตอนนี้เท่าที่เห็นในสองร่างด้านล่าง

ไดอะแกรมของแทร็กเสียงที่สร้างก่อนกำหนดค่าตัวแปลงสัญญาณ
รูปที่ 2 แทร็กเสียงที่สร้างขึ้นก่อนที่จะกำหนดค่าตัวแปลงสัญญาณ
ไดอะแกรมของแทร็กเสียงที่สร้างขึ้นหลังจากกำหนดค่าตัวแปลงสัญญาณ
รูปที่ 3 แทร็กเสียงที่สร้างขึ้นหลังจากกำหนดค่าตัวแปลงสัญญาณ

สำหรับผู้ผลิตอุปกรณ์

OEMs ควรสร้างวิดีโอแยกต่างหากถอดรหัส OpenMAX อิลลินอยส์ (OMX) ส่วนประกอบที่ให้การสนับสนุนการเล่นวิดีโอเจาะ องค์ประกอบ OMX นี้จะต้องโฆษณาว่ามันเป็นความสามารถในการเล่นเจาะ (ใน media_codecs.xml )

<Feature name=”tunneled-playback” required=”true” />

องค์ประกอบที่ยังต้องสนับสนุนการขยาย OMX พารามิเตอร์ OMX.google.android.index.configureVideoTunnelMode ที่ใช้ ConfigureVideoTunnelModeParams โครงสร้าง

struct ConfigureVideoTunnelModeParams {
    OMX_U32 nSize;              // IN
    OMX_VERSIONTYPE nVersion;   // IN
    OMX_U32 nPortIndex;         // IN
    OMX_BOOL bTunneled;         // IN/OUT
    OMX_U32 nAudioHwSync;       // IN
    OMX_PTR pSidebandWindow;    // OUT
};

เมื่อเจาะ MediaCodec คำขอสร้างทำกรอบกำหนดค่าองค์ประกอบ OMX ในโหมดเจาะ (โดยการตั้งค่า bTunneled เพื่อ OMX_TRUE ) และผ่านอุปกรณ์ส่งออกที่เกี่ยวข้องเสียงที่สร้างขึ้นด้วยการ AUDIO_HW_AV_SYNC ธงองค์ประกอบ OMX (ใน nAudioHwSync )

หากสนับสนุนองค์ประกอบการกำหนดค่านี้ก็ควรจัดสรรจับ sideband เพื่อแปลงสัญญาณนี้และผ่านมันกลับผ่าน pSidebandWindow สมาชิก จับ sideband เป็นแท็ก ID ชั้นเจาะที่ช่วยให้ นักแต่งเพลงฮาร์ดแวร์ (HW นักแต่งเพลง) ระบุว่า หากองค์ประกอบไม่สนับสนุนการกำหนดค่านี้ก็ควรตั้ง bTunneled เพื่อ OMX_FALSE

เฟรมเวิร์กดึงข้อมูลเลเยอร์ tunneled (แฮนเดิลไซด์แบนด์) ที่จัดสรรโดยคอมโพเนนต์ OMX และส่งผ่านไปยัง HW Composer ของชั้นนี้ compositionType ได้รับการตั้งค่าให้ HWC_SIDEBAND (ดู hardware/libhardware/include/hardware/hwcomposer.h .)

HW Composer มีหน้าที่รับบัฟเฟอร์รูปภาพใหม่จากสตรีมในเวลาที่เหมาะสม (เช่น ซิงโครไนซ์กับอุปกรณ์เอาต์พุตเสียงที่เกี่ยวข้อง) ประกอบเข้ากับเนื้อหาปัจจุบันของเลเยอร์อื่น และแสดงรูปภาพที่ได้ สิ่งนี้เกิดขึ้นโดยไม่ขึ้นกับรอบการเตรียม/ชุดปกติ การเรียกเตรียม/ตั้งค่าจะเกิดขึ้นก็ต่อเมื่อเลเยอร์อื่นเปลี่ยนแปลง หรือเมื่อคุณสมบัติของเลเยอร์แถบด้านข้าง (เช่น ตำแหน่งหรือขนาด) เปลี่ยนไป

การกำหนดค่า

frameworks/av/services/audioflinger/AudioFlinger.cpp

ฮาลกลับ HW_AV_SYNC ID เป็นตัวแทนสายอักขระทศนิยมของจำนวนเต็ม 64 บิต (ดู frameworks/av/services/audioflinger/AudioFlinger.cpp .)

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

frameworks/av/services/audioflinger/Threads.cpp

กรอบเสียงจะต้องพบกับกระแสออก HAL ที่นี้สอดคล้อง ID เซสชั่นและแบบสอบถาม HAL สำหรับ hwAVSyncId ใช้ set_parameters

mOutput->stream->common.set_parameters(&mOutput->stream->common,
AUDIO_PARAMETER_STREAM_HW_AV_SYNC=hwAVSyncId);

การกำหนดค่าตัวถอดรหัส OMX

MediaCodec.java

กรอบเสียงพบว่ากระแสออกสอดคล้อง HAL สำหรับ ID เซสชั่นนี้และดึง audio-hw-sync ID โดยสอบถาม HAL สำหรับ AUDIO_PARAMETER_STREAM_HW_AV_SYNC ธงใช้ get_parameters

// Retrieve HW AV sync audio output device from Audio Service
// in MediaCodec.configure()
if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

// ...

ID HW นี้ซิงค์ถูกส่งไปยัง OMX เจาะถอดรหัสวิดีโอโดยใช้พารามิเตอร์ที่กำหนดเอง OMX.google.android.index.configureVideoTunnelMode

ACodec.cpp

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

// Assume you're going to use tunneled video rendering.
// Configure OMX component in tunneled mode and grab sideband handle (sidebandHandle) from OMX
// component.

native_handle_t* sidebandHandle;

// Configure OMX component in tunneled mode
status_t err = mOMX->configureVideoTunnelMode(mNode, kPortIndexOutput,
        OMX_TRUE, audioHwSync, &sidebandHandle);

OMXNodeInstance.cpp

องค์ประกอบ OMX มีการกำหนดค่าโดย configureVideoTunnelMode วิธีการข้างต้น

// paraphrased

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);

err = OMX_GetParameter(mHandle, index, &tunnelParams);

sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

ACodec.cpp

หลังจากกำหนดค่าคอมโพเนนต์ OMX ในโหมดทันเนล แฮนเดิลไซด์แบนด์จะเชื่อมโยงกับพื้นผิวการแสดงผล

  err = native_window_set_sideband_stream(nativeWindow.get(),
  sidebandHandle); if (err != OK) { ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).",
  sidebandHandle, err); return err; }

จากนั้นคำใบ้ความละเอียดสูงสุด หากมี จะถูกส่งไปยังคอมโพเนนต์

// Configure max adaptive playback resolution - as for any other video decoder
int32_t maxWidth = 0, maxHeight = 0;
if (msg->findInt32("max-width", &maxWidth) &&
    msg->findInt32("max-height", &maxHeight)) {
    err = mOMX->prepareForAdaptivePlayback(
              mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight);
}

หยุดสนับสนุน

Android 5.0 และต่ำกว่าไม่รองรับการหยุดชั่วคราว คุณสามารถหยุดการเล่นทันเนลชั่วคราวได้ด้วยการอดอาหาร A/V เท่านั้น แต่ถ้าบัฟเฟอร์ภายในสำหรับวิดีโอมีขนาดใหญ่ (เช่น มีข้อมูล 1 วินาทีในองค์ประกอบ OMX) จะทำให้การหยุดชั่วคราวดูเหมือนไม่ตอบสนอง

ใน Android 5.1 ขึ้นไป AudioFlinger รองรับการหยุดชั่วคราวและเล่นต่อสำหรับเอาต์พุตเสียงโดยตรง (แบบอุโมงค์) หาก HAL ใช้การหยุดชั่วคราว/เล่นต่อ การติดตามการหยุดชั่วคราว/ดำเนินการต่อจะถูกส่งต่อไปยัง HAL

ลำดับการเรียกหยุดชั่วคราว ล้าง และเล่นต่อนั้นทำได้โดยเรียกใช้การเรียก HAL ในเธรดการเล่น (เหมือนกับ offload)

รองรับ Codec2

สำหรับ Android 11 หรือสูงกว่า Codec2 รองรับการเล่นแบบทันเนล

CCcodec.cpp

เพื่อรองรับการเล่นแบบทันเนล Codec2 ทำงานคล้ายกับ OMX เพื่อรองรับ HW sync ID จาก Tuner, Codec2 จะค้นหา sync ID จากแหล่งที่มาด้านล่าง

sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get());
int32_t audioHwSync = 0;
if (!msg->findInt32("hw-av-sync-id", &audioHwSync)) {
       if (!msg->findInt32("audio-hw-sync", &audioHwSync)) {
       }
}
err = configureTunneledVideoPlayback(comp, audioHwSync, nativeWindow);

คำแนะนำในการดำเนินการ

เครื่องเสียง HAL

สำหรับ Android 11 สามารถใช้ HW sync ID จาก PCR หรือ STC สำหรับการซิงค์ A/V ได้ ดังนั้นจึงรองรับการสตรีมเฉพาะวิดีโอเท่านั้น

สำหรับ Android 10 หรือต่ำกว่าอุปกรณ์ที่รองรับการเล่นวิดีโอเจาะควรจะมีรายละเอียดกระแสออกอย่างน้อยหนึ่งเสียงที่มีธง FLAG_HW_AV_SYNC และ AUDIO_OUTPUT_FLAG_DIRECT ในของ audio_policy.conf ไฟล์ แฟล็กเหล่านี้ใช้เพื่อตั้งค่านาฬิการะบบจากนาฬิกาเสียง

OMX

ผู้ผลิตอุปกรณ์ควรมีส่วนประกอบ OMX แยกต่างหากสำหรับการเล่นวิดีโอแบบทันเนล ผู้ผลิตสามารถมีส่วนประกอบ OMX เพิ่มเติมสำหรับการเล่นเสียงและวิดีโอประเภทอื่นๆ เช่น การเล่นที่ปลอดภัย

ส่วนนี้ควรระบุ 0 บัฟเฟอร์ ( nBufferCountMin , nBufferCountActual ) บนพอร์ตเอาท์พุท

องค์ประกอบเจาะยังต้องใช้ OMX.google.android.index.prepareForAdaptivePlayback setParameter ขยาย

องค์ประกอบเจาะต้องระบุความสามารถของตนใน media_codecs.xml ไฟล์และประกาศคุณสมบัติการเล่นเจาะ นอกจากนี้ยังควรชี้แจงข้อจำกัดเกี่ยวกับขนาดเฟรม การจัดตำแหน่ง หรือบิตเรต


    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>

หากใช้องค์ประกอบ OMX เดียวกันเพื่อรองรับการถอดรหัสแบบทันเนลและไม่ใช่แบบทันเนล คอมโพเนนต์ดังกล่าวควรปล่อยให้ฟีเจอร์การเล่นแบบทันเนลเป็นแบบไม่จำเป็น ตัวถอดรหัสทั้งแบบทันเนลและที่ไม่ใช่แบบทันเนลจะมีข้อจำกัดด้านความสามารถเหมือนกัน

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC" type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>

HW นักแต่งเพลง

เมื่อมีการเจาะชั้น (ชั้นกับ HWC_SIDEBAND compositionType ) บนจอแสดงผลชั้นของ sidebandStream เป็นหมายเลขอ้างอิง sideband ที่จัดสรรโดยองค์ประกอบวิดีโอ OMX

HW นักแต่งเพลงซิงค์ถอดรหัสภาพวิดีโอ (จากองค์ประกอบ OMX เจาะ) ไปแทร็กเสียงที่เกี่ยวข้อง (กับ audio-hw-sync ID) เมื่อเฟรมวิดีโอใหม่กลายเป็นเฟรมปัจจุบัน HW Composer จะรวมเฟรมกับเนื้อหาปัจจุบันของเลเยอร์ทั้งหมดที่ได้รับในระหว่างการเตรียม/ตั้งค่าการโทรล่าสุด และแสดงภาพที่ได้ การจัดเตรียม/ตั้งค่าการโทรจะเกิดขึ้นเมื่อเลเยอร์อื่นเปลี่ยนแปลง หรือเมื่อคุณสมบัติของเลเยอร์ไซด์แบนด์ (เช่น ตำแหน่งหรือขนาด) เปลี่ยนไป

รูปที่ 4 แสดง HW Composer ที่ทำงานร่วมกับตัวซิงโครไนซ์ HW (หรือเคอร์เนล/ไดรเวอร์) เพื่อรวมเฟรมวิดีโอ (7b) กับองค์ประกอบล่าสุด (7a) สำหรับการแสดงผลในเวลาที่ถูกต้อง โดยอิงจากเสียง (7c)

ไดอะแกรมของผู้แต่งฮาร์ดแวร์ที่รวมเฟรมวิดีโอตามเสียง
รูปที่ 4 HW นักแต่งเพลงที่ทำงานร่วมกับ HW (หรือเคอร์เนล / ขับ) Synchronizer