เครื่องมือจัดการ TV Input Framework (TIF) ทำงานร่วมกับ API การกำหนดเส้นทางเสียงเพื่อรองรับเสียงที่ยืดหยุ่น
เปลี่ยนเส้นทาง เมื่อระบบใช้ระบบวงจรรวมบนชิป (SoC) ใช้เลเยอร์แอบสแตรกชันของฮาร์ดแวร์ทีวี (HAL) (HAL)
อินพุตทีวี (HDMI IN, ตัวรับสัญญาณ เป็นต้น) ให้ TvInputHardwareInfo
ที่ระบุข้อมูล AudioPort สำหรับประเภทเสียงและที่อยู่
- อุปกรณ์อินพุต/เอาต์พุตเสียงทางกายภาพมี AudioPort ที่เกี่ยวข้องกัน
- เอาต์พุตเสียง/สตรีมอินพุตซอฟต์แวร์จะแสดงเป็น AudioMixPort (คลาสย่อยของ AudioPort)
จากนั้น TIF จะใช้ข้อมูล AudioPort สำหรับ Audio Routing API
ข้อกำหนด
SoC ต้องใช้ HAL เสียงที่มีการรองรับ API การกำหนดเส้นทางเสียงดังต่อไปนี้
พอร์ตเสียง |
|
---|---|
อินพุตเริ่มต้น | AudioRecord (สร้างขึ้นด้วยแหล่งที่มาของอินพุต DEFAULT) ต้องใช้แหล่งที่มาของอินพุตเสมือนสำหรับ การได้ผู้ใช้ใหม่ AUDIO_DEVICE_IN_DEFAULT บน Android TV |
Loopback ของอุปกรณ์ | ต้องรองรับอินพุต AUDIO_DEVICE_IN_LOOPBACK ที่ผสมผสานเอาต์พุตเสียงทั้งหมดอย่างสมบูรณ์ ของเอาต์พุตทีวีทั้งหมด (11 กิโลเฮิร์ตซ์, โมโน 16 บิต หรือ 48 กิโลเฮิร์ตซ์, โมโน 16 บิต) ใช้สำหรับบันทึกเสียงเท่านั้น |
อุปกรณ์เสียงจากทีวี
Android สนับสนุนอุปกรณ์เสียงต่อไปนี้สำหรับอินพุต/เอาต์พุตเสียงทีวี
system/media/audio/include/system/audio.h
หมายเหตุ: ใน Android 5.1 และรุ่นก่อนหน้า เส้นทางไปยัง
ไฟล์นี้คือ system/core/include/system/audio.h
/* output devices */ AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL, /* HDMI Audio Return Channel */ AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000, /* S/PDIF out */ AUDIO_DEVICE_OUT_SPDIF = 0x80000, /* input devices */ AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL, /* TV tuner input */ AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000, /* S/PDIF in */ AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000, AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000,
ส่วนขยาย HAL ของเสียง
ส่วนขยาย HAL เสียงสำหรับ API การกำหนดเส้นทางเสียงจะกำหนดดังนี้
system/media/audio/include/system/audio.h
หมายเหตุ: ใน Android 5.1 และรุ่นก่อนหน้า เส้นทางไปยัง
ไฟล์นี้คือ system/core/include/system/audio.h
/* audio port configuration structure used to specify a particular configuration of an audio port */ struct audio_port_config { audio_port_handle_t id; /* port unique ID */ audio_port_role_t role; /* sink or source */ audio_port_type_t type; /* device, mix ... */ unsigned int config_mask; /* e.g. AUDIO_PORT_CONFIG_ALL */ unsigned int sample_rate; /* sampling rate in Hz */ audio_channel_mask_t channel_mask; /* channel mask if applicable */ audio_format_t format; /* format if applicable */ struct audio_gain_config gain; /* gain to apply if applicable */ union { struct audio_port_config_device_ext device; /* device specific info */ struct audio_port_config_mix_ext mix; /* mix specific info */ struct audio_port_config_session_ext session; /* session specific info */ } ext; }; struct audio_port { audio_port_handle_t id; /* port unique ID */ audio_port_role_t role; /* sink or source */ audio_port_type_t type; /* device, mix ... */ unsigned int num_sample_rates; /* number of sampling rates in following array */ unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES]; unsigned int num_channel_masks; /* number of channel masks in following array */ audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS]; unsigned int num_formats; /* number of formats in following array */ audio_format_t formats[AUDIO_PORT_MAX_FORMATS]; unsigned int num_gains; /* number of gains in following array */ struct audio_gain gains[AUDIO_PORT_MAX_GAINS]; struct audio_port_config active_config; /* current audio port configuration */ union { struct audio_port_device_ext device; struct audio_port_mix_ext mix; struct audio_port_session_ext session; } ext; };
hardware/libhardware/include/hardware/audio.h
struct audio_hw_device { : /** * Routing control */ /* Creates an audio patch between several source and sink ports. * The handle is allocated by the HAL and should be unique for this * audio HAL module. */ int (*create_audio_patch)(struct audio_hw_device *dev, unsigned int num_sources, const struct audio_port_config *sources, unsigned int num_sinks, const struct audio_port_config *sinks, audio_patch_handle_t *handle); /* Release an audio patch */ int (*release_audio_patch)(struct audio_hw_device *dev, audio_patch_handle_t handle); /* Fills the list of supported attributes for a given audio port. * As input, "port" contains the information (type, role, address etc...) * needed by the HAL to identify the port. * As output, "port" contains possible attributes (sampling rates, formats, * channel masks, gain controllers...) for this port. */ int (*get_audio_port)(struct audio_hw_device *dev, struct audio_port *port); /* Set audio port configuration */ int (*set_audio_port_config)(struct audio_hw_device *dev, const struct audio_port_config *config);
กำลังทดสอบ DEVICE_IN_LOOPBACK
หากต้องการทดสอบ DEVICE_IN_LOOPBACK สำหรับการตรวจสอบทีวี ให้ใช้รหัสการทดสอบต่อไปนี้ หลังจากเรียกใช้
เสียงที่บันทึกไว้จะบันทึกไปยัง /sdcard/record_loopback.raw
ซึ่งคุณจะฟังได้
โดยใช้ FFmpeg
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> AudioRecord mRecorder; Handler mHandler = new Handler(); int mMinBufferSize = AudioRecord.getMinBufferSize(RECORD_SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);; static final int RECORD_SAMPLING_RATE = 48000; public void doCapture() { mRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, RECORD_SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mMinBufferSize * 10); AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); ArrayList<AudioPort> audioPorts = new ArrayList<AudioPort>(); am.listAudioPorts(audioPorts); AudioPortConfig srcPortConfig = null; AudioPortConfig sinkPortConfig = null; for (AudioPort audioPort : audioPorts) { if (srcPortConfig == null && audioPort.role() == AudioPort.ROLE_SOURCE && audioPort instanceof AudioDevicePort) { AudioDevicePort audioDevicePort = (AudioDevicePort) audioPort; if (audioDevicePort.type() == AudioManager.DEVICE_IN_LOOPBACK) { srcPortConfig = audioPort.buildConfig(48000, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_DEFAULT, null); Log.d(LOG_TAG, "Found loopback audio source port : " + audioPort); } } else if (sinkPortConfig == null && audioPort.role() == AudioPort.ROLE_SINK && audioPort instanceof AudioMixPort) { sinkPortConfig = audioPort.buildConfig(48000, AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_DEFAULT, null); Log.d(LOG_TAG, "Found recorder audio mix port : " + audioPort); } } if (srcPortConfig != null && sinkPortConfig != null) { AudioPatch[] patches = new AudioPatch[] { null }; int status = am.createAudioPatch( patches, new AudioPortConfig[] { srcPortConfig }, new AudioPortConfig[] { sinkPortConfig }); Log.d(LOG_TAG, "Result of createAudioPatch(): " + status); } mRecorder.startRecording(); processAudioData(); mRecorder.stop(); mRecorder.release(); } private void processAudioData() { OutputStream rawFileStream = null; byte data[] = new byte[mMinBufferSize]; try { rawFileStream = new BufferedOutputStream( new FileOutputStream(new File("/sdcard/record_loopback.raw"))); } catch (FileNotFoundException e) { Log.d(LOG_TAG, "Can't open file.", e); } long startTimeMs = System.currentTimeMillis(); while (System.currentTimeMillis() - startTimeMs < 5000) { int nbytes = mRecorder.read(data, 0, mMinBufferSize); if (nbytes <= 0) { continue; } try { rawFileStream.write(data); } catch (IOException e) { Log.e(LOG_TAG, "Error on writing raw file.", e); } } try { rawFileStream.close(); } catch (IOException e) { } Log.d(LOG_TAG, "Exit audio recording."); }
ค้นหาไฟล์เสียงที่บันทึกไว้ใน /sdcard/record_loopback.raw
และฟังโดยใช้
FFmpeg
:
adb pull /sdcard/record_loopback.raw
ffmpeg -f s16le -ar 48k -ac 1 -i record_loopback.raw record_loopback.wav
ffplay record_loopback.wav
กรณีการใช้งาน
ส่วนนี้ประกอบด้วยกรณีการใช้งานทั่วไปสำหรับเสียงทีวี
ตัวรับสัญญาณทีวีพร้อมเอาต์พุตลำโพง
เมื่อตัวรับสัญญาณทีวีทำงาน API การกำหนดเส้นทางเสียงจะสร้างแพตช์เสียงระหว่างตัวรับสัญญาณ และเอาต์พุตเริ่มต้น (เช่น ลำโพง) เอาต์พุตตัวรับสัญญาณไม่จำเป็นต้องมีการถอดรหัส แต่จำเป็นต้องถอดรหัส เอาต์พุตเสียงจะผสมกับซอฟต์แวร์output_stream
HDMI OUT ระหว่างดูรายการทีวีสด
ผู้ใช้กำลังดูรายการทีวีสดแล้วเปลี่ยนไปใช้เอาต์พุตเสียง HDMI (Intent.ACTION_HDMI_AUDIO_PLUG) ที่ใช้เวลาเพียง 2 นาที อุปกรณ์เอาต์พุตของ exit_streams ทั้งหมดจะเปลี่ยนเป็นพอร์ต HDMI_OUT และตัวจัดการ TIF เปลี่ยนไป ใช้พอร์ตซิงก์ของแพตช์เสียงตัวรับสัญญาณที่มีอยู่ไปยังพอร์ต HDMI_OUT