Bug 1180589 part 1 - Add simulator code for TV Manager API; r=seanlin

This commit is contained in:
Mantaroh Yoshinaga 2015-09-30 14:32:32 +09:00
parent 172ec52f3a
commit 8478bd2e88
10 changed files with 521 additions and 7 deletions

View File

@ -8,6 +8,7 @@
#include "mozilla/dom/TVListeners.h"
#include "mozilla/Preferences.h"
#include "nsITVService.h"
#include "nsITVSimulatorService.h"
#include "nsServiceManagerUtils.h"
#include "TVServiceFactory.h"
@ -27,8 +28,14 @@ TVServiceFactory::AutoCreateTVService()
nsresult rv;
nsCOMPtr<nsITVService> service = do_CreateInstance(TV_SERVICE_CONTRACTID);
if (!service) {
// Fallback to the fake service.
service = do_CreateInstance(FAKE_TV_SERVICE_CONTRACTID, &rv);
if (Preferences::GetBool("dom.testing.tv_enabled_for_hosted_apps", false)) {
// Fallback to the fake service.
service = do_CreateInstance(FAKE_TV_SERVICE_CONTRACTID, &rv);
} else {
// Fallback to the TV Simulator Service
service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID, &rv);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}

View File

@ -0,0 +1,423 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function debug(aMsg) {
//dump("[TVSimulatorService] " + aMsg + "\n");
}
const Cc = Components.classes;
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cr = Components.returnCode;
const TV_SIMULATOR_DUMMY_DIRECTORY = "dummy";
const TV_SIMULATOR_DUMMY_FILE = "settings.json";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function TVSimulatorService() {
this._internalTuners = null;
this._scanCompleteTimer = null;
this._scanningWrapTunerData = null;
this._init();
}
TVSimulatorService.prototype = {
classID: Components.ID("{f0ab9850-24b4-4f5d-83dd-0fea0c249ca1}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsITVSimulatorService,
Ci.nsITVService,
Ci.nsITimerCallback]),
_init: function TVSimInit() {
if (this._internalTuners) {
return;
}
// Load the setting file from local JSON file.
// Synchrhronous File Reading.
let dsFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
dsFile.append(TV_SIMULATOR_DUMMY_DIRECTORY);
dsFile.append(TV_SIMULATOR_DUMMY_FILE);
let file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
file.initWithPath(dsFile.path);
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Ci.nsIConverterInputStream);
let settingStr = "";
try {
fstream.init(file, -1, 0, 0);
cstream.init(fstream,
"UTF-8",
1024,
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
let str = {};
while (cstream.readString(0xffffffff, str) != 0) {
settingStr += str.value;
}
} catch(e) {
debug("Error occurred : " + e );
return;
} finally {
cstream.close();
}
let settingObj;
try {
/*
*
* Setting JSON file format:
*
* Note: This setting JSON is not allow empty array.
* If set the empty array, _init() will fail.
* e.g.
* - "tuners": []
* - "channels":[]
* Format:
* {
* "tuners": [{
* "id": "The ID of the tuner",
* "supportedType": ["The array of source type to be used."],
* "sources": [{
* "type": "The source type to be used",
* "channels" : [{
* "networkId": "The ID of the channel network",
* "transportStreamId": "The ID of channel transport stream",
* "serviceId": "The ID of channel service",
* "type": "The type of channel",
* "name": "The channel name",
* "number" : The LCN (Logical Channel Number) of the channel,
* "isEmergency" : Whether this channel is emergency status,
* "isFree": Whether this channel is free or not,
* "videoFilePath": "The path of the fake movie file",
* "programs":[{
* "eventId": "The ID of this program event",
* "title" : "This program's title",
* "startTime": "The start time of this program",
* "duration": "The duration of this program",
* "description": "The description of this program",
* "rating": "The rating of this program",
* "audioLanugages": ["The array of audio language"],
* "subtitleLanguages":["The array of subtitle language"],
* },]
* },]
* },]
* },]
* }
*/
settingObj = JSON.parse(settingStr);
} catch(e) {
debug("File load error: " + e);
return;
}
// Key is as follow
// {'tunerId':tunerId, 'sourceType':sourceType}
this._internalTuners = new Map();
// TVTunerData
for each (let tunerData in settingObj.tuners) {
let tuner = Cc["@mozilla.org/tv/tvtunerdata;1"]
.createInstance(Ci.nsITVTunerData);
tuner.id = tunerData.id;
tuner.streamType = tuner.TV_STREAM_TYPE_SIMULATOR;
tuner.setSupportedSourceTypes(tunerData.supportedType.length,
tunerData.supportedType);
let wrapTunerData = {
'tuner': tuner,
'channels': new Map(),
'sourceType': undefined,
};
// TVSource
for each (let sourceData in tunerData.sources) {
wrapTunerData.sourceType = sourceData.type;
// TVChannel
for each (let channelData in sourceData.channels) {
let channel = Cc["@mozilla.org/tv/tvchanneldata;1"]
.createInstance(Ci.nsITVChannelData);
channel.networkId = channelData.networkId;
channel.transportStreamId = channelData.transportStreamId;
channel.serviceId = channelData.serviceId;
channel.type = channelData.type;
channel.name = channelData.name;
channel.number = channelData.number;
channel.isEmergency = channelData.isEmergency;
channel.isFree = channelData.isFree;
let wrapChannelData = {
'channel': channel,
'programs': new Array(),
'videoFilePath': channelData.videoFilePath,
};
// TVProgram
for each (let programData in channelData.programs) {
let program = Cc["@mozilla.org/tv/tvprogramdata;1"]
.createInstance(Ci.nsITVProgramData);
program.eventId = programData.eventId;
program.title = programData.title;
program.startTime = programData.startTime;
program.duration = programData.duration;
program.description = programData.description;
program.rating = programData.rating;
program.setAudioLanguages(programData.audioLanguages.length,
programData.audioLanguages);
program.setSubtitleLanguages(programData.subtitleLanguages.length,
programData.subtitleLanguages);
wrapChannelData.programs.push(program);
}
// Sort the program according to the startTime
wrapChannelData.programs.sort(function(a, b) {
return a.startTime - b.startTime;
});
wrapTunerData.channels.set(channel.number, wrapChannelData);
}
// Sort the channel according to the channel number
wrapTunerData.channels = new Map([...wrapTunerData.channels.entries()].sort(function(a, b) {
return a[0] - b[0];
}));
this._internalTuners.set(
this._getTunerMapKey(tuner.id, sourceData.type),
wrapTunerData);
}
}
},
getTuners: function TVSimGetTuners(aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
let tuners = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
for (let [k,wrapTunerData] of this._internalTuners) {
tuners.appendElement(wrapTunerData.tuner, false);
}
return aCallback.notifySuccess(tuners);
},
setSource: function TVSimSetSource(aTunerId, aSourceType, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return NS_ERROR_INVALID_ARG;
}
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
// Bug 1180589 : We should change video source.
return aCallback.notifySuccess(null);
},
startScanningChannels: function TVSimStartScanningChannels(aTunerId, aSourceType, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData || !wrapTunerData.channels) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
if (this._scanningWrapTunerData) {
return aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
}
this._scanningWrapTunerData = wrapTunerData;
aCallback.notifySuccess(null);
for (let [key, wrapChannelData] of wrapTunerData.channels) {
this._sourceListener.notifyChannelScanned(
wrapTunerData.tuner.id,
wrapTunerData.sourceType,
wrapChannelData.channel);
}
this._scanCompleteTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
rv = this._scanCompleteTimer.initWithCallback(this, 10,
Ci.nsITimer.TYPE_ONE_SHOT);
return Cr.NS_OK;
},
notify: function TVSimTimerCallback(aTimer) {
if (!this._scanningWrapTunerData) {
return;
}
this._scanCompleteTimer = null;
this._scanningWrapTunerData = null;
return this._sourceListener.notifyChannelScanComplete(
this._scanningWrapTunerData.tuner.id,
this._scanningWrapTunerData.sourceType);
},
stopScanningChannels: function TVSimStopScanningChannels(aTunerId, aSourceType, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
if (!this._scanningWrapTunerData) {
return aCallback.notifyError(Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
}
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
if (this._scanCompleteTimer) {
this._scanCompleteTimer.cancel();
this._scanCompleteTimer = null;
}
if (wrapTunerData.tuner.id === this._scanningWrapTunerData.tuner.id &&
wrapTunerData.sourceType === this._scanningWrapTunerData.sourceType) {
this._scanningWrapTunerData = null;
this._sourceListener.notifyChannelScanStopped(
wrapTunerData.tuner.id,
wrapTunerData.sourceType);
}
return aCallback.notifySuccess(null);
},
clearScannedChannelsCache: function TVSimClearScannedChannelsCache(aTunerId, aSourceType, aCallback) {
// Doesn't support for this method.
return Cr.NS_OK;
},
setChannel: function TVSimSetChannel(aTunerId, aSourceType, aChannelNumber, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
let channel = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData || !wrapTunerData.channels) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
let wrapChannelData = wrapTunerData.channels.get(aChannelNumber);
if (!wrapChannelData) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
channel.appendElement(wrapChannelData.channel, false);
return aCallback.notifySuccess(channel);
},
getChannels: function TVSimGetChannels(aTunerId, aSourceType, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
let channelArray = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData || !wrapTunerData.channels) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
for (let [key, wrapChannelData] of wrapTunerData.channels) {
channelArray.appendElement(wrapChannelData.channel, false);
}
return aCallback.notifySuccess(channelArray);
},
getPrograms: function TVSimGetPrograms(aTunerId, aSourceType, aChannelNumber, aStartTime, aEndTime, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
let programArray = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData || !wrapTunerData.channels) {
return aCallback.notifyError(Ci.nsITVServiceCallback.TV_ERROR_FAILURE);
}
let wrapChannelData = wrapTunerData.channels.get(aChannelNumber);
if (!wrapChannelData || !wrapChannelData.programs) {
return Cr.NS_ERROR_INVALID_ARG;
}
for each (let program in wrapChannelData.programs) {
programArray.appendElement(program, false);
}
return aCallback.notifySuccess(programArray);
},
getOverlayId: function TVSimGetOverlayId(aTunerId, aCallback) {
if (!aCallback) {
debug("aCallback is null\n");
return Cr.NS_ERROR_INVALID_ARG;
}
// TVSimulatorService does not use this parameter.
overlayIds = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
return aCallback.notifySuccess(overlayIds);
},
set sourceListener(aListener) {
this._sourceListener = aListener;
},
get sourceListener() {
return this._sourceListener;
},
getSimulatorVideoFilePath: function TVSimGetSimulatorVideoFilePath(aTunerId, aSourceType, aChannelNumber) {
let wrapTunerData = this._getWrapTunerData(aTunerId, aSourceType);
if (!wrapTunerData || !wrapTunerData.channels || wrapTunerData.channels.size <= 0) {
return null;
}
return wrapTunerData.channels.get(aChannelNumber).videoFilePath;
},
_getTunerMapKey: function TVSimGetTunerMapKey(aTunerId, aSourceType) {
return JSON.stringify({'tunerId': aTunerId, 'sourceType': aSourceType});
},
_getWrapTunerData: function TVSimGetWrapTunerData(aTunerId, aSourceType) {
if (!this._internalTuners || this._internalTuners.size <= 0) {
return null;
}
return this._internalTuners.get(this._getTunerMapKey(aTunerId, aSourceType));
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TVSimulatorService]);

