To set video quality for ios.
I have tried to load m3u8 video url from server and i downloaded the m3u8 file & i segregate all RESOLUTION from video quality & AFTER SEGMENTS get the bandwidth of url in array.
When i load base url sample.m3u8 it has video & audio after that i set the base url of before segments and i have append the bandwidth url from array it was loading video as per quality selected but no audio came.
To achieve this i have made some solutions will work
I make separate to run original url which contains both video & audio and i run separately low bandwidth url which contains no audio to make sync
ex: RESOLUTION=1280x720,SAMPLE_720p_v4.m3u8
SAMPLE.m3u8
#EXTM3U
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-0",NAME="Default",AUTOSELECT=YES,DEFAULT=YES,URI="segments/SAMPLE_audio_v4.m3u8"
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=30681000,CODECS="avc1.640028",URI="segments/SAMPLE_1080p_iframe.m3u8"
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=30140000,CODECS="avc1.4d001f",URI="segments/SAMPLE_720p_iframe.m3u8"
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=15431000,CODECS="avc1.42001f",URI="segments/SAMPLE_480p_iframe.m3u8"
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=11009000,CODECS="avc1.42001e",URI="segments/SAMPLE_360p_iframe.m3u8"
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7850000,CODECS="avc1.420015",URI="segments/SAMPLE_270p_iframe.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=4080000,RESOLUTION=1280x720,CODECS="avc1.640028,mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_1080p_v4.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3471000,RESOLUTION=1280x720,CODECS="avc1.4d001f,mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_720p_v4.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1934000,RESOLUTION=854x480,CODECS="avc1.42001f,mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_480p_v4.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1106000,RESOLUTION=640x360,CODECS="avc1.42001e,mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_360p_v4.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=837000,RESOLUTION=480x270,CODECS="avc1.420015,mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_270p_v4.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=185000,CODECS="mp4a.40.2",AUDIO="audio-0"
segments/SAMPLE_audio_v4.m3u8
Use the preferredPeakBitRate property on your playeritem https://developer.apple.com/documentation/avfoundation/avplayeritem/1388541-preferredpeakbitrate you need to pass a valid bandwidth value.
Not sure why you are downloading the m3u8 file AVFoundation manage this for you.
Is there a way to get the HLS URL for YouTube live streams like this one https://www.youtube.com/embed/WVZpCdHq3Qg
I've tried the typical get_video_info methods but they don't seem to work with live streams.
You need to get the HLS m3u8 playlist files from the video's manifest. There are ways to do this by hand, but for simplicity I'll be using the youtube-dl tool to get this information. I'll be using this live stream as an example: https://www.youtube.com/watch?v=_Gtc-GtLlTk
First, get the formats of the video:
➜ ~ youtube-dl --list-formats https://www.youtube.com/watch\?v\=_Gtc-GtLlTk
[youtube] _Gtc-GtLlTk: Downloading webpage
[youtube] _Gtc-GtLlTk: Downloading video info webpage
[youtube] Downloading multifeed video (_Gtc-GtLlTk, aflWCT1tYL0) - add --no-playlist to just download video _Gtc-GtLlTk
[download] Downloading playlist: Southwest Florida Eagle Cam
[youtube] playlist Southwest Florida Eagle Cam: Collected 2 video ids (downloading 2 of them)
[download] Downloading video 1 of 2
[youtube] _Gtc-GtLlTk: Downloading webpage
[youtube] _Gtc-GtLlTk: Downloading video info webpage
[youtube] _Gtc-GtLlTk: Extracting video information
[youtube] _Gtc-GtLlTk: Downloading formats manifest
[youtube] _Gtc-GtLlTk: Downloading DASH manifest
[info] Available formats for _Gtc-GtLlTk:
format code extension resolution note
140 m4a audio only DASH audio 144k , m4a_dash container, mp4a.40.2#128k (48000Hz)
160 mp4 256x144 DASH video 124k , avc1.42c00b, 30fps, video only
133 mp4 426x240 DASH video 258k , avc1.4d4015, 30fps, video only
134 mp4 640x360 DASH video 646k , avc1.4d401e, 30fps, video only
135 mp4 854x480 DASH video 1171k , avc1.4d401f, 30fps, video only
136 mp4 1280x720 DASH video 2326k , avc1.4d401f, 30fps, video only
137 mp4 1920x1080 DASH video 4347k , avc1.640028, 30fps, video only
151 mp4 72p HLS , h264, aac # 24k
132 mp4 240p HLS , h264, aac # 48k
92 mp4 240p HLS , h264, aac # 48k
93 mp4 360p HLS , h264, aac #128k
94 mp4 480p HLS , h264, aac #128k
95 mp4 720p HLS , h264, aac #256k
96 mp4 1080p HLS , h264, aac #256k (best)
[download] Downloading video 2 of 2
[youtube] aflWCT1tYL0: Downloading webpage
[youtube] aflWCT1tYL0: Downloading video info webpage
[youtube] aflWCT1tYL0: Extracting video information
[youtube] aflWCT1tYL0: Downloading formats manifest
[youtube] aflWCT1tYL0: Downloading DASH manifest
[info] Available formats for aflWCT1tYL0:
format code extension resolution note
140 m4a audio only DASH audio 144k , m4a_dash container, mp4a.40.2#128k (48000Hz)
160 mp4 256x144 DASH video 124k , avc1.42c00b, 30fps, video only
133 mp4 426x240 DASH video 258k , avc1.4d4015, 30fps, video only
134 mp4 640x360 DASH video 646k , avc1.4d401e, 30fps, video only
135 mp4 854x480 DASH video 1171k , avc1.4d401f, 30fps, video only
136 mp4 1280x720 DASH video 2326k , avc1.4d401f, 30fps, video only
151 mp4 72p HLS , h264, aac # 24k
132 mp4 240p HLS , h264, aac # 48k
92 mp4 240p HLS , h264, aac # 48k
93 mp4 360p HLS , h264, aac #128k
94 mp4 480p HLS , h264, aac #128k
95 mp4 720p HLS , h264, aac #256k (best)
[download] Finished downloading playlist: Southwest Florida Eagle Cam
In this case, there are two videos because the live stream contains two cameras. From here, we need to get the HLS URL for a specific stream. Use -f to pass in the format you would like to watch, and -g to get that stream's URL:
➜ ~ youtube-dl -f 95 -g https://www.youtube.com/watch\?v\=_Gtc-GtLlTk
https://manifest.googlevideo.com/api/manifest/hls_playlist/id/_Gtc-GtLlTk.2/itag/95/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/cmbypass/yes/gir/yes/dg_shard/X0d0Yy1HdExsVGsuMg.95/hls_chunk_host/r1---sn-ab5l6ne6.googlevideo.com/playlist_type/LIVE/gcr/us/pmbypass/yes/mm/32/mn/sn-ab5l6ne6/ms/lv/mv/m/pl/20/dover/3/sver/3/fexp/9408495,9410706,9416126,9418581,9420452,9422596,9422780,9423059,9423661,9423662,9425349,9425959,9426661,9426720,9427325,9428422,9429306/upn/xmL7zNht848/mt/1456412649/ip/64.125.177.124/ipbits/0/expire/1456434315/sparams/ip,ipbits,expire,id,itag,source,requiressl,ratebypass,live,cmbypass,gir,dg_shard,hls_chunk_host,playlist_type,gcr,pmbypass,mm,mn,ms,mv,pl/signature/7E48A727654105FF82E158154FCBA7569D52521B.1FA117183C664F00B7508DDB81274644F520C27F/key/dg_yt0/playlist/index.m3u8
https://manifest.googlevideo.com/api/manifest/hls_playlist/id/aflWCT1tYL0.2/itag/95/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/cmbypass/yes/gir/yes/dg_shard/YWZsV0NUMXRZTDAuMg.95/hls_chunk_host/r13---sn-ab5l6n7y.googlevideo.com/pmbypass/yes/playlist_type/LIVE/gcr/us/mm/32/mn/sn-ab5l6n7y/ms/lv/mv/m/pl/20/dover/3/sver/3/upn/vdBkD9lrq8Q/fexp/9408495,9410706,9416126,9418581,9420452,9422596,9422780,9423059,9423661,9423662,9425349,9425959,9426661,9426720,9427325,9428422,9429306/mt/1456412649/ip/64.125.177.124/ipbits/0/expire/1456434316/sparams/ip,ipbits,expire,id,itag,source,requiressl,ratebypass,live,cmbypass,gir,dg_shard,hls_chunk_host,pmbypass,playlist_type,gcr,mm,mn,ms,mv,pl/signature/4E83CD2DB23C2331CE349CE9AFE806C8293A01ED.880FD2E253FAC8FA56FAA304C78BD1D62F9D22B4/key/dg_yt0/playlist/index.m3u8
These are your HLS m3u8 playlists, one for each camera associated with the live stream.
Without youtube-dl, your flow might look like this:
Take your video id and make a GET request to the get_video_info endpoint:
HTTP GET: https://www.youtube.com/get_video_info?&video_id=_Gtc-GtLlTk&el=info&ps=default&eurl=&gl=US&hl=en
In the response, the hlsvp value will be the link to the m3u8 HLS playlist:
https://manifest.googlevideo.com/api/manifest/hls_variant/maudio/1/ipbits/0/key/yt6/ip/64.125.177.124/gcr/us/source/yt_live_broadcast/upn/BYS1YGuQtYI/id/_Gtc-GtLlTk.2/fexp/9416126%2C9416984%2C9417367%2C9420452%2C9422596%2C9423039%2C9423661%2C9423662%2C9423923%2C9425346%2C9427672%2C9428946%2C9429162/sparams/gcr%2Cid%2Cip%2Cipbits%2Citag%2Cmaudio%2Cplaylist_type%2Cpmbypass%2Csource%2Cexpire/sver/3/expire/1456449859/pmbypass/yes/playlist_type/LIVE/itag/0/signature/1E6874232CCAC397B601051699A03DC5A32F66D9.1CABCD9BFC87A2A886A29B86CF877077DD1AEEAA/file/index.m3u8
This URL return to player actual video_id
https://www.youtube.com/embed/live_stream?channel=UCkA21M22vGK9GtAvq3DvSlA
Where UCkA21M22vGK9GtAvq3DvSlA is your channel id.
You can find it inside YouTube account on "My Channel" link.
or here https://www.youtube.com/account_advanced
Yes this is possible
Since the question is update, this solution can only gives you the embed url not the HLS url, check #JAL answer.
with the ressource search.list and the parameters:
* part: id
* channelId: UCURGpU4lj3dat246rysrWsw
* eventType: live
* type: video
Request :
GET https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=UCURGpU4lj3dat246rysrWsw&eventType=live&type=video&key={YOUR_API_KEY}
Result:
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"DsOZ7qVJA4mxdTxZeNzis6uE6ck/enc3-yCp8APGcoiU_KH-mSKr4Yo\"",
"id": {
"kind": "youtube#video",
"videoId": "WVZpCdHq3Qg"
}
},
Then get the videoID value WVZpCdHq3Qg for example and add the value to this url:
https://www.youtube.com/embed/ + videoID
https://www.youtube.com/watch?v= + videoID
For those who: want to see a live streaming video from youtube in your exoplanet in android then do this: https://exoplayer.dev/hls.html
Read the above documentation and create the ExoPlayer in android studio.
Careful: To get HLS Url from youtube live videos link:
Example of live URL: https://youtu.be/21X5lGlDOfg
. A common mistake in copying URL: https://www.youtube.com/watch?v=FdQA-pE2luQ. You can see that the second one is a link to a video that can be live but still a video. It's not HLS.
USAGE:
Just call convertLinkToHls() instead of initializing player, because we called initializePlayer() inside it.
Here we call the GET request:
void convertLinkToHls(String youtubeLiveLink) {
final String[] hlsLink = {""};
Runnable runnableCode = () -> {
try {
URL url = new URL(youtubeLiveLink);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
if(youtubeLiveLink.contains("m3u8")){
hlsLink[0] = youtubeLiveLink;
}else{
hlsLink[0] = extractHlsUrl(content.toString());
}
con.disconnect();
} catch (Exception e) {
hlsLink[0] = youtubeLiveLink;
e.printStackTrace();
Log.d("TAG", "convertLinkToHls: " +
": Reason is that the link is not a live url," +
"copy the link by open video then right click, copy video url from youtube.");
}
//extractHlsUrl(response);
};
Thread requestThread = new Thread(runnableCode);
requestThread.start();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
if (!requestThread.isAlive()) {
// hlsLink[0]
runOnUiThread(() -> {
et_url.setText(hlsLink[0]);
initializePlayer();
});
timer.cancel();
/* Here initialize the player,
* make you do it in runOnUiTHread() */
}
}
}, 1000, 1000);
}
Here we extract the link from the GET response:
private static String extractHlsUrl(String response) {
String keyName = "hlsManifestUrl";
if (response.contains(keyName)) {
int index = response.indexOf(keyName);
index = index + 17;
int lastIndex = index;
while (lastIndex < response.length()) {
if ((response.charAt(lastIndex) == '8') &&
(response.charAt(lastIndex - 1) == 'u') &&
(response.charAt(lastIndex - 2) == '3') &&
(response.charAt(lastIndex - 3) == 'm')) {
break;
}
lastIndex++;
}
return response.substring(index, lastIndex + 1);
}
return null;
}
Here we initialize Exo player for HLS streaming:
private void initializePlayer() {
player = new SimpleExoPlayer.Builder(this)
.build();
playerView.setPlayer(player);
String tempUrl = et_url.getText().toString();
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSourceFactory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(tempUrl));
player.setMediaSource(hlsMediaSource);
player.setPlayWhenReady(playWhenReady);
player.seekTo(currentWindow, playbackPosition);
player.prepare();
player.play();
}
You can also show the progress bar until we extract the link, it usually takes 300 milliseconds. This code works in java and android. As I have used the traditional way of getting requests and threads for background tasks.
What If the link is already an m3u8?
Then without any changes, we will link to the player, hence it works for youtube live videos and any other m3u8 links.
What is the concept behind this?
I am calling a get request using the live video URL and in response to ~400KB of StringBuffer, I extract the tag hlsManifestUrl from it. There is no rocketScience.
Ok, I have been trying to wrap my head around this http live streaming. I just do not understand it and yes I have read all the apple docs and watched the wwdc videos, but still super confused, so please help a wanna be programer out!!!
The code you write goes on the server? not in xcode?
If I am right how do i set this up?
Do I need to set up something special on my server? like php or something?
How do use the tools that are supplied by Apple.. segmenter and such?
Please help me,
Thanks
HTTP Live Streaming
HTTP Live Streaming is a streaming standard proposed by Apple. See the latest draft standard.
Files involved are
.m4a for audio (if you want a stream of audio only).
.ts for video. This is a MPEG-2 transport, usually with a h.264/AAC payload. It contains 10 seconds of video and it is created by splitting your original video file, or by converting live video.
.m3u8 for the playlist. This is a UTF-8 version of the WinAmp format.
Even when it's called live streaming, usually there is a delay of one minute or so during which the video is converted, the ts and m3u8 files written, and your client refresh the m3u8 file.
All these files are static files on your server. But in live events, more .ts files are added, and the m3u8 file is updated.
Since you tagged this question iOS it is relevant to mention related App Store rules:
You can only use progressive download for videos smaller than 10 minutes or 5 MB every 5 minutes. Otherwise you must use HTTP Live Streaming.
If you use HTTP Live Streaming you must provide at least one stream at 64 Kbps or lower bandwidth (the low-bandwidth stream may be audio-only or audio with a still image).
Example
Get the streaming tools
To download the HTTP Live Streaming Tools do this:
Get a Mac or iPhone developer account.
Go to https://developer.apple.com and search for "HTTP Live Streaming Tools", or look around at https://developer.apple.com/streaming/.
Command line tools installed:
/usr/bin/mediastreamsegmenter
/usr/bin/mediafilesegmenter
/usr/bin/variantplaylistcreator
/usr/bin/mediastreamvalidator
/usr/bin/id3taggenerator
Descriptions from the man page:
Media Stream Segmenter: Create segments from MPEG-2 Transport streams for HTTP Live Streaming.
Media File Segmenter: Create segments for HTTP Live Streaming from media files.
Variant Playlist Creator: Create playlist for stream switching from HTTP Live streaming segments created by mediafilesegmenter.
Media Stream Validator: Validates HTTP Live Streaming streams and servers.
ID3 Tag Generator: Create ID3 tags.
Create the video
Install Macports, go to the terminal and sudo port install ffmpeg. Then convert the video to transport stream (.ts) using this FFMpeg script:
# bitrate, width, and height, you may want to change this
BR=512k
WIDTH=432
HEIGHT=240
input=${1}
# strip off the file extension
output=$(echo ${input} | sed 's/\..*//' )
# works for most videos
ffmpeg -y -i ${input} -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s ${WIDTH}x${HEIGHT} -vcodec libx264 -b ${BR} -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 7 -trellis 0 -refs 0 -coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 200k -maxrate ${BR} -bufsize ${BR} -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 30 -qmax 51 -qdiff 4 -level 30 -aspect ${WIDTH}:${HEIGHT} -g 30 -async 2 ${output}-iphone.ts
This will generate one .ts file. Now we need to split the files in segments and create a playlist containing all those files. We can use Apple's mediafilesegmenter for this:
mediafilesegmenter -t 10 myvideo-iphone.ts
This will generate one .ts file for each 10 seconds of the video plus a .m3u8 file pointing to all of them.
Setup a web server
To play a .m3u8 on iOS we point to the file with mobile safari.
Of course, first we need to put them on a web server. For Safari (or other player) to recognize the ts files, we need to add its MIME types. In Apache:
AddType application/x-mpegURL m3u8
AddType video/MP2T ts
In lighttpd:
mimetype.assign = ( ".m3u8" => "application/x-mpegURL", ".ts" => "video/MP2T" )
To link this from a web page:
<html><head>
<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
</head><body>
<video width="320" height="240" src="stream.m3u8" />
</body></html>
To detect the device orientation see Detect and Set the iPhone & iPad's Viewport Orientation Using JavaScript, CSS and Meta Tags.
More stuff you can do is create different bitrate versions of the video, embed metadata to read it while playing as notifications, and of course have fun programming with the MoviePlayerController and AVPlayer.
This might help in swift:
import UIKit
import MediaPlayer
class ViewController: UIViewController {
var streamPlayer : MPMoviePlayerController = MPMoviePlayerController(contentURL: NSURL(string:"http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"))
override func viewDidLoad() {
super.viewDidLoad()
streamPlayer.view.frame = self.view.bounds
self.view.addSubview(streamPlayer.view)
streamPlayer.fullscreen = true
// Play the movie!
streamPlayer.play()
}
}
MPMoviePlayerController is deprecated from iOS 9 onwards. We can use AVPlayerViewController() or AVPlayer for the purpose. Have a look:
import AVKit
import AVFoundation
import UIKit
AVPlayerViewController :
override func viewDidAppear(animated: Bool){
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(URL: videoURL!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
AVPlayer :
override func viewDidAppear(animated: Bool){
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(URL: videoURL!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
Another explanation from Cloudinary http://cloudinary.com/documentation/video_manipulation_and_delivery#http_live_streaming_hls
HTTP Live Streaming (also known as HLS) is an HTTP-based media streaming communications protocol that provides mechanisms that are scalable and adaptable to different networks. HLS works by breaking down a video file into a sequence of small HTTP-based file downloads, with each download loading one short chunk of a video file.
As the video stream is played, the client player can select from a number of different alternate video streams containing the same material encoded at a variety of data rates, allowing the streaming session to adapt to the available data rate with high quality playback on networks with high bandwidth and low quality playback on networks where the bandwidth is reduced.
At the start of the streaming session, the client software downloads a master M3U8 playlist file containing the metadata for the various sub-streams which are available. The client software then decides what to download from the media files available, based on predefined factors such as device type, resolution, data rate, size, etc.
I'm doing a project which requires converting SWF movies to H.264 video on server-side, to be able to play them both in Flash player and on iPhone/iPad. And I really got stuck.
I'm using Melt from http://www.mltframework.org/ and this is my command-line:
melt movie.swf -consumer avformat:video.mp4 r=30 s=640x360 f=mp4 acodec=aac ab=128k ar=48000 vcodec=libx264 b=1000k an=1
It does play in Flash player, but fails to play on iDevices. I googled for iPhone video requirements and it seems my video files do satisfy them(frame size, framerate and bitrate). What settings should I change to make it play?
I've spent a lot of time in google but managed to gather all the pieces, so these are parameters that work for iPhone:
r=30 s=640x360 f=mp4 acodec=aac ab=128k ar=48000 vcodec=libx264 level=30 b=1024k flags=+loop+mv4 cmp=256 partitions=+parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 me_method=hex subq=7 trellis=1 refs=1 bf=0 flags2=+mixed_refs-wpred-dct8x8 coder=0 wpredp=0 me_range=16 g=250 keyint_min=25 sc_threshold=40 i_qfactor=0.71 qmin=10 qmax=51 qdiff=4 maxrate=10M bufsize=10M an=1 threads=0
Also, I use faac -w to convert audio to appropriate format and MP4Box to join video and sound.