lsdyna如何设置set中的node_list_如何在Matlab GUI中播放带声音的视频?

992010f939e0b0a3d332f1b836846d96.png

各位同学好,各位知乎的小伙伴们大家好。今天,我要给大家分享的内容是:“如何在Matlab GUI中播放带声音的视频?”

这个问题有点奇怪,什么叫做带声音的视频?视频一般不都是带声音的吗?

没错,一般视频都是带声音的。而且,Matlab是擅长处理视频的,网上找到的教程也确实能够把视频文件读取,处理成一帧一帧的,然后播放出来。但是,问题出在很少有教程把“声音”问题处理好,视频处理成图像逐帧播放却没有声音,让人非常着急。

“没有声音,再好的戏也出不来!”

这个教程会完整地教会大家怎么把视频文件处理好,既能够播放图像,同时也能够播放声音。

我们把整个教程分为六个部分:

(1)读取视频文件(不带声音);

(2)读取视频文件的声音部分;

(3)搭建播放视频的Matlab GUI框架(表面);

(4)实现播放视频的操作;

(5)实现播放声音的操作;

(6)利用跟随变量在一个按钮上实现播放和暂停。

(1)读取视频文件(不带声音)

读取视频文件的标准操作需要用到VideoReader,我们看下面这句代码:

mp4FileName = 'Friends.mp4';
mVideo = VideoReader(mp4FileName);

读取后,mVideo这个结构体包含如下变量:

Duration - 视频的总时长(秒)

FrameRate - 视频帧速(帧/秒)

NumberOfFrames - 视频的总帧数

Height - 视频帧的高度

Width - 视频帧的宽度

BitsPerPixel - 视频帧每个像素的数据长度(比特)

VideoFormat - 视频的类型, 如 'RGB24'.

Tag - 视频对象的标识符,默认为空字符串''

Type - 视频对象的类名,默认为'VideoReader'.

UserData - 用户定义的数据,指定为任意数据类型的值,一般默认为“[ ]”

NumberOfFrames - 视频的总帧数。

宽和高信息对于确定窗口和坐标轴控件的属性是有用的,所以一般我们会有意识地把这个变量提取出来:

vWidth = mVideo.Width;
vHeight = mVideo.Height;
vFrameRate = mVideo.Framerate;

我们还提取了视频帧速这个变量,它可以用来决定播放速度的。

(2)读取视频文件的声音部分

读取视频文件的声音部分需要用到mmread这个工具包,代码示例如下:

[mVar01 mAudio] = mmread(mp4FileName);

这里的问题是mmread不像VideoReader,是Matlab自带的函数,它需要专门做一次“设置路径”的操作。

2e8f9b9f4c6b5e86be964db9f464bdda.png

这个“设置路径”的按钮就在中间的位置,“布局”按钮的右侧,“预设”按钮的下方。点击这个按钮,会弹出一个对话框:

06be3f4833517168e88e16202f229c9b.png

我们采用“添加并包含子文件夹”的方式添加了当前工作目录下面的mmread文件夹,至此,我们就可以利用这个工具包中的mmread函数来读取.mp4文件的音频部分的信息。

(3)搭建播放视频的Matlab GUI框架(表面)

function y = matlabgui_mp4Player(~)

% prepare
mp4FileName = 'Friends.mp4'; 
mVideo = VideoReader(mp4FileName);

% get the info of width and height
vWidth = mVideo.Width * 2; 
vHeight = mVideo.Height * 2;

% create a figure
hFigure = figure(1);
set(hFigure, 'position',[100 50 vWidth vHeight+100]);

% create an axes
hAxes = axes('parent',hFigure);
set(hAxes, 'units','pixels', 'position',[1 101 vWidth vHeight]);

% create a image object, and set its 'parent' property
hImage = image(readFrame(mVideo), 'parent',hAxes);
% 
% % create a pushbutton
hPushbutton = uicontrol(hFigure, 'Style','pushbutton', 'String','Play', 'position',[1 1 vWidth 100]);  

