【问题标题】:Serve mp4 file through coldfusion and play with jwplayer通过coldfusion提供mp4文件并使用jwplayer播放
【发布时间】:2017-08-21 17:14:58
【问题描述】:

我在 Coldfusion 中有一个 Web 应用程序,可以录制视频并向用户提供视频。

视频在 android 和桌面浏览器上运行良好,但在 IOS 中出现错误“加载媒体时出错:无法播放文件”。

这是我目前正在运行的 JWPlayer 代码。

jwplayer("element").setup({
  file: "/video.cfm?token=4514_129_9B2F727D-5056-A85D-6EBE3E48FC2AB9C6",
  image: "path/to/image",
  width: 450,
  height: 360,
  type: "mp4",
  logo: {
     file: 'path/to/logo',
     link: 'example.com',
     hide : true
  }
});

这是我验证后到服务器 mp4 的 video.cfm。

<cfset videoFile = 'path\to\file'>
<cfset fileInfo = GetFileInfo(videoFile)>
<cfset length = fileInfo.size>
<cfset start = 0>
<cfset end = fileInfo.size - 1>
<cfheader name="Content-type" value="video/mp4">
<cfheader name="Accept-Ranges" value="0-#length#">
<cfheader name="Content-Range" value="bytes #start#-#end#/#fileInfo.size#">
<cfheader name="Content-Length" value="#length#">
<cfcontent file="#videoFile#" type="video/mp4">

我通过添加一些标题尝试了一些解决方案。但这不起作用。谁能帮我解决问题。

【问题讨论】:

  • 如果您允许我们访问该站点(至少是前端),我们或许可以帮助诊断它。我已经一起交付了包含 CF、视频和 jwplayer 的项目。
  • 远离您自己的应用程序,这些文件是否可以在您的 iOS 设备中播放?我的意思是确保文件数据本身对于 iOS 播放是可以接受的。将一个文件从服务器保存到存储并尝试在某些媒体播放器应用程序中播放......会发生什么?在不同的(规格/电源)iOS 设备上测试相同的文件?
  • @VC.One。我在jwplayer-techy.fwd.wf 上添加了不同的演示。只有一个在 IOS 上运行的文件是通过 hlshtml:true。您知道如何使用此参数从文件中运行 mp4 吗?
  • @Jules。这是使用我的代码jwplayer-techy.fwd.wf 的测试示例网址。请看
  • @VC.One。谢谢。我已经想通了。我已经发布了我的答案。

标签: ios coldfusion jwplayer mp4


【解决方案1】:

我能够解决我的问题。 iOS 使用部分内容标题来运行视频。感谢 rickward 提供了这个可爱的解决方案:Media Delivery to iPhones and iPads。我做了一些小改动,它开始为我工作。

这是最终的 video.cfm 文件。

<cfset videoPath = 'path\to\mp4\file'>
<cfif FileExists(videoPath)>
    <cfset fileInfoVar = GetFileInfo(videoPath)>
    <cfheader name="Last-Modified" value="#fileInfoVar.Lastmodified#">
    <cfheader name="ETag" value="#hash(videoPath, 'MD5')#">
    <cfheader name="Content-Location" value="http://example.com/video.cfm">

    <cfif structKeyExists(GetHttpRequestData().headers, 'Range')>
        <cfset rangeDownload(videoPath)>
    <cfelse>
        <cffile action="readbinary" file="#videoPath#" variable="theData">
        <cfscript>
            context = getPageContext();
            context.setFlushOutput(false);
            response = context.getResponse().getResponse();
            response.setContentType("video/mp4");
            response.setContentLength(arrayLen(theData));

            out = response.getOutputStream();
            out.write(theData);
            out.flush();
            out.close();
        </cfscript>
    </cfif>
</cfif>

