Tapestry5 无法将视频流传输到 iPad

2024-03-06

我想通过后端带有 Tapestry5 (5.3.5) 的 HTML5 视频标签将视频流式传输到我的 iPad。通常,服务器端框架甚至不应该在其中发挥作用,但不知何故它确实发挥了作用。

无论如何,希望这里有人能帮助我。请记住,我的项目很大程度上是一个原型,我所描述的内容被简化/简化为相关部分。如果人们没有以强制性的“你想做错事”或与问题无关的安全/性能挑剔来回应,我将非常感激。

所以事情是这样的:

Setup

我有一段从 Apple HTML5 展示中拍摄的视频,所以我知道格式不是问题。我有一个简单的 tml 页面“Play”,仅包含一个“video”标签。

Problem

我首先实现一个 RequestFilter,它通过打开引用的视频文件并将其流式传输到客户端来处理来自视频控件的请求。这是基本的“如果路径以“文件”开头,则将文件输入流复制到响应输出流”。这在 Chrome 上运行得很好,但在 Ipad 上则不然。好吧,不过,我一定是缺少了一些标题,所以我再次查看了 Apple Showcase,并包含了相同的标题和内容类型,但没有任何乐趣。

接下来,我想,好吧,让我们看看如果让 t5 提供该文件会发生什么。我将视频复制到 web 应用程序上下文,禁用请求过滤器并将简单的文件名放入视频的 src 属性中。这适用于 Chrome 和 iPad。 这让我感到惊讶,并促使我研究 T5 如何处理静态文件/上下文请求。到目前为止,我只感觉到有两条不同的路径,我通过将硬连线的“视频 src”切换到带有 @Path(“context:”) 的资产来确认这两条路径。这同样适用于 Chrome,但不适用于 iPad。

所以我真的迷路了。允许它在 iPad 上运行的“简单上下文”请求中的秘密是什么?没有什么特别的事情发生,但这是唯一有效的方法。问题是,我无法真正从我的网络应用程序上下文中提供这些视频......

Solution

因此,事实证明,有一个名为“Range”的 http 标头,并且与 Chrome 不同的是,iPad 将其用于视频。那么“秘密武器”是静态资源请求的 servlet 处理程序知道如何处理范围请求,而 T5 则不知道。这是我的自定义实现:

        OutputStream os = response.getOutputStream("video/mp4");
        InputStream is = new BufferedInputStream( new FileInputStream(f));
        try {
            String range = request.getHeader("Range");
            if( range != null && !range.equals("bytes=0-")) {
                logger.info("Range response _______________________");
                String[] ranges = range.split("=")[1].split("-");
                int from = Integer.parseInt(ranges[0]);
                int to = Integer.parseInt(ranges[1]);
                int len = to - from + 1 ;

                response.setStatus(206);
                response.setHeader("Accept-Ranges", "bytes");
                String responseRange = String.format("bytes %d-%d/%d", from, to, f.length());
                logger.info("Content-Range:" + responseRange);
                response.setHeader("Connection", "close");
                response.setHeader("Content-Range", responseRange);
                response.setDateHeader("Last-Modified", new Date().getTime());
                response.setContentLength(len);
                logger.info("length:" + len);

                byte[] buf = new byte[4096];
                is.skip(from);
                while( len != 0) {

                    int read = is.read(buf, 0, len >= buf.length ? buf.length : len);
                    if( read != -1) {
                        os.write(buf, 0, read);
                        len -= read;
                    }
                }


            } else {
                    response.setStatus(200);
                    IOUtils.copy(is, os);
            }

        } finally {
            os.close();
            is.close();
        }

我想从上面发布我的改进解决方案。希望这对某人有用。

所以基本上问题似乎是我忽略了 iPad 不喜欢的“Range”http 请求标头。简而言之,此标头意味着客户端只需要响应的特定部分(在本例中为字节范围)。

iPad html 视频请求如下所示:

