安卓使用VLC播放视频,实现截图和录制功能

这篇具有很好参考价值的文章主要介绍了安卓使用VLC播放视频,实现截图和录制功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

VLC是一款非常强大的开源媒体播放器,由VideoLAN组织开发和维护。它最初是为学校项目开发的,但现在已经成为全球最流行的媒体播放器之一。

VLC具有以下几个主要特点:

  1. 多平台支持:VLC支持几乎所有主流的操作系统,包括Windows、macOS、Linux、iOS和Android。这意味着你可以在几乎任何设备上使用VLC播放媒体。

  2. 多格式支持:VLC支持大量的视频和音频格式,包括MP4, MKV, AVI, MOV, OGG, FLAC, TS, M2TS, WV, AAC等视频格式,MPEG-1/2, MPEG-4, H.263, H.264, H.265/HEVC, VP8, VP9, AV1等视频编码格式,以及MP3, AAC, Vorbis, FLAC, ALAC, WMA, MIDI等音频编码格式。此外,VLC还支持各种网络流格式,如HTTP, RTSP, HLS, Dash, Smooth Streaming等。

  3. 高级功能:除了基本的播放功能,VLC还提供了一系列高级功能,如播放列表管理、音频和视频效果调整、字幕支持、流媒体服务器和客户端、媒体转码等。

  4. 开源和免费:VLC是完全开源的,这意味着任何人都可以查看和修改它的源代码。同时,VLC是完全免费的,没有任何广告或者内购。

VLC的Android库(libVLC)提供了一套完整的API,开发者可以使用这套API在Android应用中播放视频和音频。除了基本的播放控制,libVLC还提供了一些高级功能,如视频滤镜、字幕支持、媒体元数据获取等。

下面,我将介绍在安卓中如何使用VLC库进行播放RTSP视频流。vlc-android github

1.首先,添加VLC库依赖:

在你的Android项目的build.gradle文件中,添加以下依赖项:

dependencies {
    implementation 'org.videolan.android:libvlc-all:4.0.0-eap9'
}

同步项目以导入VLC库。

2.创建一个布局文件:

res/layout目录下创建一个布局文件,例如activity_main.xml,并添加一个TextureView用于播放视频:(为什么使用TextureView?下面会讲到)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

       <TextureView
          android:id="@+id/video_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

</RelativeLayout>

 3.封装一个播放器工具类


/**
 * VLC播放视频工具类
 */
public class VLCPlayer implements MediaPlayer.EventListener{

    private LibVLC libVLC;
    private MediaPlayer mediaPlayer;

    private int videoWidth = 0;  //视频宽度
    private int videoHeight = 0; //视频高度

    public VLCPlayer(Context context) {
        ArrayList<String> options = new ArrayList<>();
        options.add("--no-drop-late-frames"); //防止掉帧
        options.add("--no-skip-frames"); //防止掉帧
        options.add("--rtsp-tcp");//强制使用TCP方式
        options.add("--avcodec-hw=any"); //尝试使用硬件加速
        options.add("--live-caching=0");//缓冲时长

        libVLC = new LibVLC(context, options);
        mediaPlayer = new MediaPlayer(libVLC);
        mediaPlayer.setEventListener(this);
    }

