Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtered set of the various fixes PR #91

Merged
merged 10 commits into from
Dec 11, 2020
45 changes: 44 additions & 1 deletion src/components/codecSupportHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@ import { deviceIds } from './castDevices';

const castContext = cast.framework.CastReceiverContext.getInstance();

export function hasEAC3Support(): boolean {
// Some error causes this not to work at all
//return castContext.canDisplayType('audio/mp4', 'ec-3');
return false;
}

export function hasAC3Support(): boolean {
// Some error causes this not to work at all
//return castContext.canDisplayType('audio/mp4', 'ac-3');
return false;
}

export function hasSurroundSupport(deviceId: number): boolean {
// AC-3 in this client is broken. The cause is not known yet.
// However, the device does report correctly in this check.
// We can use that to estimate if we can send AAC 6ch.

// From my testing:
// GEN1+GEN2+GEN3 can only do 2.0 when AC3 is lacking.
// AUDIO has toslink at most, which doesn't do pcm 6ch.
// Forums indicate that they only support the passthrough option across the lineup.
// See https://support.google.com/chromecast/thread/362511

// This will turn on surround support if passthrough is available.
return hasAC3Support();

// If there are cast devices that can decode 6ch audio:
// We cannot check if the connected system supports pcm 6ch, but we can check for ac-3.
// Sadly there are some situations (toslink) that supports ac-3 but not pcm 6ch.
// In those cases we will rely on chromecast downmixing.
//return castContext.canDisplayType('audio/mp4', 'ac-3');
}

