Qt Webp qml 动图在线加载失败解决办法

目前webp图片格式流行,Qt虽已支持,但在线webp动图在QML (AnimatedImage) 中无法加载。经查看源码,发现是从QNetworkRreply解码失败导致。解决办法是修改源码,将webp动图内容存到QBuffer再解码加载,然后编译替换Qt5Quick库,不同系统编译方式有别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目前 webp 图片格式大行其道,Qt 已经支持了,只是在线 webp 动图在 QML (AnimatedImage), 不能加载,向Qt提bug(https://bugreports.qt.io/browse/QTBUG-75044),也不理采,只能自己动手丰衣足食了。

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Column {
        anchors.centerIn: parent
        // error
        AnimatedImage {
            source: "https://p.upyun.com/demo/webp/webp/animated-gif-0.webp"
            Text {
                text: qsTr("AnimatedImage online webp animation image")
                color: "red"
            }
        }

        // ok
        Image {
            source: "https://p.upyun.com/demo/webp/webp/animated-gif-0.webp"
            Text {
                text: qsTr("Image webp online animation image")
                color: "red"
            }
        }

        // ok
        AnimatedImage {
            source: "qrc:/image/animated-gif-0.webp"
            Text {
                text: qsTr("AnimatedImage webp local animation image")
                color: "red"
            }
        }
    }
}

在线动图会加载失败,报错如下:

QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once QUrl("https://p.upyun.com/demo/webp/webp/animated-gif-0.webp") Sequential devices are not supported qrc:/main.qml:13:9: QML AnimatedImage: Error Reading Animated Image File https://p.upyun.com/demo/webp/webp/animated-gif-0.webp

查看源码发现在线webp 从网络下载 图片缓存在 QNetworkReply, 然而webp 从 QNetworkRreply 解码因为 QNetworkRreply isSequential 失败导致。

解决办法,修改源码(Src\qtdeclarative\src\quick\items\qquickanimatedimage.cpp)

找到函数:movieRequestFinished

把QNetworkRreply人webp动图的内容存到QBuffer里面,然后再用于解码加载

// 修改前
#if QT_CONFIG(qml_network)
    if (d->reply) {
        d->redirectCount++;
        if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
            QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
            if (redirect.isValid()) {
                QUrl url = d->reply->url().resolved(redirect.toUrl());
                d->reply->deleteLater();
                setSource(url);
                return;
            }
        }

        d->redirectCount=0;
        d->setMovie(new QMovie(d->reply));
    }
#endif

// 修改后
#if QT_CONFIG(qml_network)
    if (d->reply) {
        d->redirectCount++;
        if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
            QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
            if (redirect.isValid()) {
                QUrl url = d->reply->url().resolved(redirect.toUrl());
                d->reply->deleteLater();
                setSource(url);
                return;
            }
        }

        d->redirectCount=0;
        // for webp animation
        static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
        QByteArray header = d->reply->peek(riffHeaderSize);
        if(header.startsWith("RIFF") && header.endsWith("WEBP")) {
            //qmlWarning(this) << "=============================== QFile replace d->reply";
            QBuffer *imgBuf = new QBuffer(this);
            imgBuf->setData(d->reply->readAll());
            d->setMovie(new QMovie(imgBuf));
        } else {
            d->setMovie(new QMovie(d->reply));
        }
    }
#endif

 然后编译,直接替换Qt5Quick库

注意: windows android 可以有直接编译 qtdeclarative 模块, ios 要编译全部源码。如有其它问题,欢迎各位大佬指导

