Linux光盘刻录
创始人
2024-11-14 13:05:30
0

一、文档保存

在文档的持久化保存中,刻录光盘原来是很常见的方法。不过随着技术的演进,云端存储和U盘等技术的快速发展,目前已经用得不多了。但在某些特定行业下,还是有需要使用光盘刻录的情况。本文将以Linux环境为基础,分析举例一下如何通过程序来处理光盘刻录的问题。

二、光盘存储一些基础技术

在进行光驱刻录光盘以前,需要知道一些基础的入门级的光盘存储的技术相关。光盘的存储和硬盘存储类似,也要划分基础物理存储单元,扇区(Sector,机械硬盘也有这个概念);它和硬盘一样,也是通过一圈圈的环形路径来读取光盘的。而这样的一个圆形的路径,被称为轨道(Track)。为了能够抽象的理解刻录,每次刻录会生成一个会话(Session),它是光盘上的一个逻辑刻录单元。每一次的记录动作,都是一个新的Session.
光盘从产生到现在,有很多种类,大致可以分为以下几类:
1、CD光盘
这也是最早出现的光盘,主要是用来存储多媒体的容易一般较小(700M左右)。它一般有只读型CD,一次写入CD和多次擦写CD。

2、DVD
它可以分成单层和双层,单层的容量大小约在4.7G左右,双层的容量大小约在8.5G左右。它也分成只读型DVD,一次性写入DVD(又分为DVD-R,DVD+R)以及可擦写型DVD(DVD-RW,DVD+RW)。
3、Blu-ray Disc(蓝光光盘)
主打就是一个容量大,高清电影分发。单层容量25G,双层容量50G。
4、Mini光盘
这个一般用在比较特殊的场合。打开光驱可以看到光盘的托盘上往往有两个圈,内部的小圈儿就可以读写这种小光盘。

三、刻录方法

对光盘的写入,一般需要第三方库的支持。如果单纯只是使用文件系统进行光盘的记录,虽然在个别情况下可以达到目的,但应用起来不灵活,也不方便。本文介绍两种实现方法,一种是使用命令方式;另外一种是使用引三方库。
1、使用growisofs命令
首先需要安装相关软件命令:

sudo apt-get update sudo apt-get -y install growisofs #Debian中可能需要安装genisoimage 

它的用法也比较简单:

- usage: growisofs [-dvd-compat] [-overburn] [-speed=1] \          -[ZM] /dev/dvd    for  see 'genisoimage -help' 

常用的命令选项如下:

-Z:指定目标设备和文件。这个参数告诉growisofs要刻录的设备和要使用的ISO文件或目录。 -R: 用于支持 UNIX 文件系统的长文件名和权限 -M 添加一个新的Session, 选项来避免关闭会话,从而在后续操作中保持会话打开 -J: 用于支持 Windows 文件系统的长文件名。 -dvd-compat:这个选项用于确保刻录的光盘与尽可能多的DVD播放器兼容 -speed:指定刻录速度。例如,-speed=4x将限制刻录速度为4倍速。 -data:这个参数表明正在刻录的是数据光盘。 -iso-level:设定ISO9660的等级,一般使用1或2。 -Joliet:启用Joliet扩展,允许使用长文件名和Unicode字符。 -udf:启用UDF文件系统支持,这对于大容量DVD-RW和BD-RW光盘尤其有用。 -append:用于向已存在的光盘会话追加数据。 -track:指定要刻录的轨道类型,例如数据或音频。 -session:控制会话的数量和类型。 -dao:表示要进行“一次写入”,即数据区域和尾部一起刻录。 -close:关闭当前的会话,但光盘仍保持可追加的状态。 -overburn:允许超量刻录,但这可能导致光盘在某些驱动器上无法读取。 -v:显示详细的刻录信息,便于调试。 -quiet:减少输出,仅显示关键信息。 

这里需要注意的是,对-Z和-M的使用,第一次使用前者,如果想不断追加可使用后者。一般使用的命令行如下:

# 一般光驱为/dev/cdrom,USB光驱一般为/dev/srx(x为数字序号) # 初始化写入:其的写入可为目录,也可以为文件,可写入多个 growisofs -Z /dev/sr0 -R -J /home/users/file1 /home/users/   # 追加写入   growisofs -M /dev/sr0 -R -J /home/users/file 

2、使用libburn第三方库
安装相关开发库的命令:

sudo apt-get install libburn-dev 

如果没有libburn-dev,也可以直接安装libburn即可,这和具体的OS有关系。libburn是为了写入ISO文件设计的,虽然在文档中其也可以写入相关的其它文件,但一直未测试成功非ISO文件的写入。或者说写入后,未成功读出,所以没办法给一个此类的例程。所以可以把非ISO的相关文件转成ISO,命令如下:

mkisofs -o mydata.iso -R /path/to/MyData 

再使用此库即可。

