How to display HD Youtube Playlist Videos with CefSharp in WPF window - wpf

I have these problem for quite some time now. At first I thought it requieres some time until youtube videos HD quality is available, but is already 2 weeks since that and nothing had change.
I have 2 different youtube PlayList(all videos uploaded in HD) that work different, one in HD an the other in SD, in a web project that is displayed in a WPF window with CefSharp.
I tried everything but I could not get this bad quality playlist in HD quality. The default quality in youtube channel is HD and if I open the index.html file in any other browser is also HD quality, but only in Cefsharp is not displaying the HD option.
In WPF window with CefSharp:
playlist with low quality
playlist with hd quality
In any browser (Mozilla firefox and Edge tested):
playlist with low quality
playlist with hd quality
After trying all possible settings, but nothing worked.
My currrent Cefsharp nugets:
CefSharp.Common, CefSharp.Wpf and CefSharp.OffScreen version 109.1.110.
These are my current codes:
In Cefsharp settings
Cef.EnableHighDPISupport();
if (settings.CefCommandLineArgs.ContainsKey("enable-system-flash"))
{
settings.CefCommandLineArgs["enable-system-flash"] = "0";
}
else
{
settings.CefCommandLineArgs.Add("enable-system-flash", "0");
}
var featuresToDisable = "NetworkService,VizDisplayCompositor";
if (WpfUtils.IsDarkThemeUsed())
{
settings.CefCommandLineArgs.Add("force-dark-mode", "1");
}
else
{
featuresToDisable = $"{featuresToDisable},DarkMode";
}
settings.EnableAudio();
settings.CefCommandLineArgs.Add("no-proxy-server");
//settings.CefCommandLineArgs.Add("disable-plugins-discovery", "1");
//settings.CefCommandLineArgs.Add("disable-remote-playback-api", "1");
settings.CefCommandLineArgs.Add("disable-sync", "1");
settings.CefCommandLineArgs.Add("disable-wake-on-wifi", "1");
settings.CefCommandLineArgs.Add("disable-voice-input", "1");
settings.CefCommandLineArgs.Add("disable-features", featuresToDisable);
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
settings.CefCommandLineArgs.Add("disable-gpu-vsync");
settings.SetOffScreenRenderingBestPerformanceArgs();
Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null);
In youtube player iframe
vid_frame[m] = new YT.Player('vid_frame' + m, {
width: '1280',
height: '720',
videoId: vidIDs[n][0],
host: 'https://www.youtube-nocookie.com',
playerVars: {
'html5': 1,
'showinfo': showVideoInfo,
'autoplay': 1,
'disablekb': 1,
'controls': showPlayerControls,
'playsinline': 1,
'modestbranding': 1,
'hl': 'en-US',
'rel': showRelatedVideos,
'widget-referrer': 'resource://web'
},
frameborder: 0,
enablejsapi: 1,
events: {
'onStateChange': onPlayerStateChange
}
});
I expect both playlist displaying videos in HD in Cefsharp and the browsers.
Does somebody know what could be the problem?
What are the viewing conditions that make effect in one list but not in the other?, refer to this link:
iframe_api_reference

Resolution Problem
There was a mistake when uploading the video in youtube, the format uploaded was 1920x1080, but it was not enough for the HD resolution option in CefSharp. After transforming the video into 4K and upload to youtube, it worked!

Related

Playing Audio from a URL source