[INFO] RequestLogger Accept:*/*
[INFO] RequestLogger Accept-Encoding:identity
[INFO] RequestLogger Connection:keep-alive
[INFO] RequestLogger Host:mars:8080
[INFO] RequestLogger If-Modified-Since:Wed, 10 Oct 2012 22:27:38 GMT
[INFO] RequestLogger Range:bytes=0-1
[INFO] RequestLogger User-Agent:AppleCoreMedia/1.0.0.9B176 (iPad; U; CPU OS 5_1 like Mac OS X; en_us)
[INFO] RequestLogger X-Playback-Session-Id:BC3B397D-D57D-411F-B596-931F5AD9879F

这意味着 iPad 只需要第一个字节。如果您忽略此标头并仅发送带有完整正文的 200 响应,则视频将无法播放。因此,您需要发送 206 响应(部分响应)并设置以下响应标头:

[INFO] RequestLogger Content-Range:bytes 0-1/357772702
[INFO] RequestLogger Content-Length:2

这意味着“我正在向您发送 357772702 个可用字节中的第 0 到 1 个字节”。

当您实际开始播放视频时,下一个请求将如下所示(除了范围标头之外的所有内容都被省略):

[INFO] RequestLogger Range:bytes=0-357772701

所以我的改进解决方案如下所示:

OutputStream os = response.getOutputStream("video/mp4");

        try {
                String range = request.getHeader("Range");
                /** if there is no range requested we will just send everything **/
                if( range == null) {
                    InputStream is = new BufferedInputStream( new FileInputStream(f));
                    try {
                        IOUtils.copy(is, os);
                        response.setStatus(200);
                    } finally {
                        is.close();
                    }
                    return true; 
                }
                requestLogger.info("Range response _______________________");


                String[] ranges = range.split("=")[1].split("-");
                int from = Integer.parseInt(ranges[0]);
                /**  
                 * some clients, like chrome will send a range header but won't actually specify the upper bound.
                 * For them we want to send out our large video in chunks.
                 */
                int to = HTTP_DEFAULT_CHUNK_SIZE + from;
                if( to >= f.length()) {
                    to = (int) (f.length() - 1);
                }
                if( ranges.length == 2) {
                    to = Integer.parseInt(ranges[1]);
                }
                int len = to - from + 1 ;

                response.setStatus(206);
                response.setHeader("Accept-Ranges", "bytes");
                String responseRange = String.format("bytes %d-%d/%d", from, to, f.length());

                response.setHeader("Content-Range", responseRange);
                response.setDateHeader("Last-Modified", new Date().getTime());
                response.setContentLength(len);

                requestLogger.info("Content-Range:" + responseRange);
                requestLogger.info("length:" + len);
                long start = System.currentTimeMillis();
                RandomAccessFile raf = new RandomAccessFile(f, "r");
                raf.seek(from);
                byte[] buf = new byte[IO_BUFFER_SIZE];
                try {
                    while( len != 0) {
                        int read = raf.read(buf, 0, buf.length > len ? len : buf.length);
                        os.write(buf, 0, read);
                        len -= read;
                    }
                } finally {
                    raf.close();
                }
                logger.info("r/w took:" + (System.currentTimeMillis() - start));




        } finally {
            os.close();

        }

这个解决方案比我的第一个解决方案更好,因为它处理“范围”请求的所有情况,这似乎是像 Chrome 这样的客户端能够支持在视频中跳过的先决条件(此时他们将为此发出范围请求)视频中的点)。