<think>好的,用户想了解如何在前端监听WebP格式的播放结束事件。首先,我需要回忆一下WebP的支持情况。WebP是Google开发的像格式,支持有损、无损和画,相比GIF有更好的压缩率。但它的浏览器兼容性可能有问题,比如Safari早期版本不支持,但现在应该已经改善了。 接下来,用户提到的是监听画播放结束事件。对于GIF或APNG,通常的做法是使用JavaScript计算帧数或时间,或者利用第三方库来检测结束。但WebP画的情况可能类似吗?WebP画本身是否提供了事件机制呢?可能没有直接的事件,所以需要考虑其他方法。 常见的控制方法包括使用<img>标签加载,但普通的<img>标签无法直接监听画结束。因此可能需要借助其他技术,比如将WebP转换为Canvas来绘制,并在绘制过程中控制画。或者使用Web Animations API,但不确定是否适用于WebP。 另外,用户提供的引用中提到了APNG的缺点,提到可以通过第三方库转换为Canvas来播放和暂停画。这可能是一个线索,即对于WebP,也可以采用类似的方法:将WebP渲染到Canvas上,然后通过Canvas控制画,从而监听结束事件。 具体步骤可能包括:使用Canvas绘制WebP像,逐帧控制,并在最后一帧触发自定义事件。或者使用requestAnimationFrame来检测当前播放时间是否达到总时长,从而判断是否结束。 还需要考虑浏览器的支持情况,比如Canvas的drawImage是否支持WebP画的逐帧绘制。可能需要查阅相关文档或示例。此外,第三方库如libwebp可能提供解码功能,帮助逐帧处理。 总结可能的解决方案:1. 转换为Canvas,手控制播放,监听结束;2. 使用Web Animations API,但可能不直接适用;3. 使用第三方库处理WebP,暴露播放事件。 现在需要验证这些方法的可行性,并给出具体的代码示例。例如,使用Canvas和JavaScript逐帧绘制,计算总帧数或持续时间,然后触发结束事件。或者使用第三方库如webp-hero来解码WebP并控制播放。 最后,确保回答结构清晰,分步骤说明,并引用提供的参考资料中的相关内容,比如APNG通过Canvas控制的部分。</think>### 前端监听WebP播放结束的实现方法 #### 1. **WebP的特性与限制** WebP是一种支持画的现代像格式,具备高压缩率和较好的画质[^2]。然而,与APNG类似,**WebP本身不提供直接的事件监听接口**(如`onend`),需要通过间接方法实现播放结束的检测。 --- #### 2. **实现方案** ##### 方法一:转换为Canvas控制画 通过将WebP渲染到Canvas上,手控制播放并计算结束时机: ```html <canvas id="webpCanvas"></canvas> <script> const canvas = document.getElementById('webpCanvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.src = 'animation.webp'; let startTime; img.onload = () => { canvas.width = img.width; canvas.height = img.height; startTime = Date.now(); animate(); }; function animate() { ctx.drawImage(img, 0, 0); // 假设时长为3秒(需提前获取或约定) if (Date.now() - startTime < 3000) { requestAnimationFrame(animate); } else { console.log('画结束'); // 触发自定义事件 } } </script> ``` **缺点**: 需要手设置画时长,无法态获取实际帧数或时间。 --- ##### 方法二:使用第三方库解码WebP 通过如[`webp-hero`](https://github.com/GoogleChromeLabs/webp-hero)等库解码WebP,逐帧控制播放: ```javascript import { WebPAnimDecoder } from 'webp-hero'; const decoder = new WebPAnimDecoder(); fetch('animation.webp') .then(response => response.arrayBuffer()) .then(buffer => { decoder.decode(buffer).then(animation => { let frameIndex = 0; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); function renderFrame() { if (frameIndex >= animation.frames.length) { console.log('画结束'); return; } const frame = animation.frames[frameIndex]; ctx.drawImage(frame.image, 0, 0); setTimeout(renderFrame, frame.duration); frameIndex++; } renderFrame(); }); }); ``` **优点**: 可精确获取每帧时长和总帧数,态触发结束事件。 --- ##### 方法三:结合`<img>`标签与定时器 若循环次数已知(如仅播放一次),可结合`setTimeout`近似模拟结束: ```javascript const img = document.createElement('img'); img.src = 'animation.webp'; document.body.appendChild(img); // 假设画时长为3秒 setTimeout(() => { console.log('画结束'); }, 3000); ``` **缺点**: 依赖手设置时长,不适用于态内容。 --- #### 3. **关键注意事项** - **时长获取**: 若需态获取WebP画时长,需服务端解析文件头信息或使用前端解码库。 - **兼容性**: 检查浏览器对WebP的支持(如Safari 14+)。 - **性能**: Canvas逐帧渲染可能增加CPU负载,需优化帧率控制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值