export function hasH265Support(): boolean {
return castContext.canDisplayType('video/mp4', 'hev1.1.6.L150.B0');
}
Expand All @@ -25,6 +58,7 @@ export function hasVP9Support(): boolean {
export function getMaxBitrateSupport(): number {
// FIXME: We should get this dynamically or hardcode this to values
// we see fit for each Cast device. More testing is needed.
// 120Mb/s ?
return 120000000;
}

Expand Down Expand Up @@ -121,7 +155,16 @@ export function getSupportedMP4VideoCodecs(): Array<string> {
* @returns Supported MP4 audio codecs.
*/
export function getSupportedMP4AudioCodecs(): Array<string> {
return ['aac', 'mp3'];
const codecs = [];
if (hasEAC3Support()) {
codecs.push('eac3');
}
if (hasAC3Support()) {
codecs.push('ac3');
}
codecs.push('aac');
codecs.push('mp3');
return codecs;
}

/**
Expand Down
7 changes: 6 additions & 1 deletion src/components/commandHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class commandHandler {
private playbackManager: playbackManager;
private supportedCommands: SupportedCommands = {
PlayNext: this.playNextHandler,
PlayNow: this.playNowHandler,
PlayLast: this.playLastHandler,
Shuffle: this.shuffleHandler,
InstantMix: this.instantMixHandler,
Expand Down Expand Up @@ -73,6 +74,10 @@ export class commandHandler {
translateItems(data, data.options, data.options.items, data.command);
}

playNowHandler(data: DataMessage): void {
translateItems(data, data.options, data.options.items, data.command);
}

playLastHandler(data: DataMessage): void {
translateItems(data, data.options, data.options.items, data.command);
}
Expand Down Expand Up @@ -215,7 +220,7 @@ export class commandHandler {
);
commandHandler.bind(this)(data);
} else {
console.debug(
console.log(
`Command "${command}" received. Could not identify handler, calling default handler.`
);
this.defaultHandler(data);
Expand Down
38 changes: 20 additions & 18 deletions src/components/deviceprofileBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ProfileConditionValue } from '../api/generated/models/profile-condition
import { deviceIds, getActiveDeviceId } from './castDevices';

import {
hasSurroundSupport,
hasTextTrackSupport,
hasVP8Support,
hasVP9Support,
Expand All @@ -33,9 +34,8 @@ import {
} from './codecSupportHelper';

interface ProfileOptions {
audioChannels?: number;
enableHls?: boolean;
bitrateSetting?: number;
enableHls: boolean;
bitrateSetting: number;
}

let profileOptions: ProfileOptions;
Expand Down Expand Up @@ -266,31 +266,33 @@ function getCodecProfiles(): Array<CodecProfile> {
function getTranscodingProfiles(): Array<TranscodingProfile> {
const TranscodingProfiles: Array<TranscodingProfile> = [];

const physicalAudioChannels: number = profileOptions.audioChannels ? 6 : 2;
const hlsAudioCodecs = getSupportedHLSAudioCodecs();
const audioChannels: number = hasSurroundSupport(currentDeviceId) ? 6 : 2;

if (profileOptions.enableHls !== false) {
TranscodingProfiles.push({
Container: 'ts',
Type: DlnaProfileType.Audio,
AudioCodec: 'aac',
AudioCodec: hlsAudioCodecs.join(','),
Context: EncodingContext.Streaming,
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MaxAudioChannels: audioChannels.toString(),
MinSegments: 1,
BreakOnNonKeyFrames: false
});
}

const supportedAudio = getSupportedAudioCodecs();

// audio only profiles here
for (const audioFormat of supportedAudio) {
TranscodingProfiles.push({
Container: audioFormat,
Type: DlnaProfileType.Audio,
AudioCodec: audioFormat,
Context: EncodingContext.Streaming,
Protocol: 'http',
MaxAudioChannels: physicalAudioChannels.toString()
MaxAudioChannels: audioChannels.toString()
});
}

Expand All @@ -299,21 +301,20 @@ function getTranscodingProfiles(): Array<TranscodingProfile> {
return TranscodingProfiles;
}

const hlsVideoAudioCodecs = getSupportedHLSAudioCodecs();
const hlsVideoCodecs = getSupportedHLSVideoCodecs();
if (
hlsVideoCodecs.length &&
hlsVideoAudioCodecs.length &&
hlsAudioCodecs.length &&
profileOptions.enableHls !== false
) {
TranscodingProfiles.push({
Container: 'ts',
Type: DlnaProfileType.Video,
AudioCodec: hlsVideoAudioCodecs.join(','),
AudioCodec: hlsAudioCodecs.join(','),
VideoCodec: hlsVideoCodecs.join(','),
Context: EncodingContext.Streaming,
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MaxAudioChannels: audioChannels.toString(),
MinSegments: 1,
BreakOnNonKeyFrames: false
});
Expand All @@ -329,7 +330,7 @@ function getTranscodingProfiles(): Array<TranscodingProfile> {
Protocol: 'http',
// If audio transcoding is needed, limit channels to number of physical audio channels
// Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good
MaxAudioChannels: physicalAudioChannels.toString()
MaxAudioChannels: audioChannels.toString()
});
}

Expand Down Expand Up @@ -362,17 +363,18 @@ function getSubtitleProfiles(): Array<SubtitleProfile> {
* @param Profile options
* @returns Device profile.
*/
export function getDeviceProfile(options: ProfileOptions = {}): DeviceProfile {
export function getDeviceProfile(options: ProfileOptions): DeviceProfile {
profileOptions = options;
currentDeviceId = getActiveDeviceId();

const bitrateSetting = options.bitrateSetting || getMaxBitrateSupport();

// MaxStaticBitrate seems to be for offline sync only
const profile: DeviceProfile = {
MaxStreamingBitrate: bitrateSetting,
MaxStaticBitrate: 0,
MusicStreamingTranscodingBitrate: Math.min(bitrateSetting, 192000)
MaxStreamingBitrate: options.bitrateSetting,
MaxStaticBitrate: options.bitrateSetting,
MusicStreamingTranscodingBitrate: Math.min(
options.bitrateSetting,
192000
Copy link
Contributor Author

@hawken93 hawken93 Dec 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1: Is there a good reason for this 192k cap? Should we default to bitrateSetting?
2: Setting MaxStaticBitrate is what I think solves that it always thinks the rate is exceeded.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Nah that's probably a good idea to change
  2. I believe MaxStaticBitrate is leftovers from emby for syncing content but I could be wrong

)
};

profile.DirectPlayProfiles = getDirectPlayProfiles();
Expand Down
34 changes: 11 additions & 23 deletions src/components/maincontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
tagItems
} from '../helpers';

import { getMaxBitrateSupport } from './codecSupportHelper';

import { commandHandler } from './commandHandler';
import { playbackManager } from './playbackManager';

Expand Down Expand Up @@ -222,7 +224,7 @@ window.mediaManager.addEventListener(
console.log('Application is ready, starting system');

export function reportDeviceCapabilities() {
getMaxBitrate('Video').then((maxBitrate) => {
getMaxBitrate().then((maxBitrate) => {
let capabilitiesUrl =
$scope.serverAddress + '/Sessions/Capabilities/Full';
let deviceProfile = getDeviceProfile(maxBitrate);
Expand Down Expand Up @@ -422,9 +424,8 @@ export function changeStream(ticks, params) {
var liveStreamId = $scope.liveStreamId;

var item = $scope.item;
var mediaType = item.MediaType;

getMaxBitrate(mediaType).then(async (maxBitrate) => {
getMaxBitrate().then(async (maxBitrate) => {
const deviceProfile = getDeviceProfile(maxBitrate);
const audioStreamIndex =
params.AudioStreamIndex == null
Expand Down Expand Up @@ -484,8 +485,6 @@ export function changeStream(ticks, params) {
window.castReceiverContext.addCustomMessageListener(
'urn:x-cast:com.connectsdk',
function (evt) {
console.log('Playlist message: ' + JSON.stringify(evt));

var data = evt.data;

// Apparently chromium likes to pass it as json, not as object.
Expand All @@ -500,6 +499,7 @@ window.castReceiverContext.addCustomMessageListener(
// TODO set it somewhere better perhaps
window.senderId = evt.senderId;

console.log('Received message: ' + JSON.stringify(data));
processMessage(data);
}
);
Expand Down Expand Up @@ -578,25 +578,19 @@ export function onStopPlayerBeforePlaybackDone(item, options) {
}

export function getDeviceProfile(maxBitrate) {
let transcodingAudioChannels = document
.createElement('video')
.canPlayType('audio/mp4; codecs="ac-3"')
.replace(/no/, '')
? 6
: 2;

return deviceProfileBuilder({
supportsCustomSeeking: true,
audioChannels: transcodingAudioChannels
enableHls: true,
bitrateSetting: maxBitrate
});
}

var lastBitrateDetect = 0;
var detectedBitrate = 0;
export function getMaxBitrate(mediaType) {
export function getMaxBitrate() {
console.log('getMaxBitrate');

return new Promise(function (resolve, reject) {
// The client can set this number
if (window.MaxBitrate) {
console.log('bitrate is set to ' + window.MaxBitrate);

Expand All @@ -615,12 +609,6 @@ export function getMaxBitrate(mediaType) {
return;
}

if (mediaType != 'Video') {
// We don't need to bother with bitrate detection for music
resolve(window.DefaultMaxBitrate);
return;
}

console.log('detecting bitrate');

detectBitrate($scope).then(
Expand All @@ -633,9 +621,9 @@ export function getMaxBitrate(mediaType) {
},
function () {
console.log(
'Error detecting bitrate, will return default value.'
'Error detecting bitrate, will return device maximum.'
);
resolve(window.DefaultMaxBitrate);
resolve(getMaxBitrateSupport());
}
);
});
Expand Down
6 changes: 3 additions & 3 deletions src/components/playbackManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class playbackManager {
$scope.isChangingStream = false;
setAppStatus('loading');

const maxBitrate = await getMaxBitrate(item.MediaType);
const maxBitrate = await getMaxBitrate();
const deviceProfile = getDeviceProfile(maxBitrate);
const playbackInfo = await getPlaybackInfo(
item,
Expand Down Expand Up @@ -197,7 +197,7 @@ export class playbackManager {
if (item.BackdropImageTags && item.BackdropImageTags.length) {
backdropUrl =
$scope.serverAddress +
'/emby/Items/' +
'/Items/' +
item.Id +
'/Images/Backdrop/0?tag=' +
item.BackdropImageTags[0];
Expand All @@ -208,7 +208,7 @@ export class playbackManager {
) {
backdropUrl =
$scope.serverAddress +
'/emby/Items/' +
'/Items/' +
item.ParentBackdropItemId +
'/Images/Backdrop/0?tag=' +
item.ParentBackdropImageTags[0];
Expand Down
Loading