y = hFigure;
end

细心的朋友应该注意到了,我把从视频中获取的高度和宽度都乘以了一个系数2,与此同时,我在设置hFigure的时候,在视频高度的基础上增加了一个100,并且坐标轴的高度也上提了100个像素。前者这么操作是因为我们使用的Friends.mp4文件是压缩过的,如果按照正常的比列进行播放的话会非常小,只有220 x 176。后者操作是为了给hPushbutton这个按钮控件留出空间。我们看下最后的布局效果:

da40d1de02fa886903c210b6eaaf83e7.png

大家对看到的黑色有可能产生两种理解,一种是这个黑色是控件默认的颜色,另外一种是这个黑色是读取的视频的第一帧image到坐标轴上的效果。后面这种猜测才是对的!这里关键是对readframe函数的理解。为了更准确地理解这个函数,我们试着用一个循环,假装运行readframe函数200次试试:

77046e7e93d1f2569c272085eee9557f.png

这就验证了第二种理解是正确的。同时,我们大概能够理解,readframe这个函数,运行一次,某个类似指针的用来标记frame的变量会跳到下一帧。有了这个理解,我们就可以人为地设置一个Timer控件,按照视频自身的每秒帧数的节奏播放画面,就可以达到播放视频的效果。

(4)实现播放视频的操作

因为运行一次readframe,指针就会跳到下一帧,这个就为我们播放视频提供了便利。配合Timer控件的使用,我们可以做到收放自如。先定义一个Timer:

% timer
global t;
timePeriod = round(1/vFrameRate,3);
t = timer('timerFcn', {@timerFcn, mVideo, hImage}, 'ExecutionMode','Fixedrate', 'Period',timePeriod);

然后要专门写一个它的回调函数timerFcn:

% function --> timerFcn
    function timerFcn(obj, ~, v, hIM)
        if hasFrame(v)
            set(hIM,'cdata',readFrame(v));
        else
            stop(obj);
        end
    end

在定义Timer控件的时候,可以利用元胞格式{},往timerFcn函数中传入视频变量(mVideo是实参,v是形参),还有图层句柄(hImage是实参,hIM是形参)。呈现图片用的是set函数,set(hIM,'cdata',readFrame(v))修改hImage句柄的cdata参数就可以把读取的那一帧图像呈现在图层上。

这里用了一个if判断语句,如果还有帧的话,就执行呈现图片的操作;如果没有的话(hasframe函数是用来确定视频变量中的帧是否可供读取),就把obj给停止了。这里的obj就是这个函数的源对象,也就是Timer控件t。因为t是全局变量,所以obj也可以写成stop(t)效果应该是一样的。

(5)实现播放声音的操作

之前通过mmread读取了mp4文件:

[mVar01 mAudio] = mmread(mp4FileName);

但是,这样是不能播放声音的,还需要打造一个mp3player对象:

global mp3player;
mp3player = audioplayer(mAudio.data, mAudio.rate, mAudio.bits);

有了这个对象之后,不管是播放play,终止stop,暂停pause,还是恢复播放resume,都可以直接运用在这个对象上。比如,

play(mp3player);

就可以开始播放当前的mp3,也就是从视频中读取的音频部分。

(6)利用跟随变量在一个按钮上实现播放和暂停

作为标题,这么描述有点过于简单了,实际上整个播放包括一开始的启动play,暂停pause,恢复resume和退出后的停止stop。我们会引入一个全局变量叫做mState,它可以标记当前的状态是初始状态0,播放状态2,抑或是暂停状态1,直接粘贴完整版代码(想象一下这时候是有BGM的):

function y = matlabgui_mp4Player(~)

% prepare
mp4FileName = 'Friends.mp4';

% read the video and audio via VideoReader and mmread
mVideo = VideoReader(mp4FileName);
[mVar01 mAudio] = mmread(mp4FileName);

% get the info. from mVideo
vWidth = mVideo.width * 2;
vHeight = mVideo.height * 2;
vFrames = mVideo.NumberOfFrames;
vFrameRate = mVideo.Framerate;

