如何让 FFmpeg 支持异步并行转码、截图等等操作?
直接贴代码了:
ffmpegTest02.cs
public partial class ffmpegTest02 : FormBase
{
private static readonly string TaskffmpegNETExeFullPath = ConfigurationManager.AppSettings["TaskffmpegNETExeFullPath"]; string _videoFileFullPath = @"D:\Workspace\TestVideo.mp4"; string _othereVideoFileFullPath = @"D:\Workspace\TestVideo{0}.mp4"; string snapshotParentDir = @"D:\Workspace\TestVideoSnapshot"; int exeMoreCrawlSnapshotCount = ; public ffmpegTest02()
{
base.InitForm();
InitializeComponent();
base.InitControls(this.listInfoLog);
} private void OnProgress(object sender, ConversionProgressEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]", e.Input.FileInfo.Name, e.Output?.FileInfo.Name), false, null);
ShowAndLog(string.Format("Bitrate: {0}", e.Bitrate), false, null);
ShowAndLog(string.Format("Fps: {0}", e.Fps), false, null);
ShowAndLog(string.Format("Frame: {0}", e.Frame), false, null);
ShowAndLog(string.Format("ProcessedDuration: {0}", e.ProcessedDuration), false, null);
ShowAndLog(string.Format("Size: {0} kb", e.SizeKb), false, null);
ShowAndLog(string.Format("TotalDuration: {0}\n", e.TotalDuration), false, null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnData(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}],数据 {2}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Data),
false,
null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnDataSimpleShow(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}]。", e.Input.FileInfo.Name, e.Output?.FileInfo.Name),
false,
null);
} private void OnComplete(object sender, ConversionCompleteEventArgs e)
{
ShowAndLog(string.Format("从 {0} 到 {1} 处理完成。 ", e.Input.FileInfo.FullName, e.Output?.FileInfo.FullName),
false,
null);
} private void OnError(object sender, ConversionErrorEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]: 错误: {2}\n{3}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Exception.ExitCode, e.Exception.InnerException),
false,
null);
} private IEnumerable<TimeSpan> CreateTimeSpanArray()
{
for (int i = ; i < ; i++)
{
yield return new TimeSpan(, i, );
}
} private ConversionOptions GetConversionOptions(TimeSpan tsItem)
{
return new ConversionOptions
{
Seek = tsItem,
//下面表示3行代码表示需要自定义截图的尺寸
VideoSize = VideoSize.Custom,
CustomWidth = , // 截图的宽度
CustomHeight = , // 截图的高度
FFmpegDrawTextArgs = string.Format("borderw=10:bordercolor=white:fontcolor=#A9A9A8:fontsize=100:fontfile=FreeSerif.ttf:text='{0}\\:{1}\\:{2}':x =(w-text_w-50):y=(h-text_h-50)",
tsItem.Hours.ToString().PadLeft(, ''),
tsItem.Minutes.ToString().PadLeft(, ''),
tsItem.Seconds.ToString().PadLeft(, '')
),
ExtraFFmpegArgs = " -y " // -y 表示目标文件已经存在,则覆盖输出文件
};
} private async void btnStartConvertAndSnapshot_Click(object sender, EventArgs e)
{
string mkvOutputFileFullPath = string.Format(@"D:\Workspace\TestVideo-{0}.mkv", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
string mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo-mkv-thumb-{1}.png", snapshotParentDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
try
{
var inputFile = new MediaFile(_videoFileFullPath);
var outputFile = new MediaFile(mkvOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath); var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete;
var output = await ffmpeg.ConvertAsync(inputFile, outputFile);
var thumbNail = await ffmpeg.GetThumbnailAsync(output, thumbNailFile, GetConversionOptions(TimeSpan.FromSeconds()));
var metadata = await ffmpeg.GetMetaDataAsync(output);
Console.WriteLine(metadata.FileInfo.FullName);
Console.WriteLine(metadata);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
} private async void btnGetVideoInfo_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始获取,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath);
MetaData md = await ffmpeg.GetMetaDataAsync(inputFile); ShowAndLog(string.Format("Duration:{0}", md.Duration), false, null);
ShowAndLog(string.Format("VideoData:{0}", JsonHelper.SerializeToJson(md.VideoData)), false, null);
ShowAndLog(string.Format("AudioData:{0}", JsonHelper.SerializeToJson(md.AudioData)), false, null); globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } private async void btnStartSingleSnapshot_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始转换,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath); int i = ;
string mkvThumbOutputFileFullPath;
List<string> allThumbFullName = new List<string>();
foreach (TimeSpan tsItem in timeSpanArray)
{
i++;
mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo-mp4-thumb-{1}-{2}.jpg", snapshotParentDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i);
allThumbFullName.Add(mkvThumbOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath);
await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem) );
}
ShowAndLog("ffmpeg 转换完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async void btnParelleSyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e)
{
ShowAndLog("准备开始同步多个 TestVideo{n}.mp4 上截图,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew(); for (int i = ; i < exeMoreCrawlSnapshotCount; i++)
{
await CreateSnapshotCoreAsync(i + );
}
ShowAndLog("同步多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async Task CreateSnapshotCoreAsync(int fileId)
{
IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(string.Format(_othereVideoFileFullPath, fileId)); //_videoFileFullPath int i = ;
string mkvThumbOutputFileFullPath;
List<string> allThumbFullName = new List<string>();
foreach (TimeSpan tsItem in timeSpanArray)
{
i++;
mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo{1}-mp4-thumb-{2}-{3}.jpg", snapshotParentDir, fileId, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i);
allThumbFullName.Add(mkvThumbOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath);
await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem));
}
} private async void btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e)
{
ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew(); var tasks = Enumerable.Range(, exeMoreCrawlSnapshotCount).Select(i =>
{
return Task.Run(async () =>
{
await CreateSnapshotCoreAsync(i + );
});
});
await Task.WhenAll(tasks);
ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async void btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click(object sender, EventArgs e)
{
ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元),请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
var tasks = Enumerable.Range(, exeMoreCrawlSnapshotCount).ParallelForEachAsync(i =>
{
return Task.Run(async () =>
{
await CreateSnapshotCoreAsync(i + );
});
}, Environment.ProcessorCount);
await Task.WhenAll(tasks);
ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
}
}
ffmpegTest02.Designer.cs
partial class ffmpegTest02
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null; /// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
} #region Windows Form Designer generated code /// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnStartConvertAndSnapshot = new System.Windows.Forms.Button();
this.listInfoLog = new System.Windows.Forms.ListBox();
this.btnStartSingleSnapshot = new System.Windows.Forms.Button();
this.btnGetVideoInfo = new System.Windows.Forms.Button();
this.btnParelleSyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button();
this.btnParelleAsyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button();
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnStartConvertAndSnapshot
//
this.btnStartConvertAndSnapshot.Location = new System.Drawing.Point(, );
this.btnStartConvertAndSnapshot.Name = "btnStartConvertAndSnapshot";
this.btnStartConvertAndSnapshot.Size = new System.Drawing.Size(, );
this.btnStartConvertAndSnapshot.TabIndex = ;
this.btnStartConvertAndSnapshot.Text = "1. 开始先把 TestVideo.mp4 转换 mkv,再在第 1 分钟的时候截图";
this.btnStartConvertAndSnapshot.UseVisualStyleBackColor = true;
this.btnStartConvertAndSnapshot.Click += new System.EventHandler(this.btnStartConvertAndSnapshot_Click);
//
// listInfoLog
//
this.listInfoLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listInfoLog.Font = new System.Drawing.Font("宋体", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)()));
this.listInfoLog.FormattingEnabled = true;
this.listInfoLog.HorizontalScrollbar = true;
this.listInfoLog.Location = new System.Drawing.Point(, );
this.listInfoLog.Name = "listInfoLog";
this.listInfoLog.ScrollAlwaysVisible = true;
this.listInfoLog.Size = new System.Drawing.Size(, );
this.listInfoLog.TabIndex = ;
//
// btnStartSingleSnapshot
//
this.btnStartSingleSnapshot.Location = new System.Drawing.Point(, );
this.btnStartSingleSnapshot.Name = "btnStartSingleSnapshot";
this.btnStartSingleSnapshot.Size = new System.Drawing.Size(, );
this.btnStartSingleSnapshot.TabIndex = ;
this.btnStartSingleSnapshot.Text = "2. 不转码,直接在第 1分钟的时候从 TestVideo.mp4 上截图";
this.btnStartSingleSnapshot.UseVisualStyleBackColor = true;
this.btnStartSingleSnapshot.Click += new System.EventHandler(this.btnStartSingleSnapshot_Click);
//
// btnGetVideoInfo
//
this.btnGetVideoInfo.Location = new System.Drawing.Point(, );
this.btnGetVideoInfo.Name = "btnGetVideoInfo";
this.btnGetVideoInfo.Size = new System.Drawing.Size(, );
this.btnGetVideoInfo.TabIndex = ;
this.btnGetVideoInfo.Text = "3. 仅仅获取 TestVideo.mp4 视频的信息";
this.btnGetVideoInfo.UseVisualStyleBackColor = true;
this.btnGetVideoInfo.Click += new System.EventHandler(this.btnGetVideoInfo_Click);
//
// btnParelleSyncStartMoreCrawlSnapshot
//
this.btnParelleSyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(, );
this.btnParelleSyncStartMoreCrawlSnapshot.Name = "btnParelleSyncStartMoreCrawlSnapshot";
this.btnParelleSyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(, );
this.btnParelleSyncStartMoreCrawlSnapshot.TabIndex = ;
this.btnParelleSyncStartMoreCrawlSnapshot.Text = "4. 同步多个 TestVideo{n}.mp4 上截图";
this.btnParelleSyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true;
this.btnParelleSyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleSyncStartMoreCrawlSnapshot_ClickAsync);
//
// btnParelleAsyncStartMoreCrawlSnapshot
//
this.btnParelleAsyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(, );
this.btnParelleAsyncStartMoreCrawlSnapshot.Name = "btnParelleAsyncStartMoreCrawlSnapshot";
this.btnParelleAsyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(, );
this.btnParelleAsyncStartMoreCrawlSnapshot.TabIndex = ;
this.btnParelleAsyncStartMoreCrawlSnapshot.Text = "5. 异步并行多个 TestVideo{n}.mp4 上截图";
this.btnParelleAsyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true;
this.btnParelleAsyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync);
//
// btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop
//
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Location = new System.Drawing.Point(, );
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Name = "btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop";
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Size = new System.Drawing.Size(, );
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.TabIndex = ;
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Text = "6. 异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)";
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.UseVisualStyleBackColor = true;
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click);
//
// ffmpegTest02
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(, );
this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop);
this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshot);
this.Controls.Add(this.btnParelleSyncStartMoreCrawlSnapshot);
this.Controls.Add(this.btnGetVideoInfo);
this.Controls.Add(this.btnStartSingleSnapshot);
this.Controls.Add(this.listInfoLog);
this.Controls.Add(this.btnStartConvertAndSnapshot);
this.Name = "ffmpegTest02";
this.Text = "ffmpegTest02";
this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnStartConvertAndSnapshot;
private System.Windows.Forms.ListBox listInfoLog;
private System.Windows.Forms.Button btnStartSingleSnapshot;
private System.Windows.Forms.Button btnGetVideoInfo;
private System.Windows.Forms.Button btnParelleSyncStartMoreCrawlSnapshot;
private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshot;
private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop;
}
运行结果
图 01
图 02
图 03
图 04
图 05
图 06
图 07
图 08
谢谢浏览!
如何让 FFmpeg 支持异步并行转码、截图等等操作?的更多相关文章
- Java Web 中使用ffmpeg实现视频转码、视频截图
Java Web 中使用ffmpeg实现视频转码.视频截图 转载自:[ http://www.cnblogs.com/dennisit/archive/2013/02/16/2913287.html ...
- windows下使用ffmpeg进行视频转换和截图。
author:fanfq(xiaoban) Email:fangqing.fan#gmail.comlink:http://fanfq.iteye.com/admin/blogs/655569chan ...
- C# 使用 ffmpeg 进行音频转码
先放一下 ffmpeg 的官方文档以及下载地址: 官方文档:http://ffmpeg.org/ffmpeg.html 下载地址:http://ffmpeg.org/download.html 用 f ...
- FFmpeg:视频转码、剪切、合并、播放速调整
原文:https://fzheng.me/2016/01/08/ffmpeg/ FFmpeg:视频转码.剪切.合并.播放速调整 2016-01-08 前阵子帮导师处理项目 ppt,因为插入视频的格式问 ...
- 使用ffmpeg.exe进行转码参数说明
使用ffmpeg.exe进行转码参数说明 摘自:https://blog.csdn.net/coloriy/article/details/47337641 2015年08月07日 13:04:32 ...
- ubuntu系统下安装gstreamer的ffmpeg支持
当您在安装gstreamer到您的ubuntu系统中时,为了更好地进行流媒体开发,需要安装ffmpeg支持,但一般情况下,直接使用 sudo apt-get install gstreamer0.10 ...
- [IBM][CLI Driver] SQL0270N 函数不受支持(原因码:"75")。 SQLSTATE=42997
db2 update dbm cfg using FEDERATED yes 与 自动维护 (AUTO_MAINT) = ON 自动数据库备份 (AUTO_DB_BACKUP) = OFF 自动表 ...
- Python3选择支持非ASCII码标识符的缘由
原文在: PEP 3131 -- Supporting Non-ASCII Identifiers. Python2并不支持非ASCII码标识符. PEP的全称是Python Enhancement ...
- API Studio 5.1.2 版本更新:加入全局搜索、支持批量测试API测试用例、读取代码注解生成文档支持Github与码云等
最近在EOLINKER的开发任务繁重,许久在博客园没有更新产品动态了,经过这些日子,EOLINKER又有了长足的进步,增加了更多易用的功能,比如加入全局搜索.支持批量测试API测试用例.读取代码注解生 ...
随机推荐
- Spring Cloud Gateway-自定义异常处理
前提 我们平时在用SpringMVC的时候,只要是经过DispatcherServlet处理的请求,可以通过@ControllerAdvice和@ExceptionHandler自定义不同类型异常的处 ...
- springboot热启动中那些不为人知的东东
在springboot热启动中,大家都知道在pom文件中配置devtools,但是当这个服务特别大,或者引入的包特别多的时候,重启一下就特别慢,如果开发的PC的内存和cpu如果不给里的h话,系统就卡主 ...
- HTTP Error 500.0 - ANCM In-Process Handler Load Failure 排错历程
先上报错图 环境 Window Server 2008 r2 netcore 2.2 排错历程 看到这个错 我第一个想到netcore 安装问题 先检查了下环境 发现没问题 我排查了下应用池 确定是无 ...
- hello tensorflow,我的第一个tensorflow程序
上代码: import tensorflow as tf if __name__=='__main__': g = tf.Graph() # add ops to the user created g ...
- java基础(12):构造方法、this、super
1. 构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方 ...
- 用Python程序批量删除excel里面的图片
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Rhinoceros PS:如有需要Python学习资料的小伙伴可以 ...
- vue快速复习手册
1.基本使用 <!DOCTYPE html> <head> <meta charset="UTF-8"> <title>Vue的基本 ...
- CODING 告诉你硅谷项目经理的项目管理之道
写在前面 优秀的项目管理者是怎么工作的,如何把一个研发团队的绩效激发到最大? 我们精心挑选了几篇硅谷科技公司研发管理者的 README 进行翻译. README 主要用来向团队成员展示项目管理者的工作 ...
- C# 运行时的关系
简介 记录c#对象在托管堆中运行时的相互关系,如下记录了一个方法在执行时候的生命周期,当方法在之前,CLR会先执行将方法里面所有用到的局部变量.参数对应的内存地址等全部存放当前线程栈当中,并且会将所有 ...
- 查询Mysql数据库所有数据库所占磁盘空间大小
查询Mysql数据库所有数据库所占磁盘空间大小: /,),' MB') as data_size, concat(truncate(sum(index_length)//,),'MB') as ind ...