View File

@ -0,0 +1,3 @@
component {f0ab9850-24b4-4f5d-83dd-0fea0c249ca1} TVSimulatorService.js
contract @mozilla.org/tv/simulatorservice;1 {f0ab9850-24b4-4f5d-83dd-0fea0c249ca1}
TVSimulatorService @mozilla.org/tv/simulatorservice;1

View File

@ -81,6 +81,9 @@ TVTuner::Init(nsITVTunerData* aData)
mTVService = TVServiceFactory::AutoCreateTVService();
NS_ENSURE_TRUE(mTVService, false);
rv = aData->GetStreamType(&mStreamType);
NS_ENSURE_SUCCESS(rv, false);
return true;
}
@ -198,12 +201,27 @@ nsresult
TVTuner::InitMediaStream()
{
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
nsRefPtr<DOMHwMediaStream> stream = DOMHwMediaStream::CreateHwStream(window);
nsRefPtr<DOMMediaStream> stream = nullptr;
if (mStreamType == nsITVTunerData::TV_STREAM_TYPE_HW) {
stream = DOMHwMediaStream::CreateHwStream(window);
} else if (mStreamType == nsITVTunerData::TV_STREAM_TYPE_SIMULATOR) {
// Bug 1180589 : We should MediaStream from local file.
// We should create the PART.2 patch.
//stream = CreateSimulatedMediaStream();
return NS_OK;
}
mStream = stream.forget();
return NS_OK;
}
already_AddRefed<DOMMediaStream>
TVTuner::CreateSimulatedMediaStream()
{
return nullptr;
}
nsresult
TVTuner::DispatchCurrentSourceChangedEvent(TVSource* aSource)
{

View File

@ -68,10 +68,13 @@ private:
nsresult InitMediaStream();
already_AddRefed<DOMMediaStream> CreateSimulatedMediaStream();
nsresult DispatchCurrentSourceChangedEvent(TVSource* aSource);
nsCOMPtr<nsITVService> mTVService;
nsRefPtr<DOMMediaStream> mStream;
uint16_t mStreamType;
nsRefPtr<TVSource> mCurrentSource;
nsTArray<nsRefPtr<TVSource>> mSources;
nsString mId;

View File

@ -48,6 +48,20 @@ TVTunerData::SetId(const nsAString& aId)
return NS_OK;
}
/* virtual */ NS_IMETHODIMP
TVTunerData::GetStreamType(uint16_t* aStreamType)
{
*aStreamType = mStreamType;
return NS_OK;
}
/* virtual */ NS_IMETHODIMP
TVTunerData::SetStreamType(const uint16_t aStreamType)
{
mStreamType = aStreamType;
return NS_OK;
}
/* virtual */ NS_IMETHODIMP
TVTunerData::GetSupportedSourceTypes(uint32_t* aCount,
char*** aSourceTypes)