% prepare mp3player obj
global mp3player;
mp3player = audioplayer(mAudio.data, mAudio.rate, mAudio.bits);

% create a figure
hFigure = figure(1);
set(hFigure, 'position',[100 50 vWidth vHeight + 100]);
% 
% create an axes
hAxes = axes('parent',hFigure);
set(hAxes, 'units','pixels', 'position',[1 101 vWidth vHeight]);

% create a image object, and set its 'parent' property
hImage = image(readFrame(mVideo), 'parent',hAxes);
% 
% % create a pushbutton
hPushbutton = uicontrol(hFigure, 'Style','pushbutton', 'String','Play', 'position',[1 1 vWidth 100]);   % , 
 
% timer
global t;
timePeriod = round(1/vFrameRate,3);
t = timer('timerFcn', {@timerFcn, mVideo, hImage}, 'ExecutionMode','Fixedrate', 'Period',timePeriod);

%
global mState; 
mState = 0; % 0 == original vs. 1 == pause vs. 2 == play

% assign y
y = hFigure;

% % Binding  Mechanism
set(hPushbutton,'Callback',@hPushbutton_CallbackFcn);
set(hFigure, 'DeleteFcn',@hFigure_DeleteFcn);
% 
% function --> hPushbutton_CallbackFcn
    function hPushbutton_CallbackFcn(obj, event, hs)
        if mState == 0
            start(t);
            play(mp3player);
            set(obj, 'String','Pause');
            mState = 2;
            
        else
            if mState == 1
                start(t);
                resume(mp3player);
                set(obj, 'String','Pause');
                mState = 2;
            else
                stop(t);
                pause(mp3player);
                set(obj, 'String','Resume');
                mState = 1;
            end
            
        end
     end
 
% function --> timerFcn
    function timerFcn(obj, ~, v, hIM)
        if hasFrame(v)
            set(hIM,'cdata',readFrame(v))
        else
            stop(obj);
        end
    end

% 
% % function --> hFigure_DeleteFcn
    function hFigure_DeleteFcn(obj, event, hs)
            stop(t); delete(t); 

            stop(mp3player);
    end

end

补充一些细节:

(1)一旦我们设定了一个Timer控件,就要记得在退出窗口的时候,把这个timer对象停止并删除。

(2)退出窗口的时候,同样要记得停止mp3player对象,否则窗口关闭之后,声音会一直播放感觉很奇怪。

(3)mState实际上是3种状态,一开始为0,点击按钮就开始播放;播放时为2,点击按钮会出现暂停;进入暂停状态之后为1,点击按钮继续播放。

(4)image和imshow都可以用来把图像矩阵显示在坐标轴上,这里用了image是因为两个原因。第一,它在性能方面是优于imshow的,在这种连续播放图像帧的操作中,肯定会选择性能更优的image函数;第二,是因为image有一个自适应的缩放功能,hAxes在定义时用的是图像矩阵的两倍宽和高,image自动把图像矩阵放大了呈现在坐标轴上。

(5)VideoReader类中的readframe这个操作在2018b版本是会运行报错的,原因是我的这个版本的代码中有一句是这么写的:

vFrames = mVideo.NumberOfFrames;

然后就会报错:

错误使用 VideoReader/readFrame (line 99)
不能在查询 NUMBEROFFRAMES 属性或使用 READ 方法后调用
'READFRAME' 方法。

这里有两个建议,一个是采用2019b及以上版本,另外一个是注释掉“vFrames = mVideo.NumberOfFrames;”的语句即可。

(6)还有一个很小的细节,就是我们的mp4文件名不能带中文,不然会报错。

到这里,整个教程就写完了。国庆节&中秋节快乐♥

感谢李昌锦和杨轩两位同学的卓越贡献,mmread工具包就是他们找到的。多亏了他们,要不然也不太可能有这么完整的一个教程,♥谢谢谢谢♥

.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值