草庐IT

基于声网 SDK 实现 iOS 端的一对一视频通话

茶陵後 2023-05-30 原文

在很多产品,加入实时视频通话已经不是新鲜事情了,尤其是近几年的疫情影响,个人公司国家对于实时音视频的需求一直在快速的增长。例如视频会议、社交应用、实时游戏,甚至也可能出现在一些元宇宙的场景中。

本文将教你如何通过声网视频 SDK 在 iOS 平台上实现一个视频通话应用。为此你需要先注册一个声网开发者账号,开发者每个月可获得 10000 分钟的免费使用额度,可实现各类实时音视频场景。

一、 通过开源Demo,体验视频通话

可能有些人,还不了解我们要实现的功能最后是怎样的。所以我们在 GitHub上提供一个开源的基础视频通话示例项目,在开始开发之前你可以通过该示例项目体验视频通话的体验效果。

声网在 https://github.com/AgoraIO/Basic-Video-Call/tree/master/One-to-One-Video 上提供开源的实时音视频通话示例项目 Agora-iOS-Tutorial-Objective-C-1to1 与 Agora-iOS-Tutorial-Swift-1to1。

二、 视频通话的技术原理

我们在这里要实现的是一对一的视频通话。你可以理解为是两个用户通过加入同一个频道,实现的音视频的互通。而这个频道的数据,会通过声网的 SD-RTN 实时网络来进行低延时传输的。
下图展示在 App 中集成声网视频通话的基本工作流程:

如图所示,实现视频通话的步骤如下:

  1. 获取 Token:当 app 客户端加入频道时,你需要使用 Token 验证用户身份。在测试或生产环境中,从 app 服务器中获取 Token。
  2. 加入频道:调用 joinChannel 创建并加入频道。使用同一频道名称的 app 客户端默认加入同一频道。频道可理解为专用于传输实时音视频数据的通道。
  3. 在频道内发布和订阅音视频流:加入频道后,app 客户端均可以在频道内发布和订阅音视频。

App 客户端加入频道需要以下信息:

  1. App ID:声网随机生成的字符串,用于识别你的 App,可从声网控制台获取,(声网控制台链接:https://console.agora.io/)。详细方法可见这篇教程(这篇教程链接:https://www.agora.io/cn/community/blog-119-category-21344)。
  2. 用户 ID:用户的唯一标识。你需要自行设置用户 ID,并确保它在频道内是唯一的。
  3. Token:在测试或生产环境中,app 客户端从你的服务器中获取 Token。在本文介绍的流程中,你可以从声网控制台获取临时 Token。临时 Token 的有效期为 24 小时。
  4. 频道名称:用于标识视频通话频道的字符串。

三、 开发环境

声网 SDK 的兼容性良好,对硬件设备和软件系统的要求不高,开发环境和测试环境满足以下条件即可:

  • Xcode 9.0或以上版本
  • 支持语音和视频功能的真机
  • App 要求支持iOS 8.0或以上版本的iOS设备

以下是本文的开发环境和测试环境:

开发环境

• macOS 11.6版本
• Xcode Version 13.1

测试环境

• iPhone7 (iOS 15.3)

如果你此前还未接触过声网 SDK,那么你还需要做以下准备工作:

• 注册一个声网账号,进入后台创建 AppID、获取 Token,
• 下载声网官方最新的视频通话SDK;(视频通话SDK链接:https://docs.agora.io/cn/Video/downloads?platform=iOS)

四、 项目设置

1. 实现视频通话之前,参考如下步骤设置你的项目:

a) 如需创建新项目, Xcode里,打开 Xcode 并点击 Create a new Xcode project。(创建 iOS项目链接:https://developer.apple.com/documentation/xcode/creating-an-xcode-project-for-an-app)
b) 选择平台类型为 iOS、项目类型为 Single View App,并点击 Next。
c) 输入项目名称(Product Name)、开发团队信息(Team)、组织名称(Organization Name)和语言(Language)等项目信息,并点击 Next。
注意:如果你没有添加过开发团队信息,会看到 Add account… 按钮。点击该按钮并按照屏幕提示登入 Apple ID,完成后即可选择你的 Apple 账户作为开发团队。
d) 选择项目存储路径,并点击 Create。

2. 集成SDK

选择如下任意一种方式获取最新版声网 iOS SDK。

方法一:使用 CocoaPods 获取 SDK
a) 开始前确保你已安装 Cocoapods。参考 Getting Started with CocoaPods 安装说明。(Getting Started with CocoaPods 安装说明链接:https://guides.cocoapods.org/using/getting-started.html#getting-started)
b) 在终端里进入项目根目录,并运行 pod init 命令。项目文件夹下会生成一个 Podfile 文本文件。
c) 打开 Podfile 文件,修改文件为如下内容。注意将 Your App 替换为你的 Target 名称。

