├── Android.mk
├── AndroidManifest.xml
├── README.md
├── jni
├── Android.mk
└── JniWifiDisplaySink.cpp
├── lib
├── ANetworkSession.cpp
├── ANetworkSession.h
├── Android.mk
├── MediaReceiver.cpp
├── MediaReceiver.h
├── Parameters.cpp
├── Parameters.h
├── PlantUtils.cpp
├── PlantUtils.h
├── TimeSyncer.cpp
├── TimeSyncer.h
├── VideoFormats.cpp
├── VideoFormats.h
├── rtp
│ ├── RTPAssembler.cpp
│ ├── RTPAssembler.h
│ ├── RTPBase.h
│ ├── RTPReceiver.cpp
│ └── RTPReceiver.h
└── sink
│ ├── DirectRenderer.cpp
│ ├── DirectRenderer.h
│ ├── WifiDisplaySink.cpp
│ └── WifiDisplaySink.h
├── res
├── anim
│ ├── frame.xml
│ └── frame2.xml
├── drawable-hdpi
│ ├── app_loading0.png
│ ├── app_loading1.png
│ ├── ic_launcher.png
│ ├── progress_loading_image_01.png
│ └── progress_loading_image_02.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── layout
│ ├── activity_waiting_connection.xml
│ └── activity_wifidisplay_sink.xml
├── raw
│ ├── combo_sdp.xml
│ ├── keyboard_sdp_bak.xml
│ ├── mouse_sdp.xml
│ └── mouse_sdp_temp.xml
├── values-ja
│ └── strings.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
├── values-zh
│ └── strings.xml
└── values
│ ├── arrays.xml
│ ├── strings.xml
│ └── styles.xml
├── src
└── com
│ └── lc
│ └── wifidisplaysink
│ ├── HidDeviceAdapterService.java
│ ├── HidDeviceAdapterService.java.stub
│ ├── RarpImpl.java
│ ├── WaitingConnectionActivity.java
│ ├── WifiDisplaySink.java
│ ├── WifiDisplaySinkActivity.java
│ ├── WifiDisplaySinkConstants.java
│ ├── WifiDisplaySinkUtils.java
│ └── WifiDisplaySinkView.java
└── tags
/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 | include $(CLEAR_VARS)
3 |
4 | LOCAL_MODULE_TAGS := optional
5 |
6 | ifeq ($(PLATFORM_VERSION), 6.0.1)
7 | WFDSINK_JAVA_PATH := "$(LOCAL_PATH)/src/com/lc/wifidisplaysink"
8 | $(info $(shell cp $(WFDSINK_JAVA_PATH)/HidDeviceAdapterService.java.stub $(WFDSINK_JAVA_PATH)/HidDeviceAdapterService.java))
9 | endif
10 |
11 | LOCAL_SRC_FILES := $(call all-subdir-java-files)
12 |
13 | LOCAL_JAVA_LIBRARIES := com.broadcom.bt javax.obex
14 |
15 | LOCAL_PACKAGE_NAME := WifiDisplaySink
16 | LOCAL_CERTIFICATE := platform
17 |
18 | LOCAL_PROGUARD_ENABLED := disabled
19 |
20 | LOCAL_JNI_SHARED_LIBRARIES := libWifiDisplaySink
21 | LOCAL_REQUIRED_MODULES := libWifiDisplaySink
22 |
23 | include $(BUILD_PACKAGE)
24 | include $(call all-makefiles-under, $(LOCAL_PATH))
25 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WifiDisplaySink
2 | This is a wifi display sink implementation.
3 | The native library is extended from google's old android release.
4 | The some simple activitis is added at app layer.
5 |
6 |
--------------------------------------------------------------------------------
/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | LOCAL_MODULE := libWifiDisplaySink
6 | LOCAL_SRC_FILES := JniWifiDisplaySink.cpp
7 |
8 | LOCAL_C_INCLUDES += \
9 | $(JNI_H_INCLUDE) \
10 | $(TOP)/frameworks/native/include \
11 | $(TOP)/frameworks/native/include/media/openmax \
12 | $(TOP)/frameworks/base/include \
13 | $(TOP)/frameworks/av/include/media/stagefright/foundation \
14 | $(TOP)/leadcore/packages/apps/WifiDisplaySink/lib
15 |
16 | LOCAL_SHARED_LIBRARIES:= \
17 | libbinder \
18 | libgui \
19 | libmedia \
20 | libstagefright \
21 | libstagefright_foundation \
22 | libstagefright_wfd2 \
23 | libutils \
24 | libcutils \
25 | libandroid_runtime \
26 | libnativehelper \
27 |
28 | LOCAL_CERTIFICATE := platform
29 |
30 | include $(BUILD_SHARED_LIBRARY)
31 |
--------------------------------------------------------------------------------
/jni/JniWifiDisplaySink.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | //#define LOG_NDEBUG 0
6 | #define LOG_TAG "JniWifiDisplaySink"
7 | #include
8 | #include
9 |
10 | #include "sink/WifiDisplaySink.h"
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include "jni.h"
28 | #include "JNIHelp.h"
29 | #include "android_runtime/android_view_Surface.h"
30 | #include "android_runtime/AndroidRuntime.h"
31 | #include "android_runtime/Log.h"
32 |
33 |
34 | using namespace android;
35 |
36 | struct fields_t {
37 | jfieldID context;
38 | jmethodID post_event;
39 | };
40 | static fields_t fields;
41 |
42 | class JniWfdSinkListener;
43 |
44 | struct WfdNativeContext: public RefBase {
45 | sp mWifiDisplaySink;
46 | sp mSurfaceComposerClient;
47 | sp mSurfaceControl;
48 | sp mSurface;
49 | sp mLooper;
50 | sp mListener;
51 | WfdNativeContext() {
52 | mWifiDisplaySink = NULL;
53 | mSurfaceComposerClient = NULL;
54 | mSurfaceControl = NULL;
55 | mSurface = NULL;
56 | mLooper = NULL;
57 | }
58 | ~WfdNativeContext() {
59 | mWifiDisplaySink.clear();
60 | mSurface.clear();
61 | mSurfaceControl.clear();
62 | mSurfaceComposerClient.clear();
63 | mLooper.clear();
64 | }
65 | };
66 |
67 | static WfdNativeContext *getWfdNativeContext(JNIEnv* env, jobject thiz)
68 | {
69 | ALOGD("getWfdNativeContext");
70 | WfdNativeContext* context = reinterpret_cast(env->GetLongField(thiz, fields.context));
71 | return context;
72 | }
73 |
74 | static void prepareWfdNativeContext(JNIEnv* env, jobject thiz)
75 | {
76 | ALOGD("prepareWfdNativeContext");
77 | sp context = new WfdNativeContext();
78 | context->incStrong((void*)prepareWfdNativeContext);
79 |
80 | env->SetLongField(thiz, fields.context, (jlong)context.get());
81 | }
82 |
83 | class JniWfdSinkListener: public WfdSinkListener
84 | {
85 | public:
86 | JniWfdSinkListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
87 | ~JniWfdSinkListener();
88 | virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
89 | private:
90 | JniWfdSinkListener();
91 | jclass mClass;
92 | jobject mObject;
93 | };
94 |
95 | JniWfdSinkListener::JniWfdSinkListener(JNIEnv* env, jobject thiz, jobject weak_thiz) {
96 | jclass clazz = env->GetObjectClass(thiz);
97 | if (clazz == NULL) {
98 | ALOGE("Can't find com/lc/WifiDisplaySink/WifiDisplaySink");
99 | jniThrowException(env, "java/lang/Exception", NULL);
100 | return;
101 | }
102 |
103 | ALOGD("inc GlobalRef about thiz and weak_thiz");
104 | mClass = (jclass)env->NewGlobalRef(clazz);
105 | mObject = env->NewGlobalRef(weak_thiz);
106 | }
107 |
108 | JniWfdSinkListener::~JniWfdSinkListener(){
109 | ALOGD("~JniWfdSinkListener");
110 | JNIEnv *env = AndroidRuntime::getJNIEnv();
111 | env->DeleteGlobalRef(mObject);
112 | env->DeleteGlobalRef(mClass);
113 | }
114 |
115 | void JniWfdSinkListener::notify(int msg, int ext1, int ext2, const Parcel *obj) {
116 | JNIEnv *env = AndroidRuntime::getJNIEnv();
117 |
118 | ALOGD("notify %d, %d, %d", msg, ext1, ext2);
119 |
120 | if (mObject == NULL) {
121 | ALOGW("callback on dead wfdsink object");
122 | return;
123 | }
124 |
125 | env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
126 | msg, ext1, ext2, NULL);
127 |
128 | if (env->ExceptionCheck()) {
129 | ALOGW("An exception occurred while notifying an event.");
130 | env->ExceptionClear();
131 | }
132 | }
133 |
134 | static int32_t GetInt32Property(
135 | const char *propName, int32_t defaultValue) {
136 | char val[PROPERTY_VALUE_MAX];
137 | if (property_get(propName, val, NULL)) {
138 | char *end;
139 | unsigned long x = strtoul(val, &end, 10);
140 |
141 | if (*end == '\0' && end > val && x > 0) {
142 | return x;
143 | }
144 | }
145 |
146 | return defaultValue;
147 | }
148 |
149 | static void getDisplayDimensions(ssize_t *w, ssize_t *h) {
150 | sp display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
151 | DisplayInfo info;
152 | SurfaceComposerClient::getDisplayInfo(display, &info);
153 | bool rotate = true;
154 | int dimentionRot = GetInt32Property("media.wfd.sink.dimention", 0);
155 | ALOGI("set buffer dimention: %d", dimentionRot);
156 | if (dimentionRot > 0) {
157 | *w = info.h;
158 | *h = info.w;
159 | } else {
160 | *w = info.w;
161 | *h = info.h;
162 | }
163 | }
164 |
165 | static void
166 | com_lc_wifidisplaysink_WifiDisplaySink_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint special, jint is_N10)
167 | {
168 |
169 | ProcessState::self()->startThreadPool();
170 | DataSource::RegisterDefaultSniffers();
171 |
172 | bool specialMode = special == 1;
173 | bool isN10 = is_N10 == 1;
174 |
175 | /*sp composerClient = new SurfaceComposerClient;
176 | CHECK_EQ(composerClient->initCheck(), (status_t)OK);
177 |
178 | ssize_t displayWidth = 0;
179 | ssize_t displayHeight = 0;
180 | getDisplayDimensions(&displayWidth, &displayHeight);
181 |
182 | ALOGD("Sink Display[%d, %d] Special[%d] Nexus10[%d]", displayWidth, displayHeight, specialMode, isN10);
183 |
184 | sp control =
185 | composerClient->createSurface(
186 | String8("A Sink Surface"),
187 | // displayWidth,
188 | // displayHeight,
189 | isN10 ? displayHeight : displayWidth,
190 | isN10 ? displayWidth : displayHeight,
191 | PIXEL_FORMAT_RGB_565,
192 | 0);
193 |
194 |
195 | CHECK(control != NULL);
196 | CHECK(control->isValid());
197 |
198 | SurfaceComposerClient::openGlobalTransaction();
199 | CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
200 | CHECK_EQ(control->show(), (status_t)OK);
201 | SurfaceComposerClient::closeGlobalTransaction();
202 |
203 | sp surface = control->getSurface();
204 | CHECK(surface != NULL);*/
205 |
206 | sp session = new ANetworkSession;
207 | session->start();
208 |
209 | sp looper = new ALooper;
210 |
211 | sp sink = new WifiDisplaySink(
212 | looper,
213 | specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */,
214 | session);
215 |
216 | looper->registerHandler(sink);
217 |
218 | sp listener = new JniWfdSinkListener(env, thiz, weak_this);
219 | sink->setListener(listener);
220 |
221 | prepareWfdNativeContext(env, thiz);
222 | WfdNativeContext *ctx = getWfdNativeContext(env, thiz);
223 | //ctx->mSurfaceComposerClient = composerClient;
224 | //ctx->mSurfaceControl = control;
225 | //ctx->mSurface = surface;
226 | ctx->mWifiDisplaySink = sink;
227 | ctx->mLooper = looper;
228 | //ctx->mListener = listener;
229 |
230 | looper->start(false /* runOnCallingThread */, true /*canCallJava*/ );
231 |
232 | ALOGD("setup finished");
233 | //composerClient->dispose();
234 |
235 | }
236 |
237 | static void
238 | com_lc_wifidisplaysink_WifiDisplaySink_nativeInvokeSink(JNIEnv* env, jobject thiz, jstring ipaddr, jint port) {
239 | const char *ip = env->GetStringUTFChars(ipaddr, NULL);
240 | ALOGD("Source Addr[%s] Port[%d]", ip, port);
241 |
242 | WfdNativeContext *ctx = getWfdNativeContext(env, thiz);
243 | sp sink = ctx->mWifiDisplaySink;
244 | if (sink == NULL) {
245 | ALOGE("should call setup first.");
246 | return;
247 | }
248 |
249 |
250 | ALOGD("start sink");
251 | sink->start(ip, port);
252 | }
253 |
254 | static void
255 | com_lc_wifidisplaysink_WifiDisplaySink_native_init(JNIEnv *env) {
256 | jclass clazz;
257 |
258 | clazz = env->FindClass("com/lc/wifidisplaysink/WifiDisplaySink");
259 | if (clazz == NULL) {
260 | return;
261 | }
262 |
263 | fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
264 | ALOGD("fields.context : %lld", fields.context);
265 | if (fields.context == NULL) {
266 | return;
267 | }
268 | fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
269 | "(Ljava/lang/Object;IIILjava/lang/Object;)V");
270 | if (fields.post_event == NULL) {
271 | ALOGE("postEventFromNative is NULL");
272 | return;
273 | }
274 |
275 | env->DeleteLocalRef(clazz);
276 | }
277 |
278 | static void
279 | com_lc_wifidisplaysink_WifiDisplaySink_native_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
280 | {
281 | ALOGD("setVideoSurface");
282 | sp new_st;
283 | if (jsurface) {
284 | sp surface(android_view_Surface_getSurface(env, jsurface));
285 | if (surface != NULL) {
286 | new_st = surface->getIGraphicBufferProducer();
287 | if (new_st == NULL) {
288 | jniThrowException(env, "java/lang/IllegalArgumentException",
289 | "The surface does not have a binding SurfaceTexture!");
290 | return;
291 | }
292 | WfdNativeContext *ctx = getWfdNativeContext(env, thiz);
293 | ctx->mSurface = surface;
294 | sp sink = ctx->mWifiDisplaySink;
295 | if (sink == NULL) {
296 | ALOGE("should call setup first.");
297 | return;
298 | }
299 | sink->setDisplay(new_st);
300 | } else {
301 | jniThrowException(env, "java/lang/IllegalArgumentException",
302 | "The surface has been released");
303 | return;
304 | }
305 | }
306 | }
307 |
308 | static JNINativeMethod gMethods[] = {
309 | {"native_init", "()V", (void *)com_lc_wifidisplaysink_WifiDisplaySink_native_init},
310 | {"native_setup", "(Ljava/lang/Object;II)V", (void *)com_lc_wifidisplaysink_WifiDisplaySink_native_setup},
311 | {
312 | "native_invokeSink",
313 | "(Ljava/lang/String;I)V",
314 | (void *)com_lc_wifidisplaysink_WifiDisplaySink_nativeInvokeSink
315 | },
316 | {"native_setVideoSurface", "(Landroid/view/Surface;)V", (void *)com_lc_wifidisplaysink_WifiDisplaySink_native_setVideoSurface},
317 | };
318 |
319 | jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
320 | JNIEnv* env = NULL;
321 | jint result = -1;
322 |
323 | if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
324 | ALOGE("ERROR: GetEnv failed\n");
325 | return -1;
326 | }
327 |
328 | assert(env != NULL);
329 | int ret = AndroidRuntime::registerNativeMethods(env,
330 | "com/lc/wifidisplaysink/WifiDisplaySink", gMethods, NELEM(gMethods));
331 | if (ret < 0) {
332 | ALOGE("ERROR: registerNativeMethods failed\n");
333 | return ret;
334 | } else {
335 | return JNI_VERSION_1_4;
336 | }
337 |
338 | }
339 |
340 |
--------------------------------------------------------------------------------
/lib/ANetworkSession.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef A_NETWORK_SESSION_H_
18 |
19 | #define A_NETWORK_SESSION_H_
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | namespace android {
29 |
30 | struct AMessage;
31 |
32 | // Helper class to manage a number of live sockets (datagram and stream-based)
33 | // on a single thread. Clients are notified about activity through AMessages.
34 | struct ANetworkSession : public RefBase {
35 | ANetworkSession();
36 |
37 | status_t start();
38 | status_t stop();
39 |
40 | status_t createRTSPClient(
41 | const char *host, unsigned port, const sp ¬ify,
42 | int32_t *sessionID);
43 |
44 | status_t createRTSPServer(
45 | const struct in_addr &addr, unsigned port,
46 | const sp ¬ify, int32_t *sessionID);
47 |
48 | status_t createUDPSession(
49 | unsigned localPort, const sp ¬ify, int32_t *sessionID);
50 |
51 | status_t createUDPSession(
52 | unsigned localPort,
53 | const char *remoteHost,
54 | unsigned remotePort,
55 | const sp ¬ify,
56 | int32_t *sessionID);
57 |
58 | status_t connectUDPSession(
59 | int32_t sessionID, const char *remoteHost, unsigned remotePort);
60 |
61 | // passive
62 | status_t createTCPDatagramSession(
63 | const struct in_addr &addr, unsigned port,
64 | const sp ¬ify, int32_t *sessionID);
65 |
66 | // active
67 | status_t createTCPDatagramSession(
68 | unsigned localPort,
69 | const char *remoteHost,
70 | unsigned remotePort,
71 | const sp ¬ify,
72 | int32_t *sessionID);
73 |
74 | status_t destroySession(int32_t sessionID);
75 |
76 | status_t sendRequest(
77 | int32_t sessionID, const void *data, ssize_t size = -1,
78 | bool timeValid = false, int64_t timeUs = -1ll);
79 |
80 | status_t switchToWebSocketMode(int32_t sessionID);
81 |
82 | enum NotificationReason {
83 | kWhatError,
84 | kWhatConnected,
85 | kWhatClientConnected,
86 | kWhatData,
87 | kWhatDatagram,
88 | kWhatBinaryData,
89 | kWhatWebSocketMessage,
90 | kWhatNetworkStall,
91 | };
92 |
93 | protected:
94 | virtual ~ANetworkSession();
95 |
96 | private:
97 | struct NetworkThread;
98 | struct Session;
99 |
100 | Mutex mLock;
101 | sp mThread;
102 |
103 | int32_t mNextSessionID;
104 |
105 | int mPipeFd[2];
106 |
107 | KeyedVector > mSessions;
108 |
109 | enum Mode {
110 | kModeCreateUDPSession,
111 | kModeCreateTCPDatagramSessionPassive,
112 | kModeCreateTCPDatagramSessionActive,
113 | kModeCreateRTSPServer,
114 | kModeCreateRTSPClient,
115 | };
116 | status_t createClientOrServer(
117 | Mode mode,
118 | const struct in_addr *addr,
119 | unsigned port,
120 | const char *remoteHost,
121 | unsigned remotePort,
122 | const sp ¬ify,
123 | int32_t *sessionID);
124 |
125 | void threadLoop();
126 | void interrupt();
127 |
128 | static status_t MakeSocketNonBlocking(int s);
129 |
130 | DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
131 | };
132 |
133 | } // namespace android
134 |
135 | #endif // A_NETWORK_SESSION_H_
136 |
--------------------------------------------------------------------------------
/lib/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | LOCAL_SRC_FILES:= \
6 | PlantUtils.cpp \
7 | MediaReceiver.cpp \
8 | Parameters.cpp \
9 | rtp/RTPAssembler.cpp \
10 | rtp/RTPReceiver.cpp \
11 | sink/DirectRenderer.cpp \
12 | sink/WifiDisplaySink.cpp \
13 | TimeSyncer.cpp \
14 | VideoFormats.cpp \
15 | ANetworkSession.cpp \
16 |
17 | LOCAL_C_INCLUDES:= \
18 | $(TOP)/frameworks/av/media/libstagefright \
19 | $(TOP)/frameworks/native/include/media/openmax \
20 | $(TOP)/frameworks/av/media/libstagefright/mpeg2ts \
21 |
22 | LOCAL_SHARED_LIBRARIES:= \
23 | libbinder \
24 | libcutils \
25 | liblog \
26 | libgui \
27 | libmedia \
28 | libstagefright \
29 | libstagefright_foundation \
30 | libui \
31 | libutils \
32 |
33 | ifeq ($(PLATFORM_VERSION), 6.0.1)
34 | LOCAL_CFLAGS += -DANDROID6_0
35 | else
36 | ifeq ($(PLATFORM_VERSION), 5.1.1)
37 | LOCAL_CFLAGS += -DANDROID5_1
38 | else
39 | ifeq ($(PLATFORM_VERSION), 4.4.4)
40 | LOCAL_CFLAGS += -DANDROID4_4
41 | endif
42 | endif
43 | endif
44 |
45 | LOCAL_MODULE:= libstagefright_wfd2
46 |
47 | LOCAL_MODULE_TAGS:= optional
48 |
49 | include $(BUILD_SHARED_LIBRARY)
50 |
--------------------------------------------------------------------------------
/lib/MediaReceiver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#define LOG_NDEBUG 0
18 | #define LOG_TAG "MediaReceiver"
19 | #include
20 |
21 | #include "MediaReceiver.h"
22 | #include "AnotherPacketSource.h"
23 | #include "rtp/RTPReceiver.h"
24 |
25 | #include
26 | #include
27 | #include
28 | //#include
29 | #include "ANetworkSession.h"
30 | #include
31 | #include
32 | #include "PlantUtils.h"
33 |
34 | namespace android {
35 |
36 | MediaReceiver::MediaReceiver(
37 | const sp &netSession,
38 | const sp ¬ify)
39 | : mNetSession(netSession),
40 | mNotify(notify),
41 | mMode(MODE_UNDEFINED),
42 | mGeneration(0),
43 | mInitStatus(OK),
44 | mInitDoneCount(0) {
45 | }
46 |
47 | MediaReceiver::~MediaReceiver() {
48 | }
49 |
50 | ssize_t MediaReceiver::addTrack(
51 | RTPReceiver::TransportMode rtpMode,
52 | RTPReceiver::TransportMode rtcpMode,
53 | int32_t *localRTPPort,
54 | int32_t forceRtpPort) {
55 | if (mMode != MODE_UNDEFINED) {
56 | return INVALID_OPERATION;
57 | }
58 |
59 | size_t trackIndex = mTrackInfos.size();
60 |
61 | TrackInfo info;
62 |
63 | sp notify = PlantUtils::newAMessage(kWhatReceiverNotify, this);
64 | notify->setInt32("generation", mGeneration);
65 | notify->setSize("trackIndex", trackIndex);
66 |
67 | info.mReceiver = new RTPReceiver(mNetSession, notify);
68 | looper()->registerHandler(info.mReceiver);
69 |
70 | info.mReceiver->registerPacketType(
71 | 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM);
72 |
73 | info.mReceiver->registerPacketType(
74 | 96, RTPReceiver::PACKETIZATION_AAC);
75 |
76 | info.mReceiver->registerPacketType(
77 | 97, RTPReceiver::PACKETIZATION_H264);
78 |
79 | status_t err = info.mReceiver->initAsync(
80 | rtpMode,
81 | rtcpMode,
82 | localRTPPort,
83 | forceRtpPort);
84 |
85 | if (err != OK) {
86 | looper()->unregisterHandler(info.mReceiver->id());
87 | info.mReceiver.clear();
88 |
89 | return err;
90 | }
91 |
92 | mTrackInfos.push_back(info);
93 |
94 | return trackIndex;
95 | }
96 |
97 | status_t MediaReceiver::connectTrack(
98 | size_t trackIndex,
99 | const char *remoteHost,
100 | int32_t remoteRTPPort,
101 | int32_t remoteRTCPPort) {
102 | if (trackIndex >= mTrackInfos.size()) {
103 | return -ERANGE;
104 | }
105 |
106 | TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
107 | return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort);
108 | }
109 |
110 | status_t MediaReceiver::initAsync(Mode mode) {
111 | if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW)
112 | && mTrackInfos.size() > 1) {
113 | return INVALID_OPERATION;
114 | }
115 |
116 | sp msg = PlantUtils::newAMessage(kWhatInit, this);
117 | msg->setInt32("mode", mode);
118 | msg->post();
119 |
120 | return OK;
121 | }
122 |
123 | void MediaReceiver::onMessageReceived(const sp &msg) {
124 | switch (msg->what()) {
125 | case kWhatInit:
126 | {
127 | int32_t mode;
128 | CHECK(msg->findInt32("mode", &mode));
129 |
130 | CHECK_EQ(mMode, MODE_UNDEFINED);
131 | mMode = (Mode)mode;
132 |
133 | if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
134 | notifyInitDone(mInitStatus);
135 | }
136 |
137 | mTSParser = new ATSParser(
138 | ATSParser::ALIGNED_VIDEO_DATA
139 | | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
140 |
141 | mFormatKnownMask = 0;
142 | break;
143 | }
144 |
145 | case kWhatReceiverNotify:
146 | {
147 | int32_t generation;
148 | CHECK(msg->findInt32("generation", &generation));
149 | if (generation != mGeneration) {
150 | break;
151 | }
152 |
153 | onReceiverNotify(msg);
154 | break;
155 | }
156 |
157 | default:
158 | TRESPASS();
159 | }
160 | }
161 |
162 | void MediaReceiver::onReceiverNotify(const sp &msg) {
163 | int32_t what;
164 | CHECK(msg->findInt32("what", &what));
165 |
166 | switch (what) {
167 | case RTPReceiver::kWhatInitDone:
168 | {
169 | ++mInitDoneCount;
170 |
171 | int32_t err;
172 | CHECK(msg->findInt32("err", &err));
173 |
174 | if (err != OK) {
175 | mInitStatus = err;
176 | ++mGeneration;
177 | }
178 |
179 | if (mMode != MODE_UNDEFINED) {
180 | if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
181 | notifyInitDone(mInitStatus);
182 | }
183 | }
184 | break;
185 | }
186 |
187 | case RTPReceiver::kWhatError:
188 | {
189 | int32_t err;
190 | CHECK(msg->findInt32("err", &err));
191 |
192 | notifyError(err);
193 | break;
194 | }
195 |
196 | case RTPReceiver::kWhatAccessUnit:
197 | {
198 | size_t trackIndex;
199 | CHECK(msg->findSize("trackIndex", &trackIndex));
200 |
201 | sp accessUnit;
202 | CHECK(msg->findBuffer("accessUnit", &accessUnit));
203 |
204 | int32_t followsDiscontinuity;
205 | if (!msg->findInt32(
206 | "followsDiscontinuity", &followsDiscontinuity)) {
207 | followsDiscontinuity = 0;
208 | }
209 |
210 | if (mMode == MODE_TRANSPORT_STREAM) {
211 | if (followsDiscontinuity) {
212 | mTSParser->signalDiscontinuity(
213 | ATSParser::DISCONTINUITY_TIME, NULL /* extra */);
214 | }
215 |
216 | for (size_t offset = 0;
217 | offset < accessUnit->size(); offset += 188) {
218 | status_t err = mTSParser->feedTSPacket(
219 | accessUnit->data() + offset, 188);
220 |
221 | if (err != OK) {
222 | notifyError(err);
223 | break;
224 | }
225 | }
226 |
227 | drainPackets(0 /* trackIndex */, ATSParser::VIDEO);
228 | drainPackets(1 /* trackIndex */, ATSParser::AUDIO);
229 | } else {
230 | postAccessUnit(trackIndex, accessUnit, NULL);
231 | }
232 | break;
233 | }
234 |
235 | case RTPReceiver::kWhatPacketLost:
236 | {
237 | notifyPacketLost();
238 | break;
239 | }
240 |
241 | default:
242 | TRESPASS();
243 | }
244 | }
245 |
246 | void MediaReceiver::drainPackets(
247 | size_t trackIndex, ATSParser::SourceType type) {
248 | sp source =
249 | static_cast(
250 | mTSParser->getSource(type).get());
251 |
252 | if (source == NULL) {
253 | return;
254 | }
255 |
256 | sp format;
257 | if (!(mFormatKnownMask & (1ul << trackIndex))) {
258 | sp meta = source->getFormat();
259 | CHECK(meta != NULL);
260 |
261 | meta->setInt32(kKeyRotation, 90); // yangxudong
262 | ALOGD("####################kKeyRotation 90");
263 |
264 | CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format));
265 |
266 | mFormatKnownMask |= 1ul << trackIndex;
267 | }
268 |
269 | status_t finalResult;
270 | while (source->hasBufferAvailable(&finalResult)) {
271 | sp accessUnit;
272 | status_t err = source->dequeueAccessUnit(&accessUnit);
273 | if (err == OK) {
274 | postAccessUnit(trackIndex, accessUnit, format);
275 | format.clear();
276 | } else if (err != INFO_DISCONTINUITY) {
277 | notifyError(err);
278 | }
279 | }
280 |
281 | if (finalResult != OK) {
282 | notifyError(finalResult);
283 | }
284 | }
285 |
286 | void MediaReceiver::notifyInitDone(status_t err) {
287 | sp notify = mNotify->dup();
288 | notify->setInt32("what", kWhatInitDone);
289 | notify->setInt32("err", err);
290 | notify->post();
291 | }
292 |
293 | void MediaReceiver::notifyError(status_t err) {
294 | sp notify = mNotify->dup();
295 | notify->setInt32("what", kWhatError);
296 | notify->setInt32("err", err);
297 | notify->post();
298 | }
299 |
300 | void MediaReceiver::notifyPacketLost() {
301 | sp notify = mNotify->dup();
302 | notify->setInt32("what", kWhatPacketLost);
303 | notify->post();
304 | }
305 |
306 | void MediaReceiver::postAccessUnit(
307 | size_t trackIndex,
308 | const sp &accessUnit,
309 | const sp &format) {
310 | sp notify = mNotify->dup();
311 | notify->setInt32("what", kWhatAccessUnit);
312 | notify->setSize("trackIndex", trackIndex);
313 | notify->setBuffer("accessUnit", accessUnit);
314 |
315 | if (format != NULL) {
316 | notify->setMessage("format", format);
317 | }
318 |
319 | notify->post();
320 | }
321 |
322 | status_t MediaReceiver::informSender(
323 | size_t trackIndex, const sp ¶ms) {
324 | if (trackIndex >= mTrackInfos.size()) {
325 | return -ERANGE;
326 | }
327 |
328 | TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
329 | return info->mReceiver->informSender(params);
330 | }
331 |
332 | } // namespace android
333 |
334 |
335 |
--------------------------------------------------------------------------------
/lib/MediaReceiver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 |
19 | #include "ATSParser.h"
20 | #include "rtp/RTPReceiver.h"
21 |
22 | namespace android {
23 |
24 | struct ABuffer;
25 | struct ANetworkSession;
26 | struct AMessage;
27 | struct ATSParser;
28 |
29 | // This class facilitates receiving of media data for one or more tracks
30 | // over RTP. Either a 1:1 track to RTP channel mapping is used or a single
31 | // RTP channel provides the data for a transport stream that is consequently
32 | // demuxed and its track's data provided to the observer.
33 | struct MediaReceiver : public AHandler {
34 | enum {
35 | kWhatInitDone,
36 | kWhatError,
37 | kWhatAccessUnit,
38 | kWhatPacketLost,
39 | };
40 |
41 | MediaReceiver(
42 | const sp &netSession,
43 | const sp ¬ify);
44 |
45 | ssize_t addTrack(
46 | RTPReceiver::TransportMode rtpMode,
47 | RTPReceiver::TransportMode rtcpMode,
48 | int32_t *localRTPPort,
49 | int32_t forceRtpPort);
50 |
51 | status_t connectTrack(
52 | size_t trackIndex,
53 | const char *remoteHost,
54 | int32_t remoteRTPPort,
55 | int32_t remoteRTCPPort);
56 |
57 | enum Mode {
58 | MODE_UNDEFINED,
59 | MODE_TRANSPORT_STREAM,
60 | MODE_TRANSPORT_STREAM_RAW,
61 | MODE_ELEMENTARY_STREAMS,
62 | };
63 | status_t initAsync(Mode mode);
64 |
65 | status_t informSender(size_t trackIndex, const sp ¶ms);
66 |
67 | protected:
68 | virtual void onMessageReceived(const sp &msg);
69 | virtual ~MediaReceiver();
70 |
71 | private:
72 | enum {
73 | kWhatInit,
74 | kWhatReceiverNotify,
75 | };
76 |
77 | struct TrackInfo {
78 | sp mReceiver;
79 | };
80 |
81 | sp mNetSession;
82 | sp mNotify;
83 |
84 | Mode mMode;
85 | int32_t mGeneration;
86 |
87 | Vector mTrackInfos;
88 |
89 | status_t mInitStatus;
90 | size_t mInitDoneCount;
91 |
92 | sp mTSParser;
93 | uint32_t mFormatKnownMask;
94 |
95 | void onReceiverNotify(const sp &msg);
96 |
97 | void drainPackets(size_t trackIndex, ATSParser::SourceType type);
98 |
99 | void notifyInitDone(status_t err);
100 | void notifyError(status_t err);
101 | void notifyPacketLost();
102 |
103 | void postAccessUnit(
104 | size_t trackIndex,
105 | const sp &accessUnit,
106 | const sp &format);
107 |
108 | DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver);
109 | };
110 |
111 | } // namespace android
112 |
113 |
--------------------------------------------------------------------------------
/lib/Parameters.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "Parameters.h"
18 |
19 | #include
20 |
21 | namespace android {
22 |
23 | // static
24 | sp Parameters::Parse(const char *data, size_t size) {
25 | sp params = new Parameters;
26 | status_t err = params->parse(data, size);
27 |
28 | if (err != OK) {
29 | return NULL;
30 | }
31 |
32 | return params;
33 | }
34 |
35 | Parameters::Parameters() {}
36 |
37 | Parameters::~Parameters() {}
38 |
39 | status_t Parameters::parse(const char *data, size_t size) {
40 | size_t i = 0;
41 | while (i < size) {
42 | size_t nameStart = i;
43 | while (i < size && data[i] != ':') {
44 | ++i;
45 | }
46 |
47 | if (i == size || i == nameStart) {
48 | return ERROR_MALFORMED;
49 | }
50 |
51 | AString name(&data[nameStart], i - nameStart);
52 | name.trim();
53 | name.tolower();
54 |
55 | ++i;
56 |
57 | size_t valueStart = i;
58 |
59 | while (i + 1 < size && (data[i] != '\r' || data[i + 1] != '\n')) {
60 | ++i;
61 | }
62 |
63 | AString value(&data[valueStart], i - valueStart);
64 | value.trim();
65 |
66 | mDict.add(name, value);
67 |
68 | while (i + 1 < size && data[i] == '\r' && data[i + 1] == '\n') {
69 | i += 2;
70 | }
71 | }
72 |
73 | return OK;
74 | }
75 |
76 | bool Parameters::findParameter(const char *name, AString *value) const {
77 | AString key = name;
78 | key.tolower();
79 |
80 | ssize_t index = mDict.indexOfKey(key);
81 |
82 | if (index < 0) {
83 | value->clear();
84 |
85 | return false;
86 | }
87 |
88 | *value = mDict.valueAt(index);
89 | return true;
90 | }
91 |
92 | } // namespace android
93 |
--------------------------------------------------------------------------------
/lib/Parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | namespace android {
23 |
24 | struct Parameters : public RefBase {
25 | static sp Parse(const char *data, size_t size);
26 |
27 | bool findParameter(const char *name, AString *value) const;
28 |
29 | protected:
30 | virtual ~Parameters();
31 |
32 | private:
33 | KeyedVector mDict;
34 |
35 | Parameters();
36 | status_t parse(const char *data, size_t size);
37 |
38 | DISALLOW_EVIL_CONSTRUCTORS(Parameters);
39 | };
40 |
41 | } // namespace android
42 |
--------------------------------------------------------------------------------
/lib/PlantUtils.cpp:
--------------------------------------------------------------------------------
1 | #include "PlantUtils.h"
2 | #include
3 |
4 | namespace android {
5 |
6 | sp PlantUtils::newAMessage(uint32_t what, const sp &handler) {
7 | #ifdef ANDROID6_0
8 | return new AMessage(what, handler);
9 | #else
10 | return new AMessage(what, handler->id());
11 | #endif
12 | }
13 |
14 | AString PlantUtils::newStringPrintf(const char *format, ...) {
15 | va_list ap;
16 | va_start(ap, format);
17 | char *buffer;
18 | vasprintf(&buffer, format, ap);
19 | va_end(ap);
20 | AString result(buffer);
21 | free(buffer);
22 | buffer = NULL;
23 | return result;
24 | }
25 |
26 | } // namespace android
27 |
--------------------------------------------------------------------------------
/lib/PlantUtils.h:
--------------------------------------------------------------------------------
1 | #ifndef __PLANT_UTILS__
2 | #define __PLANT_UTILS__
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 |
10 | namespace android{
11 |
12 | class PlantUtils {
13 | public:
14 | static sp newAMessage(uint32_t what, const sp &handler);
15 | static AString newStringPrintf(const char *format, ...);
16 | };
17 |
18 | } // namespace
19 | #endif //__PLANT_UTILS__
20 |
--------------------------------------------------------------------------------
/lib/TimeSyncer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#define LOG_NEBUG 0
18 | #define LOG_TAG "TimeSyncer"
19 | #include
20 |
21 | #include "TimeSyncer.h"
22 | #include "PlantUtils.h"
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | namespace android {
33 |
34 | TimeSyncer::TimeSyncer(
35 | const sp &netSession, const sp ¬ify)
36 | : mNetSession(netSession),
37 | mNotify(notify),
38 | mIsServer(false),
39 | mConnected(false),
40 | mUDPSession(0),
41 | mSeqNo(0),
42 | mTotalTimeUs(0.0),
43 | mPendingT1(0ll),
44 | mTimeoutGeneration(0) {
45 | }
46 |
47 | TimeSyncer::~TimeSyncer() {
48 | }
49 |
50 | void TimeSyncer::startServer(unsigned localPort) {
51 | sp msg = PlantUtils::newAMessage(kWhatStartServer, this);
52 | msg->setInt32("localPort", localPort);
53 | msg->post();
54 | }
55 |
56 | void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) {
57 | sp msg = PlantUtils::newAMessage(kWhatStartClient, this);
58 | msg->setString("remoteHost", remoteHost);
59 | msg->setInt32("remotePort", remotePort);
60 | msg->post();
61 | }
62 |
63 | void TimeSyncer::onMessageReceived(const sp &msg) {
64 | switch (msg->what()) {
65 | case kWhatStartClient:
66 | {
67 | AString remoteHost;
68 | CHECK(msg->findString("remoteHost", &remoteHost));
69 |
70 | int32_t remotePort;
71 | CHECK(msg->findInt32("remotePort", &remotePort));
72 |
73 | sp notify = PlantUtils::newAMessage(kWhatUDPNotify, this);
74 |
75 | CHECK_EQ((status_t)OK,
76 | mNetSession->createUDPSession(
77 | 0 /* localPort */,
78 | remoteHost.c_str(),
79 | remotePort,
80 | notify,
81 | &mUDPSession));
82 |
83 | postSendPacket();
84 | break;
85 | }
86 |
87 | case kWhatStartServer:
88 | {
89 | mIsServer = true;
90 |
91 | int32_t localPort;
92 | CHECK(msg->findInt32("localPort", &localPort));
93 |
94 | sp notify = PlantUtils::newAMessage(kWhatUDPNotify, this);
95 |
96 | CHECK_EQ((status_t)OK,
97 | mNetSession->createUDPSession(
98 | localPort, notify, &mUDPSession));
99 |
100 | break;
101 | }
102 |
103 | case kWhatSendPacket:
104 | {
105 | if (mHistory.size() == 0) {
106 | ALOGI("starting batch");
107 | }
108 |
109 | TimeInfo ti;
110 | memset(&ti, 0, sizeof(ti));
111 |
112 | ti.mT1 = ALooper::GetNowUs();
113 |
114 | CHECK_EQ((status_t)OK,
115 | mNetSession->sendRequest(
116 | mUDPSession, &ti, sizeof(ti)));
117 |
118 | mPendingT1 = ti.mT1;
119 | postTimeout();
120 | break;
121 | }
122 |
123 | case kWhatTimedOut:
124 | {
125 | int32_t generation;
126 | CHECK(msg->findInt32("generation", &generation));
127 |
128 | if (generation != mTimeoutGeneration) {
129 | break;
130 | }
131 |
132 | ALOGI("timed out, sending another request");
133 | postSendPacket();
134 | break;
135 | }
136 |
137 | case kWhatUDPNotify:
138 | {
139 | int32_t reason;
140 | CHECK(msg->findInt32("reason", &reason));
141 |
142 | switch (reason) {
143 | case ANetworkSession::kWhatError:
144 | {
145 | int32_t sessionID;
146 | CHECK(msg->findInt32("sessionID", &sessionID));
147 |
148 | int32_t err;
149 | CHECK(msg->findInt32("err", &err));
150 |
151 | AString detail;
152 | CHECK(msg->findString("detail", &detail));
153 |
154 | ALOGE("An error occurred in session %d (%d, '%s/%s').",
155 | sessionID,
156 | err,
157 | detail.c_str(),
158 | strerror(-err));
159 |
160 | mNetSession->destroySession(sessionID);
161 |
162 | cancelTimeout();
163 |
164 | notifyError(err);
165 | break;
166 | }
167 |
168 | case ANetworkSession::kWhatDatagram:
169 | {
170 | int32_t sessionID;
171 | CHECK(msg->findInt32("sessionID", &sessionID));
172 |
173 | sp packet;
174 | CHECK(msg->findBuffer("data", &packet));
175 |
176 | int64_t arrivalTimeUs;
177 | CHECK(packet->meta()->findInt64(
178 | "arrivalTimeUs", &arrivalTimeUs));
179 |
180 | CHECK_EQ(packet->size(), sizeof(TimeInfo));
181 |
182 | TimeInfo *ti = (TimeInfo *)packet->data();
183 |
184 | if (mIsServer) {
185 | if (!mConnected) {
186 | AString fromAddr;
187 | CHECK(msg->findString("fromAddr", &fromAddr));
188 |
189 | int32_t fromPort;
190 | CHECK(msg->findInt32("fromPort", &fromPort));
191 |
192 | CHECK_EQ((status_t)OK,
193 | mNetSession->connectUDPSession(
194 | mUDPSession, fromAddr.c_str(), fromPort));
195 |
196 | mConnected = true;
197 | }
198 |
199 | ti->mT2 = arrivalTimeUs;
200 | ti->mT3 = ALooper::GetNowUs();
201 |
202 | CHECK_EQ((status_t)OK,
203 | mNetSession->sendRequest(
204 | mUDPSession, ti, sizeof(*ti)));
205 | } else {
206 | if (ti->mT1 != mPendingT1) {
207 | break;
208 | }
209 |
210 | cancelTimeout();
211 | mPendingT1 = 0;
212 |
213 | ti->mT4 = arrivalTimeUs;
214 |
215 | // One way delay for a packet to travel from client
216 | // to server or back (assumed to be the same either way).
217 | int64_t delay =
218 | (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2;
219 |
220 | // Offset between the client clock (T1, T4) and the
221 | // server clock (T2, T3) timestamps.
222 | int64_t offset =
223 | (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2;
224 |
225 | mHistory.push_back(*ti);
226 |
227 | ALOGV("delay = %lld us,\toffset %lld us",
228 | delay,
229 | offset);
230 |
231 | if (mHistory.size() < kNumPacketsPerBatch) {
232 | postSendPacket(1000000ll / 30);
233 | } else {
234 | notifyOffset();
235 |
236 | ALOGI("batch done");
237 |
238 | mHistory.clear();
239 | postSendPacket(kBatchDelayUs);
240 | }
241 | }
242 | break;
243 | }
244 |
245 | default:
246 | TRESPASS();
247 | }
248 |
249 | break;
250 | }
251 |
252 | default:
253 | TRESPASS();
254 | }
255 | }
256 |
257 | void TimeSyncer::postSendPacket(int64_t delayUs) {
258 | (PlantUtils::newAMessage(kWhatSendPacket, this))->post(delayUs);
259 | }
260 |
261 | void TimeSyncer::postTimeout() {
262 | sp msg = PlantUtils::newAMessage(kWhatTimedOut, this);
263 | msg->setInt32("generation", mTimeoutGeneration);
264 | msg->post(kTimeoutDelayUs);
265 | }
266 |
267 | void TimeSyncer::cancelTimeout() {
268 | ++mTimeoutGeneration;
269 | }
270 |
271 | void TimeSyncer::notifyError(status_t err) {
272 | if (mNotify == NULL) {
273 | looper()->stop();
274 | return;
275 | }
276 |
277 | sp notify = mNotify->dup();
278 | notify->setInt32("what", kWhatError);
279 | notify->setInt32("err", err);
280 | notify->post();
281 | }
282 |
283 | // static
284 | int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) {
285 | int64_t rt1 = ti1->mT4 - ti1->mT1;
286 | int64_t rt2 = ti2->mT4 - ti2->mT1;
287 |
288 | if (rt1 < rt2) {
289 | return -1;
290 | } else if (rt1 > rt2) {
291 | return 1;
292 | }
293 |
294 | return 0;
295 | }
296 |
297 | void TimeSyncer::notifyOffset() {
298 | mHistory.sort(CompareRountripTime);
299 |
300 | int64_t sum = 0ll;
301 | size_t count = 0;
302 |
303 | // Only consider the third of the information associated with the best
304 | // (smallest) roundtrip times.
305 | for (size_t i = 0; i < mHistory.size() / 3; ++i) {
306 | const TimeInfo *ti = &mHistory[i];
307 |
308 | #if 0
309 | // One way delay for a packet to travel from client
310 | // to server or back (assumed to be the same either way).
311 | int64_t delay =
312 | (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2;
313 | #endif
314 |
315 | // Offset between the client clock (T1, T4) and the
316 | // server clock (T2, T3) timestamps.
317 | int64_t offset =
318 | (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2;
319 |
320 | ALOGV("(%d) RT: %lld us, offset: %lld us",
321 | i, ti->mT4 - ti->mT1, offset);
322 |
323 | sum += offset;
324 | ++count;
325 | }
326 |
327 | if (mNotify == NULL) {
328 | ALOGI("avg. offset is %lld", sum / count);
329 | return;
330 | }
331 |
332 | sp notify = mNotify->dup();
333 | notify->setInt32("what", kWhatTimeOffset);
334 | notify->setInt64("offset", sum / count);
335 | notify->post();
336 | }
337 |
338 | } // namespace android
339 |
--------------------------------------------------------------------------------
/lib/TimeSyncer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef TIME_SYNCER_H_
18 |
19 | #define TIME_SYNCER_H_
20 |
21 | #include
22 |
23 | namespace android {
24 |
25 | struct ANetworkSession;
26 |
27 | /*
28 | TimeSyncer allows us to synchronize time between a client and a server.
29 | The client sends a UDP packet containing its send-time to the server,
30 | the server sends that packet back to the client amended with information
31 | about when it was received as well as the time the reply was sent back.
32 | Finally the client receives the reply and has now enough information to
33 | compute the clock offset between client and server assuming that packet
34 | exchange is symmetric, i.e. time for a packet client->server and
35 | server->client is roughly equal.
36 | This exchange is repeated a number of times and the average offset computed
37 | over the 30% of packets that had the lowest roundtrip times.
38 | The offset is determined every 10 secs to account for slight differences in
39 | clock frequency.
40 | */
41 | struct TimeSyncer : public AHandler {
42 | enum {
43 | kWhatError,
44 | kWhatTimeOffset,
45 | };
46 | TimeSyncer(
47 | const sp &netSession,
48 | const sp ¬ify);
49 |
50 | void startServer(unsigned localPort);
51 | void startClient(const char *remoteHost, unsigned remotePort);
52 |
53 | protected:
54 | virtual ~TimeSyncer();
55 |
56 | virtual void onMessageReceived(const sp &msg);
57 |
58 | private:
59 | enum {
60 | kWhatStartServer,
61 | kWhatStartClient,
62 | kWhatUDPNotify,
63 | kWhatSendPacket,
64 | kWhatTimedOut,
65 | };
66 |
67 | struct TimeInfo {
68 | int64_t mT1; // client timestamp at send
69 | int64_t mT2; // server timestamp at receive
70 | int64_t mT3; // server timestamp at send
71 | int64_t mT4; // client timestamp at receive
72 | };
73 |
74 | enum {
75 | kNumPacketsPerBatch = 30,
76 | };
77 | static const int64_t kTimeoutDelayUs = 500000ll;
78 | static const int64_t kBatchDelayUs = 60000000ll; // every minute
79 |
80 | sp mNetSession;
81 | sp mNotify;
82 |
83 | bool mIsServer;
84 | bool mConnected;
85 | int32_t mUDPSession;
86 | uint32_t mSeqNo;
87 | double mTotalTimeUs;
88 |
89 | Vector mHistory;
90 |
91 | int64_t mPendingT1;
92 | int32_t mTimeoutGeneration;
93 |
94 | void postSendPacket(int64_t delayUs = 0ll);
95 |
96 | void postTimeout();
97 | void cancelTimeout();
98 |
99 | void notifyError(status_t err);
100 | void notifyOffset();
101 |
102 | static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2);
103 |
104 | DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer);
105 | };
106 |
107 | } // namespace android
108 |
109 | #endif // TIME_SYNCER_H_
110 |
--------------------------------------------------------------------------------
/lib/VideoFormats.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#define LOG_NDEBUG 0
18 | #define LOG_TAG "VideoFormats"
19 | #include
20 |
21 | #include "VideoFormats.h"
22 | #include "PlantUtils.h"
23 |
24 | #include
25 |
26 | namespace android {
27 |
28 | // static
29 | const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = {
30 | {
31 | // CEA Resolutions
32 | { 640, 480, 60, false, 0, 0},
33 | { 720, 480, 60, false, 0, 0},
34 | { 720, 480, 60, true, 0, 0},
35 | { 720, 576, 50, false, 0, 0},
36 | { 720, 576, 50, true, 0, 0},
37 | { 1280, 720, 30, false, 0, 0},
38 | { 1280, 720, 60, false, 0, 0},
39 | { 1920, 1080, 30, false, 0, 0},
40 | { 1920, 1080, 60, false, 0, 0},
41 | { 1920, 1080, 60, true, 0, 0},
42 | { 1280, 720, 25, false, 0, 0},
43 | { 1280, 720, 50, false, 0, 0},
44 | { 1920, 1080, 25, false, 0, 0},
45 | { 1920, 1080, 50, false, 0, 0},
46 | { 1920, 1080, 50, true, 0, 0},
47 | { 1280, 720, 24, false, 0, 0},
48 | { 1920, 1080, 24, false, 0, 0},
49 | { 0, 0, 0, false, 0, 0},
50 | { 0, 0, 0, false, 0, 0},
51 | { 0, 0, 0, false, 0, 0},
52 | { 0, 0, 0, false, 0, 0},
53 | { 0, 0, 0, false, 0, 0},
54 | { 0, 0, 0, false, 0, 0},
55 | { 0, 0, 0, false, 0, 0},
56 | { 0, 0, 0, false, 0, 0},
57 | { 0, 0, 0, false, 0, 0},
58 | { 0, 0, 0, false, 0, 0},
59 | { 0, 0, 0, false, 0, 0},
60 | { 0, 0, 0, false, 0, 0},
61 | { 0, 0, 0, false, 0, 0},
62 | { 0, 0, 0, false, 0, 0},
63 | { 0, 0, 0, false, 0, 0},
64 | },
65 | {
66 | // VESA Resolutions
67 | { 800, 600, 30, false, 0, 0},
68 | { 800, 600, 60, false, 0, 0},
69 | { 1024, 768, 30, false, 0, 0},
70 | { 1024, 768, 60, false, 0, 0},
71 | { 1152, 864, 30, false, 0, 0},
72 | { 1152, 864, 60, false, 0, 0},
73 | { 1280, 768, 30, false, 0, 0},
74 | { 1280, 768, 60, false, 0, 0},
75 | { 1280, 800, 30, false, 0, 0},
76 | { 1280, 800, 60, false, 0, 0},
77 | { 1360, 768, 30, false, 0, 0},
78 | { 1360, 768, 60, false, 0, 0},
79 | { 1366, 768, 30, false, 0, 0},
80 | { 1366, 768, 60, false, 0, 0},
81 | { 1280, 1024, 30, false, 0, 0},
82 | { 1280, 1024, 60, false, 0, 0},
83 | { 1400, 1050, 30, false, 0, 0},
84 | { 1400, 1050, 60, false, 0, 0},
85 | { 1440, 900, 30, false, 0, 0},
86 | { 1440, 900, 60, false, 0, 0},
87 | { 1600, 900, 30, false, 0, 0},
88 | { 1600, 900, 60, false, 0, 0},
89 | { 1600, 1200, 30, false, 0, 0},
90 | { 1600, 1200, 60, false, 0, 0},
91 | { 1680, 1024, 30, false, 0, 0},
92 | { 1680, 1024, 60, false, 0, 0},
93 | { 1680, 1050, 30, false, 0, 0},
94 | { 1680, 1050, 60, false, 0, 0},
95 | { 1920, 1200, 30, false, 0, 0},
96 | { 1920, 1200, 60, false, 0, 0},
97 | { 0, 0, 0, false, 0, 0},
98 | { 0, 0, 0, false, 0, 0},
99 | },
100 | {
101 | // HH Resolutions
102 | { 800, 480, 30, false, 0, 0},
103 | { 800, 480, 60, false, 0, 0},
104 | { 854, 480, 30, false, 0, 0},
105 | { 854, 480, 60, false, 0, 0},
106 | { 864, 480, 30, false, 0, 0},
107 | { 864, 480, 60, false, 0, 0},
108 | { 640, 360, 30, false, 0, 0},
109 | { 640, 360, 60, false, 0, 0},
110 | { 960, 540, 30, false, 0, 0},
111 | { 960, 540, 60, false, 0, 0},
112 | { 848, 480, 30, false, 0, 0},
113 | { 848, 480, 60, false, 0, 0},
114 | { 0, 0, 0, false, 0, 0},
115 | { 0, 0, 0, false, 0, 0},
116 | { 0, 0, 0, false, 0, 0},
117 | { 0, 0, 0, false, 0, 0},
118 | { 0, 0, 0, false, 0, 0},
119 | { 0, 0, 0, false, 0, 0},
120 | { 0, 0, 0, false, 0, 0},
121 | { 0, 0, 0, false, 0, 0},
122 | { 0, 0, 0, false, 0, 0},
123 | { 0, 0, 0, false, 0, 0},
124 | { 0, 0, 0, false, 0, 0},
125 | { 0, 0, 0, false, 0, 0},
126 | { 0, 0, 0, false, 0, 0},
127 | { 0, 0, 0, false, 0, 0},
128 | { 0, 0, 0, false, 0, 0},
129 | { 0, 0, 0, false, 0, 0},
130 | { 0, 0, 0, false, 0, 0},
131 | { 0, 0, 0, false, 0, 0},
132 | { 0, 0, 0, false, 0, 0},
133 | { 0, 0, 0, false, 0, 0},
134 | }
135 | };
136 |
137 | VideoFormats::VideoFormats() {
138 | memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
139 |
140 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
141 | mResolutionEnabled[i] = 0;
142 | }
143 |
144 | setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
145 | }
146 |
147 | void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
148 | CHECK_LT(type, kNumResolutionTypes);
149 | CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
150 |
151 | mNativeType = type;
152 | mNativeIndex = index;
153 |
154 | setResolutionEnabled(type, index);
155 | }
156 |
157 | void VideoFormats::getNativeResolution(
158 | ResolutionType *type, size_t *index) const {
159 | *type = mNativeType;
160 | *index = mNativeIndex;
161 | }
162 |
163 | void VideoFormats::disableAll() {
164 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
165 | mResolutionEnabled[i] = 0;
166 | for (size_t j = 0; j < 32; j++) {
167 | mConfigs[i][j].profile = mConfigs[i][j].level = 0;
168 | }
169 | }
170 | }
171 |
172 | void VideoFormats::enableAll() {
173 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
174 | mResolutionEnabled[i] = 0xffffffff;
175 | for (size_t j = 0; j < 32; j++) {
176 | mConfigs[i][j].profile = (1ul << PROFILE_CBP);
177 | mConfigs[i][j].level = (1ul << LEVEL_31);
178 | }
179 | }
180 | }
181 |
182 | void VideoFormats::enableResolutionUpto(
183 | ResolutionType type, size_t index,
184 | ProfileType profile, LevelType level) {
185 | size_t width, height, fps, score;
186 | bool interlaced;
187 | if (!GetConfiguration(type, index, &width, &height,
188 | &fps, &interlaced)) {
189 | ALOGE("Maximum resolution not found!");
190 | return;
191 | }
192 | score = width * height * fps * (!interlaced + 1);
193 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
194 | for (size_t j = 0; j < 32; j++) {
195 | if (GetConfiguration((ResolutionType)i, j,
196 | &width, &height, &fps, &interlaced)
197 | && score >= width * height * fps * (!interlaced + 1)) {
198 | setResolutionEnabled((ResolutionType)i, j);
199 | setProfileLevel((ResolutionType)i, j, profile, level);
200 | }
201 | }
202 | }
203 | }
204 |
205 | void VideoFormats::setResolutionEnabled(
206 | ResolutionType type, size_t index, bool enabled) {
207 | CHECK_LT(type, kNumResolutionTypes);
208 | CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
209 |
210 | if (enabled) {
211 | mResolutionEnabled[type] |= (1ul << index);
212 | mConfigs[type][index].profile = (1ul << PROFILE_CBP);
213 | mConfigs[type][index].level = (1ul << LEVEL_31);
214 | } else {
215 | mResolutionEnabled[type] &= ~(1ul << index);
216 | mConfigs[type][index].profile = 0;
217 | mConfigs[type][index].level = 0;
218 | }
219 | }
220 |
221 | void VideoFormats::setProfileLevel(
222 | ResolutionType type, size_t index,
223 | ProfileType profile, LevelType level) {
224 | CHECK_LT(type, kNumResolutionTypes);
225 | CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
226 |
227 | mConfigs[type][index].profile = (1ul << profile);
228 | mConfigs[type][index].level = (1ul << level);
229 | }
230 |
231 | void VideoFormats::getProfileLevel(
232 | ResolutionType type, size_t index,
233 | ProfileType *profile, LevelType *level) const{
234 | CHECK_LT(type, kNumResolutionTypes);
235 | CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
236 |
237 | int i, bestProfile = -1, bestLevel = -1;
238 |
239 | for (i = 0; i < kNumProfileTypes; ++i) {
240 | if (mConfigs[type][index].profile & (1ul << i)) {
241 | bestProfile = i;
242 | }
243 | }
244 |
245 | for (i = 0; i < kNumLevelTypes; ++i) {
246 | if (mConfigs[type][index].level & (1ul << i)) {
247 | bestLevel = i;
248 | }
249 | }
250 |
251 | if (bestProfile == -1 || bestLevel == -1) {
252 | ALOGE("Profile or level not set for resolution type %d, index %d",
253 | type, index);
254 | bestProfile = PROFILE_CBP;
255 | bestLevel = LEVEL_31;
256 | }
257 |
258 | *profile = (ProfileType) bestProfile;
259 | *level = (LevelType) bestLevel;
260 | }
261 |
262 | bool VideoFormats::isResolutionEnabled(
263 | ResolutionType type, size_t index) const {
264 | CHECK_LT(type, kNumResolutionTypes);
265 | CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
266 |
267 | return mResolutionEnabled[type] & (1ul << index);
268 | }
269 |
270 | // static
271 | bool VideoFormats::GetConfiguration(
272 | ResolutionType type,
273 | size_t index,
274 | size_t *width, size_t *height, size_t *framesPerSecond,
275 | bool *interlaced) {
276 | CHECK_LT(type, kNumResolutionTypes);
277 |
278 | if (index >= 32) {
279 | return false;
280 | }
281 |
282 | const config_t *config = &mResolutionTable[type][index];
283 |
284 | if (config->width == 0) {
285 | return false;
286 | }
287 |
288 | if (width) {
289 | *width = config->width;
290 | }
291 |
292 | if (height) {
293 | *height = config->height;
294 | }
295 |
296 | if (framesPerSecond) {
297 | *framesPerSecond = config->framesPerSecond;
298 | }
299 |
300 | if (interlaced) {
301 | *interlaced = config->interlaced;
302 | }
303 |
304 | return true;
305 | }
306 |
307 | bool VideoFormats::parseH264Codec(const char *spec) {
308 | unsigned profile, level, res[3];
309 |
310 | if (sscanf(
311 | spec,
312 | "%02x %02x %08X %08X %08X",
313 | &profile,
314 | &level,
315 | &res[0],
316 | &res[1],
317 | &res[2]) != 5) {
318 | return false;
319 | }
320 |
321 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
322 | for (size_t j = 0; j < 32; ++j) {
323 | if (res[i] & (1ul << j)){
324 | mResolutionEnabled[i] |= (1ul << j);
325 | if (profile > mConfigs[i][j].profile) {
326 | // prefer higher profile (even if level is lower)
327 | mConfigs[i][j].profile = profile;
328 | mConfigs[i][j].level = level;
329 | } else if (profile == mConfigs[i][j].profile &&
330 | level > mConfigs[i][j].level) {
331 | mConfigs[i][j].level = level;
332 | }
333 | }
334 | }
335 | }
336 |
337 | return true;
338 | }
339 |
340 | // static
341 | bool VideoFormats::GetProfileLevel(
342 | ProfileType profile, LevelType level, unsigned *profileIdc,
343 | unsigned *levelIdc, unsigned *constraintSet) {
344 | CHECK_LT(profile, kNumProfileTypes);
345 | CHECK_LT(level, kNumLevelTypes);
346 |
347 | static const unsigned kProfileIDC[kNumProfileTypes] = {
348 | 66, // PROFILE_CBP
349 | 100, // PROFILE_CHP
350 | };
351 |
352 | static const unsigned kLevelIDC[kNumLevelTypes] = {
353 | 31, // LEVEL_31
354 | 32, // LEVEL_32
355 | 40, // LEVEL_40
356 | 41, // LEVEL_41
357 | 42, // LEVEL_42
358 | };
359 |
360 | static const unsigned kConstraintSet[kNumProfileTypes] = {
361 | 0xc0, // PROFILE_CBP
362 | 0x0c, // PROFILE_CHP
363 | };
364 |
365 | if (profileIdc) {
366 | *profileIdc = kProfileIDC[profile];
367 | }
368 |
369 | if (levelIdc) {
370 | *levelIdc = kLevelIDC[level];
371 | }
372 |
373 | if (constraintSet) {
374 | *constraintSet = kConstraintSet[profile];
375 | }
376 |
377 | return true;
378 | }
379 |
380 | bool VideoFormats::parseFormatSpec(const char *spec) {
381 | CHECK_EQ(kNumResolutionTypes, 3);
382 |
383 | disableAll();
384 |
385 | unsigned native, dummy;
386 | unsigned res[3];
387 | size_t size = strlen(spec);
388 | size_t offset = 0;
389 |
390 | if (sscanf(spec, "%02x %02x ", &native, &dummy) != 2) {
391 | return false;
392 | }
393 |
394 | offset += 6; // skip native and preferred-display-mode-supported
395 | CHECK_LE(offset + 58, size);
396 | while (offset < size) {
397 | parseH264Codec(spec + offset);
398 | offset += 60; // skip H.264-codec + ", "
399 | }
400 |
401 | mNativeIndex = native >> 3;
402 | mNativeType = (ResolutionType)(native & 7);
403 |
404 | bool success;
405 | if (mNativeType >= kNumResolutionTypes) {
406 | success = false;
407 | } else {
408 | success = GetConfiguration(
409 | mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
410 | }
411 |
412 | if (!success) {
413 | ALOGW("sink advertised an illegal native resolution, fortunately "
414 | "this value is ignored for the time being...");
415 | }
416 |
417 | return true;
418 | }
419 |
420 | AString VideoFormats::getFormatSpec(bool forM4Message) const {
421 | CHECK_EQ(kNumResolutionTypes, 3);
422 |
423 | // wfd_video_formats:
424 | // 1 byte "native"
425 | // 1 byte "preferred-display-mode-supported" 0 or 1
426 | // one or more avc codec structures
427 | // 1 byte profile
428 | // 1 byte level
429 | // 4 byte CEA mask
430 | // 4 byte VESA mask
431 | // 4 byte HH mask
432 | // 1 byte latency
433 | // 2 byte min-slice-slice
434 | // 2 byte slice-enc-params
435 | // 1 byte framerate-control-support
436 | // max-hres (none or 2 byte)
437 | // max-vres (none or 2 byte)
438 |
439 | return PlantUtils::newStringPrintf(
440 | "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none",
441 | forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType),
442 | mConfigs[mNativeType][mNativeIndex].profile,
443 | mConfigs[mNativeType][mNativeIndex].level,
444 | mResolutionEnabled[0],
445 | mResolutionEnabled[1],
446 | mResolutionEnabled[2]);
447 | }
448 |
449 | // static
450 | bool VideoFormats::PickBestFormat(
451 | const VideoFormats &sinkSupported,
452 | const VideoFormats &sourceSupported,
453 | ResolutionType *chosenType,
454 | size_t *chosenIndex,
455 | ProfileType *chosenProfile,
456 | LevelType *chosenLevel) {
457 | #if 0
458 | // Support for the native format is a great idea, the spec includes
459 | // these features, but nobody supports it and the tests don't validate it.
460 |
461 | ResolutionType nativeType;
462 | size_t nativeIndex;
463 | sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
464 | if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
465 | if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
466 | ALOGI("Choosing sink's native resolution");
467 | *chosenType = nativeType;
468 | *chosenIndex = nativeIndex;
469 | return true;
470 | }
471 | } else {
472 | ALOGW("Sink advertised native resolution that it doesn't "
473 | "actually support... ignoring");
474 | }
475 |
476 | sourceSupported.getNativeResolution(&nativeType, &nativeIndex);
477 | if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
478 | if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
479 | ALOGI("Choosing source's native resolution");
480 | *chosenType = nativeType;
481 | *chosenIndex = nativeIndex;
482 | return true;
483 | }
484 | } else {
485 | ALOGW("Source advertised native resolution that it doesn't "
486 | "actually support... ignoring");
487 | }
488 | #endif
489 |
490 | bool first = true;
491 | uint32_t bestScore = 0;
492 | size_t bestType = 0;
493 | size_t bestIndex = 0;
494 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
495 | for (size_t j = 0; j < 32; ++j) {
496 | size_t width, height, framesPerSecond;
497 | bool interlaced;
498 | if (!GetConfiguration(
499 | (ResolutionType)i,
500 | j,
501 | &width, &height, &framesPerSecond, &interlaced)) {
502 | break;
503 | }
504 |
505 | if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j)
506 | || !sourceSupported.isResolutionEnabled(
507 | (ResolutionType)i, j)) {
508 | continue;
509 | }
510 |
511 | ALOGV("type %u, index %u, %u x %u %c%u supported",
512 | i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
513 |
514 | uint32_t score = width * height * framesPerSecond;
515 | if (!interlaced) {
516 | score *= 2;
517 | }
518 |
519 | if (first || score > bestScore) {
520 | bestScore = score;
521 | bestType = i;
522 | bestIndex = j;
523 |
524 | first = false;
525 | }
526 | }
527 | }
528 |
529 | if (first) {
530 | return false;
531 | }
532 |
533 | *chosenType = (ResolutionType)bestType;
534 | *chosenIndex = bestIndex;
535 |
536 | // Pick the best profile/level supported by both sink and source.
537 | ProfileType srcProfile, sinkProfile;
538 | LevelType srcLevel, sinkLevel;
539 | sourceSupported.getProfileLevel(
540 | (ResolutionType)bestType, bestIndex,
541 | &srcProfile, &srcLevel);
542 | sinkSupported.getProfileLevel(
543 | (ResolutionType)bestType, bestIndex,
544 | &sinkProfile, &sinkLevel);
545 | *chosenProfile = srcProfile < sinkProfile ? srcProfile : sinkProfile;
546 | *chosenLevel = srcLevel < sinkLevel ? srcLevel : sinkLevel;
547 |
548 | return true;
549 | }
550 |
551 | bool VideoFormats::getResolutionType(
552 | size_t displayWidth,
553 | size_t displayHeight,
554 | ResolutionType *chosenType,
555 | size_t *chosenIndex) {
556 |
557 | for (size_t i = 0; i < kNumResolutionTypes; ++i) {
558 | for (size_t j = 0; j < 32; ++j) {
559 | size_t width, height, framesPerSecond;
560 | bool interlaced;
561 | if (!GetConfiguration(
562 | (ResolutionType)i,
563 | j,
564 | &width, &height, &framesPerSecond, &interlaced)) {
565 | break;
566 | }
567 | if (width == displayWidth && height == displayHeight) {
568 | *chosenType = (ResolutionType)i;
569 | *chosenIndex = j;
570 | return true;
571 | }
572 | }
573 | }
574 |
575 | return false;
576 | }
577 |
578 | } // namespace android
579 |
580 |
--------------------------------------------------------------------------------
/lib/VideoFormats.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef VIDEO_FORMATS_H_
18 |
19 | #define VIDEO_FORMATS_H_
20 |
21 | #include
22 |
23 | #include
24 |
25 | namespace android {
26 |
27 | struct AString;
28 |
29 | // This class encapsulates that video resolution capabilities of a wfd source
30 | // or sink as outlined in the wfd specs. Currently three sets of resolutions
31 | // are specified, each of which supports up to 32 resolutions.
32 | // In addition to its capabilities each sink/source also publishes its
33 | // "native" resolution, presumably one that is preferred among all others
34 | // because it wouldn't require any scaling and directly corresponds to the
35 | // display capabilities/pixels.
36 | struct VideoFormats {
37 | VideoFormats();
38 |
39 | struct config_t {
40 | size_t width, height, framesPerSecond;
41 | bool interlaced;
42 | unsigned char profile, level;
43 | };
44 |
45 | enum ProfileType {
46 | PROFILE_CBP = 0,
47 | PROFILE_CHP,
48 | kNumProfileTypes,
49 | };
50 |
51 | enum LevelType {
52 | LEVEL_31 = 0,
53 | LEVEL_32,
54 | LEVEL_40,
55 | LEVEL_41,
56 | LEVEL_42,
57 | kNumLevelTypes,
58 | };
59 |
60 | enum ResolutionType {
61 | RESOLUTION_CEA,
62 | RESOLUTION_VESA,
63 | RESOLUTION_HH,
64 | kNumResolutionTypes,
65 | };
66 |
67 | void setNativeResolution(ResolutionType type, size_t index);
68 | void getNativeResolution(ResolutionType *type, size_t *index) const;
69 |
70 | void disableAll();
71 | void enableAll();
72 | void enableResolutionUpto(
73 | ResolutionType type, size_t index,
74 | ProfileType profile, LevelType level);
75 |
76 | void setResolutionEnabled(
77 | ResolutionType type, size_t index, bool enabled = true);
78 |
79 | bool isResolutionEnabled(ResolutionType type, size_t index) const;
80 |
81 | void setProfileLevel(
82 | ResolutionType type, size_t index,
83 | ProfileType profile, LevelType level);
84 |
85 | void getProfileLevel(
86 | ResolutionType type, size_t index,
87 | ProfileType *profile, LevelType *level) const;
88 |
89 | static bool GetConfiguration(
90 | ResolutionType type, size_t index,
91 | size_t *width, size_t *height, size_t *framesPerSecond,
92 | bool *interlaced);
93 |
94 | static bool GetProfileLevel(
95 | ProfileType profile, LevelType level,
96 | unsigned *profileIdc, unsigned *levelIdc,
97 | unsigned *constraintSet);
98 |
99 | bool parseFormatSpec(const char *spec);
100 | AString getFormatSpec(bool forM4Message = false) const;
101 |
102 | static bool PickBestFormat(
103 | const VideoFormats &sinkSupported,
104 | const VideoFormats &sourceSupported,
105 | ResolutionType *chosenType,
106 | size_t *chosenIndex,
107 | ProfileType *chosenProfile,
108 | LevelType *chosenLevel);
109 |
110 | static bool getResolutionType(
111 | size_t displayWidth,
112 | size_t displayHeight,
113 | ResolutionType *chosenType,
114 | size_t *chosenIndex);
115 |
116 | private:
117 | bool parseH264Codec(const char *spec);
118 | ResolutionType mNativeType;
119 | size_t mNativeIndex;
120 |
121 | uint32_t mResolutionEnabled[kNumResolutionTypes];
122 | static const config_t mResolutionTable[kNumResolutionTypes][32];
123 | config_t mConfigs[kNumResolutionTypes][32];
124 |
125 | DISALLOW_EVIL_CONSTRUCTORS(VideoFormats);
126 | };
127 |
128 | } // namespace android
129 |
130 | #endif // VIDEO_FORMATS_H_
131 |
132 |
--------------------------------------------------------------------------------
/lib/rtp/RTPAssembler.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#define LOG_NDEBUG 0
18 | #define LOG_TAG "RTPAssembler"
19 | #include
20 |
21 | #include "RTPAssembler.h"
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | namespace android {
30 |
31 | RTPReceiver::Assembler::Assembler(const sp ¬ify)
32 | : mNotify(notify) {
33 | }
34 |
35 | void RTPReceiver::Assembler::postAccessUnit(
36 | const sp &accessUnit, bool followsDiscontinuity) {
37 | sp notify = mNotify->dup();
38 | notify->setInt32("what", RTPReceiver::kWhatAccessUnit);
39 | notify->setBuffer("accessUnit", accessUnit);
40 | notify->setInt32("followsDiscontinuity", followsDiscontinuity);
41 | notify->post();
42 | }
43 | ////////////////////////////////////////////////////////////////////////////////
44 |
45 | RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify)
46 | : Assembler(notify),
47 | mSawDiscontinuity(false) {
48 | }
49 |
50 | void RTPReceiver::TSAssembler::signalDiscontinuity() {
51 | mSawDiscontinuity = true;
52 | }
53 |
54 | status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) {
55 | int32_t rtpTime;
56 | CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
57 |
58 | packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9);
59 |
60 | postAccessUnit(packet, mSawDiscontinuity);
61 |
62 | if (mSawDiscontinuity) {
63 | mSawDiscontinuity = false;
64 | }
65 |
66 | return OK;
67 | }
68 |
69 | ////////////////////////////////////////////////////////////////////////////////
70 |
71 | RTPReceiver::H264Assembler::H264Assembler(const sp ¬ify)
72 | : Assembler(notify),
73 | mState(0),
74 | mIndicator(0),
75 | mNALType(0),
76 | mAccessUnitRTPTime(0) {
77 | }
78 |
79 | void RTPReceiver::H264Assembler::signalDiscontinuity() {
80 | reset();
81 | }
82 |
83 | status_t RTPReceiver::H264Assembler::processPacket(const sp &packet) {
84 | status_t err = internalProcessPacket(packet);
85 |
86 | if (err != OK) {
87 | reset();
88 | }
89 |
90 | return err;
91 | }
92 |
93 | status_t RTPReceiver::H264Assembler::internalProcessPacket(
94 | const sp &packet) {
95 | const uint8_t *data = packet->data();
96 | size_t size = packet->size();
97 |
98 | switch (mState) {
99 | case 0:
100 | {
101 | if (size < 1 || (data[0] & 0x80)) {
102 | ALOGV("Malformed H264 RTP packet (empty or F-bit set)");
103 | return ERROR_MALFORMED;
104 | }
105 |
106 | unsigned nalType = data[0] & 0x1f;
107 | if (nalType >= 1 && nalType <= 23) {
108 | addSingleNALUnit(packet);
109 | ALOGV("added single NAL packet");
110 | } else if (nalType == 28) {
111 | // FU-A
112 | unsigned indicator = data[0];
113 | CHECK((indicator & 0x1f) == 28);
114 |
115 | if (size < 2) {
116 | ALOGV("Malformed H264 FU-A packet (single byte)");
117 | return ERROR_MALFORMED;
118 | }
119 |
120 | if (!(data[1] & 0x80)) {
121 | ALOGV("Malformed H264 FU-A packet (no start bit)");
122 | return ERROR_MALFORMED;
123 | }
124 |
125 | mIndicator = data[0];
126 | mNALType = data[1] & 0x1f;
127 | uint32_t nri = (data[0] >> 5) & 3;
128 |
129 | clearAccumulator();
130 |
131 | uint8_t byte = mNALType | (nri << 5);
132 | appendToAccumulator(&byte, 1);
133 | appendToAccumulator(data + 2, size - 2);
134 |
135 | int32_t rtpTime;
136 | CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
137 | mAccumulator->meta()->setInt32("rtp-time", rtpTime);
138 |
139 | if (data[1] & 0x40) {
140 | // Huh? End bit also set on the first buffer.
141 | addSingleNALUnit(mAccumulator);
142 | clearAccumulator();
143 |
144 | ALOGV("added FU-A");
145 | break;
146 | }
147 |
148 | mState = 1;
149 | } else if (nalType == 24) {
150 | // STAP-A
151 |
152 | status_t err = addSingleTimeAggregationPacket(packet);
153 | if (err != OK) {
154 | return err;
155 | }
156 | } else {
157 | ALOGV("Malformed H264 packet (unknown type %d)", nalType);
158 | return ERROR_UNSUPPORTED;
159 | }
160 | break;
161 | }
162 |
163 | case 1:
164 | {
165 | if (size < 2
166 | || data[0] != mIndicator
167 | || (data[1] & 0x1f) != mNALType
168 | || (data[1] & 0x80)) {
169 | ALOGV("Malformed H264 FU-A packet (indicator, "
170 | "type or start bit mismatch)");
171 |
172 | return ERROR_MALFORMED;
173 | }
174 |
175 | appendToAccumulator(data + 2, size - 2);
176 |
177 | if (data[1] & 0x40) {
178 | addSingleNALUnit(mAccumulator);
179 |
180 | clearAccumulator();
181 | mState = 0;
182 |
183 | ALOGV("added FU-A");
184 | }
185 | break;
186 | }
187 |
188 | default:
189 | TRESPASS();
190 | }
191 |
192 | int32_t marker;
193 | CHECK(packet->meta()->findInt32("M", &marker));
194 |
195 | if (marker) {
196 | flushAccessUnit();
197 | }
198 |
199 | return OK;
200 | }
201 |
202 | void RTPReceiver::H264Assembler::reset() {
203 | mNALUnits.clear();
204 |
205 | clearAccumulator();
206 | mState = 0;
207 | }
208 |
209 | void RTPReceiver::H264Assembler::clearAccumulator() {
210 | if (mAccumulator != NULL) {
211 | // XXX Too expensive.
212 | mAccumulator.clear();
213 | }
214 | }
215 |
216 | void RTPReceiver::H264Assembler::appendToAccumulator(
217 | const void *data, size_t size) {
218 | if (mAccumulator == NULL) {
219 | mAccumulator = new ABuffer(size);
220 | memcpy(mAccumulator->data(), data, size);
221 | return;
222 | }
223 |
224 | if (mAccumulator->size() + size > mAccumulator->capacity()) {
225 | sp buf = new ABuffer(mAccumulator->size() + size);
226 | memcpy(buf->data(), mAccumulator->data(), mAccumulator->size());
227 | buf->setRange(0, mAccumulator->size());
228 |
229 | int32_t rtpTime;
230 | if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) {
231 | buf->meta()->setInt32("rtp-time", rtpTime);
232 | }
233 |
234 | mAccumulator = buf;
235 | }
236 |
237 | memcpy(mAccumulator->data() + mAccumulator->size(), data, size);
238 | mAccumulator->setRange(0, mAccumulator->size() + size);
239 | }
240 |
241 | void RTPReceiver::H264Assembler::addSingleNALUnit(const sp &packet) {
242 | if (mNALUnits.empty()) {
243 | int32_t rtpTime;
244 | CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
245 |
246 | mAccessUnitRTPTime = rtpTime;
247 | }
248 |
249 | mNALUnits.push_back(packet);
250 | }
251 |
252 | void RTPReceiver::H264Assembler::flushAccessUnit() {
253 | if (mNALUnits.empty()) {
254 | return;
255 | }
256 |
257 | size_t totalSize = 0;
258 | for (List >::iterator it = mNALUnits.begin();
259 | it != mNALUnits.end(); ++it) {
260 | totalSize += 4 + (*it)->size();
261 | }
262 |
263 | sp accessUnit = new ABuffer(totalSize);
264 | size_t offset = 0;
265 | for (List >::iterator it = mNALUnits.begin();
266 | it != mNALUnits.end(); ++it) {
267 | const sp nalUnit = *it;
268 |
269 | memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
270 |
271 | memcpy(accessUnit->data() + offset + 4,
272 | nalUnit->data(),
273 | nalUnit->size());
274 |
275 | offset += 4 + nalUnit->size();
276 | }
277 |
278 | mNALUnits.clear();
279 |
280 | accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll);
281 | postAccessUnit(accessUnit, false /* followsDiscontinuity */);
282 | }
283 |
284 | status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket(
285 | const sp &packet) {
286 | const uint8_t *data = packet->data();
287 | size_t size = packet->size();
288 |
289 | if (size < 3) {
290 | ALOGV("Malformed H264 STAP-A packet (too small)");
291 | return ERROR_MALFORMED;
292 | }
293 |
294 | int32_t rtpTime;
295 | CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
296 |
297 | ++data;
298 | --size;
299 | while (size >= 2) {
300 | size_t nalSize = (data[0] << 8) | data[1];
301 |
302 | if (size < nalSize + 2) {
303 | ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)");
304 | return ERROR_MALFORMED;
305 | }
306 |
307 | sp unit = new ABuffer(nalSize);
308 | memcpy(unit->data(), &data[2], nalSize);
309 |
310 | unit->meta()->setInt32("rtp-time", rtpTime);
311 |
312 | addSingleNALUnit(unit);
313 |
314 | data += 2 + nalSize;
315 | size -= 2 + nalSize;
316 | }
317 |
318 | if (size != 0) {
319 | ALOGV("Unexpected padding at end of STAP-A packet.");
320 | }
321 |
322 | ALOGV("added STAP-A");
323 |
324 | return OK;
325 | }
326 |
327 | } // namespace android
328 |
329 |
--------------------------------------------------------------------------------
/lib/rtp/RTPAssembler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RTP_ASSEMBLER_H_
18 |
19 | #define RTP_ASSEMBLER_H_
20 |
21 | #include "RTPReceiver.h"
22 |
23 | namespace android {
24 |
25 | // A helper class to reassemble the payload of RTP packets into access
26 | // units depending on the packetization scheme.
27 | struct RTPReceiver::Assembler : public RefBase {
28 | Assembler(const sp ¬ify);
29 |
30 | virtual void signalDiscontinuity() = 0;
31 | virtual status_t processPacket(const sp &packet) = 0;
32 |
33 | protected:
34 | virtual ~Assembler() {}
35 |
36 | void postAccessUnit(
37 | const sp &accessUnit, bool followsDiscontinuity);
38 |
39 | private:
40 | sp mNotify;
41 |
42 | DISALLOW_EVIL_CONSTRUCTORS(Assembler);
43 | };
44 |
45 | struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler {
46 | TSAssembler(const sp ¬ify);
47 |
48 | virtual void signalDiscontinuity();
49 | virtual status_t processPacket(const sp &packet);
50 |
51 | private:
52 | bool mSawDiscontinuity;
53 |
54 | DISALLOW_EVIL_CONSTRUCTORS(TSAssembler);
55 | };
56 |
57 | struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler {
58 | H264Assembler(const sp ¬ify);
59 |
60 | virtual void signalDiscontinuity();
61 | virtual status_t processPacket(const sp &packet);
62 |
63 | private:
64 | int32_t mState;
65 |
66 | uint8_t mIndicator;
67 | uint8_t mNALType;
68 |
69 | sp mAccumulator;
70 |
71 | List > mNALUnits;
72 | int32_t mAccessUnitRTPTime;
73 |
74 | status_t internalProcessPacket(const sp &packet);
75 |
76 | void addSingleNALUnit(const sp &packet);
77 | status_t addSingleTimeAggregationPacket(const sp &packet);
78 |
79 | void flushAccessUnit();
80 |
81 | void clearAccumulator();
82 | void appendToAccumulator(const void *data, size_t size);
83 |
84 | void reset();
85 |
86 | DISALLOW_EVIL_CONSTRUCTORS(H264Assembler);
87 | };
88 |
89 | } // namespace android
90 |
91 | #endif // RTP_ASSEMBLER_H_
92 |
93 |
--------------------------------------------------------------------------------
/lib/rtp/RTPBase.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RTP_BASE_H_
18 |
19 | #define RTP_BASE_H_
20 |
21 | namespace android {
22 |
23 | struct RTPBase {
24 | enum PacketizationMode {
25 | PACKETIZATION_TRANSPORT_STREAM,
26 | PACKETIZATION_H264,
27 | PACKETIZATION_AAC,
28 | PACKETIZATION_NONE,
29 | };
30 |
31 | enum TransportMode {
32 | TRANSPORT_UNDEFINED,
33 | TRANSPORT_NONE,
34 | TRANSPORT_UDP,
35 | TRANSPORT_TCP,
36 | TRANSPORT_TCP_INTERLEAVED,
37 | };
38 |
39 | enum {
40 | // Really UDP _payload_ size
41 | kMaxUDPPacketSize = 1472, // 1472 good, 1473 bad on Android@Home
42 | };
43 |
44 | static int32_t PickRandomRTPPort();
45 | };
46 |
47 | } // namespace android
48 |
49 | #endif // RTP_BASE_H_
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib/rtp/RTPReceiver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RTP_RECEIVER_H_
18 |
19 | #define RTP_RECEIVER_H_
20 |
21 | #include "RTPBase.h"
22 |
23 | #include
24 |
25 | namespace android {
26 |
27 | struct ABuffer;
28 | struct ANetworkSession;
29 |
30 | // An object of this class facilitates receiving of media data on an RTP
31 | // channel. The channel is established over a UDP or TCP connection depending
32 | // on which "TransportMode" was chosen. In addition different RTP packetization
33 | // schemes are supported such as "Transport Stream Packets over RTP",
34 | // or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
35 | struct RTPReceiver : public RTPBase, public AHandler {
36 | enum {
37 | kWhatInitDone,
38 | kWhatError,
39 | kWhatAccessUnit,
40 | kWhatPacketLost,
41 | };
42 |
43 | enum Flags {
44 | FLAG_AUTO_CONNECT = 1,
45 | };
46 | RTPReceiver(
47 | const sp &netSession,
48 | const sp ¬ify,
49 | uint32_t flags = 0);
50 |
51 | status_t registerPacketType(
52 | uint8_t packetType, PacketizationMode mode);
53 |
54 | status_t initAsync(
55 | TransportMode rtpMode,
56 | TransportMode rtcpMode,
57 | int32_t *outLocalRTPPort,
58 | int32_t forceRtpPort);
59 |
60 | status_t connect(
61 | const char *remoteHost,
62 | int32_t remoteRTPPort,
63 | int32_t remoteRTCPPort);
64 |
65 | status_t informSender(const sp ¶ms);
66 |
67 | protected:
68 | virtual ~RTPReceiver();
69 | virtual void onMessageReceived(const sp &msg);
70 |
71 | private:
72 | enum {
73 | kWhatRTPNotify,
74 | kWhatRTCPNotify,
75 | kWhatSendRR,
76 | };
77 |
78 | enum {
79 | kSourceID = 0xdeadbeef,
80 | kPacketLostAfterUs = 100000,
81 | kRequestRetransmissionAfterUs = -1,
82 | };
83 |
84 | struct Assembler;
85 | struct H264Assembler;
86 | struct Source;
87 | struct TSAssembler;
88 |
89 | sp mNetSession;
90 | sp mNotify;
91 | uint32_t mFlags;
92 | TransportMode mRTPMode;
93 | TransportMode mRTCPMode;
94 | int32_t mRTPSessionID;
95 | int32_t mRTCPSessionID;
96 | bool mRTPConnected;
97 | bool mRTCPConnected;
98 |
99 | int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode.
100 | int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode.
101 |
102 | KeyedVector mPacketTypes;
103 | KeyedVector > mSources;
104 |
105 | void onNetNotify(bool isRTP, const sp &msg);
106 | status_t onRTPData(const sp &data);
107 | status_t onRTCPData(const sp &data);
108 | void onSendRR();
109 |
110 | void scheduleSendRR();
111 | void addSDES(const sp &buffer);
112 |
113 | void notifyInitDone(status_t err);
114 | void notifyError(status_t err);
115 | void notifyPacketLost();
116 |
117 | sp makeAssembler(uint8_t packetType);
118 |
119 | void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo);
120 |
121 | DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver);
122 | };
123 |
124 | } // namespace android
125 |
126 | #endif // RTP_RECEIVER_H_
127 |
--------------------------------------------------------------------------------
/lib/sink/DirectRenderer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#define LOG_NDEBUG 0
18 | #define LOG_TAG "DirectRenderer"
19 | #include
20 |
21 | #include "DirectRenderer.h"
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include "PlantUtils.h"
36 |
37 | namespace android {
38 |
39 | /*
40 | Drives the decoding process using a MediaCodec instance. Input buffers
41 | queued by calls to "queueInputBuffer" are fed to the decoder as soon
42 | as the decoder is ready for them, the client is notified about output
43 | buffers as the decoder spits them out.
44 | */
45 | struct DirectRenderer::DecoderContext : public AHandler {
46 | enum {
47 | kWhatOutputBufferReady,
48 | };
49 | DecoderContext(const sp ¬ify);
50 |
51 | status_t init(
52 | const sp &format,
53 | const sp &surfaceTex);
54 |
55 | void queueInputBuffer(const sp &accessUnit);
56 |
57 | status_t renderOutputBufferAndRelease(size_t index);
58 | status_t releaseOutputBuffer(size_t index);
59 |
60 | protected:
61 | virtual ~DecoderContext();
62 |
63 | virtual void onMessageReceived(const sp &msg);
64 |
65 | private:
66 | enum {
67 | kWhatDecoderNotify,
68 | };
69 |
70 | sp mNotify;
71 | sp mDecoderLooper;
72 | sp mDecoder;
73 | Vector > mDecoderInputBuffers;
74 | Vector > mDecoderOutputBuffers;
75 | List mDecoderInputBuffersAvailable;
76 | bool mDecoderNotificationPending;
77 |
78 | List > mAccessUnits;
79 |
80 | void onDecoderNotify();
81 | void scheduleDecoderNotification();
82 | void queueDecoderInputBuffers();
83 |
84 | void queueOutputBuffer(
85 | size_t index, int64_t timeUs, const sp &buffer);
86 |
87 | DISALLOW_EVIL_CONSTRUCTORS(DecoderContext);
88 | };
89 |
90 | ////////////////////////////////////////////////////////////////////////////////
91 |
92 | /*
93 | A "push" audio renderer. The primary function of this renderer is to use
94 | an AudioTrack in push mode and making sure not to block the event loop
95 | be ensuring that calls to AudioTrack::write never block. This is done by
96 | estimating an upper bound of data that can be written to the AudioTrack
97 | buffer without delay.
98 | */
99 | struct DirectRenderer::AudioRenderer : public AHandler {
100 | AudioRenderer(const sp &decoderContext);
101 |
102 | void queueInputBuffer(
103 | size_t index, int64_t timeUs, const sp &buffer);
104 |
105 | protected:
106 | virtual ~AudioRenderer();
107 | virtual void onMessageReceived(const sp &msg);
108 |
109 | private:
110 | enum {
111 | kWhatPushAudio,
112 | };
113 |
114 | struct BufferInfo {
115 | size_t mIndex;
116 | int64_t mTimeUs;
117 | sp mBuffer;
118 | };
119 |
120 | sp mDecoderContext;
121 | sp mAudioTrack;
122 |
123 | List mInputBuffers;
124 | bool mPushPending;
125 |
126 | size_t mNumFramesWritten;
127 |
128 | void schedulePushIfNecessary();
129 | void onPushAudio();
130 |
131 | ssize_t writeNonBlocking(const uint8_t *data, size_t size);
132 |
133 | DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer);
134 | };
135 |
136 | ////////////////////////////////////////////////////////////////////////////////
137 |
138 | DirectRenderer::DecoderContext::DecoderContext(const sp ¬ify)
139 | : mNotify(notify),
140 | mDecoderNotificationPending(false) {
141 | }
142 |
143 | DirectRenderer::DecoderContext::~DecoderContext() {
144 | if (mDecoder != NULL) {
145 | mDecoder->release();
146 | mDecoder.clear();
147 |
148 | mDecoderLooper->stop();
149 | mDecoderLooper.clear();
150 | }
151 | }
152 |
153 | status_t DirectRenderer::DecoderContext::init(
154 | const sp &format,
155 | const sp &surfaceTex) {
156 | CHECK(mDecoder == NULL);
157 |
158 | AString mime;
159 | CHECK(format->findString("mime", &mime));
160 |
161 | mDecoderLooper = new ALooper;
162 | mDecoderLooper->setName("video codec looper");
163 |
164 | mDecoderLooper->start(
165 | false /* runOnCallingThread */,
166 | false /* canCallJava */,
167 | PRIORITY_DEFAULT);
168 |
169 | mDecoder = MediaCodec::CreateByType(
170 | mDecoderLooper, mime.c_str(), false /* encoder */);
171 |
172 | CHECK(mDecoder != NULL);
173 |
174 | status_t err = mDecoder->configure(
175 | format,
176 | surfaceTex == NULL
177 | ? NULL : new Surface(surfaceTex),
178 | NULL /* crypto */,
179 | 0 /* flags */);
180 | CHECK_EQ(err, (status_t)OK);
181 |
182 | err = mDecoder->start();
183 | CHECK_EQ(err, (status_t)OK);
184 |
185 | err = mDecoder->getInputBuffers(
186 | &mDecoderInputBuffers);
187 | CHECK_EQ(err, (status_t)OK);
188 |
189 | err = mDecoder->getOutputBuffers(
190 | &mDecoderOutputBuffers);
191 | CHECK_EQ(err, (status_t)OK);
192 |
193 | scheduleDecoderNotification();
194 |
195 | return OK;
196 | }
197 |
198 | void DirectRenderer::DecoderContext::queueInputBuffer(
199 | const sp &accessUnit) {
200 | CHECK(mDecoder != NULL);
201 |
202 | mAccessUnits.push_back(accessUnit);
203 | queueDecoderInputBuffers();
204 | }
205 |
206 | status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease(
207 | size_t index) {
208 | return mDecoder->renderOutputBufferAndRelease(index);
209 | }
210 |
211 | status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) {
212 | return mDecoder->releaseOutputBuffer(index);
213 | }
214 |
215 | void DirectRenderer::DecoderContext::queueDecoderInputBuffers() {
216 | if (mDecoder == NULL) {
217 | return;
218 | }
219 |
220 | bool submittedMore = false;
221 |
222 | while (!mAccessUnits.empty()
223 | && !mDecoderInputBuffersAvailable.empty()) {
224 | size_t index = *mDecoderInputBuffersAvailable.begin();
225 |
226 | mDecoderInputBuffersAvailable.erase(
227 | mDecoderInputBuffersAvailable.begin());
228 |
229 | sp srcBuffer = *mAccessUnits.begin();
230 | mAccessUnits.erase(mAccessUnits.begin());
231 |
232 | const sp &dstBuffer =
233 | mDecoderInputBuffers.itemAt(index);
234 |
235 | memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size());
236 |
237 | int64_t timeUs;
238 | CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs));
239 |
240 | status_t err = mDecoder->queueInputBuffer(
241 | index,
242 | 0 /* offset */,
243 | srcBuffer->size(),
244 | timeUs,
245 | 0 /* flags */);
246 | CHECK_EQ(err, (status_t)OK);
247 |
248 | submittedMore = true;
249 | }
250 |
251 | if (submittedMore) {
252 | scheduleDecoderNotification();
253 | }
254 | }
255 |
256 | void DirectRenderer::DecoderContext::onMessageReceived(
257 | const sp &msg) {
258 | switch (msg->what()) {
259 | case kWhatDecoderNotify:
260 | {
261 | onDecoderNotify();
262 | break;
263 | }
264 |
265 | default:
266 | TRESPASS();
267 | }
268 | }
269 |
270 | void DirectRenderer::DecoderContext::onDecoderNotify() {
271 | mDecoderNotificationPending = false;
272 |
273 | for (;;) {
274 | size_t index;
275 | status_t err = mDecoder->dequeueInputBuffer(&index);
276 |
277 | if (err == OK) {
278 | mDecoderInputBuffersAvailable.push_back(index);
279 | } else if (err == -EAGAIN) {
280 | break;
281 | } else {
282 | TRESPASS();
283 | }
284 | }
285 |
286 | queueDecoderInputBuffers();
287 |
288 | for (;;) {
289 | size_t index;
290 | size_t offset;
291 | size_t size;
292 | int64_t timeUs;
293 | uint32_t flags;
294 | status_t err = mDecoder->dequeueOutputBuffer(
295 | &index,
296 | &offset,
297 | &size,
298 | &timeUs,
299 | &flags);
300 |
301 | if (err == OK) {
302 | queueOutputBuffer(
303 | index, timeUs, mDecoderOutputBuffers.itemAt(index));
304 | } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
305 | err = mDecoder->getOutputBuffers(
306 | &mDecoderOutputBuffers);
307 | CHECK_EQ(err, (status_t)OK);
308 | } else if (err == INFO_FORMAT_CHANGED) {
309 | // We don't care.
310 | } else if (err == -EAGAIN) {
311 | break;
312 | } else {
313 | TRESPASS();
314 | }
315 | }
316 |
317 | scheduleDecoderNotification();
318 | }
319 |
320 | void DirectRenderer::DecoderContext::scheduleDecoderNotification() {
321 | if (mDecoderNotificationPending) {
322 | return;
323 | }
324 |
325 | sp notify =
326 | PlantUtils::newAMessage(kWhatDecoderNotify, this);
327 |
328 | mDecoder->requestActivityNotification(notify);
329 | mDecoderNotificationPending = true;
330 | }
331 |
332 | void DirectRenderer::DecoderContext::queueOutputBuffer(
333 | size_t index, int64_t timeUs, const sp &buffer) {
334 | sp msg = mNotify->dup();
335 | msg->setInt32("what", kWhatOutputBufferReady);
336 | msg->setSize("index", index);
337 | msg->setInt64("timeUs", timeUs);
338 | msg->setBuffer("buffer", buffer);
339 | msg->post();
340 | }
341 |
342 | ////////////////////////////////////////////////////////////////////////////////
343 |
344 | DirectRenderer::AudioRenderer::AudioRenderer(
345 | const sp &decoderContext)
346 | : mDecoderContext(decoderContext),
347 | mPushPending(false),
348 | mNumFramesWritten(0) {
349 | mAudioTrack = new AudioTrack(
350 | AUDIO_STREAM_DEFAULT,
351 | 48000.0f,
352 | AUDIO_FORMAT_PCM,
353 | AUDIO_CHANNEL_OUT_STEREO,
354 | (int)0 /* frameCount */);
355 |
356 | CHECK_EQ((status_t)OK, mAudioTrack->initCheck());
357 |
358 | mAudioTrack->start();
359 | }
360 |
361 | DirectRenderer::AudioRenderer::~AudioRenderer() {
362 | }
363 |
364 | void DirectRenderer::AudioRenderer::queueInputBuffer(
365 | size_t index, int64_t timeUs, const sp &buffer) {
366 | BufferInfo info;
367 | info.mIndex = index;
368 | info.mTimeUs = timeUs;
369 | info.mBuffer = buffer;
370 |
371 | mInputBuffers.push_back(info);
372 | schedulePushIfNecessary();
373 | }
374 |
375 | void DirectRenderer::AudioRenderer::onMessageReceived(
376 | const sp &msg) {
377 | switch (msg->what()) {
378 | case kWhatPushAudio:
379 | {
380 | onPushAudio();
381 | break;
382 | }
383 |
384 | default:
385 | break;
386 | }
387 | }
388 |
389 | void DirectRenderer::AudioRenderer::schedulePushIfNecessary() {
390 | if (mPushPending || mInputBuffers.empty()) {
391 | return;
392 | }
393 |
394 | mPushPending = true;
395 |
396 | uint32_t numFramesPlayed;
397 | CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed),
398 | (status_t)OK);
399 |
400 | uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed;
401 |
402 | // This is how long the audio sink will have data to
403 | // play back.
404 | const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate();
405 |
406 | int64_t delayUs =
407 | msecsPerFrame * numFramesPendingPlayout * 1000ll;
408 |
409 | // Let's give it more data after about half that time
410 | // has elapsed.
411 | (PlantUtils::newAMessage(kWhatPushAudio, this))->post(delayUs / 2);
412 | }
413 |
414 | void DirectRenderer::AudioRenderer::onPushAudio() {
415 | mPushPending = false;
416 |
417 | while (!mInputBuffers.empty()) {
418 | const BufferInfo &info = *mInputBuffers.begin();
419 |
420 | ssize_t n = writeNonBlocking(
421 | info.mBuffer->data(), info.mBuffer->size());
422 |
423 | if (n < (ssize_t)info.mBuffer->size()) {
424 | CHECK_GE(n, 0);
425 |
426 | info.mBuffer->setRange(
427 | info.mBuffer->offset() + n, info.mBuffer->size() - n);
428 | break;
429 | }
430 |
431 | mDecoderContext->releaseOutputBuffer(info.mIndex);
432 |
433 | mInputBuffers.erase(mInputBuffers.begin());
434 | }
435 |
436 | schedulePushIfNecessary();
437 | }
438 |
439 | ssize_t DirectRenderer::AudioRenderer::writeNonBlocking(
440 | const uint8_t *data, size_t size) {
441 | uint32_t numFramesPlayed;
442 | status_t err = mAudioTrack->getPosition(&numFramesPlayed);
443 | if (err != OK) {
444 | return err;
445 | }
446 |
447 | ssize_t numFramesAvailableToWrite =
448 | mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed);
449 |
450 | size_t numBytesAvailableToWrite =
451 | numFramesAvailableToWrite * mAudioTrack->frameSize();
452 |
453 | if (size > numBytesAvailableToWrite) {
454 | size = numBytesAvailableToWrite;
455 | }
456 |
457 | CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size);
458 |
459 | size_t numFramesWritten = size / mAudioTrack->frameSize();
460 | mNumFramesWritten += numFramesWritten;
461 |
462 | return size;
463 | }
464 |
465 | ////////////////////////////////////////////////////////////////////////////////
466 |
467 | DirectRenderer::DirectRenderer(
468 | const sp &bufferProducer)
469 | : mSurfaceTex(bufferProducer),
470 | mVideoRenderPending(false),
471 | mNumFramesLate(0),
472 | mNumFrames(0) {
473 | }
474 |
475 | DirectRenderer::~DirectRenderer() {
476 | }
477 |
478 | void DirectRenderer::onMessageReceived(const sp &msg) {
479 | switch (msg->what()) {
480 | case kWhatDecoderNotify:
481 | {
482 | onDecoderNotify(msg);
483 | break;
484 | }
485 |
486 | case kWhatRenderVideo:
487 | {
488 | onRenderVideo();
489 | break;
490 | }
491 |
492 | case kWhatQueueAccessUnit:
493 | onQueueAccessUnit(msg);
494 | break;
495 |
496 | case kWhatSetFormat:
497 | onSetFormat(msg);
498 | break;
499 |
500 | default:
501 | TRESPASS();
502 | }
503 | }
504 |
505 | void DirectRenderer::setFormat(size_t trackIndex, const sp &format) {
506 | sp msg = PlantUtils::newAMessage(kWhatSetFormat, this);
507 | msg->setSize("trackIndex", trackIndex);
508 | msg->setMessage("format", format);
509 | msg->post();
510 | }
511 |
512 | void DirectRenderer::onSetFormat(const sp &msg) {
513 | size_t trackIndex;
514 | CHECK(msg->findSize("trackIndex", &trackIndex));
515 |
516 | sp format;
517 | CHECK(msg->findMessage("format", &format));
518 |
519 | internalSetFormat(trackIndex, format);
520 | }
521 |
522 | void DirectRenderer::internalSetFormat(
523 | size_t trackIndex, const sp &format) {
524 | CHECK_LT(trackIndex, 2u);
525 |
526 | CHECK(mDecoderContext[trackIndex] == NULL);
527 |
528 | sp notify = PlantUtils::newAMessage(kWhatDecoderNotify, this);
529 | notify->setSize("trackIndex", trackIndex);
530 |
531 | mDecoderContext[trackIndex] = new DecoderContext(notify);
532 | looper()->registerHandler(mDecoderContext[trackIndex]);
533 |
534 | CHECK_EQ((status_t)OK,
535 | mDecoderContext[trackIndex]->init(
536 | format, trackIndex == 0 ? mSurfaceTex : NULL));
537 |
538 | if (trackIndex == 1) {
539 | // Audio
540 | mAudioRenderer = new AudioRenderer(mDecoderContext[1]);
541 | looper()->registerHandler(mAudioRenderer);
542 | }
543 | }
544 |
545 | void DirectRenderer::queueAccessUnit(
546 | size_t trackIndex, const sp &accessUnit) {
547 | sp msg = PlantUtils::newAMessage(kWhatQueueAccessUnit, this);
548 | msg->setSize("trackIndex", trackIndex);
549 | msg->setBuffer("accessUnit", accessUnit);
550 | msg->post();
551 | }
552 |
553 | void DirectRenderer::onQueueAccessUnit(const sp &msg) {
554 | size_t trackIndex;
555 | CHECK(msg->findSize("trackIndex", &trackIndex));
556 |
557 | sp accessUnit;
558 | CHECK(msg->findBuffer("accessUnit", &accessUnit));
559 |
560 | CHECK_LT(trackIndex, 2u);
561 | CHECK(mDecoderContext[trackIndex] != NULL);
562 |
563 | mDecoderContext[trackIndex]->queueInputBuffer(accessUnit);
564 | }
565 |
566 | void DirectRenderer::onDecoderNotify(const sp &msg) {
567 | size_t trackIndex;
568 | CHECK(msg->findSize("trackIndex", &trackIndex));
569 |
570 | int32_t what;
571 | CHECK(msg->findInt32("what", &what));
572 |
573 | switch (what) {
574 | case DecoderContext::kWhatOutputBufferReady:
575 | {
576 | size_t index;
577 | CHECK(msg->findSize("index", &index));
578 |
579 | int64_t timeUs;
580 | CHECK(msg->findInt64("timeUs", &timeUs));
581 |
582 | sp buffer;
583 | CHECK(msg->findBuffer("buffer", &buffer));
584 |
585 | queueOutputBuffer(trackIndex, index, timeUs, buffer);
586 | break;
587 | }
588 |
589 | default:
590 | TRESPASS();
591 | }
592 | }
593 |
594 | void DirectRenderer::queueOutputBuffer(
595 | size_t trackIndex,
596 | size_t index, int64_t timeUs, const sp &buffer) {
597 | if (trackIndex == 1) {
598 | // Audio
599 | mAudioRenderer->queueInputBuffer(index, timeUs, buffer);
600 | return;
601 | }
602 |
603 | OutputInfo info;
604 | info.mIndex = index;
605 | info.mTimeUs = timeUs;
606 | info.mBuffer = buffer;
607 | mVideoOutputBuffers.push_back(info);
608 |
609 | scheduleVideoRenderIfNecessary();
610 | }
611 |
612 | void DirectRenderer::scheduleVideoRenderIfNecessary() {
613 | if (mVideoRenderPending || mVideoOutputBuffers.empty()) {
614 | return;
615 | }
616 |
617 | mVideoRenderPending = true;
618 |
619 | int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs;
620 | int64_t nowUs = ALooper::GetNowUs();
621 |
622 | int64_t delayUs = timeUs - nowUs;
623 |
624 | (PlantUtils::newAMessage(kWhatRenderVideo, this))->post(delayUs);
625 | }
626 |
627 | void DirectRenderer::onRenderVideo() {
628 | mVideoRenderPending = false;
629 |
630 | int64_t nowUs = ALooper::GetNowUs();
631 |
632 | while (!mVideoOutputBuffers.empty()) {
633 | const OutputInfo &info = *mVideoOutputBuffers.begin();
634 |
635 | if (info.mTimeUs > nowUs) {
636 | break;
637 | }
638 |
639 | if (info.mTimeUs + 15000ll < nowUs) {
640 | ++mNumFramesLate;
641 | ALOGD("DirectRenderer::onRenderVideo, late frames(>15ms) vs total frames (%d : %d)", mNumFramesLate, mNumFrames);
642 | // TODO: skip render late frame.
643 | }
644 | ++mNumFrames;
645 |
646 | status_t err =
647 | mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex);
648 | CHECK_EQ(err, (status_t)OK);
649 |
650 | mVideoOutputBuffers.erase(mVideoOutputBuffers.begin());
651 | }
652 |
653 | scheduleVideoRenderIfNecessary();
654 | }
655 |
656 | } // namespace android
657 |
658 |
--------------------------------------------------------------------------------
/lib/sink/DirectRenderer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012, The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef DIRECT_RENDERER_H_
18 |
19 | #define DIRECT_RENDERER_H_
20 |
21 | #include
22 |
23 | namespace android {
24 |
25 | struct ABuffer;
26 | struct IGraphicBufferProducer;
27 |
28 | // Renders audio and video data queued by calls to "queueAccessUnit".
29 | struct DirectRenderer : public AHandler {
30 | DirectRenderer(const sp &bufferProducer);
31 |
32 | void setFormat(size_t trackIndex, const sp &format);
33 | void queueAccessUnit(size_t trackIndex, const sp &accessUnit);
34 |
35 | protected:
36 | virtual void onMessageReceived(const sp