Add sample ATSC TvInputService app.
Change-Id: I67204020bf1eb0c833d507cb0c9e4914e5985b3c
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.atsctvinput;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.TvContract;
|
||||
import android.tv.TvInputService;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.example.android.atsctvinput.SampleTsStream.TsStream;
|
||||
import com.example.android.atsctvinput.SectionParser.EITItem;
|
||||
import com.example.android.atsctvinput.SectionParser.VCTItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A sample TvInputService which plays ATSC TV stream.
|
||||
*/
|
||||
public class AtscTvInputService extends TvInputService {
|
||||
private static final String TAG = "AtscTvInputService";
|
||||
private static final int SEC_IN_MS = 1000;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "onCreate()");
|
||||
setAvailable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.d(TAG, "onDestroy()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TvInputSessionImpl onCreateSession() {
|
||||
return new MyTvInputSessionImpl();
|
||||
}
|
||||
|
||||
|
||||
public TsStream getTsStreamForChannel(Uri channelUri) {
|
||||
String[] projection = { TvContract.Channels.DATA };
|
||||
if (channelUri == null) {
|
||||
return null;
|
||||
}
|
||||
Cursor cursor = this.getContentResolver().query(
|
||||
channelUri, projection, null, null, null);
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
if (cursor.getCount() < 1) {
|
||||
cursor.close();
|
||||
return null;
|
||||
}
|
||||
cursor.moveToNext();
|
||||
TsStream stream = SampleTsStream.getTsStreamFromTuneInfo(cursor.getString(0));
|
||||
cursor.close();
|
||||
return stream;
|
||||
}
|
||||
|
||||
private class MyTvInputSessionImpl extends TvInputSessionImpl {
|
||||
private MediaPlayer mPlayer;
|
||||
|
||||
protected MyTvInputSessionImpl() {
|
||||
mPlayer = new MediaPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease() {
|
||||
if (mPlayer != null) {
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSetSurface(Surface surface) {
|
||||
Log.d(TAG, "onSetSurface(" + surface + ")");
|
||||
mPlayer.setSurface(surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetVolume(float volume) {
|
||||
Log.d(TAG, "onSetVolume(" + volume + ")");
|
||||
mPlayer.setVolume(volume, volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTune(Uri channelUri) {
|
||||
Log.d(TAG, "onTune(" + channelUri + ")");
|
||||
mPlayer.reset();
|
||||
TsStream stream = getTsStreamForChannel(channelUri);
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
new ProgramUpdateTask().execute(stream, channelUri);
|
||||
AssetFileDescriptor afd = getResources().openRawResourceFd(stream.mResourceId);
|
||||
try {
|
||||
mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
|
||||
afd.getLength());
|
||||
mPlayer.prepare();
|
||||
afd.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to tune to(" + channelUri + ")");
|
||||
mPlayer.reset();
|
||||
return false;
|
||||
}
|
||||
mPlayer.start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgramUpdateTask extends AsyncTask<Object, Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground(Object... objs) {
|
||||
TsStream stream = (TsStream) objs[0];
|
||||
Uri channelUri = (Uri) objs[1];
|
||||
Pair<VCTItem, List<EITItem>> result =
|
||||
SampleTsStream.extractChannelInfo(AtscTvInputService.this, stream);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
clearPrograms(channelUri);
|
||||
// The sample streams have passed timestamps. In order to show the metadata properly in
|
||||
// TV app, we offset the time here.
|
||||
long timeOffsetMs = Long.MIN_VALUE;
|
||||
long currentTimeMs = System.currentTimeMillis();
|
||||
for (EITItem i : result.second) {
|
||||
if (timeOffsetMs == Long.MIN_VALUE) {
|
||||
timeOffsetMs = currentTimeMs - i.getStartTime() * SEC_IN_MS;
|
||||
}
|
||||
insertProgram(channelUri, i, timeOffsetMs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void clearPrograms(Uri channelUri) {
|
||||
String selection = TvContract.Programs.CHANNEL_ID + " = ?";
|
||||
String[] selectionArgs =
|
||||
new String[] { Long.toString(ContentUris.parseId(channelUri)) };
|
||||
getContentResolver().delete(TvContract.Programs.CONTENT_URI, selection, selectionArgs);
|
||||
}
|
||||
|
||||
private Uri insertProgram(Uri channelUri, EITItem event, long timeOffsetMs) {
|
||||
Log.d(TAG, "insertProgram " + event.getTitleText());
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TvContract.Programs.CHANNEL_ID, ContentUris.parseId(channelUri));
|
||||
values.put(TvContract.Programs.TITLE, event.getTitleText());
|
||||
values.put(TvContract.Programs.START_TIME_UTC_MILLIS, timeOffsetMs
|
||||
+ event.getStartTime() * SEC_IN_MS);
|
||||
values.put(TvContract.Programs.END_TIME_UTC_MILLIS, timeOffsetMs
|
||||
+ (event.getStartTime() + event.getLengthInSecond()) * SEC_IN_MS);
|
||||
return getContentResolver().insert(
|
||||
TvContract.Programs.CONTENT_URI, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user