方法二:从官网获取 SDK
a) 前往 SDK 下载页面,获取最新版的声网 iOS SDK,然后解压。(视频通话SDK链接:https://docs.agora.io/cn/Video/downloads?platform=iOS)
b) 根据你的需求,将 libs 文件夹中的动态库复制到项目的 ./project_name 文件夹下(project_name 为你的项目名称)。
c) 打开 Xcode,进入 TARGETS > Project Name > Build Phases > Link Binary with Libraries 菜单,点击 + 添加如下库(如:)。在添加 AgoraRtcEngineKit.framework 文件时,还需在点击 + 后点击 Add Other…,找到本地文件并打开。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMM224bm-1663063848743)(upload://gOFc9CzhWzmOk7Ef4Dh0QVyu5kp.png)]

共需要添加11个库文件:
i. AgoraRtcEngineKit.framework
ii. Accelerate.framework
iii. AudioToolbox.framework
iv. AVFoundation.framework
v. CoreMedia.framework
vi. CoreML.framework
vii. CoreTelephony.framework
viii. libc++.tbd
ix. libresolv.tbd
x. SystemConfiguration.framework
xi. VideoToolbox.framework
注意: 如需支持 iOS 9.0 或更低版本的设备,请在 Xcode 中将对 CoreML.framework 的依赖设为 Optional。

d) 打开 Xcode,进入 TARGETS > Project Name > General > Frameworks, Libraries, and Embedded Content 菜单。
e) 点击 + > Add Other… > Add Files 添加对应动态库,并确保添加的动态库 Embed 属性设置为 Embed & Sign。添加完成后,项目会自动链接所需系统库。

注意:

  • 根据 Apple 官方要求,app 的 Extension 中不允许包含动态库。如果项目中的 Extension 需要集成 SDK,则添加动态库时需将文件状态改为 Do Not Embed。
  • 声网 SDK 默认使用 libc++ (LLVM),如需使用 libstdc++ (GNU),请联系 sales@agora.io。SDK 提供的库是 FAT Image,包含 32/64 位模拟器、32/64 位真机版本。

3. 权限设置

  1. Xcode进入 TARGETS > Project Name > General > Signing 菜单,选择 Automatically manage signing,并在弹出菜单中点击 Enable Automatic。

  2. 添加媒体设备权限
    根据场景需要,在 info.plist 文件中,点击 + 图标开始添加如下内容,获取相应的设备权限:

4. 导入声网相关的类

在项目中导入 AgoraRtcEngineKit 类:

// Objective-C
// ViewController.h
// 导入 AgoraRtcKit 类
// 自 3.0.0 版本起,AgoraRtcEngineKit 类名更换为 AgoraRtcKit
// 如果获取的是 3.0.0 以下版本的 SDK,请改用 #import <AgoraRtcEngineKit/AgoraRtcEngineKit.h>
#import <AgoraRtcKit/AgoraRtcEngineKit.h>
// 声明 AgoraRtcEngineDelegate,用于监听回调
@interface ViewController : UIViewController <AgoraRtcEngineDelegate>
// 定义 agoraKit 变量
@property (strong, nonatomic) AgoraRtcEngineKit *agoraKit;



// Swift
// ViewController.swift
// 导入 AgoraRtcKit 类
// 自 3.0.0 版本起,AgoraRtcEngineKit 类名更换为 AgoraRtcKit
// 如果获取的是 3.0.0 以下版本的 SDK,请改用 import AgoraRtcEngineKit
import AgoraRtcKit
class ViewController: UIViewController {
    ...
    // 定义 agoraKit 变量
    var agoraKit: AgoraRtcEngineKit?
}

5. 设置声网账号信息

在AppID.swift文件中,将你的AppID填写到let AppID,可替换“Your App ID”;

//  Objective-C
//  AppID.m
//  Agora iOS Tutorial Objective-C
#import <Foundation/Foundation.h>
NSString *const appID = <#Your App ID#>;

//  Swift
// AppID.swift
//  Agora iOS Tutorial
let AppID: String = Your App ID

五、 客户端实现

本节介绍如何使用声网视频SDK在你的App里实现视频通话的几个小贴士:

1. 创建用户界面

根据场景需要,为你的项目创建视频通话的用户界面。我们推荐你在项目中添加元素:本地视频窗口、远端视频窗口。
你可以参考以下代码创建一个基础的用户界面。