    /**
     * 设置播放视图
     * @param textureView
     */
    public void setVideoSurface(TextureView textureView) {
        mediaPlayer.getVLCVout().setVideoSurface(textureView.getSurfaceTexture());
        mediaPlayer.getVLCVout().setWindowSize(textureView.getWidth(), textureView.getHeight());
        textureView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
                // 获取新的宽度和高度
                int newWidth = right - left;
                int newHeight = bottom - top;
                // 设置VLC播放器的宽高参数
                mediaPlayer.getVLCVout().setWindowSize(newWidth, newHeight);
            }
        });

        mediaPlayer.getVLCVout().attachViews();

    }

    /**
     * 设置播放地址
     * @param url
     */
    public void setDataSource(String url) {
        try {
            Media media = new Media(libVLC, Uri.parse(url));
            mediaPlayer.setMedia(media);
        }catch (Exception e){
            Log.e("VLCPlayer",e.getMessage(),e);
        }
    }

    /**
     * 播放
     */
    public void play() {
        if (mediaPlayer == null) {
            return;
        }
        mediaPlayer.play();
    }

    /**
     * 暂停
     */
    public void pause() {
        if (mediaPlayer == null) {
            return;
        }
        mediaPlayer.pause();
    }

    /**
     * 停止播放
     */
    public void stop() {
        if (mediaPlayer == null) {
            return;
        }
        mediaPlayer.stop();
    }
    

    /**
     * 释放资源
     */
    public void release() {
        if(mediaPlayer!=null) {
            mediaPlayer.release();
        }
        if(libVLC!=null) {
            libVLC.release();
        }
    }


    @Override
    public void onEvent(MediaPlayer.Event event) {
        switch (event.type) {
            case MediaPlayer.Event.Buffering:
                // 处理缓冲事件
                if (callback != null) {
                    callback.onBuffering(event.getBuffering());
                }
                break;
            case MediaPlayer.Event.EndReached:
                // 处理播放结束事件
                if (callback != null) {
                    callback.onEndReached();
                }
                break;
            case MediaPlayer.Event.EncounteredError:
                // 处理播放错误事件
                if (callback != null) {
                    callback.onError();
                }
                break;
            case MediaPlayer.Event.TimeChanged:
                // 处理播放进度变化事件
                if (callback != null) {
                    callback.onTimeChanged(event.getTimeChanged());
                }
                break;
            case MediaPlayer.Event.PositionChanged:
                // 处理播放位置变化事件
                if (callback != null) {
                    callback.onPositionChanged(event.getPositionChanged());
                }
                break;
            case MediaPlayer.Event.Vout:
                //在视频开始播放之前,视频的宽度和高度可能还没有被确定,因此我们需要在MediaPlayer.Event.Vout事件发生后才能获取到正确的宽度和高度
                IMedia.VideoTrack vtrack = (IMedia.VideoTrack) mediaPlayer.getSelectedTrack(Media.Track.Type.Video);
                videoWidth = vtrack.width;
                videoHeight = vtrack.height;
                break;
        }
    }
    

    private VLCPlayerCallback callback;

    public void setCallback(VLCPlayerCallback callback) {
        this.callback = callback;
    }

    public interface VLCPlayerCallback {
        void onBuffering(float bufferPercent);
        void onEndReached();
        void onError();
        void onTimeChanged(long currentTime);
        void onPositionChanged(float position);
    }

}

4.在MainActivity调用

public class MainActivity extends AppCompatActivity implements VLCPlayer.VLCPlayerCallback {
    private static final int REQUEST_STORAGE_PERMISSION = 1;

    private static final String rtspUrl = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";

    private ProgressBar progressBar;

    private TextureView textureView;

    private VLCPlayer vlcPlayer;
    private boolean isRecording;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        textureView = findViewById(R.id.video_view);

