• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 package com.android.cts.verifier.streamquality;
18 
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.content.DialogInterface;
22 import android.content.DialogInterface.OnClickListener;
23 import android.content.Intent;
24 import android.database.DataSetObserver;
25 import android.os.AsyncTask;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.widget.TextView;
29 
30 import com.android.cts.verifier.ArrayTestListAdapter;
31 import com.android.cts.verifier.PassFailButtons;
32 import com.android.cts.verifier.R;
33 import com.android.cts.verifier.TestListAdapter;
34 import com.android.cts.verifier.TestListAdapter.TestListItem;
35 
36 import java.io.BufferedReader;
37 import java.io.IOException;
38 import java.io.InputStreamReader;
39 import java.io.Serializable;
40 import java.net.HttpURLConnection;
41 import java.net.URL;
42 
43 /**
44  * Tests for verifying the quality of streaming videos.  Plays streams of different formats over
45  * different protocols for a short amount of time, after which users can mark Pass/Fail depending
46  * on the smoothness and subjective quality of the video.
47  */
48 public class StreamingVideoActivity extends PassFailButtons.TestListActivity {
49     /**
50      * Simple storage class for stream information.
51      */
52     static class Stream implements Serializable {
53         /**
54          * Human-readable name for the stream.
55          */
56         public final String name;
57 
58         /**
59          * Code name to append to the class name to identify this test.
60          */
61         public final String code;
62 
63         /**
64          * URI of the stream
65          */
66         public final String uri;
67 
Stream(String name, String code, String uri)68         public Stream(String name, String code, String uri) {
69             this.name = name;
70             this.code = code;
71             this.uri = uri;
72         }
73 
74         @Override
equals(Object o)75         public boolean equals(Object o) {
76             if (this == o) {
77                 return true;
78             } else if (o == null || !(o instanceof Stream)) {
79                 return false;
80             } else {
81                 Stream stream = (Stream) o;
82                 return name.equals(stream.name)
83                         && code.equals(stream.code)
84                         && uri.equals(stream.uri);
85             }
86         }
87 
88         @Override
hashCode()89         public int hashCode() {
90             return name.hashCode() ^ uri.hashCode() ^ code.hashCode();
91         }
92 
93         @Override
toString()94         public String toString() {
95             return name;
96         }
97     }
98 
99     private static final String TAG = StreamingVideoActivity.class.getName();
100     private static final int RTSP_URL_ERROR = 1;
101     private static final String ITAG_13_SIGNATURE =
102             "53A3A3A46DAB71E3C599DE8FA6A1484593DFACE3" +
103             ".9476B91AD5035D88C3895CC2A4703B6DD442E972";
104     private static final String ITAG_17_SIGNATURE =
105             "10D6D263112C41DA98822D74821DF47340F1A361" +
106             ".803649A2258E26BF40E76A95E646FBAE4009CEE8";
107     private static final String ITAG_18_SIGNATURE =
108             "618FBB112E1B2FBB66DA9F203AE8CC7DF93C7400" +
109             ".20498AA006E999F42BE69D66E3596F2C7CA18114";
110     private static final String RTSP_LOOKUP_URI_TEMPLATE =
111             "http://redirector.gvt1.com/videoplayback?id=271de9756065677e" +
112             "&source=youtube&protocol=rtsp&sparams=ip,ipbits,expire,id,itag,source" +
113             "&ip=0.0.0.0&ipbits=0&expire=19000000000&key=ik0&alr=yes" +
114             "&itag=%d" +
115             "&signature=%s";
116 
117     private static final Stream[] HTTP_STREAMS = {
118         // TODO(b/150835350): Re-enable H263 test once the clip is updated to profile 0
119         /*
120         new Stream("H263 Video, AMR Audio", "http_h263_amr",
121                 "http://redirector.gvt1.com/"
122                 + "videoplayback?id=271de9756065677e"
123                 + "&itag=13&ip=0.0.0.0&ipbits=0&expire=19000000000"
124                 + "&sparams=ip,ipbits,expire,id,itag"
125                 + "&signature=073A731E2BDF1E05206AC7B9B895C922ABCBA01D"
126                 + ".1DDA3F999541D2136E6755F16FC44CA972767169"
127                 + "&source=youtube"
128                 + "&key=ik0&user=android-device-test"),
129         */
130         new Stream("MPEG4 SP Video, AAC Audio", "http_mpeg4_aac",
131               "https://dl.google.com/"
132               + "android/xts/cts/apps/CtsVerifier/streaming-video-activity-itag-17.mp4"),
133         new Stream("H264 Base Video, AAC Audio", "http_h264_aac",
134               "https://dl.google.com/"
135               + "android/xts/cts/apps/CtsVerifier/streaming-video-activity-itag-18.mp4"),
136     };
137 
138     @Override
onCreate(Bundle savedInstanceState)139     protected void onCreate(Bundle savedInstanceState) {
140         super.onCreate(savedInstanceState);
141         setContentView(R.layout.pass_fail_list);
142         setPassFailButtonClickListeners();
143         setInfoResources(R.string.streaming_video, R.string.streaming_video_info, -1);
144 
145         TextView empty = (TextView) findViewById(android.R.id.empty);
146         empty.setText(R.string.sv_no_data);
147 
148         getPassButton().setEnabled(false);
149         setTestListAdapter(getStreamAdapter());
150     }
151 
152     @Override
onCreateDialog(int id, Bundle args)153     public Dialog onCreateDialog(int id, Bundle args) {
154         switch (id) {
155             case RTSP_URL_ERROR:
156                 return new AlertDialog.Builder(this)
157                         .setTitle(getString(R.string.sv_failed_title))
158                         .setMessage(getString(R.string.sv_failed_message))
159                         .setNegativeButton("Close", new OnClickListener() {
160                             @Override
161                             public void onClick(DialogInterface dialog, int which) {
162                                 setTestResultAndFinish(false);
163                             }
164                         }).show();
165             default:
166                 return super.onCreateDialog(id, args);
167         }
168     }
169 
170     private TestListAdapter getStreamAdapter() {
171         ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
172 
173         // TODO(b/161675976): Re-enable RTSP tests once they can be served reliably.
174         /*
175         adapter.add(TestListItem.newCategory("RTSP"));
176         addRtspStreamToTest(
177                 adapter, "H263 Video, AMR Audio", "rtsp_h263_amr", 13, ITAG_13_SIGNATURE);
178         addRtspStreamToTest(
179                 adapter, "MPEG4 SP Video, AAC Audio", "rtsp_mpeg4_aac", 17, ITAG_17_SIGNATURE);
180         addRtspStreamToTest(
181                 adapter, "H264 Base Video, AAC Audio", "rtsp_h264_aac", 18, ITAG_18_SIGNATURE);
182         */
183 
184         adapter.add(TestListItem.newCategory("HTTP Progressive"));
185         for (Stream stream : HTTP_STREAMS) {
186             addStreamToTests(adapter, stream);
187         }
188 
189         adapter.registerDataSetObserver(new DataSetObserver() {
190             @Override
191             public void onChanged() {
192                 updatePassButton();
193             }
194         });
195 
196         return adapter;
197     }
198 
199     private void addRtspStreamToTest(
200             ArrayTestListAdapter adapter, String name, String code, int itag, String signature) {
201         String rtspUrl = lookupRtspUrl(itag, signature);
202         if (rtspUrl == null) {
203             showDialog(RTSP_URL_ERROR);
204         }
205         Stream stream = new Stream(name, code, rtspUrl);
206         addStreamToTests(adapter, stream);
207     }
208 
209     private void addStreamToTests(ArrayTestListAdapter streams, Stream stream) {
210         Intent i = new Intent(StreamingVideoActivity.this, PlayVideoActivity.class);
211         i.putExtra(PlayVideoActivity.EXTRA_STREAM, stream);
212         streams.add(TestListItem.newTest(stream.name, PlayVideoActivity.getTestId(stream.code),
213                 i, null));
214     }
215 
216     /** @returns the appropriate RTSP url, or null in case of failure */
217     private String lookupRtspUrl(int itag, String signature) {
218         String rtspLookupUri = String.format(RTSP_LOOKUP_URI_TEMPLATE, itag, signature);
219         try {
220             return new LookupRtspUrlTask().execute(rtspLookupUri).get();
221         } catch (Exception e) {
222             Log.e(TAG, "RTSP URL lookup time out.", e);
223             showDialog(RTSP_URL_ERROR);
224             return null;
225         }
226     }
227 
228     /** Retrieve the URL for an RTSP stream */
229     private class LookupRtspUrlTask extends AsyncTask<String, Void, String> {
230         protected String doInBackground(String... rtspLookupUri) {
231             HttpURLConnection urlConnection = null;
232             try {
233                 URL url = new URL(rtspLookupUri[0]);
234                 urlConnection = (HttpURLConnection) url.openConnection();
235                 if (urlConnection.getResponseCode() != 200) {
236                     throw new IOException("unable to get rtsp uri. Response Code:"
237                             + urlConnection.getResponseCode());
238                 }
239                 BufferedReader reader = new BufferedReader(
240                         new InputStreamReader(urlConnection.getInputStream()));
241                 return reader.readLine();
242             } catch (Exception e) {
243                 Log.e(TAG, "RTSP URL lookup failed.", e);
244                 return null;
245             } finally {
246                 if (urlConnection != null) {
247                     urlConnection.disconnect();
248                 }
249             }
250         }
251     }
252 }
253