【问题标题】:Glide - load single frame from video at specific time?Glide - 在特定时间从视频中加载单帧?
【发布时间】:2015-06-10 20:40:38
【问题描述】:

我正在尝试使用 Glide 逐步浏览视频文件中的帧(不会遇到 Android 遇到的关键帧搜索问题)。我可以在 Picasso 中通过以下方式做到这一点:

picasso = new Picasso.Builder(MainActivity.this).addRequestHandler(new PicassoVideoFrameRequestHandler()).build();
picasso.load("videoframe://" + Environment.getExternalStorageDirectory().toString() +
                    "/source.mp4#" + frameNumber)
                    .placeholder(drawable)
                    .memoryPolicy(MemoryPolicy.NO_CACHE)
                    .into(imageView);

(frameNumber 只是一个 int,每次增加 50000 微秒)。我也有这样的 PicassoVideoFrameRequestHandler:

public class PicassoVideoFrameRequestHandler extends RequestHandler {
public static final String SCHEME = "videoframe";

@Override public boolean canHandleRequest(Request data) {
    return SCHEME.equals(data.uri.getScheme());
}

@Override
public Result load(Request data, int networkPolicy) throws IOException {
    FFmpegMediaMetadataRetriever mediaMetadataRetriever = new FFmpegMediaMetadataRetriever();
    mediaMetadataRetriever.setDataSource(data.uri.getPath());
    String offsetString = data.uri.getFragment();
    long offset = Long.parseLong(offsetString);
    Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(offset, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
    return new Result(bitmap, Picasso.LoadedFrom.DISK);
}

}

我想改用 Glide,因为它可以更好地处理内存。有没有办法在 Glide 中拥有这个功能?

或者,真的,任何其他方式来从视频中创建一组我可以单步执行的帧!

谢谢!

【问题讨论】:

    标签: android picasso android-glide


    【解决方案1】:

    您需要传递“.override(width, height)”才能使 Sam Judd 的方法起作用。否则,您将仅获得视频的第一帧,因为我已经测试了数小时的各种方法。希望它可以为某人节省时间。

    BitmapPool bitmapPool = Glide.get(getApplicationContext()).getBitmapPool();
    int microSecond = 6000000;// 6th second as an example
    VideoBitmapDecoder videoBitmapDecoder = new VideoBitmapDecoder(microSecond);
    FileDescriptorBitmapDecoder fileDescriptorBitmapDecoder = new FileDescriptorBitmapDecoder(videoBitmapDecoder, bitmapPool, DecodeFormat.PREFER_ARGB_8888);
    Glide.with(getApplicationContext())
        .load(yourUri)
        .asBitmap()
        .override(50,50)// Example
        .videoDecoder(fileDescriptorBitmapDecoder)
        .into(yourImageView);
    

    【讨论】:

    • 你真的用 50,50 测试过吗? Override 仅替换 ImageView 大小,因此如果您使用具有您提供的宽度和高度来覆盖的 ImageView,您应该会获得完全相同的效果。
    • 更清楚地说,Glide 很可能从媒体商店加载小尺寸,而不是从它自己的视频解码器。我猜大尺寸会检索您指定的帧,因为它们将使用我们的解码器检索,但小尺寸不会,因为它们将从媒体存储中检索。
    • 我用 Glide 测试了各种尺寸和许多不同的方法,当我不使用覆盖方法时,结果只有第一帧。我不知道它背后的真正原因,因为我从未尝试过深入研究源代码。
    • Glide 的另一个奇怪行为让我在未来离开它使用 FFmpeg 是我无法获得很多帧。我的意思是,即使我为每个调用使用不同的 VideoBitmapDecoders,这种方法也只会给我一帧。你知道为什么吗?是因为 BitmapPool 的 Glides 统一性还是类似的原因?我从未在 Glide 上尝试过自定义 bitmapPools 来查看差异。
    • 感谢它在离线视频中正常工作,但我想使用网络 url 显示缩略图。
    【解决方案2】:

    谢谢,这篇文章帮助我到达那里。顺便说一句,如果您使用 Glide 4.4,它们会改变您获得此结果的方式。从视频 uri 加载特定帧。

    您只需要像这样使用对象 RequestOptions:

    long interval = positionInMillis * 1000;
    RequestOptions options = new RequestOptions().frame(interval);
    Glide.with(context).asBitmap()
                        .load(videoUri)
                        .apply(options)
                        .into(viewHolder.imgPreview);
    

    其中“positionInMillis”是您想要图像的视频位置的长变量。

    【讨论】:

    • 真的很有帮助。谢谢!
    • 尝试使用相同代码的 mp4 服务器 URL 对我不起作用。此外,检查间隔的静态值,如 2,3..等。
    • 我不知道为什么它不适合我
    【解决方案3】:

    您可以将帧时间(以微秒为单位,请参阅MediaMetadataRetriever docs)传递给VideoBitmapDecoder。这是未经测试的,但它应该可以工作:

    BitmapPool bitmapPool = Glide.get(context).getBitmapPool();
    FileDescriptorBitmapDecoder decoder = new FileDescriptorBitmapDecoder(
        new VideoBitmapDecoder(frameTimeMicros),
        bitmapPool,
        DecodeFormat.PREFER_ARGB_8888);
    
    Glide.with(fragment)
        .load(uri)
        .asBitmap()
        .videoDecoder(decoder)
        .into(imageView);
    

    【讨论】:

    • 看起来不错!但是“.videoDecoder”似乎只支持ResourceDecoder,不支持VideoBitmapDecoder...
    • 啊,我错过了包装解码器,我更新了答案以包含正确的答案:bumptech.github.io/glide/javadocs/360/com/bumptech/glide/load/…
    • 谢谢!有什么理由必须是 ARGB_8888 吗?
    • 不,在这种情况下,解码格式被简单地忽略了,因为 MediaMetadataRetriever 无法让我们指定我们想要的格式。
    • 经过更多测试,似乎这种方法除了视频的第一帧外,仍然没有抓取任何内容。在“onResourceReady”回调中,我添加了一个日志来查看发生了什么,如下所示:Log.e("glide", "loaded bitmap of size " + resource.getByteCount() + " (position " + position + ") in " + (System.currentTimeMillis() - time) + "ms"); - 结果如下所示:
    猜你喜欢
    • 2018-11-07
    • 2019-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多