        progressBar = findViewById(R.id.progressBar);

        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
                initVLCPlayer();
            }

            @Override
            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

            }
        });

    }

    private void initVLCPlayer() {
        vlcPlayer = new VLCPlayer(this);
        vlcPlayer.setVideoSurface(textureView);
        vlcPlayer.setDataSource(rtspUrl);
        vlcPlayer.setCallback(this);
        vlcPlayer.play();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        vlcPlayer.release();
    }


    @Override
    public void onBuffering(float bufferPercent) {
        if (bufferPercent >= 100) {
            progressBar.setVisibility(View.GONE);
        } else{
            progressBar.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onEndReached() {
        progressBar.setVisibility(View.GONE);
        Toast.makeText(this, "播放结束",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError() {
        progressBar.setVisibility(View.GONE);
        Toast.makeText(this, "播放出错",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onTimeChanged(long currentTime) {

    }

    @Override
    public void onPositionChanged(float position) {

    }

}

好了,现在视频播放功能已经完成了

然而,在实际开发中,可能还会遇到要对视频进行截图和录制的功能,下面一一介绍:

1.视频录制

针对视频录制并保存到本地mp4文件,这个vlc库已经提供了一个MediaPlayer.record()方法。

我们在VLCPlayer添加如下代码

public class VLCPlayer{

    //其他省略
    
    /**
     * 录制视频
     * @param filePath 保存文件的路径
     */
    public boolean startRecording(String filePath) {
        if (mediaPlayer == null) {
            return false;
        }
        return mediaPlayer.record(filePath);
    }

    /**
     * 停止录制
     */
    public void stopRecording() {
        if (mediaPlayer == null) {
            return;
        }
        mediaPlayer.record(null);
    }
}

在MainActivity中申请权限

private static final int REQUEST_STORAGE_PERMISSION = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    requestStoragePermission();
}

private void requestStoragePermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION);
    } else {
        initVLCPlayer();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_STORAGE_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initVLCPlayer();
        } else {
            Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

2.视频截图

在一些老版本中有MediaPlayer.takeSnapshot()方法可直接使用。

在VLC库的最近的版本中,MediaPlayer.takeSnapshot()方法被移除。原因可能是该功能在跨平台场景下的实现存在问题或性能低下,或者这个功能需要重构以适应库的其他更改。

然而,在VLC的新版本(如VLC 4.x)中,已经引入了新的截图API。在这个版本中,你可以使用libvlc_video_take_snapshot()函数截取当前帧并保存为文件。这个函数的签名如下:

int libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, unsigned num, const char *psz_filepath, unsigned int i_width, unsigned int i_height );

该函数接受以下参数:

  • p_mi:一个libvlc_media_player_t指针,指向要截取当前帧的媒体播放器实例。
  • num:要截取的视频轨道的序号。
  • psz_filepath:一个C字符串,指定要保存截图的文件路径。
  • i_width:要保存的截图的宽度。可以设置为0以使用源视频的宽度。
  • i_height:要保存的截图的高度。可以设置为0以使用源视频的高度。

如果库没有提供直接的API,你需要下载vlc源码,自行编译,编写JNI代码来访问底层libVLC库。但请注意,这种方法需要对底层libVLC库以及JNI编程有一定了解。

针对这个方式,我上传了一份源码,里面包含了一个自编译的库支持截图功能:源码

既然新版本它删除了这个方法,另一个解决方案是使用TextureView进行截图。(SurfaceView的双缓冲机制,截不了图)

在VLCPlayer中添加如下代码:

/**
     * 截图保存
     * @param textureView
     */
    public boolean takeSnapShot(TextureView textureView,String path) {
        if(videoHeight == 0 || videoWidth == 0){
            return false;
        }
        Bitmap snapshot = textureView.getBitmap();
        if (snapshot != null) {
            // 获取TextureView的尺寸和视频的尺寸
            int viewWidth = textureView.getWidth();
            int viewHeight = textureView.getHeight();

            // 计算视频在TextureView中的实际显示区域
            float viewAspectRatio = (float) viewWidth / viewHeight;
            float videoAspectRatio = (float) videoWidth / videoHeight;

            int left, top, width, height;
            if (viewAspectRatio > videoAspectRatio) {
                // 视频在TextureView中是上下居中显示的
                width = viewWidth; // 宽度为屏幕宽度
                height = viewWidth * videoHeight / videoWidth; // 计算对应的高度
                left = 0; // 起始位置为左边
                top = (viewHeight - height) / 2; // 计算上边距,保证视频在TextureView中居中
            } else {
                // 视频在TextureView中是左右居中显示的
                width = viewWidth;
                height = viewWidth * videoHeight / videoWidth;
                left = 0;
                top = (viewHeight - height) / 2;
            }

            // 截取视频的实际显示区域
            Bitmap croppedSnapshot = Bitmap.createBitmap(snapshot, left, top, width, height);

            try {
                File snapshotFile = new File(path, "snapshot.jpg");
                FileOutputStream outputStream = new FileOutputStream(snapshotFile);
                croppedSnapshot.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                outputStream.close();

            } catch (IOException e) {
                Log.e("VlcPlayer",e.getMessage(), e);
                return false;
            }
        }
        return true;
    }

里面的videoWidth和videoHeight是我们从MediaPlayer.Event.Vout事件发生后获取到的。

在MainActivity调用:

     @Override
     public void onClick(View view) {
        String sdcardPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();

        switch (view.getId()) {
            case R.id.tv_takesnap:
                new Thread(() -> {
                    vlcPlayer.takeSnapShot(textureView, sdcardPath);
                }).start();
                Toast.makeText(MainActivity.this, "截图完成", Toast.LENGTH_SHORT).show();
                break;

            case R.id.tv_record:
                if (!isRecording) {
                    if (vlcPlayer.startRecording(sdcardPath)) {
                        Toast.makeText(MainActivity.this, "录制开始", Toast.LENGTH_SHORT).show();
                        tvRecord.setText("停止");
                        isRecording = true;
                    }
                } else {
                    vlcPlayer.stopRecording();
                    Toast.makeText(MainActivity.this, "录制结束", Toast.LENGTH_SHORT).show();
                    isRecording = false;
                    tvRecord.setText("录制");
                }
                break;

        }
    }

OK,打完收工。

后续

在使用vlc播放器时,经常会遇到MediaPlayer.stop()方法卡顿或者引起应用崩溃的问题。可能的原因是在调用stop()方法时,视频正在缓冲中。此时调用stop()方法会等待缓冲完成后才停止播放器,如果缓冲时间过长,容易引起卡顿或者ANR。可以使用异步线程来停止播放器,避免在主线程中等待缓冲。文章来源地址https://www.toymoban.com/news/detail-492661.html

new Thread(new Runnable() {
    @Override
    public void run() {
        mMediaPlayer.stop();
    }
}).start();

到了这里,关于安卓使用VLC播放视频,实现截图和录制功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ubuntu软件:录制视频和截图工具,压缩视频

    使用方式: 无需下载 开始录屏/结束录屏:Ctrl + Alt +Shift + r 当看到 Ubuntu 桌面的右上方多了一个红色的小圆点,代表正在录制 注意: 录屏默认的时长30秒,超时会自动结束! 录屏后文件默认存放在主目录内的视频目录中 录屏是直接录制 不能录制声音 临时增加下一次录屏时

    2024年02月06日
    浏览(63)
  • HarmonyOS实战开发-录音机、如何实现音频录制和播放的功能

    本示例使用audio相关接口实现音频录制和播放的功能,使用mediaLibrary实现音频文件的管理。 相关概念: AudioRecorder:音频录制的主要工作是捕获音频信号,完成音频编码并保存到文件中,帮助开发者轻松实现音频录制功能。它允许调用者指定音频录制的采样率、声道数、编码

    2024年04月17日
    浏览(113)
  • gstreamer推流SRT协议视频,VLC播放SRT视频(一)

    Secure Reliable Transport(SRT)是安全、可靠、低延时的多媒体实时传输协议。SRT协议使用AES进行数据加密,运用FEC进行前向纠错,并且有流量控制、拥塞控制。类似于QUIC协议,SRT采用UDP代替TCP,在应用层提供发送确认机制、ARQ自动重传,减少端到端的延迟。 SRT探测实时网络带宽状

    2024年01月22日
    浏览(38)
  • 测试C#调用Vlc.DotNet组件播放视频

      除了Windows Media Player组件,在百度上搜索到还有不少文章介绍采用Vlc.DotNet组件播放视频,关于Vlc.DotNet的详细介绍见参考文献1,本文学习Vlc.DotNet的基本用法。   VS2022中新建基于.net core的winform程序,在Nuget包管理器中搜索Vlc.DotNet,选择其中的Vlc.DotNet.Forms包,该包用于

    2024年02月06日
    浏览(41)
  • 用VLC开发视频播放器/组件(两种方式:libVLC / VLC-Qt)

    MSVC-2015 Qt 5.14.2 QCreator 参考:心流剑 libVLC 各版本 下载链接 我的下载版本为:3.0.11 sdk/lib文件夹目录 qmake vlc 部分的配置(路径根据自己的修改) 只需要 : libvlc.lib、libvlccore.lib 把 plugins 文件夹、libvlc.dll、libvlccore.dll 复制到 bin_Debug / bin_Release VLC-Qt 下载地址 参考链接1:链接

    2024年02月12日
    浏览(92)
  • linux平台vlc无法播放.h265视频解决方法

    PS:发现windows环境下安装的vlc可以直接打开.h265视频,但在linux环境下(本人ubuntu环境)却无法直接打开。 解决办法: 1、点击工具下面的偏好设置 2、选择左下角的“全部”,之后点击“输入/输出编码器”下面的“去复用器”,之后右边的“去复用模块”中选择“HEVC/H.265视

    2024年02月16日
    浏览(42)
  • 基于开源的Micro-RTSP,使用VLC和ffmpeg拉流播放RTSP视频流,本例使用安信可ESP32 CAM进行推流。

    基于开源的Micro-RTSP,使用VLC和ffmpeg拉流播放RTSP视频流,本例使用安信可ESP32 CAM进行推流。 vlc播放命令为:rtsp://192.168.43.128:8554/mjpeg/1。 ffmpeg播放命令为:ffplay rtsp://192.168.43.128:8554/mjpeg/1。 使用ESP-IDF5.0编译成功。esp-idf-v4.4.2编译不成功,有成功的小伙伴可以分享一下。 git cl

    2024年02月01日
    浏览(47)
  • 短视频app开发:如何实现实时短视频录制功能

    在当今的移动互联网时代,短视频app已经成为了人们生活中不可或缺的一部分。短视频app的数量和用户量都在不断增加。如今,越来越多的人开始关注短视频app的开发,尤其是如何实现实时短视频录制功能。本文将分享如何开发短视频app并实现实时短视频录制功能。 短视频

    2023年04月23日
    浏览(37)
  • 基于h5端的视频录制和回溯功能的实现

    出于目前的对保险项目的监管要求,需要对用户操作进行录屏保存,特别是提交资料、文件审核等核心操作。这样在用户与保险公司之间产生纠纷时,就可以有迹可循。市面上有的录屏系统太大并且收费较贵,无法适用于目前我们内嵌于ReactNative 的一个单页h5应用。基于此我

    2024年02月09日
    浏览(40)
  • Vue系列:Vue Element UI中,使用按钮实现视频的播放、停止、停止后继续播放、播放完成后重新播放功能

    最近在工作中有个政务大屏用到了视频播放; 技术栈是Vue2、Element UI; 要实现的功能是:使用按钮实现视频的播放、停止、停止后继续播放、播放完成后重新播放功能 具体可以按照以下步骤进行操作: 引入插件: 在Vue组件中引入Element UI的按钮组件: import { Button } from \\\'ele

    2024年02月04日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包