// Objective-C
// ViewController.m
// 导入 UIKit
#import <UIKit/UIKit.h>
@interface ViewController ()
// 定义 localView 变量
@property (nonatomic, strong) UIView *localView;
// 定义 remoteView 变量
@property (nonatomic, strong) UIView *remoteView;
@end
@implementation ViewController
...
- (void)viewDidLoad {
    [super viewDidLoad];
    // 调用初始化视频窗口函数
    [self initViews];
    // 后续步骤调用声网 API 使用的函数
    [self initializeAgoraEngine];
    [self setupLocalVideo];
    [self joinChannel];
}
// 设置视频窗口布局
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.remoteView.frame = self.view.bounds;
    self.localView.frame = CGRectMake(self.view.bounds.size.width - 90, 0, 90, 160);
}
- (void)initViews {
    // 初始化远端视频窗口
    self.remoteView = [[UIView alloc] init];
    [self.view addSubview:self.remoteView];
    // 初始化本地视频窗口
    self.localView = [[UIView alloc] init];
    [self.view addSubview:self.localView];
}

// Swift
// ViewController.swift
// 导入 UIKit
import UIKit
class ViewController: UIViewController {
    ...
    // 定义 localView 变量
    var localView: UIView!
    // 定义 remoteView 变量
    var remoteView: UIView!
     override func viewDidLoad() {
        super.viewDidLoad()
        // 调用初始化视频窗口函数
        initView()
        // 后续步骤调用声网 API 使用的函数
        initializeAgoraEngine()
        setupLocalVideo()
        joinChannel()
     }
    // 设置视频窗口布局
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        remoteView.frame = self.view.bounds
        localView.frame = CGRect(x: self.view.bounds.width - 90, y: 0, width: 90, height: 160)
    }
    func initView() {
        // 初始化远端视频窗口
        remoteView = UIView()
        self.view.addSubview(remoteView)
        // 初始化本地视频窗口
        localView = UIView()
        self.view.addSubview(localView)
    }
}

2. 实现视频通话逻辑

现在,我们已经将声网 iOS SDK 集成到项目中了。接下来我们要在 ViewController 中调用声网 iOS SDK 提供的核心 API 实现基础的视频通话功能。你可以在 Agora-iOS-Tutorial-Objective-C-1to1/Agora-iOS-Tutorial-Swift-1to1 示例项目的 VideoChatViewController.m/VideoChatViewController.swift 文件中查看完整的源码和代码逻辑。

API 调用时序见下图:

按照以下步骤实现该逻辑:

a) 初始化AgoraRtcEngineKit对象
在调用其他声网 API 前,需要创建并初始化 AgoraRtcEngineKit 对象。调用 sharedEngineWithAppId 方法,传入获取到的 App ID,即可初始化 AgoraRtcEngineKit 。

// Objective-C
- (void)initializeAgoraEngine {
    // 输入 App ID 并初始化 AgoraRtcEngineKit 类。
    self.agoraKit = [AgoraRtcEngineKit sharedEngineWithAppId:appID delegate:self];
}

// Swift
func initializeAgoraEngine() {
   // 输入 App ID 并初始化 AgoraRtcEngineKit 类。
   agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: AppID, delegate: self)
}

你还可以根据场景需要,在初始化时注册想要监听的回调事件,如本地用户加入频道,及解码远端用户视频首帧等。

b) 设置本地视图
成功初始化 AgoraRtcEngineKit 对象后,需要在加入频道前设置本地视图,以便在通话中看到本地图像。参考以下步骤设置本地视图:
· 调用 enableVideo 方法启用视频模块。
· 调用 setupLocalVideo 方法设置本地视图。

// Objective-C
// 启用视频模块。
[self.agoraKit enableVideo];
- (void)setupLocalVideo {
    AgoraRtcVideoCanvas *videoCanvas = [[AgoraRtcVideoCanvas alloc] init];
    videoCanvas.uid = 0;
    videoCanvas.view = self.localVideo;
    videoCanvas.renderMode = AgoraVideoRenderModeHidden;
    // 设置本地视图。
    [self.agoraKit setupLocalVideo:videoCanvas];
}

// Swift
// 启用视频模块。
agoraKit.enableVideo()
func setupLocalVideo() {
  let videoCanvas = AgoraRtcVideoCanvas()
  videoCanvas.uid = 0
  videoCanvas.view = localVideo
  videoCanvas.renderMode = .hidden
  // 设置本地视图。
  agoraKit.setupLocalVideo(videoCanvas)
}

c) 加入频道
频道是人们在同一个视频通话中的公共空间。完成初始化和设置本地视图后(视频通话场景),你就可以调用 joinChannelByToken 方法加入频道。你需要在该方法中传入如下参数:

  1. channelId: 传入能标识频道的频道 ID。输入频道 ID 相同的用户会进入同一个频道。
  2. token: 传入能标识用户角色和权限的 Token。你可以设置如下值:
    a) nil 。
    b) 控制台中生成的临时 Token。一个临时 Token 的有效期为 24 小时,详情见获取临时 Token。
    c) 你的服务器端生成的正式 Token。适用于对安全要求较高的生产环境,详情见生成 Token。若项目已启用 App 证书,请使用 Token。
    d) uid: 本地用户的 ID。数据类型为整型,且频道内每个用户的 uid 必须是唯一的。若将 uid 设为 0,则 SDK 会自动分配一个 uid ,并在 joinSuccessBlock 回调中报告。
    e) joinSuccessBlock:成功加入频道回调。 joinSuccessBlock 优先级高于 didJoinChannel ,2 个同时存在时, didJoinChannel 会被忽略。需要有 didJoinChannel 回调时,请将 joinSuccessBlock 设置为 nil 。