四、例程

看一下相关的例程:

//h #include  #include  #include  #include  #include  #include  using CALLFUNC = std::function; class BurnArchiveFile { public:   BurnArchiveFile();   ~BurnArchiveFile();  public:   void initBurnLib(CALLFUNC func);   bool writeArchiveFile(const std::string &fileOrpathName, int num = 0);   bool writeArchinveFile(const std::vector &fileOrpathNameList);   void cancel();  private:   long long getTotalFileSize(const std::string &path);   bool burnFile(const std::string &cmd);   bool burnFilePopen(const std::string &cmd);  private:   std::string m_addParameter = "";   std::string m_initParameter = "";   std::string m_burnCmd = "growisofs";   std::atomic_bool m_cancel = false;   CALLFUNC m_burnProgress = nullptr;   long long m_totalFileSize = 0;   std::shared_ptr m_burnHandle = nullptr;   std::shared_ptr m_progressHandle = nullptr; };  //cpp #include "BurnArchiveFile.h" #include  #include  #include  #include  #include  #include  #include   namespace fs = std::filesystem; long long kKB = 1024; const long long kMUL = 4; const long long kMAX_SIZE = kMUL * kKB * kKB * kKB; const double kSPEED_BURN = 8.2 * 1352;  BurnArchiveFile::BurnArchiveFile() {} BurnArchiveFile::~BurnArchiveFile() {   if (this->m_burnHandle != nullptr && this->m_burnHandle->joinable()) {     this->m_burnHandle->join();   }   if (this->m_progressHandle != nullptr && this->m_progressHandle->joinable()) {     this->m_progressHandle->join();   } } void BurnArchiveFile::initBurnLib(CALLFUNC func) {   this->m_burnProgress = func;   std::string cdDevice = "/dev/cdrom";    // set parameter   // burn speed ,Ex:24x   std::string compatFlag = " "; // "-dvd-compat";   std::string devInitFlag = "-Z";   std::string devAddFlag = "-M";   std::string blank = "  ";   std::string speedFlag = "-speed=8x";   std::string filenameFlag = "-R -J";    this->m_initParameter = blank + compatFlag + blank + devInitFlag + blank + cdDevice + blank + filenameFlag + blank;   this->m_addParameter = blank + compatFlag + blank + devAddFlag + blank + cdDevice + blank + filenameFlag + blank; } bool BurnArchiveFile::writeArchiveFile(const std::string &fileOrpathName, int num) {   bool bRet = false;    if (this->getTotalFileSize(fileOrpathName) > kMAX_SIZE) {     std::cerr << "The burned file is too large!" << std::endl;     return bRet;   } else {     this->m_burnProgress(10, "File size calculation completed!");   }   if (num == 0) {     this->m_burnCmd = this->m_burnCmd + this->m_initParameter + fileOrpathName;   } else {     this->m_burnCmd = this->m_burnCmd + this->m_addParameter + fileOrpathName;   }    this->m_burnHandle = std::make_shared([&] { bRet = this->burnFile(m_burnCmd); });   this->m_progressHandle = std::make_shared([&] {     double progeress = 0.0;     int count = 1;     std::cerr << "cur run:" << m_cancel << std::endl;     while (!m_cancel) {       sleep(1);       progeress = kSPEED_BURN * count++ / m_totalFileSize;       progeress *= 100 * 1024;        if (progeress > 1 && progeress < 60) {         this->m_burnProgress(progeress + 10, "cur progress");       } else {         return;       }     }   });    return bRet; } bool BurnArchiveFile::writeArchinveFile(const std::vector &fileOrpathNameList) {   bool bRet = false;   std::size_t count = fileOrpathNameList.size();   double per = 0.0f;   std::string burnFile = "";   for (int num = 0; num < count; num++) {     auto fileSize = this->getTotalFileSize(fileOrpathNameList[num]);     if (fileSize < 0) {       return bRet;     }      this->m_totalFileSize += fileSize;     burnFile += fileOrpathNameList[num] + "  ";   }    if (this->m_totalFileSize > kMAX_SIZE) {     std::cerr << "The burned file is too large!" << std::endl;     return bRet;   } else {     this->m_burnProgress(5, "File size calculation completed!");   }    bRet = this->writeArchiveFile(burnFile, 0);    return bRet; } bool BurnArchiveFile::burnFile(const std::string &cmd) {   int iRet = 0;   std::cerr << "run burn cmd:" << cmd << std::endl;   iRet = system(cmd.c_str());    m_cancel = true;   this->m_burnProgress(100, "end");   if (iRet == 0) {     return true;   }    return false; } bool BurnArchiveFile::burnFilePopen(const std::string &cmd) {   FILE *pipe;   char buffer[128];   char *buf = nullptr;   size_t len = 0;    pipe = popen(cmd.c_str(), "r");   if (!pipe) {     perror("pipe open err!");     return false;   }    while (!feof(pipe)) {     /*if (fgets(buffer, 128, pipe) != NULL)*/     if (getline(&buf, &len, pipe) != -1) {       this->m_burnProgress(1, buffer);     }   }    pclose(pipe);    return true; } void BurnArchiveFile::cancel() {   this->m_cancel = true;   this->m_burnProgress(100, "end");   system("pkill growisofs"); } long long BurnArchiveFile::getTotalFileSize(const std::string &path) {   long long size = 0;   // check path   if (fs::exists(path)) {     if (fs::is_regular_file(path)) {       // get file or subfolder file size       size = fs::file_size(path);     } else if (fs::is_directory(path)) {       for (const auto &entry : fs::directory_iterator(path)) {         if (fs::is_regular_file(entry.status())) {           size += entry.file_size();         } else if (fs::is_directory(entry.path())) {           size += getTotalFileSize(entry.path());         }       }     } else {       std::cout << "nothing" << std::endl;       return -1;     }   } else {     std::cout << "File does not exist or is not a regular file.\n";     return -1;   }    return size; } 

