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