更多的参数设置注意事项请参考 joinChannelByToken 接口中的参数描述。

// Objective-C
- (void)joinChannel {
    // 加入频道。
    [self.agoraKit joinChannelByToken:token channelId:@"demoChannel1" info:nil uid:0 joinSuccess:^(NSString *channel, NSUInteger uid, NSInteger elapsed) {
    }];
}

// Swift
func joinChannel() {
    // 加入频道。
    agoraKit.joinChannel(byToken: Token, channelId: "demoChannel1", info:nil, uid:0) { [unowned self] (channel, uid, elapsed) -> Void in}
    self.isLocalVideoRender = true
            self.logVC?.log(type: .info, content: "did join channel")
        }
        isStartCalling = true
}

d) 设置远端视图
视频通话中,通常你也需要看到其他用户。在加入频道后,可通过调用 setupRemoteVideo 方法设置远端用户的视图。

远端用户成功加入频道后,SDK 会触发 firstRemoteVideoDecodedOfUid 回调,该回调中会包含这个远端用户的 uid 信息。在该回调中调用setupRemoteVideo 方法,传入获取到的 uid ,设置远端用户的视图。


// Objective-C
// 监听 firstRemoteVideoDecodedOfUid 回调。
// SDK 接收到第一帧远端视频并成功解码时,会触发该回调。
// 可以在该回调中调用 setupRemoteVideo 方法设置远端视图。
- (void)rtcEngine:(AgoraRtcEngineKit *)engine firstRemoteVideoDecodedOfUid:(NSUInteger)uid size: (CGSize)size elapsed:(NSInteger)elapsed {
    if (self.remoteVideo.hidden) {
        self.remoteVideo.hidden = NO;
    }
    AgoraRtcVideoCanvas *videoCanvas = [[AgoraRtcVideoCanvas alloc] init];
    videoCanvas.uid = uid;
    videoCanvas.view = self.remoteVideo;
    videoCanvas.renderMode = AgoraVideoRenderModeHidden;
    // 设置远端视图。
    [self.agoraKit setupRemoteVideo:videoCanvas];
}

// Swift
// 监听 firstRemoteVideoDecodedOfUid 回调。
// SDK 接收到第一帧远端视频并成功解码时,会触发该回调。
// 可以在该回调中调用 setupRemoteVideo 方法设置远端视图。
func rtcEngine(_ engine: AgoraRtcEngineKit, firstRemoteVideoDecodedOfUid uid:UInt, size:CGSize, elapsed:Int) {
        isRemoteVideoRender = true
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = uid
        videoCanvas.view = remoteVideo
        videoCanvas.renderMode = .hidden
        // 设置远端视图。
        agoraKit.setupRemoteVideo(videoCanvas)
    }

e) 离开频道
根据场景需要,如结束通话、关闭 App 或 App 切换至后台时,调用 leaveChannel 离开当前通话频道。

// Objective-C
- (void)leaveChannel {
    // 离开频道。
    [self.agoraKit leaveChannel:^(AgoraChannelStats *stat)
}

// Swift
func leaveChannel() {
        // 离开频道。
        agoraKit.leaveChannel(nil)
        isRemoteVideoRender = false
        isLocalVideoRender = false
        isStartCalling = false
        self.logVC?.log(type: .info, content: "did leave channel")
    }

f) 销毁AgoraRtcEngineKit对象
最后,离开频道,我们需要调用 destroy 销毁 AgoraRtcEngineKit 对象,释放声网 SDK 使用的所有资源。

// Objective-C
// ViewController.m
// 将以下代码填入你定义的函数中
[AgoraRtcEngineKit destroy];

// Swift
// ViewController.swift
// 将以下代码填入你定义的函数中
AgoraRtcEngineKit.destroy()

至此,完成,运行看看效果。拿两部 iOS 手机安装编译好的App,加入同一个频道名,如果2个手机都能看见本地和远端视频图像,说明你成功了。

如果你在开发过程中遇到问题,可以访问论坛提问与声网工程师交流https://rtcdeveloper.agora.io/
也可以访问后台获取更进一步的技术支持 https://sso2.agora.io/cn/v5/login/with-email

有关基于声网 SDK 实现 iOS 端的一对一视频通话的更多相关文章

  1. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  9. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  10. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

随机推荐