View File

@ -26,6 +26,7 @@ private:
nsString mId;
char** mSupportedSourceTypes;
uint32_t mCount;
uint16_t mStreamType;
};
class TVChannelData final : public nsITVChannelData

View File

@ -34,6 +34,12 @@ UNIFIED_SOURCES += [
XPIDL_SOURCES += [
'nsITVService.idl',
'nsITVSimulatorService.idl',
]
EXTRA_COMPONENTS += [
'TVSimulatorService.js',
'TVSimulatorService.manifest',
]
XPIDL_MODULE = 'dom_tv'

View File

@ -20,9 +20,19 @@ interface nsIArray;
* and then uses the setter functions to adjust the properties of the object
* before passing it.
*/
[scriptable, builtinclass, uuid(608d3f7e-f9f1-4b3c-82c2-3eb60b1d3de8)]
[scriptable, builtinclass, uuid(c6d39e86-022b-4db5-b0df-602abfbeac69)]
interface nsITVTunerData : nsISupports
{
/**
* Switch TVTuner.stream type.
* TV_STREAM_TYPE_SIMULATOR : Simulate the MediaStream. This MediaStream load from Profile Directory.
* TV_STREAM_TYPE_HW : Get from real device
*/
const unsigned short TV_STREAM_TYPE_SIMULATOR = 0;
const unsigned short TV_STREAM_TYPE_HW = 1;
attribute unsigned short streamType;
attribute DOMString id;
/**
@ -138,7 +148,7 @@ interface nsITVProgramData : nsISupports
[array, size_is(count)] in string languages);
};
[builtinclass, uuid(c3fd7a8c-21e4-11e4-97e8-74d02b97e723)]
[scriptable, builtinclass, uuid(47746633-1b77-4df4-9424-d315bde3d455)]
interface nsITVSourceListener : nsISupports
{
/**
@ -198,7 +208,7 @@ interface nsITVSourceListener : nsISupports
in unsigned long count);
};
[builtinclass, uuid(a19e6e7e-2293-11e4-b335-74d02b97e723)]
[scriptable, builtinclass, uuid(01582a11-4707-455d-8d2a-2c8de8227dad)]
interface nsITVServiceCallback : nsISupports
{
const unsigned short TV_ERROR_OK = 0;
@ -258,7 +268,7 @@ interface nsITVServiceCallback : nsISupports
* new TVServiceNotifyRunnable(callback, dataList, optional errorCode);
* return NS_DispatchToCurrentThread(runnable);
*/
[uuid(1b17e3cc-1c84-11e4-a4d4-74d02b97e723)]
[scriptable, uuid(e52f93f1-6071-468b-a198-d8e6bc5ca348)]
interface nsITVService : nsISupports
{
attribute nsITVSourceListener sourceListener;
@ -380,3 +390,4 @@ interface nsITVService : nsISupports
void getOverlayId(in DOMString tunerId,
in nsITVServiceCallback callback);
};

View File

@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsITVService.idl"
%{C++
#define TV_SIMULATOR_SERVICE_CONTRACTID\
"@mozilla.org/tv/simulatorservice;1"
%}
[scriptable, uuid(f0ab9850-24b4-4f5d-83dd-0fea0c249ca1)]
interface nsITVSimulatorService : nsITVService
{
/*
* Get the simulate movie file.The path is full location path.
* e.g. /home/user/simulator/gaia/profile/dummy/tv-movie1.ogv
*
* @param tunerId The ID of the tuner.
* @param sourceType The source type to be used.
* @param channelNumber The LCN (Logical Channel Number) of the channel.
*/
void getSimulatorVideoFilePath(in DOMString tunerId,
in DOMString sourceType,
in DOMString channelNumber,
[retval] out string filePath);
};