• 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         new Stream("H263 Profile 0 Video, AMR Audio", "http_h263_amr",
119               "https://dl.google.com/android/xts/cts/apps/CtsVerifier/"
120               + "streaming_media_player_test_http_h263_amr_video1.mp4"),
121         new Stream("MPEG4 SP Video, AAC Audio", "http_mpeg4_aac",
122               "https://dl.google.com/"
123               + "android/xts/cts/apps/CtsVerifier/streaming-video-activity-itag-17.mp4"),
124         new Stream("H264 Base Video, AAC Audio", "http_h264_aac",
125               "https://dl.google.com/"
126               + "android/xts/cts/apps/CtsVerifier/streaming-video-activity-itag-18.mp4"),
127     };
128 
129     @Override
onCreate(Bundle savedInstanceState)130     protected void onCreate(Bundle savedInstanceState) {
131         super.onCreate(savedInstanceState);
132         setContentView(R.layout.pass_fail_list);
133         setPassFailButtonClickListeners();
134         setInfoResources(R.string.streaming_video, R.string.streaming_video_info, -1);
135 
136         TextView empty = (TextView) findViewById(android.R.id.empty);
137         empty.setText(R.string.sv_no_data);
138 
139         getPassButton().setEnabled(false);
140         setTestListAdapter(getStreamAdapter());
141     }
142 
143     @Override
onCreateDialog(int id, Bundle args)144     public Dialog onCreateDialog(int id, Bundle args) {
145         switch (id) {
146             case RTSP_URL_ERROR:
147                 return new AlertDialog.Builder(this)
148                         .setTitle(getString(R.string.sv_failed_title))
149                         .setMessage(getString(R.string.sv_failed_message))
150                         .setNegativeButton("Close", new OnClickListener() {
151                             @Override
152                             public void onClick(DialogInterface dialog, int which) {
153                                 setTestResultAndFinish(false);
154                             }
155                         }).show();
156             default:
157                 return super.onCreateDialog(id, args);
158         }
159     }
160 
161     private TestListAdapter getStreamAdapter() {
162         ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
163 
164         // TODO(b/161675976): Re-enable RTSP tests once they can be served reliably.
165         /*
166         adapter.add(TestListItem.newCategory("RTSP"));
167         addRtspStreamToTest(
168                 adapter, "H263 Video, AMR Audio", "rtsp_h263_amr", 13, ITAG_13_SIGNATURE);
169         addRtspStreamToTest(
170                 adapter, "MPEG4 SP Video, AAC Audio", "rtsp_mpeg4_aac", 17, ITAG_17_SIGNATURE);
171         addRtspStreamToTest(
172                 adapter, "H264 Base Video, AAC Audio", "rtsp_h264_aac", 18, ITAG_18_SIGNATURE);
173         */
174 
175         adapter.add(TestListItem.newCategory("HTTP Progressive"));
176         for (Stream stream : HTTP_STREAMS) {
177             addStreamToTests(adapter, stream);
178         }
179 
180         adapter.registerDataSetObserver(new DataSetObserver() {
181             @Override
182             public void onChanged() {
183                 updatePassButton();
184             }
185         });
186 
187         return adapter;
188     }
189 
190     private void addRtspStreamToTest(
191             ArrayTestListAdapter adapter, String name, String code, int itag, String signature) {
192         String rtspUrl = lookupRtspUrl(itag, signature);
193         if (rtspUrl == null) {
194             showDialog(RTSP_URL_ERROR);
195         }
196         Stream stream = new Stream(name, code, rtspUrl);
197         addStreamToTests(adapter, stream);
198     }
199 
200     private void addStreamToTests(ArrayTestListAdapter streams, Stream stream) {
201         Intent i = new Intent(StreamingVideoActivity.this, PlayVideoActivity.class);
202         i.putExtra(PlayVideoActivity.EXTRA_STREAM, stream);
203         streams.add(TestListItem.newTest(stream.name, PlayVideoActivity.getTestId(stream.code),
204                 i, null));
205     }
206 
207     /** @returns the appropriate RTSP url, or null in case of failure */
208     private String lookupRtspUrl(int itag, String signature) {
209         String rtspLookupUri = String.format(RTSP_LOOKUP_URI_TEMPLATE, itag, signature);
210         try {
211             return new LookupRtspUrlTask().execute(rtspLookupUri).get();
212         } catch (Exception e) {
213             Log.e(TAG, "RTSP URL lookup time out.", e);
214             showDialog(RTSP_URL_ERROR);
215             return null;
216         }
217     }
218 
219     /** Retrieve the URL for an RTSP stream */
220     private class LookupRtspUrlTask extends AsyncTask<String, Void, String> {
221         protected String doInBackground(String... rtspLookupUri) {
222             HttpURLConnection urlConnection = null;
223             try {
224                 URL url = new URL(rtspLookupUri[0]);
225                 urlConnection = (HttpURLConnection) url.openConnection();
226                 if (urlConnection.getResponseCode() != 200) {
227                     throw new IOException("unable to get rtsp uri. Response Code:"
228                             + urlConnection.getResponseCode());
229                 }
230                 BufferedReader reader = new BufferedReader(
231                         new InputStreamReader(urlConnection.getInputStream()));
232                 return reader.readLine();
233             } catch (Exception e) {
234                 Log.e(TAG, "RTSP URL lookup failed.", e);
235                 return null;
236             } finally {
237                 if (urlConnection != null) {
238                     urlConnection.disconnect();
239                 }
240             }
241         }
242     }
243 }
244