对于libburn库的刻录代码,可以参考官网的例程,此处就不再拷贝代码了。

五、Windows平台简单拓展

其实在Windows平台上,进行光盘数据的刻录,更简单。在Windows平台上,一般使用IMAPI(Image Mastering API)来操作光驱,它提供了CoInitializeEx和CoCreateInstance等接口,用来处理获取光驱数量、刻录准备等一系列的动作。
IMAPI的说明和相关代码比较多,此处就不再赘述,有兴趣的可以在网上查找相关资料即可。

六、总结

这类技术如果不是说专门要搞这个行业,一般不用钻研进去多深。只要明白基本的技术流程,然后借助第三方库完成即可。这就是经常说的抓大放小,提纲挈领。

相关内容

热门资讯

玩家攻略,金花房卡专卖店九酷众... 微信游戏中心:九酷众娱房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...
正规平台有哪些,金花房卡批发价... 正规平台有哪些,金花房卡批发价火神大厅/房卡链接怎么获取Sa9Ix苹果iPhone 17手机即将进入...
ia攻略/金花房卡如何购买嫦娥... 嫦娥大厅是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:【3329006910】或QQ:332900...
推荐一款!牛牛充值房卡海草众厅... 海草众厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
一分钟了解!金花房卡出售红桃众... 一分钟了解!金花房卡出售红桃众娱/微信链接房卡充值链接红桃众娱是一款非常受欢迎的游戏,咨询房/卡添加...
我来教你/金花房卡制作链接熊猫... 我来教你/金花房卡制作链接熊猫大厅/微信链接房卡批发价Sa9Ix苹果iPhone 17手机即将进入量...
科技实测!金花房卡出售新八戒/... 新八戒房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根据...
ia攻略/斗牛房卡充值乐乐大厅... 微信游戏中心:乐乐大厅房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...
ia攻略/金花房卡批发嫦娥大厅... 嫦娥大厅是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:【3329006910】或QQ:332900...
头条推荐!金花房卡出售乐乐大厅... 头条推荐!金花房卡出售乐乐大厅//全网房卡低价售Sa9Ix苹果iPhone 17手机即将进入量产阶段...
重大通报,金花房卡制作链接黄帝... 您好!微信黄帝大厅大厅链接获取房卡可以通过以下几种方式购买: 1.微信渠道:(黄帝大厅)大厅介绍:...
头条推荐!金花房卡是正规的兄弟... 兄弟大厅/新道游房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 ...
科技实测!怎么买斗牛房卡龙马大... 龙马大厅是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:【3329006910】或QQ:332900...
科技实测!游戏推荐斗牛房卡出售... 您好!微信悠悠众娱大厅链接获取房卡可以通过以下几种方式购买: 1.微信渠道:(悠悠众娱)大厅介绍:...
玩家攻略,怎么买斗牛房卡至尊大... 今 日消息,至尊大厅房卡添加微信33549083 苹果今日发布了 iOS 16.1 正式版更新,简单...
玩家攻略,游戏推荐牛牛房卡出售... 金牛座厅/新西部房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 ...
IA解析/怎么买斗牛房卡海蓝大... IA解析/怎么买斗牛房卡海蓝大厅/开群怎么买房卡海蓝大厅是一款非常受欢迎的游戏,咨询房/卡添加微信:...
我来教你/金花房卡批发价龙王大... 我来教你/金花房卡批发价龙王大厅/房卡怎么搞Sa9Ix苹果iPhone 17手机即将进入量产阶段。有...
我来教你/牛牛房卡制作链接卡农... 今 日消息,卡农大厅房卡添加微信33549083 苹果今日发布了 iOS 16.1 正式版更新,简单...
IA解析/金花房卡专卖店芝麻大... 微信游戏中心:芝麻大厅房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...