但它仍然不完美。进一步的改进将是正确设置“Last-Modified”标头,并正确处理客户端请求无效范围或字节以外的其他内容的范围。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tapestry5 无法将视频流传输到 iPad 的相关文章

  • viewDidLoad 中的帧大小错误[重复]

    这个问题在这里已经有答案了 可能的重复 为什么我必须在 viewDidLoad 中手动设置视图的框架 https stackoverflow com questions 6757018 why am i having to manually
  • UITableView 的 reloadRowsAtIndexPaths: (NSArray *) indexPaths 无法导致重新加载,除非您调用它两次?

    我有一个 UITableViewController 管理 iPad 应用程序中的 UITableView 对象 表格视图与相当复杂的其他对象群联系在一起 当我要求它重新加载行时遇到问题 如下所示 indexPath is an NSInd
  • iOS 中的 NSCachesDirectory 和 NSDownloadsDirectory 有什么区别?

    我想将下载的杂志保存到我的 iOS 应用程序中的一个目录中 它似乎NSCachesDirectory and NSDownloadsDirectory是合适的 我不知道它们之间有什么区别 以及哪一种适合下载杂志 任何建议表示赞赏 内存不足时
  • stringFromDate 始终为 NIL

    我知道这是一个重复的问题 但是在 stackoverflow 和 google 上搜索了许多类似的问题后 没有一个解决方案对我有用 我正在尝试将从数据库收到的日期转换为字符串格式以在 iPhone 应用程序中显示 我正在按以下方式将日期转换
  • 为什么 HttpServletRequest 输入流为空?

    我有这段代码 我从请求输入流读取输入并使用 JacksonMapper 转换为 POJO 它在具有 guice 支持的 jetty 7 容器中运行 Override protected void doPost HttpServletRequ
  • 如何在操作表中添加日期选择器?

    IBAction showCatPicker if self catList nil self catList nil catList release self catList NSMutableArray alloc init self
  • 在 Objective-C iPad 开发中发布

    我正在尝试发出 POST 请求 但我似乎无法弄清楚出了什么问题 我从服务器收到响应 但我的电子邮件 密码对似乎没有正确发送 读取 由服务器 它告诉我不存在这样的帐户 这是我的代码 它包含在一个函数中 当用户按下我创建的 登录 按钮时调用该函
  • 检测 iPad Safari 用户的最佳方法

    添加用于检测 iPad Safari 用户的代码的最佳方法是什么 我的意思是我们应该使用 1 CSS 通过链接媒体 2 JS 通过navigator对象 我听说使用用户代理字符串并不是检测 iPad 的最佳方法 因为存在不一致的情况 请建议
  • RestKit链接器错误

    我一直遵循 RestKit 安装说明 但现在在尝试构建应用程序时出现错误 这是针对 ios iPad 的 我收到 命令 Developer Platforms iPhoneSimulator platform Developer usr b
  • Xcode 4 .xib 创建 iPad 版本

    我有一台 iPhone xib 我想将其变成 iPad xib 在 Xcode 3 中 有一个 创建 iPad 版本 菜单选项 我如何在 Xcode 4 中执行此操作 我目前调整了 xib 的大小 但是当我打开模拟项目 导航栏等 时 它会将
  • phonegap 插件,用于从库中选择视频

    我需要能够从库中选择视频并将其上传到我的服务器 我可以录制新视频 captureVideo 并上传 文件传输 没问题 但我似乎找不到任何方法来打开视频库并选择视频然后上传 有什么办法可以做到这一点吗 以某种方式更改 MediaType na
  • UIView 和 UITableView 中的 UITapGestureRecognizer 冲突

    我有一个UIView我在其中添加了一个UITapGestureRecognizer 在该视图中 我还有一个子视图 其中基本上是某种UITableView 问题是为什么不UITableView识别连续点击 而是始终转到点击手势识别器的处理程序
  • 检测 HTML5 视频何时结束

    如何检测 HTML5
  • Apple 针对 http 直播流媒体应用程序的政策

    这里有要求 http developer apple com library ios documentation NetworkingInternet Conceptual StreamingMediaGuide UsingHTTPLive
  • 如何禁用 YouTube Iframe 上的全屏?

    我里面有一个 div 容器和 Iframe 我指定宽度 200 和高度 200 当我点击全屏时 视频变得模糊且质量非常差 所以 我想看看是否可以在 YouTube iframe 上禁用全屏 I used controls 0在我的网址末尾
  • java inputstream 打印控制台内容

    sock new Socket www google com 80 out new BufferedOutputStream sock getOutputStream in new BufferedInputStream sock getI
  • 用于具有转换的非导航应用程序的视图控制器/NIB 架构?

    我正在修补一个 iPad 应用程序 就像许多 iPad 应用程序一样 它不使用 UINavigation 根视图控制系统 因此我没有每个应用程序 视图 的自然所有权 我基本上有两个基本视图 文档列表视图和文档编辑视图 我正在使用 UIVie
  • HTML5 视频自动播放功能在 fullpage.js 中不起作用

    我的 HTML5 视频自动播放不起作用
  • 在情节提要中将 Segue 拖至自身

    我想将一个 Segue 从我的视图控制器拖到其自身 所以我可以推送该特定视图控制器的 无限 实例 我知道如何在代码中执行此操作 即以编程方式实例化视图控制器 但是 我想尽可能使用 segues 我发现了一些在故事板中进行自我延续的 技巧 但
  • 如何在 iOS 上固定证书的公钥

    在提高我们正在开发的 iOS 应用程序的安全性时 我们发现需要对服务器的 SSL 证书 全部或部分 进行 PIN 操作以防止中间人攻击 尽管有多种方法可以做到这一点 但当您搜索此内容时 我只找到了固定整个证书的示例 这种做法会带来一个问题

