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