I am trying to play an audio file from a remote URL. The code works alright but the problem is when I tap on the play button, the app hangs for sometime before the audio plays. My code is shown below. How can I optimise this for a better experience.
if(!isDraft)
{
toPlay = URL+"/getFile/"+code+"_audioFile";
}
final Media mp = MediaManager.createMedia(toPlay, false);
f.addComponent("Center", new MediaPlayer(mp));
mp.play();
f.getToolbar().setBackCommand("", ed -> {
mp.pause();
mp.cleanup();
caseForm.showBack();

Codename One - Zoom, center and crop a video to force it to occupy all the screen

I have an intro video in the center of a BorderLayout with the option BorderLayout.CENTER_BEHAVIOR_TOTAL_BELOW. In the top I have a logo, in the south I have login buttons.
I write code similar to the following one, that chooses the right video according the rotation of the device. I tested that the video will be automatically zoomed to the available space: in the smartphones that I tested (Android and iPhone models), the video covers all the screen area (because the video length and height are proportional to the screen length and height). That's good, it's exactly what I want.
However, probably there are smartphones with different screen size ratio from the ones that I tested. Moreover, all the tablets have different screen size ratio from the smartphones.
I need that the video will ALWAYS occupy all the screen area. If necessary, it should be automatically zoomed, centered and cropped to occupy all the screen.
I didn't find in the Codename One API what I need to implement this use case. What code can I use? My target devices are Android and iOS devices (smartphones and tablets).
Example of code:
Form hi = new Form("Hi World", new BorderLayout(BorderLayout.CENTER_BEHAVIOR_TOTAL_BELOW));
disableToolbar(hi);
introVideoMP4 = "/intro-landscape.mp4";
if (Display.getInstance().isPortrait()) {
introVideoMP4 = "/intro-portrait.mp4";
}
MediaPlayer introVideo = new MediaPlayer();
try {
InputStream videoInputStream = Display.getInstance().getResourceAsStream(getClass(), introVideoMP4);
introVideo.setDataSource(videoInputStream, "video/mp4", () -> {
introVideo.getMedia().setTime(0);
introVideo.getMedia().play();
});
introVideo.setHideNativeVideoControls(true);
introVideo.hideControls();
introVideo.setAutoplay(true);
hi.add(BorderLayout.CENTER, introVideo);
} catch (Exception err) {
Log.e(err);
}
hi.addOrientationListener(l -> {
introVideoMP4 = "/intro-landscape.mp4";
if (Display.getInstance().isPortrait()) {
introVideoMP4 = "/intro-portrait.mp4";
}
try {
InputStream videoInputStream = Display.getInstance().getResourceAsStream(getClass(), introVideoMP4);
introVideo.setDataSource(videoInputStream, "video/mp4", () -> {
introVideo.getMedia().setTime(0);
introVideo.getMedia().play();
});
} catch (Exception err) {
Log.e(err);
}
});
hi.add(BorderLayout.NORTH, new Label("My App"));
Button myButton = new Button("Tap me!");
myButton.addActionListener(l -> {
Log.p("myButton tapped");
});
hi.add(BorderLayout.SOUTH, myButton);
hi.show();
Usually sites generate video downloads/streams based on the device proportions on the server (using tools such as ffmpeg. Then deliver a video with the right aspect ratio, bitrate & format.
There is no builtin functionality for cropping the video similar to "fit" scale in Codename One.
However, if you are OK with the sides or top being cropped then you can probably create your own layout manager for the video component and position/size it based on your knowledge of the video dimensions and screen dimensions. Creating a layout manager is really easy, it's mostly just the work of implementing the layoutContainer method where you can set the X/Y/Width/Height of the component. See https://www.codenameone.com/blog/map-layout-update.html

Video/3gpp in MediaPlayer on device

I am building a chat app. I am trying to add support for videos.
the URL for the video is https://api.twilio.com/2010-04-01/Accounts/AC5c869939f6863233a73ac697207c3697/Messages/MMf01fb40e39c41feafbc0967590f161e8/Media/MEbd3e3d9a0c2be95e01de341889e8cfbb
Container container = new Container(new BorderLayout());
Media video = com.codename1.media.MediaManager.createMedia(url, true); // url is the url above
video.setNativePlayerMode(true);
MediaPlayer mp = new MediaPlayer(video);
//place the media player in the container
container.add(BorderLayout.CENTER, mp);
Component component = Container.encloseIn(new FlowLayout(Container.RIGHT), container);
When I make a debug build and put this on my iPhone, I just get a black box where I would expect the video player to be.
In the simulator, I got playback controls, but no video either.
What am I doing incorrectly?
Something like this should work on the device although the simulator might be a bit flaky:
Form hi = new Form("Player", new BorderLayout());
try {
Media video = MediaManager.createMedia("https://api.twilio.com/2010-04-01/Accounts/AC5c869939f6863233a73ac697207c3697/Messages/MMf01fb40e39c41feafbc0967590f161e8/Media/MEbd3e3d9a0c2be95e01de341889e8cfbb", true);
hi.add(CENTER, video.getVideoComponent());
video.setNativePlayerMode(true);
hi.show();
hi.addShowListener(e -> video.play());
} catch(IOException err) {
Log.e(err);
hi.add(CENTER, "Failed to load video");
hi.show();
}
Notice a few things:
the simulator doesn't support HTTPS URL's for media due to limitations of JavaFX. It doesn't support some video stream types and might not support 3gp properly
iOS requires an HTTPS URL
I placed the video component in the center of the border layout in a form. This forces a specific size for the video which is important.
3gp is not supported. I convert the video to mp4 and supply the url to that file to the Media Manager and it works fine.

Streaming Live audio using Codename One

I am trying to convince a friend of mine to use CN1 as his dev platform for mobile apps. One of the challenges he has brought up is the ability to stream live audio from a radio station using a CN1 app.
I have had a look at the docs and I can see examples of loading media files that are already on the phone, but I cannot see an example of where you point it at a URL and stream live audio.
As you have probably guessed he is interested in developing radio apps for remote radio stations that don't already have their own
Is it possible to do this in CN1?
That's quite possible with CN1 and number of developers have done that in the past.
You can use MediaManager and point it to your remote streaming URL.
Below works for playing an audio file from remote URL and could work for streaming:
Form radio = new Form(new BorderLayout());
Display.getInstance().scheduleBackgroundTask(() -> {
try {
Media audio = MediaManager.createMedia(streamingUrl, false);
audio.prepare();
Display.getInstance().callSerially(() -> {
final MediaPlayer player = new MediaPlayer(audio);
player.setAutoplay(true);
audio.setNativePlayerMode(false);
radio.add(BorderLayout.CENTER, player);
radio.revalidate();
});
} catch (IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error streaming audio: " + err);
}
});
radio.show();
To avoid this error "Video Playing is not supported on this platform", you have to put true to isVidio parameter like this when handling video stream.
Media audio = MediaManager.createMedia(streamingUrl, true);

Stream YouTube video from extracted url

I am trying to find solution to play YouTube video from http stream. Not as embedded player or via website. The reason of that as I need to play some clips locally in app that prevented to be embedded.
So the first task to obtain http stream is easy to solve, e.g. using YouTubeExtractor. For example from this youtube url
https://www.youtube.com/watch?v=31crA53Dgu0
YouTubeExtractor extracts such url for downloading video. As you can see there is no piece of path to concrete .mp4 or .mov file. There is also binding to IP, so the url won't work on your side.
https://r3---sn-puu8-3c2e.googlevideo.com:443/videoplayback?sparams=dur,gcr,id,initcwndbps,ip,ipbits,itag,lmt,mime,mm,mn,ms,mv,pl,ratebypass,requiressl,source,upn,expire&source=youtube&initcwndbps=3147500&pl=19&upn=oYYkSNBwoc8&fexp=9412913,9416126,9416891,9422596,9428398,9429016,9431012,9431364,9431901,9432206,9432825,9433096,9433424,9433623,9433946,9434085,9435504,9435703,9435937&id=o-AGanAaajMgOmp4VrXIwo9jewJnqlsvZecDxCcRpSN3lS&expire=1462821352&gcr=ua&ip=12.34.56.149&lmt=1458705532562546&ms=au&mt=1462799507&mv=m&dur=217.849&sver=3&itag=22&key=yt6&mn=sn-puu8-3c2e&mm=31&ipbits=0&mime=video/mp4&ratebypass=yes&requiressl=yes&signature=A142619EBA90D00CC46FF05B9CF1E3469B0EF196.072B532670A4D833582F94CF9C46F1F7D298F230&fallback_host=tc.v1.cache5.googlevideo.com
private string ExtractVideoUrl(string youtubeUrl)
{
IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(youtubeUrl, false);
var video = videoInfos.First();
if (video.RequiresDecryption)
{
DownloadUrlResolver.DecryptDownloadUrl(video);
}
return video.DownloadUrl; // videoInfos.First().DownloadUrl;
}
Then to download video this lib uses the code below. The interesting moment is in reading stream and writing to file the contents of youtube video.
var request = (HttpWebRequest)WebRequest.Create(this.Video.DownloadUrl);
if (this.BytesToDownload.HasValue)
{
request.AddRange(0, this.BytesToDownload.Value - 1);
}
// the following code is alternative, you may implement the function after your needs
using (WebResponse response = request.GetResponse())
{
using (Stream source = response.GetResponseStream())
{
**// HOW to Play source stream???**
using (FileStream target = File.Open(this.SavePath, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = source.Read(buffer, 0, buffer.Length)) > 0)
{
target.Write(buffer, 0, bytes);
....
So the general question is how to play video from that open stream? For example Chrome, Firefox and KMPlayer are doing this easily. The browsers generate a simple page with video tag, so it will be trivial to manage the player through JS. But...internal WebBrowser control can't, it suggests to download file. I tried CEFSharp (Chrome Embeded Framework) and no luck there. Maybe anybody know good video player WPF library which can streaming video? I also tried VLC.wpf and Shockwave Flash, but still unhappy with this extracted url. A lot of searching but results insufficient for now.
After 2 days researching I found the solution of how to play youtube video url directly from WPF application.
Some notes before:
WebBrowser component for WPF and the same for WinForms does not support HTML5. So no way to play video using <video> tag there
Chrome Embeded Framework (CEF)and its CEFSharp wrapper does not support <audio> and <video> tags by default. As I found on SO it is needed to recompile CEF to support audio and it seems video too. --enable-multimedia-streams option didn't work on my side at all to play video
WPF MediaElement component does not support playing video from streams. However there are workaroung exists, they are unsuitable to play youtube url's.
No .NET media player found which could play video from Stream object returned by HttpWebRequest
So the solution is in using GeckoFx webbrowser which is also available via nuget. As it is WinForms based component it is needed to use WinFormsHosting to use it in WPF. Final solution is:
<xmlns:gecko="clr-namespace:Gecko;assembly=Geckofx-Winforms">
....
<WindowsFormsHost Width="400" Height="300" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<gecko:GeckoWebBrowser x:Name="_geckoBrowser"/>
</WindowsFormsHost>
And simple code to use:
public void PlayVideo(string videoId)
{
var url = string.Format("https://www.youtube.com/watch?v={0}", videoId);
var videoUrl = ExtractVideoUrl(url);
_geckoBrowser.Navigate(videoUrl);
}
GeckoFx is depeneded on XulRunner runtime sdk. It is installed with nuget package though. One cons in this solution is the size of all app dependencies increased for over +70mb.

Resources