随机推荐

  • tesseract 无法识别该图像中的这个单词,这正常吗?

    我需要从这样的小图像中提取单词 我在命令行中使用带有西班牙语选项的 tesseract 如下所示 tesseract category png l spa psm 7 category txt 我认为该文本一定很容易被 OCR 解析 但该单
  • JavaScript 中的 array.select()

    JavaScript 是否具有与 Ruby 类似的功能 array select x x gt 3 就像是 array select function x if x gt 3 return true 有Array filter var nu
  • 第一次捆绑安装,堆栈级别太深

    我使用命令创建了一个全新的 Rails 项目rails new qbc database mysql 它完美地创建了所有文件 但是 在捆绑包安装时出现错误 bundle install Fetching gem metadata from
  • 在 RESTful Web 服务中,服务器花很长时间来响应是否可以接受?

    我正在使用 Flask restful 开发 RESTful Web 服务 客户端需要能够请求服务器执行作业 这项工作可能需要大约 1 秒到大约 1 小时才能完成 一般情况下 预计需要 1 5 分钟 作业完成后 客户端需要下载 JSON 转
  • 插入时返回 ID?

    我有一个 INSERT 查询 我希望数据库返回我刚刚插入的行的 ID sqlString INSERT INTO MagicBoxes OwnerID Key Name Permissions Active LastUpdated VALU
  • 有条件切换情况

    我是否在条件下编写了正确的 switch case var cnt div1 p length alert cnt switch cnt case cnt gt 10 cnt lt 20 alert 10 break case cnt gt
  • EL 表达式将整数解析为 long

    我在 JBoss 7 上使用带有 primefaces 的 JSF 2 0 在代码的某些部分 我有以下内容 public void setItemValue int value this value value 并在 xhtml 中
  • 无法从 SwfTreeView 选择复选框

    在我的应用程序中 有一个 swfTreeView 对象 它有 2 个父复选框 Total Systmatic 并且两个父复选框都有 2 个子复选框 EQ FX for Total 和 EX IR for Systematic 我无法选择这些
  • 如何等待流完成管道传输? (节点)

    我有一个 Promise 的 for 循环数组 所以我使用 Promise all 来遍历它们 然后调用 then let promises promises push promise1 promises push promise2 pro
  • 我可以为背景大小实现纯 CSS 后备吗?

    这对于支持的浏览器来说效果很好background size 否则 图像会缩放 2 倍 a background image url img2x jpg 1000x1000 background size 100 height 500px
  • Android自定义EditText和后退按钮覆盖

    我想在显示软键盘时覆盖后退按钮 基本上 当按下后退按钮时 我希望键盘关闭 并且我想将一些文本附加到用户在编辑文本字段中键入的内容上 所以基本上我需要知道键盘何时关闭 经过搜索后 我意识到没有可用的 API 唯一真正的方法是创建 EditTe
  • 如何定位按钮精灵 css

    button1 background E68A00 url wooden jpg repeat x border 2px solid eee height 28px width 115px margin 50px 0 0 50px padd
  • Shell 脚本:获取 python: 命令未找到错误

    当我从 shell 脚本调用 Python 脚本时 它运行良好 python script py 但是当我从 Gerrit 中提取相同的脚本 然后添加调用 Python 脚本的代码后 它给了我以下错误 script sh line 126
  • php require_once 尝试仅在我的生产服务器上包含第二次

    我在各种包含文件的顶部都有这段代码 require once functions php 有时我需要包含几个包含文件来生成页面 并且在我的本地服务器上这工作正常 因为上面的代码告诉它只包含一次functions php 因此它不会尝试声明函
  • MS Entity Framework VS NHibernate 及其派生贡献(FluentNHibernate、Linq for NHibernate)

    我刚刚读过这个article http visualstudiomagazine com Articles 2009 12 01 Entity Sequel aspx Page 1关于实体框架 4 实际上是版本 2 实体框架 http ms
  • 将 for-each 循环替换为 lambda 表达式

    我只是重构一些旧项目以使用 Java 8 的功能 int counter 1 for Checker checker checkers if counter lt checkers size checker setNextChecker c
  • 不要使用 Xcode 8 复制 swift 库吗?

    How to not在 Xcode 8 中自动嵌入 Swift 动态库 我尝试过设置ALWAYS EMBED SWIFT STANDARD LIBRARIES为 否 无论如何默认为 否 但它仍然将 Swift 动态库复制到应用程序包中 我正
  • Require.js 延迟加载远程 url

    我的本地文件系统上有一个名为 moment js 的文件 并使用 require js 加载它 如下所示 initialize function require moment function data console log data 但
  • Java中杀死进程的正确方法

    在 Java 中终止进程的最佳方法是什么 获取 PID 然后用以下命令杀死它Runtime exec Use destroyForcibly 这两种方法有什么区别 还有其他解决方案吗 如果您要终止的进程已由您的应用程序启动 那么你可能已经参
  • Tapestry5 无法将视频流传输到 iPad

    我想通过后端带有 Tapestry5 5 3 5 的 HTML5 视频标签将视频流式传输到我的 iPad 通常 服务器端框架甚至不应该在其中发挥作用 但不知何故它确实发挥了作用 无论如何 希望这里有人能帮助我 请记住 我的项目很大程度上是一