<cffunction name="rangeDownload" returnType="void" output="yes">
    <cfargument name="file" type="string" required="true" hint="path to file">

    <cfset var l = {}>
    <cfset l.request = GetHttpRequestData()>

    <cffile action="readbinary" file="#ARGUMENTS.file#" variable="l.theData">

    <cfset l.size = arrayLen(l.theData)>
    <cfset l.length = l.size>
    <cfset l.start  = 0>
    <cfset l.end = l.size - 1>

    <!--- Now that we've gotten so far without errors we send the accept range header
    /* At the moment we only support single ranges.
     * Multiple ranges requires some more work to ensure it works correctly
     * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
     *
     * Multirange support annouces itself with:
     * header('Accept-Ranges: bytes');
     *
     * Multirange content must be sent with multipart/byteranges mediatype,
     * (mediatype = mimetype)
     * as well as a boundry header to indicate the various chunks of data.
     */
    --->
    <cfheader name="Accept-Ranges" value="0-#l.length#">
    <!---<cfheader name="Accept-Ranges" value="bytes"> --->
    <!---
      multipart/byteranges
      http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 --->
    <cfif structKeyExists(l.request.headers, 'Range')>

        <cfset l.c_start = l.start>
        <cfset l.c_end = l.end>

        <!--- Extract the range string --->
        <cfset l.range = ListGetAt(l.request.headers.range, 2, '=')>
        <!--- Make sure the client hasn't sent us a multibyte range --->
        <cflog file="rangeDownload" text="#l.range#" />
        <cfif l.range contains ','>
            <!--- (?) Should this be issued here, or should the first
             range be used? Or should the header be ignored and
             we output the whole content?
            --->
            <cfheader statusCode = "416" statusText = "Requested Range Not Satisfiable">
            <cfheader name="Content-Range" value="bytes #l.start#-#l.end#/#l.size#">
            <!--- (?) Echo some info to the client? --->
            <cfabort>
        </cfif>
        <!--- If the range starts with an '-' we start from the beginning
            If not, we forward the file pointer
            And make sure to get the end byte if specified --->
        <cfif Left(l.range, 1) eq '-'>
        <!--- The n-number of the last bytes is requested --->
            <cfset l.c_start = l.size - Mid(l.range, 2, Len(l.range))>
        <cfelse>
            <cfset l.rangeArray = ListToArray(l.range, '-')>
            <cfset l.c_start = l.rangeArray[1]>
            <cfif ArrayLen(l.rangeArray) eq 2 and val(l.rangeArray[2]) gt 0>
                <cfset l.c_end = l.rangeArray[2]>
            <cfelse>
                <cfset l.c_end = l.size>
            </cfif>
        </cfif>
        <!---
        /* Check the range and make sure it's treated according to the specs.
         * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
         */
        // End bytes can not be larger than l.end. --->
        <cfif l.c_end gt l.end>
            <cfset l.c_end = l.end>
        </cfif>

        <!--- Validate the requested range and return an error if it's not correct. --->
        <cfif l.c_start gt l.c_end || l.c_start gt (l.size - 1) || l.c_end gte l.size>
            <cfheader statusCode = "416" statusText = "Requested Range Not Satisfiable">
            <cfheader name="Content-Range" value="bytes #l.start#-#l.end#/#l.size#">
            <!--- (?) Echo some info to the client? --->
            <cfabort>
        </cfif>

        <cfset l.start = l.c_start>
        <cfset l.end = l.c_end>
        <cfset l.length = l.end - l.start + 1><!--- Calculate new content length --->


        <cfscript>
            context = getPageContext();
            context.setFlushOutput(false);
            response = context.getResponse().getResponse();
            response.setContentType("video/mp4");
            response.setContentLength(l.length);
        </cfscript>
        <cfheader statusCode = "206" statusText = "Partial Content">

    </cfif>

    <!--- Notify the client the byte range we'll be outputting --->
    <cfheader name="Content-Range" value="bytes #l.start#-#l.end#/#l.size#">
    <cfheader name="Content-Length" value="#l.length#">

    <cfscript>
        // Start buffered download
        out = response.getOutputStream();
        // write the portion requested
        out.write(l.theData, javacast('int', l.start), javacast('int', l.length));
        out.flush();
        out.close();
    </cfscript>
</cffunction>

【讨论】:

  • 很高兴你知道了。小建议,您可以先简化&lt;cfelse&gt;,然后使用&lt;cfcontent&gt;。另外,不要忘记var/local 作用于函数中的所有变量,包括context, response, out 等。
  • @Leigh。谢谢你的建议。我会根据您的建议进行更改。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多