Add Tuner framework AudioPresentation support
DVB MPEG-2 transport streams can include audio preselection descriptors
for next-generation audio (NGA) as specified in ETSI EN 300 468. The
tuner framework audio preselection is mapped to media framework audio
presentations and made available via MediaEvent.
Bug: 264812332
Test: CtsTvTestCases:TunerTest#testAudioFilterStreamTypeConfig
on aosp_cf_x86_tv with android.hardware.tv.tuner-service.example
Change-Id: I57ff6fd29da0744f622aec64c1344abf4cfb0d91
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b4594e0..6151e69 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7876,6 +7876,7 @@
public class MediaEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getAudioHandle();
+ method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public long getAvDataId();
method public long getDataLength();
method public long getDts();
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index d958db1..8f2e7b5 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -18,10 +18,15 @@
import android.annotation.BytesLong;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.media.AudioPresentation;
import android.media.MediaCodec.LinearBlock;
+import java.util.Collections;
+import java.util.List;
+
/**
* Filter event sent from {@link Filter} objects with media type.
*
@@ -51,12 +56,13 @@
private final boolean mIsPrivateData;
private final int mScIndexMask;
private final AudioDescriptor mExtraMetaData;
+ private final List<AudioPresentation> mAudioPresentations;
// This constructor is used by JNI code only
private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
int mpuSequenceNumber, boolean isPrivateData, int scIndexMask,
- AudioDescriptor extraMetaData) {
+ AudioDescriptor extraMetaData, List<AudioPresentation> audioPresentations) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
@@ -71,6 +77,7 @@
mIsPrivateData = isPrivateData;
mScIndexMask = scIndexMask;
mExtraMetaData = extraMetaData;
+ mAudioPresentations = audioPresentations;
}
/**
@@ -215,6 +222,13 @@
return mExtraMetaData;
}
+ /**
+ * Gets audio presentations.
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations() {
+ return mAudioPresentations == null ? Collections.emptyList() : mAudioPresentations;
+ }
/**
* Finalize the MediaEvent object.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index a0304bb..ed1072c 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,6 +88,7 @@
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
"android.hardware.drm-V1-ndk",
+ "android.hardware.tv.tuner-V2-ndk",
],
header_libs: [
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index a3adddd..0c83ddf 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -19,10 +19,14 @@
#include "jni.h"
+#include <aidl/android/hardware/tv/tuner/AudioPresentation.h>
#include <media/stagefright/foundation/ADebug.h> // CHECK
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>
+using ::aidl::android::hardware::tv::tuner::AudioPreselectionRenderingIndicationType;
+using TunerAudioPresentation = ::aidl::android::hardware::tv::tuner::AudioPresentation;
+
namespace android {
struct JAudioPresentationInfo {
@@ -97,6 +101,38 @@
}
static void addPresentations(JNIEnv *env, const fields_t& fields,
+ const std::vector<TunerAudioPresentation>& tunerAudioPresentations,
+ jobject presentationsJObj) {
+ AudioPresentationCollection apc = {};
+ static const std::map<AudioPreselectionRenderingIndicationType, MasteringIndication> mMap {
+ { AudioPreselectionRenderingIndicationType::NOT_INDICATED, MASTERING_NOT_INDICATED },
+ { AudioPreselectionRenderingIndicationType::STEREO, MASTERED_FOR_STEREO },
+ { AudioPreselectionRenderingIndicationType::TWO_DIMENSIONAL, MASTERED_FOR_SURROUND },
+ { AudioPreselectionRenderingIndicationType::THREE_DIMENSIONAL, MASTERED_FOR_3D },
+ { AudioPreselectionRenderingIndicationType::HEADPHONE, MASTERED_FOR_HEADPHONE },
+ };
+ for (const auto& tap : tunerAudioPresentations) {
+ AudioPresentationV1 ap;
+ ap.mPresentationId = tap.preselection.preselectionId;
+ ap.mProgramId = tap.ac4ShortProgramId;
+ for (const auto& md : tap.preselection.labels) {
+ ap.mLabels.insert(std::pair(md.language, md.text));
+ }
+ ap.mLanguage = tap.preselection.language;
+ ap.mMasteringIndication = MASTERING_NOT_INDICATED;
+ auto masteringSearch = mMap.find(tap.preselection.renderingIndication);
+ if (masteringSearch != mMap.end()) {
+ ap.mMasteringIndication = masteringSearch->second;
+ }
+ ap.mAudioDescriptionAvailable = tap.preselection.hasAudioDescription;
+ ap.mSpokenSubtitlesAvailable = tap.preselection.hasSpokenSubtitles;
+ ap.mDialogueEnhancementAvailable = tap.preselection.hasDialogueEnhancement;
+ apc.push_back(ap);
+ }
+ addPresentations(env, fields, apc, presentationsJObj);
+ }
+
+ static void addPresentations(JNIEnv *env, const fields_t& fields,
const AudioPresentationCollection& presentations, jobject presentationsJObj) {
for (const auto& ap : presentations) {
ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index aacea3d..0147406 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -20,6 +20,7 @@
#include "android_media_tv_Tuner.h"
#include <aidl/android/hardware/tv/tuner/AudioExtraMetaData.h>
+#include <aidl/android/hardware/tv/tuner/AudioPresentation.h>
#include <aidl/android/hardware/tv/tuner/AudioStreamType.h>
#include <aidl/android/hardware/tv/tuner/AvStreamType.h>
#include <aidl/android/hardware/tv/tuner/Constant.h>
@@ -155,12 +156,14 @@
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
+#include "android_media_AudioPresentation.h"
#include "android_media_MediaCodecLinearBlock.h"
#include "android_runtime/AndroidRuntime.h"
#pragma GCC diagnostic ignored "-Wunused-function"
using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::AudioPreselection;
using ::aidl::android::hardware::tv::tuner::AudioStreamType;
using ::aidl::android::hardware::tv::tuner::AvStreamType;
using ::aidl::android::hardware::tv::tuner::Constant;
@@ -350,6 +353,9 @@
}
namespace android {
+
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
+
/////////////// LnbClientCallbackImpl ///////////////////////
void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
ALOGV("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
@@ -620,27 +626,45 @@
eventClazz,
"<init>",
"(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
- "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+ "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
+ "Ljava/util/List;)V");
jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
jobject audioDescriptor = nullptr;
- if (mediaEvent.extraMetaData.getTag() == DemuxFilterMediaEventExtraMetaData::Tag::audio) {
- jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
- jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
+ gAudioPresentationFields.init(env);
+ jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+ switch (mediaEvent.extraMetaData.getTag()) {
+ case DemuxFilterMediaEventExtraMetaData::Tag::audio: {
+ jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
+ jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
- const AudioExtraMetaData &ad =
- mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
- jbyte adFade = ad.adFade;
- jbyte adPan = ad.adPan;
- jchar versionTextTag = ad.versionTextTag;
- jbyte adGainCenter = ad.adGainCenter;
- jbyte adGainFront = ad.adGainFront;
- jbyte adGainSurround = ad.adGainSurround;
+ const AudioExtraMetaData &ad =
+ mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
+ jbyte adFade = ad.adFade;
+ jbyte adPan = ad.adPan;
+ jchar versionTextTag = ad.versionTextTag;
+ jbyte adGainCenter = ad.adGainCenter;
+ jbyte adGainFront = ad.adGainFront;
+ jbyte adGainSurround = ad.adGainSurround;
- audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
- adGainCenter, adGainFront, adGainSurround);
- env->DeleteLocalRef(adClazz);
+ audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
+ adGainCenter, adGainFront, adGainSurround);
+ env->DeleteLocalRef(adClazz);
+ break;
+ }
+ case DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations: {
+ JAudioPresentationInfo::addPresentations(
+ env, gAudioPresentationFields,
+ mediaEvent.extraMetaData
+ .get<DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations>(),
+ presentationsJObj);
+ break;
+ }
+ default: {
+ ALOGE("FilterClientCallbackImpl::getMediaEvent: unknown extraMetaData");
+ break;
+ }
}
jlong dataLength = mediaEvent.dataLength;
@@ -669,7 +693,8 @@
jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
- mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor);
+ mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor,
+ presentationsJObj);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -688,6 +713,7 @@
}
env->DeleteLocalRef(obj);
env->DeleteLocalRef(eventClazz);
+ env->DeleteLocalRef(presentationsJObj);
}
void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,