Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
Y
yzg-util
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
YZG
yzg-util
Commits
07f99ec5
Commit
07f99ec5
authored
Nov 15, 2021
by
yanzg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
下载视频
parent
89cdde35
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
208 additions
and
185 deletions
+208
-185
HlsDownloader.java
...ge/src/main/java/com/yanzuoguang/media/HlsDownloader.java
+189
-185
MediaFirstTest.java
yzg-util-image/src/test/java/helper/MediaFirstTest.java
+19
-0
No files found.
yzg-util-image/src/main/java/com/yanzuoguang/media/HlsDownloader.java
View file @
07f99ec5
package
com
.
yanzuoguang
.
media
;
package
com
.
yanzuoguang
.
media
;
import
com.yanzuoguang.util.helper.HttpHelper
;
import
com.yanzuoguang.util.helper.HttpHelper
;
import
com.yanzuoguang.util.helper.StringHelper
;
import
com.yanzuoguang.util.thread.ThreadHelper
;
import
java.io.*
;
import
java.io.*
;
import
java.net.MalformedURLException
;
import
java.net.URL
;
import
java.net.URL
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -17,243 +19,245 @@ import java.util.regex.Pattern;
...
@@ -17,243 +19,245 @@ import java.util.regex.Pattern;
* @author 颜佐光
* @author 颜佐光
*/
*/
public
class
HlsDownloader
{
public
class
HlsDownloader
{
public
static
Pattern
pattern
=
Pattern
.
compile
(
".*ts"
);
/**
/**
*
下载uuid
*
匹配ts文件
*/
*/
private
String
uuid
;
private
static
Pattern
pattern
=
Pattern
.
compile
(
".*ts"
)
;
/**
/**
* 源地址
* 源地址
*/
*/
private
String
originUrlPath
;
private
String
serverUrl
=
StringHelper
.
EMPTY
;
/**
/**
*
*
*/
*/
private
String
preUrlPath
;
private
String
localUrl
=
StringHelper
.
EMPTY
;
/**
/**
* 文件根目录
* 线程数量
*/
private
String
rootPath
;
/**
*
*/
*/
private
String
fileName
;
private
int
threadCount
;
/**
/**
*
当前视频 ts 文件存储临时目录
*
下载ts的文件数量
*/
*/
private
String
folderPath
;
private
int
maxTs
;
/**
/**
*
线程数量
*
服务器url路径
*/
*/
private
int
threadQuantity
=
10
;
private
String
tempServerPath
;
public
HlsDownloader
(
String
serverUrl
,
String
localUrl
)
{
this
(
serverUrl
,
localUrl
,
5
,
-
1
);
}
public
HlsDownloader
(
String
originUrlPath
,
String
preUrlPath
,
String
rootPath
)
{
public
HlsDownloader
(
String
serverUrl
,
String
localUrl
,
int
threadCount
,
int
maxTs
)
{
this
.
uuid
=
UUID
.
randomUUID
().
toString
().
replaceAll
(
"-"
,
""
);
if
(
StringHelper
.
isEmpty
(
localUrl
))
{
this
.
originUrlPath
=
originUrlPath
;
return
;
this
.
preUrlPath
=
preUrlPath
;
this
.
rootPath
=
rootPath
;
this
.
fileName
=
uuid
+
".mp4"
;
this
.
folderPath
=
rootPath
+
File
.
separator
+
uuid
;
File
file
=
new
File
(
folderPath
);
if
(!
file
.
exists
())
{
file
.
mkdirs
();
}
}
this
.
serverUrl
=
serverUrl
;
this
.
localUrl
=
localUrl
;
this
.
threadCount
=
threadCount
;
this
.
maxTs
=
maxTs
;
}
}
public
int
getThreadQuantity
()
{
/**
return
threadQuantity
;
* 开启下载
*
* @param isAsync 是否异步下载
* @param removeTs 删除ts临时文件
* @return
* @throws Exception
*/
public
String
download
(
boolean
isAsync
,
boolean
removeTs
)
throws
Exception
{
// 处理临时服务器路径
tempServerPath
=
StringHelper
.
left
(
this
.
serverUrl
,
this
.
serverUrl
.
length
()
-
new
File
(
this
.
serverUrl
).
getName
().
length
());
// 用于判断url是否已经存在
HashMap
<
String
,
Integer
>
urlHas
=
new
HashMap
<>();
// 待下载的数组
HashMap
<
Integer
,
String
>
waitDown
=
new
HashMap
<>();
// 已下载数组文件列表
HashMap
<
Integer
,
String
>
hasDown
=
new
HashMap
<>();
// 开启线程
if
(
isAsync
)
{
for
(
int
i
=
0
;
i
<
threadCount
;
i
++)
{
Thread
thread
=
new
Thread
(
new
Runnable
()
{
@Override
public
void
run
()
{
// 循环下载
while
(
isDown
(
hasDown
))
{
// 下载片段
downLoadIndexFile
(
waitDown
,
hasDown
,
hasDown
.
size
());
// 等到100ms
if
(
waitDown
.
isEmpty
())
{
ThreadHelper
.
sleep
(
100
);
}
}
}
});
thread
.
start
();
}
}
do
{
// 添加m3mu到待下载目录
addWaitDown
(
urlHas
,
waitDown
,
hasDown
);
// 同步或者异步下载
if
(
isAsync
)
{
// 等待500毫秒
Thread
.
sleep
(
500
);
}
else
{
downLoadIndexFile
(
waitDown
,
hasDown
,
hasDown
.
size
());
}
}
while
(
isDown
(
hasDown
));
// 组合下载的ts文件为视频
composeFile
(
hasDown
,
removeTs
);
// 下载后的本地文件地址
return
this
.
localUrl
;
}
}
p
ublic
void
setThreadQuantity
(
int
threadQuantity
)
{
p
rivate
boolean
isDown
(
HashMap
<
Integer
,
String
>
hasDown
)
{
this
.
threadQuantity
=
threadQuantity
;
return
hasDown
.
size
()
<=
this
.
maxTs
||
this
.
maxTs
<
0
;
}
}
public
String
download
(
boolean
isAsync
)
throws
Exception
{
/**
* 添加m3mu到待下载目录
String
indexStr
=
getIndexFile
();
*
* @param urlPos 用于判断url是否已经存在
List
urlList
=
analysisIndex
(
indexStr
);
* @param waitDown 待下载的数组
* @param hasDown 已下载数组文件列表
HashMap
<
Integer
,
String
>
keyFileMap
=
new
HashMap
<>();
* @throws MalformedURLException
*/
if
(
isAsync
)
{
private
void
addWaitDown
(
HashMap
<
String
,
Integer
>
urlPos
,
HashMap
<
Integer
,
String
>
waitDown
,
HashMap
<
Integer
,
String
>
hasDown
)
throws
MalformedURLException
{
downLoadIndexFileAsync
(
urlList
,
keyFileMap
);
// 当没有需要下载的时候,则继续读取列表下载
if
(
waitDown
.
isEmpty
())
{
while
(
keyFileMap
.
size
()
<
urlList
.
size
())
{
// 解析的索引文件内容
//System.out.println("当前下载数量"+keyFileMap.size());
String
m3muTsList
=
getIndexFile
();
Thread
.
sleep
(
3000
);
// 解析成 *.ts 的文件列表
List
<
String
>
urlList
=
analysisIndex
(
m3muTsList
);
// 添加到待下载目录中
for
(
int
i
=
0
;
i
<
urlList
.
size
();
i
++)
{
String
url
=
urlList
.
get
(
i
);
// 处理url
url
=
tempServerPath
+
url
;
// 判断url是否存在
if
(
urlPos
.
containsKey
(
url
))
{
continue
;
}
// 已经下载的ts包含的url数量
int
totalSize
=
waitDown
.
size
()
+
hasDown
.
size
();
// 写入url
urlPos
.
put
(
url
,
totalSize
);
waitDown
.
put
(
totalSize
,
url
);
}
}
}
else
{
keyFileMap
=
downLoadIndexFile
(
urlList
);
}
}
return
composeFile
(
keyFileMap
);
}
}
/* 下载索引文件 */
/**
public
String
getIndexFile
()
throws
Exception
{
* 下载索引文件
URL
url
=
new
URL
(
originUrlPath
);
*
String
content
=
""
;
* @return 返回索引文件的呢日哦嗯
* @throws Exception
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
//这里的循环是当下载直播流的时候,一次下载只能有一个ts文件,这里我循环了四次,下载四个ts文件,在合成视频。
*/
//下载资源
private
String
getIndexFile
()
throws
MalformedURLException
{
try
{
StringBuilder
content
=
new
StringBuilder
();
BufferedReader
in
=
new
BufferedReader
(
new
InputStreamReader
(
url
.
openStream
(),
"UTF-8"
));
URL
url
=
new
URL
(
this
.
serverUrl
);
//下载资源
System
.
err
.
println
(
in
.
readLine
());
try
(
BufferedReader
in
=
new
BufferedReader
(
new
InputStreamReader
(
url
.
openStream
(),
"UTF-8"
)))
{
String
line
;
String
line
;
while
((
line
=
in
.
readLine
())
!=
null
)
{
while
((
line
=
in
.
readLine
())
!=
null
)
{
content
+=
line
+
"\n"
;
content
.
append
(
line
);
}
content
.
append
(
"\n"
);
in
.
close
();
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"错误为:"
);
System
.
err
.
println
(
e
);
}
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
return
content
;
return
content
.
toString
()
;
}
}
/* 解析索引文件 */
/**
public
List
analysisIndex
(
String
content
)
throws
Exception
{
* 解析索引文件
*
* @param content m3mu文件内容
* @return
* @throws Exception
*/
private
List
<
String
>
analysisIndex
(
String
content
)
{
List
<
String
>
list
=
new
ArrayList
<
String
>();
List
<
String
>
list
=
new
ArrayList
<
String
>();
Matcher
ma
=
pattern
.
matcher
(
content
);
Matcher
ma
=
pattern
.
matcher
(
content
);
while
(
ma
.
find
())
{
while
(
ma
.
find
())
{
list
.
add
(
ma
.
group
());
list
.
add
(
ma
.
group
());
}
}
return
list
;
return
list
;
}
}
/* 下载视频片段 */
/**
public
HashMap
downLoadIndexFile
(
List
<
String
>
urlList
)
{
* 下载视频片段
HashMap
<
Integer
,
String
>
keyFileMap
=
new
HashMap
<>();
*
* @return
for
(
int
i
=
0
;
i
<
urlList
.
size
();
i
++)
{
*/
String
subUrlPath
=
urlList
.
get
(
i
);
private
void
downLoadIndexFile
(
HashMap
<
Integer
,
String
>
waitDown
,
HashMap
<
Integer
,
String
>
hasDown
,
int
pos
)
{
String
fileOutPath
=
folderPath
+
File
.
separator
+
i
+
".ts"
;
// 获取源文件地址
keyFileMap
.
put
(
i
,
fileOutPath
);
String
serverTsUrl
;
try
{
synchronized
(
waitDown
)
{
serverTsUrl
=
waitDown
.
remove
(
pos
);
HttpHelper
.
downToLocal
(
preUrlPath
+
subUrlPath
,
fileOutPath
);
System
.
out
.
println
(
"成功:"
+
(
i
+
1
)
+
"/"
+
urlList
.
size
());
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"失败:"
+
(
i
+
1
)
+
"/"
+
urlList
.
size
());
}
}
}
if
(
StringHelper
.
isEmpty
(
serverTsUrl
))
{
return
keyFileMap
;
return
;
}
public
void
downLoadIndexFileAsync
(
List
<
String
>
urlList
,
HashMap
<
Integer
,
String
>
keyFileMap
)
throws
Exception
{
int
downloadForEveryThread
=
(
urlList
.
size
()
+
threadQuantity
-
1
)
/
threadQuantity
;
if
(
downloadForEveryThread
==
0
)
{
downloadForEveryThread
=
urlList
.
size
();
}
}
// 写入生成地址到文件列表
for
(
int
i
=
0
;
i
<
urlList
.
size
();
i
+=
downloadForEveryThread
)
{
try
{
int
startIndex
=
i
;
// 获取生成的地址
int
endIndex
=
i
+
downloadForEveryThread
-
1
;
String
localTs
=
this
.
localUrl
+
"."
+
pos
+
".ts"
;
if
(
endIndex
>=
urlList
.
size
())
{
// 设置文件下载成功
endIndex
=
urlList
.
size
()
-
1
;
hasDown
.
put
(
pos
,
localTs
);
}
// 下载服务器文件
new
DownloadThread
(
urlList
,
startIndex
,
endIndex
,
keyFileMap
).
start
();
HttpHelper
.
downToLocal
(
serverTsUrl
,
localTs
);
}
catch
(
Exception
e
)
{
// 判断文件是否下载失败
e
.
printStackTrace
();
}
}
}
}
/**
/**
* 视频片段合成
* 视频片段合成
*
*
* @param
keyFileMap
文件段列表
* @param
hasDown 已经下载的
文件段列表
* @return
* @return
* @throws Exception
* @throws Exception
*/
*/
private
String
composeFile
(
HashMap
<
Integer
,
String
>
keyFileMap
)
throws
Exception
{
private
void
composeFile
(
HashMap
<
Integer
,
String
>
hasDown
,
boolean
removeTs
)
throws
Exception
{
if
(
keyFileMap
.
isEmpty
())
{
if
(
hasDown
.
isEmpty
())
{
return
null
;
return
;
}
}
// todo: 输出文件,需要封装成函数
File
fileTo
=
new
File
(
this
.
localUrl
);
String
fileOutPath
=
rootPath
+
File
.
separator
+
fileName
;
if
(
hasDown
.
size
()
==
1
&&
removeTs
)
{
// 订单输出文件流
try
(
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
new
File
(
fileOutPath
)))
{
// 获取第一个 ts 文件
// 获取第一个 ts 文件
File
file
=
new
File
(
keyFileMap
.
get
(
0
));
File
file
=
new
File
(
hasDown
.
get
(
0
));
if
(
file
.
exists
())
{
file
.
renameTo
(
fileTo
);
// 读取文件
}
else
{
FileInputStream
fis
=
new
FileInputStream
(
file
);
// 订单输出文件流
// 定义文件缓存
try
(
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
fileTo
))
{
byte
[]
bytes
=
new
byte
[
1024
];
// 遍历已经下载的ts文件
// 读取的文件长度
for
(
int
i
=
0
;
i
<
hasDown
.
size
();
i
++)
{
int
length
=
0
;
// 获取 ts 文件
// 读取来源文件
File
file
=
new
File
(
hasDown
.
get
(
i
));
while
((
length
=
fis
.
read
(
bytes
))
!=
-
1
)
{
if
(!
file
.
exists
())
{
// 写入目标文件
continue
;
fileOutputStream
.
write
(
bytes
,
0
,
length
);
}
}
// 读取文件
}
try
(
FileInputStream
fis
=
new
FileInputStream
(
file
))
{
}
// 定义文件缓存
return
fileName
;
byte
[]
bytes
=
new
byte
[
1024
];
}
// 读取的文件长度
int
length
=
0
;
// 读取来源文件
/**
while
((
length
=
fis
.
read
(
bytes
))
!=
-
1
)
{
* 下载线程
// 写入目标文件
*/
fileOutputStream
.
write
(
bytes
,
0
,
length
);
private
class
DownloadThread
extends
Thread
{
}
/**
}
* 下载地址列表
// 删除ts文件
*/
if
(
removeTs
)
{
private
List
<
String
>
urlList
;
file
.
delete
();
/**
}
* 开始索引
*/
private
int
startIndex
;
/**
* 结束索引
*/
private
int
endIndex
;
/**
* 本地存储缓存地址
*/
private
HashMap
<
Integer
,
String
>
keyFileMap
;
/**
* 构造函数
*
* @param urlList
* @param startIndex
* @param endIndex
* @param keyFileMap
*/
public
DownloadThread
(
List
<
String
>
urlList
,
int
startIndex
,
int
endIndex
,
HashMap
<
Integer
,
String
>
keyFileMap
)
{
this
.
urlList
=
urlList
;
this
.
startIndex
=
startIndex
;
this
.
endIndex
=
endIndex
;
this
.
keyFileMap
=
keyFileMap
;
}
@Override
public
void
run
()
{
for
(
int
i
=
startIndex
;
i
<=
endIndex
;
i
++)
{
// 获取源文件地址
String
subUrlPath
=
urlList
.
get
(
i
);
// 获取生成的地址
String
fileOutPath
=
folderPath
+
File
.
separator
+
i
+
".ts"
;
// 写入生成地址到文件列表
keyFileMap
.
put
(
i
,
fileOutPath
);
try
{
// 下载服务器文件
HttpHelper
.
downToLocal
(
preUrlPath
+
subUrlPath
,
fileOutPath
);
// 判断文件是否下载成功
System
.
out
.
println
(
String
.
format
(
"message"
,
"成功"
,
keyFileMap
.
size
()));
}
catch
(
Exception
e
)
{
// 判断文件是否下载失败
System
.
err
.
println
(
String
.
format
(
"message"
,
"失败"
,
keyFileMap
.
size
()));
}
}
}
}
}
}
...
...
yzg-util-image/src/test/java/helper/MediaFirstTest.java
View file @
07f99ec5
package
helper
;
package
helper
;
import
com.yanzuoguang.media.HlsDownloader
;
import
com.yanzuoguang.media.MediaFirst
;
import
com.yanzuoguang.media.MediaFirst
;
import
com.yanzuoguang.media.MediaReqVo
;
import
com.yanzuoguang.media.MediaReqVo
;
import
com.yanzuoguang.media.MediaResVo
;
import
com.yanzuoguang.media.MediaResVo
;
...
@@ -11,6 +12,12 @@ import java.io.File;
...
@@ -11,6 +12,12 @@ import java.io.File;
public
class
MediaFirstTest
{
public
class
MediaFirstTest
{
private
String
getFile
(
String
file
)
{
// 注意,路径应为文件在工程中的相对路径
File
f
=
new
File
(
"src/test/java/helper/"
+
file
);
return
f
.
getAbsolutePath
();
}
@Test
@Test
public
void
test
()
{
public
void
test
()
{
MediaFirst
first
=
new
MediaFirst
();
MediaFirst
first
=
new
MediaFirst
();
...
@@ -26,4 +33,16 @@ public class MediaFirstTest {
...
@@ -26,4 +33,16 @@ public class MediaFirstTest {
}
}
}
}
@Test
public
void
testHlsDown
()
throws
Exception
{
HlsDownloader
downloader
=
new
HlsDownloader
(
"http://rtmp.tourbida.com/hls/0bfbec86-7a75-4b70-9318-dd32a3d59633.m3u8"
,
getFile
(
"downHls.mp4"
),
3
,
100
);
downloader
.
download
(
true
,
true
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment