B4A Library YouTubeStreamFinder

YouTubeStreamFinder

Background
You Tube have a public Data API that allows you to pass a You Tube video id and get a streaming video URL that you can use to play a video in a VideoView.
Sounds Ok BUT they only make the URLs to low quality 3GP video streams avaialble.
You Tube have no official API to get URLs to the higher quality video streams.
Low quality 3GP dates back to the era of 2.5G connections and low power, low resolution handsets - it's 2012 and we have HSDPA, powerful handsets and high resolution displays - 3GP sucks big time!!

There exists an open source android-youtube-player which i was originally going to turn into a B4A library.
The android-youtube-player is actually an entire Activity that you'd add to your B4A (or native Android) project and call using an Intent.
The Intent would identify the video id to play.
android-youtube-player is a full screen landscape mode only Activity - you could not embed it within a B4A Activity as you can a VideoView.

android-youtube-player does however contain code which retrieves all available stream URLs for a video so what i have done is to to identify how the android-youtube-player gets these URLs and create the YouTubeStreamFinder library.

android-youtube-player is licensed under the Apache License, Version 2.0 so no problems there.
I think i'm obliged to make the library code publicly available so the library source code can be found here: Index of /b4a/youtubestreamfinder/src

Technical stuff!

YouTubeStreamFinder is not an Activity object, you should be able to use it as a Process Global with no problems.

The library adds three objects to the B4A IDE: YouTubeStreamFinder, YouTubeVideoInfo and YouTubeStreamInfo.

YouTubeStreamFinder is used to retrieve info about one or more videos using it's GetVideoInfo or GetVideoInfo2 methods.
Retrieval is done in a thread in the library - no blocking of the UI thread should occur.

When video info has been retrieved (or an error has occurred trying to retrieve video info) the YouTubeStreamFinder Ready event will be raised and passed the video id and a YouTubeVideoInfo object if retrieval was successful.

Version: 1.3
  • YouTubeStreamFinder
    Events:
    • Ready (VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo)
    Methods:
    • GetLastError (VideoId As String) As String
      Get the last error associated with this VideoId.
    • GetVideoInfo (VideoId As String)
      Get VideoInfo for a single video.
      The Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo) event
      will be raised when VideoInfo is available or an error occurs.
    • GetVideoInfo2 (VideoIds() As String)
      Get VideoInfo for more than one video.
      The Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo) event
      will be raised for each video when VideoInfo is available or an error occurs.
    • Initialize (EventName As String)
      Initialize the YouTubeStreamFinder with an EventName.
    Permissions:
    • android.permission.INTERNET
  • YouTubeStreamInfo
    Properties:
    • FormatId As Int [read only]
    • Height As Int [read only]
    • StreamUrl As String [read only]
    • Width As Int [read only]
  • YouTubeVideoInfo
    Properties:
    • Author As String [read only]
    • Duration As Int [read only]
      Get the duration of the video in seconds.
    • Keywords() As String [read only]
    • Streams As Map [read only]
      Get a Map where the Map keys are Int FormatIds and
      the Map values are YouTubeStreamInfo objects.
    • ThumbnailUrl As String [read only]
    • Timestamp As Long [read only]
      Get the date this video was uploaded to You Tube as a PHP timestamp.
    • Title As String [read only]
    • VideoId As String [read only]

Some demo code:

B4X:
'Activity module
Sub Process_Globals
End Sub

Sub Globals
   Dim SelectedVideoTitle As String
   Dim Streams As Map
   Dim VideoStreamSpinner As Spinner
   Dim VideoTitleSpinner As Spinner
   Dim VideoView1 As VideoView
   Dim YouTubeStreamFinder1 As YouTubeStreamFinder
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")
   
   Streams.Initialize
   VideoView1.Initialize("VideoView1")
   VideoView1.MediaControllerEnabled=True
   Activity.AddView(VideoView1, 0, VideoTitleSpinner.Height, 100%x, 100%y-VideoTitleSpinner.Height)
   
   '   Initialize the YouTubeStreamFinder with an EventName
   YouTubeStreamFinder1.Initialize("YouTubeStreamFinder1")
   
   '   get video info for three videos, these Strings are the YouTube video ids
   YouTubeStreamFinder1.GetVideoInfo2(Array As String("t56cHZymmvs", "0ZFhjLw6f_k", "HfT7cPI_mRc"))
   
   ToastMessageShow("Waiting for YouTubeStreamFinder", False)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub VideoTitleSpinner_ItemClick (Position As Int, Value As Object)
   '   the user has selected a video by it's title
   '   populate the VideoStreamSpinner with available streams for the selected video
   
   If VideoView1.IsPlaying Then
      VideoView1.Stop
   End If
   
   SelectedVideoTitle=Value
   Activity.Title=SelectedVideoTitle
   Dim SelectedVideoStreams As Map
   SelectedVideoStreams=Streams.Get(SelectedVideoTitle)
   
   Dim YouTubeStreamInfo1 As YouTubeStreamInfo
   
   VideoStreamSpinner.Clear
   
   Dim i As Int
   For i=0 To SelectedVideoStreams.Size-1
      YouTubeStreamInfo1=SelectedVideoStreams.GetValueAt(i)
      VideoStreamSpinner.Add(YouTubeStreamInfo1.Width&" x "&YouTubeStreamInfo1.Height&" ("&YouTubeStreamInfo1.FormatId&")")
   Next
   
   ToastMessageShow("Now select video stream, 18 is recommended", False)
End Sub

Sub VideoStreamSpinner_ItemClick (Position As Int, Value As Object)

   '   the user has selected a video quality stream - try to play the selected stream

   '   note that playback of the selected quality/format varies according to the device
   '   quality/format 18 seems to work on all devices

   Dim SelectedVideoStreams As Map
   SelectedVideoStreams=Streams.Get(SelectedVideoTitle)
   
   Dim YouTubeStreamInfo1 As YouTubeStreamInfo
   YouTubeStreamInfo1=SelectedVideoStreams.GetValueAt(Position)
   
'   uncomment if desired
'   VideoView1.Height=YouTubeStreamInfo1.Height*1dip
'   VideoView1.Width=YouTubeStreamInfo1.Width*1dip

   VideoView1.LoadVideo("http", YouTubeStreamInfo1.StreamUrl)
   VideoView1.Play
End Sub

Sub YouTubeStreamFinder1_Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo)
   '   video info retrieval for a single video has completed
   If YouTubeVideoInfo1=Null Then
      '   YouTubeVideoInfo1 will be Null if an error has occurred
      ToastMessageShow("Failed to find streams for "&VideoId1, False)
      Log(YouTubeStreamFinder1.GetLastError(VideoId1))
   Else
      '   save the Streams property as a global so we can use it when a Spinner item is clicked
      Streams.Put(YouTubeVideoInfo1.Title, YouTubeVideoInfo1.Streams)
      '   add the video title to the VideoTitleSpinner
      VideoTitleSpinner.Add(YouTubeVideoInfo1.Title)
   End If
End Sub

I pass an Array of 3 video ids to the YouTubeStreamFinder GetVideoInfo2 method and the Sub YouTubeStreamFinder1_Ready is called each time YouTubeStreamFinder has finished requesting video info for each of these 3 videos.
VideoId1 passed to that Sub identifies which video the video info request has completed for and if the request was a success then YouTubeVideoInfo1 will contain the requested info.

Different videos will have different available streams and playback support for each available streams seems to vary widely depending on the device in use.
The stream with the FormatId of 18 seems to be available for all videos and also seems to be supported on all devices - but your mileage may vary lol!

This library makes use of an undocumented You Tube web service, i think this web service is used by the official Android You Tube application so it is unlikely to change or stop working as that would break all current versions of the Android You Tube application.
But the web service is an undocumented web service not intended for developer access so there is always a possibility that at some point in the future You Tube will make changes to the web service and the YouTubeStreamFinder library will then be broken.


Library and demo project attached.

Martin.
 

Attachments

  • YouTubeStreamFinder_v_1_30.zip
    15.4 KB · Views: 1,082
Last edited:

warwound

Expert
Licensed User
Longtime User
YouTubeStreamFinder updated to version 1.10

YouTubeStreamFinder cannot retrieve stream URLs for videos where embedding of that video has been disabled by the video uploader.

With version 1.00 the LastError reported if you tried to get stream URLs for such a video was a rather uninformative:

unable to parse 'null' as integer

This library update changes the LastError message.

If an unexpected error has occured then LastError will probably be much the same - a vague message that indicates it has not been possible to parse the request for video info.

However if it has been possible to parse the request BUT the request does not contain a successful request for stream URLs then LastError will return the entire request.

For example a request for video info for a video where embedding has been disabled will result in LastError returning a String much like:

status=fail&errorcode=150&reason=Embedding+disabled+by+request%3Cbr%2F%3E%3Cu%3E%3Ca+href%3D%27http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D3F4-jw3NdzA%26feature%3Dplayer_embedded%27+target%3D%27_blank%27%3EWatch+on+YouTube%3C%2Fa%3E%3C%2Fu%3E

That is the raw response returned for the video info request, decoded it looks like:

status=fail&errorcode=150&reason=Embedding disabled by request<br/><u><a href='http://www.youtube.com/watch?v=3F4-jw3NdzA&feature=player_embedded' target='_blank'>Watch on YouTube</a></u>

Hopefully the library update will make it easier to catch and handle failed requests for video info.

Version 1.10 is attached to the first post in this thread.

Martin.
 

warwound

Expert
Licensed User
Longtime User
YouTubeStreamFinder updated to version 1.20

This update changes the request that the library makes to the YouTube web service for video info, and now you can get video info including available streams for videos that have embedding disabled.

See my previous post - the library could not get video info for a video that had embedding disabled.
This update fixes that.

Version 1.20 is attached to the first post in this thread.

Martin.
 

birnesoft

Active Member
Licensed User
Longtime User
great lib

is it possible to zoom the video in the videoview??
best regards
Björn
 

sally3599

Member
Licensed User
Longtime User
Does Anyone know this string?

I found a weird string from YouTubeStreamInfo1.StreamUrl in the VideoView1.LoadVideo:

B4X:
http%3A%2F%2Fo-o---preferred---tpe05s06---v22---lscache2.c.youtube.com%2Fvideoplayback%3Fupn%3DTL9NJMOQ6eM%26sparams%3Dcp%252Cgcr%252Cid%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26fexp%3D924805%252C906439%252C914113%252C915507%252C907217%252C922401%252C919804%252C920704%252C912806%252C906055%252C924500%252C906831%252C925701%252C924700%252C911406%252C913550%252C904721%252C920706%252C907344%252C912706%26key%3Dyt1%26itag%3D18%26ipbits%3D8%26signature%3D43E923AD548255CE89CE577656520BAB540E1217.98C39AD89745F29D59E110F4AA886AF147F1FB42%26mv%3Dm%26sver%3D3%26mt%3D1344987011%26ratebypass%3Dyes%26source%3Dyoutube%26ms%3Dau%26gcr%3Dtw%26expire%3D1345009050%26ip%3D114.24.115.50%26cp%3DU0hTSlBLV19KSkNOM19IRUFBOkYxQ2VIYnpIelF5%26id%3D70e7bb6473a486a7

Does it come from youtube API?
Any help will be appreciate it.
 

warwound

Expert
Licensed User
Longtime User
Does the StreamUrl work?
If so i'd not worry about it!

Take a look at this StreamUrl:

http%3A%2F%2Fo-o---preferred---plusnet-lcy1---v18---lscache2.c.youtube.com%2Fvideoplayback%3Fupn%3DQ_IpcAE-b6U%26sparams%3Dcp%252Cgcr%252Cid%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26fexp%3D919105%252C915507%252C907217%252C919804%252C920704%252C912806%252C906055%252C924500%252C906831%252C925701%252C924700%252C911406%252C913550%252C904721%252C920706%252C907344%252C912706%26key%3Dyt1%26itag%3D18%26ipbits%3D8%26signature%3D9F64DCFD5BD90D1BBA4758C30CE165D6B0530622.C33CB3D83AAA396111FD2D3CDC607E1F19E4CA22%26mv%3Dm%26sver%3D3%26mt%3D1345009871%26ratebypass%3Dyes%26source%3Dyoutube%26ms%3Dau%26gcr%3Dgb%26expire%3D1345032927%26ip%3D87.112.22.137%26cp%3DU0hTSlBOUF9HUUNOM19ISFRKOjZRNnBMY1RlQnpu%26id%3Db79e9c1d9ca69afb

It's pretty obscure but works.

Martin.
 

Inman

Well-Known Member
Licensed User
Longtime User
Thanks for the tip, man. I hope Martin updates the library soon.
 

warwound

Expert
Licensed User
Longtime User
YouTubeStreamFinder updated to version 1.30

This update simply fixes the library so that it continues to work after the recent changes to the You Tube API.
The fix was as vb1992 posted, the request URL now requiring a 'signature' parameter.

Version 1.30 is attached to the first post in this thread.

Martin.
 

shashkiranr

Active Member
Licensed User
Longtime User
Hi warwound

I have an app where i have to display select youtube videos inside my app. As per the example given by you, u have different resolution videos retrieved. I just want to show the video and dont want to give the user the choice to select the resolution.

My question is , is there any way you can set the stream resolution that best fits the device?? can you suggest me a solution for this.

Thanks, will be waiting for your reply.

SK
 

warwound

Expert
Licensed User
Longtime User
Hi warwound

I have an app where i have to display select youtube videos inside my app. As per the example given by you, u have different resolution videos retrieved. I just want to show the video and dont want to give the user the choice to select the resolution.

My question is , is there any way you can set the stream resolution that best fits the device?? can you suggest me a solution for this.

Thanks, will be waiting for your reply.

SK

That's not as simple as it sounds.

Take a look here: Android Supported Media Formats | Android Developers
That page lists what video codecs are built into the various versions of Android.

Unfortunately the web service that this library uses is not an officially supported web service, there's no documentation or reference for it.
The library YouTubeStreamInfo object has a FormatId properties which represents the format of each available stream.
FormatId is just an integer, and the only references i've found which describe what each integer value represents is rather limited:

13 - 3GPP (MPEG-4 encoded) Low quality
17 - 3GPP (MPEG-4 encoded) Medium quality
18 - MP4 (H.264 encoded) Normal quality
22 - MP4 (H.264 encoded) High quality
37 - MP4 (H.264 encoded) High quality

FormatId will sometimes be a value that is not in that list - that stream's video codec and quality is unknown.

So there's no straightforward way to dynamically choose the best quality supported video stream for a device at runtime.
You cannot even assume that a certain FormatId stream is available for all videos, imagine someone uploads a low quality, low resolution 3GP video.
It's unlikely that You Tube will transcode that video to MP4 format as it would be a waste of time - the original video not having enough quality to make an MP4 version worthwhile.

I think i'd define a number of 'preferred versions' in order of most preferred to least preferred and playback the stream whose FomatId comes first in those preferred versions.

Pseudo code:

B4X:
if formatid 18 is available then
  play stream with formatid of 18
else
  if formatid 17 is available then
    play stream with formatid of 17
else
  play stream with formatid of 13

Even this pseudo code assumes that FormatId of 13 will be available for ALL videos and that cannot be guaranteed.

All you can really do is look at which FormatsIds are available for each You Tube video and write code to choose the best quality available for device your app is running on - but you have to dothis without knowing what each and every FormatId is - and how would you know if a particular device supports a format or not...

Sorry i can't be more useful - maybe other forum members have more experience with the library?

Martin.
 

shashkiranr

Active Member
Licensed User
Longtime User
Hi Warwound,

Thank you for the info.:) So its not possible to automate the stream quality based on the device. I will try to use the psuedo code or another webview to show the video.

Thank you for ur suggestion :)
 

Inman

Well-Known Member
Licensed User
Longtime User
Google releases YouTube Player API for Android

flipboard-final-framed_device-2012-12-21-133225.png


Starting today, you can embed and play YouTube videos in your app using the new YouTube Android Player API.

YouTube API Blog: No WebView required, with native YouTube Player API for Android

Martin, work your magic :)
 
Top