Hi all,
I've been working to fix an add-on working against a content provider that serves video content via HLS. This specific provider requires the correct cookies
to be sent with the HTTP requests for all segments of the m3u8 file.
I know ffmpeg was recently bumped to v1.2 and that it supports HLS much better now. I also know that XBMC now supports passing protocol options to ffmpeg but
I can't get it to work and I think there's a bug (consulting before opening a bug..)
Side note: Some of these HLS content providers are quite picky and will not like HTTP HEAD methods. Specifically, when XBMC attempts to find the mime type
(CCurlFile::GetMimeType). The only workaround is to set the mime-type for the listItem yourself. (could have enjoyed a way to change the method to GET).
Anyway, the current code in CDVDDemuxFFmpeg::GetFFMpegOptionsFromURL will parse the options passed and will set them into ffmpeg's dictionary.
The problem is - it does so for very selected options (like user-agent). All other options are handled as HTTP headers, which seems not to be enough.
for(std::map<CStdString, CStdString>::const_iterator it = protocolOptions.begin(); it != protocolOptions.end(); ++it)
{
const CStdString &name = it->first;
const CStdString &value = it->second;
if (name.Equals("seekable"))
m_dllAvUtil.av_dict_set(&options, "seekable", value.c_str(), 0);
else if (name.Equals("User-Agent"))
{
m_dllAvUtil.av_dict_set(&options, "user-agent", value.c_str(), 0);
hasUserAgent = true;
}
else if (!name.Equals("auth") && !name.Equals("Encoding"))
// all other protocol options can be added as http header.
headers.append(name).append(": ").append(value).append("\r\n");
}
if (!hasUserAgent)
// set default xbmc user-agent.
m_dllAvUtil.av_dict_set(&options, "user-agent", g_advancedSettings.m_userAgent.c_str(), 0);
if (!headers.empty())
m_dllAvUtil.av_dict_set(&options, "headers", headers.c_str(), 0);
The URL I'm passing to play the video looks like the following:
http://<some_host>/segments.m3u8|User-Agent=<some_good_user_agent>&Cookie=<cookies_that_are_valid>
This is enough for XBMC to obtain the playlist (with all the TS segments) but later when ffmpeg tries to access each file it receives a 403 Forbidden since it didn't
pass those cookies. Note: I know ffmpeg will save cookies and keep them for later requests (see hls.c in ffmpeg's lib avformat).
I couldn't find anywhere, in ffmpeg's code, where headers from outside get turned into cookies. I saw where it parses cookies returning from the server,
I saw that when it builds the HTTP request it will add cookies if they exist within the HTTPContext, but I guess they're not there (http.c -> http_connect function).
The request for the first TS segment looks like this:
GET /<host>/segment-0.ts HTTP/1.1
User-Agent: stagefright/1.2 (Linux; Android 4.2.2)
Accept: */*
Connection: close
Host:<host>
As you can see, it will preserve the user agent as it was specifically set as an option.
Am I doing something wrong or is there really some kind of bug ?
I think we should add the following block of code to GetFFMpegOptionsFromURL:
else if (name.Equals("Cookie"))
{
m_dllAvUtil.av_dict_set(&options, "cookies", value.c_str(), 0);
}
Since ffmpeg has a specific protocol option for passing in cookie data. Side note - ffmpeg is extremely picky with cookies. It requires the path, domain and cookie data to be present in order to use the cookie later.
I'm currently trying to build XBMC but ffmpeg keeps failing to compile. I'll update if I have more luck.
Any help is very much appreciated!
I've been working to fix an add-on working against a content provider that serves video content via HLS. This specific provider requires the correct cookies
to be sent with the HTTP requests for all segments of the m3u8 file.
I know ffmpeg was recently bumped to v1.2 and that it supports HLS much better now. I also know that XBMC now supports passing protocol options to ffmpeg but
I can't get it to work and I think there's a bug (consulting before opening a bug..)
Side note: Some of these HLS content providers are quite picky and will not like HTTP HEAD methods. Specifically, when XBMC attempts to find the mime type
(CCurlFile::GetMimeType). The only workaround is to set the mime-type for the listItem yourself. (could have enjoyed a way to change the method to GET).
Anyway, the current code in CDVDDemuxFFmpeg::GetFFMpegOptionsFromURL will parse the options passed and will set them into ffmpeg's dictionary.
The problem is - it does so for very selected options (like user-agent). All other options are handled as HTTP headers, which seems not to be enough.
for(std::map<CStdString, CStdString>::const_iterator it = protocolOptions.begin(); it != protocolOptions.end(); ++it)
{
const CStdString &name = it->first;
const CStdString &value = it->second;
if (name.Equals("seekable"))
m_dllAvUtil.av_dict_set(&options, "seekable", value.c_str(), 0);
else if (name.Equals("User-Agent"))
{
m_dllAvUtil.av_dict_set(&options, "user-agent", value.c_str(), 0);
hasUserAgent = true;
}
else if (!name.Equals("auth") && !name.Equals("Encoding"))
// all other protocol options can be added as http header.
headers.append(name).append(": ").append(value).append("\r\n");
}
if (!hasUserAgent)
// set default xbmc user-agent.
m_dllAvUtil.av_dict_set(&options, "user-agent", g_advancedSettings.m_userAgent.c_str(), 0);
if (!headers.empty())
m_dllAvUtil.av_dict_set(&options, "headers", headers.c_str(), 0);
The URL I'm passing to play the video looks like the following:
http://<some_host>/segments.m3u8|User-Agent=<some_good_user_agent>&Cookie=<cookies_that_are_valid>
This is enough for XBMC to obtain the playlist (with all the TS segments) but later when ffmpeg tries to access each file it receives a 403 Forbidden since it didn't
pass those cookies. Note: I know ffmpeg will save cookies and keep them for later requests (see hls.c in ffmpeg's lib avformat).
I couldn't find anywhere, in ffmpeg's code, where headers from outside get turned into cookies. I saw where it parses cookies returning from the server,
I saw that when it builds the HTTP request it will add cookies if they exist within the HTTPContext, but I guess they're not there (http.c -> http_connect function).
The request for the first TS segment looks like this:
GET /<host>/segment-0.ts HTTP/1.1
User-Agent: stagefright/1.2 (Linux; Android 4.2.2)
Accept: */*
Connection: close
Host:<host>
As you can see, it will preserve the user agent as it was specifically set as an option.
Am I doing something wrong or is there really some kind of bug ?
I think we should add the following block of code to GetFFMpegOptionsFromURL:
else if (name.Equals("Cookie"))
{
m_dllAvUtil.av_dict_set(&options, "cookies", value.c_str(), 0);
}
Since ffmpeg has a specific protocol option for passing in cookie data. Side note - ffmpeg is extremely picky with cookies. It requires the path, domain and cookie data to be present in order to use the cookie later.
I'm currently trying to build XBMC but ffmpeg keeps failing to compile. I'll update if I have more luck.
Any help is very much appreciated!