-
-
Save UnluckyNinja/fe63c946bc27d97f30e11e4516505d26 to your computer and use it in GitHub Desktop.
Revisions
-
UnluckyNinja revised this gist
Aug 24, 2016 . 1 changed file with 67 additions and 52 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -24,7 +24,7 @@ class WatchThread implements Runnable { WatchThread(){ super() Runtime.runtime.addShutdownHook { worker?.stop() } } @Lazy def worker = new RecordWorker(FFMPEG, ROOMID, OUTPUTDIR) @@ -36,9 +36,7 @@ class WatchThread implements Runnable { void run() { if (shouldRecord()) { // 检测是否应该开始录制 if (!worker.working) { // 当前没有ffmpeg进程 worker.start() } else { checkAndSplit() // 当前正在录制,开始按时长分割,防止ffmpeg录制出错时无法检测到 } @@ -58,7 +56,7 @@ class WatchThread implements Runnable { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 直播关闭了" worker.stop() } } livingCheck++ @@ -77,12 +75,11 @@ class WatchThread implements Runnable { } class RecordWorker { boolean working = false; def workerThread def FFMPEG def ROOMID @@ -94,67 +91,85 @@ class RecordWorker extends Thread{ this.OUTPUTDIR = OUTPUTDIR } def start(){ working = true println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 启动录制线程" workerThread = new WorkerThread(FFMPEG, ROOMID, OUTPUTDIR) workerThread.start() } def restart(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 重启录制线程" stop() start() } def stop(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 关闭录制线程" if(!working){ return false } workerThread.quitFFmpeg() working = false return true } class WorkerThread extends Thread{ volatile Process process volatile int retry = 0 def FFMPEG def ROOMID def OUTPUTDIR WorkerThread(FFMPEG, ROOMID, OUTPUTDIR){ this.FFMPEG = FFMPEG this.ROOMID = ROOMID this.OUTPUTDIR = OUTPUTDIR } @Override void run(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 准备启动 FFMPEG" def h5play = new URL("http://live.bilibili.com/api/playurl?player=1&cid=${ROOMID}&quality=0").text def matcher = h5play =~ /<url><!\[CDATA\[(.+)\]\]><\/url>/ if (matcher.find()) { retry = 0 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在直播中" println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" def m3u8 = matcher.group 1 String[] command = [FFMPEG, "-i", "$m3u8", "-c", "copy", "-f", "flv", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.flv"] process = command.execute() println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] FFMPEG 启动完毕,等待输出" process.waitForProcessOutput System.out, System.err } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址" retry++ if (retry == 10) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址,重试已达上限" System.exit 1 } } } void quitFFmpeg() { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在关闭 FFMPEG" if (process?.alive) { process.out.withWriter{ writer -> writer.write "q" writer.flush() } println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 已退出录制" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 进程不存在" } println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] FFMPEG 已停止活动" } } } -
UnluckyNinja revised this gist
Aug 20, 2016 . 1 changed file with 129 additions and 76 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,106 +2,159 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit def ARGS = [ UID : "276904", // B站UID ROOMID : "131985", // 直播间的房间编号,不是地址编号 OUTPUTDIR : /F:\FLV\workground\ffmpeg\bin\output/, // 录制文件输出目录 FFMPEG : /F:\FLV\workground\ffmpeg\bin\ffmpeg.exe/, // ffmpeg可执行程序位置 CHECK_INTERVAL : 60, // 直播检测线程的调度间隔,单位:秒 SPLIT_INTERVAL : 60 * 10, // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 ] def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.scheduleWithFixedDelay(new WatchThread(ARGS), 0, 1, TimeUnit.SECONDS) class WatchThread implements Runnable { def UID def ROOMID def OUTPUTDIR def FFMPEG def CHECK_INTERVAL def SPLIT_INTERVAL WatchThread(){ super() Runtime.runtime.addShutdownHook { worker?.stopWorker() } } @Lazy def worker = new RecordWorker(FFMPEG, ROOMID, OUTPUTDIR) boolean isLiving = false int livingCheck = 0 // 检测计时器 int splitCheck = 0 // 分割计时器 @Override void run() { if (shouldRecord()) { // 检测是否应该开始录制 if (!worker.working) { // 当前没有ffmpeg进程 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在启动录制线程" worker.start() worker.join() } else { checkAndSplit() // 当前正在录制,开始按时长分割,防止ffmpeg录制出错时无法检测到 } } } boolean shouldRecord(){ if (livingCheck == CHECK_INTERVAL) { // overflow reset livingCheck = 0 } if (livingCheck == 0) { // 60秒一检测API直播活动状态 def isLvingURL = new URL("http://live.bilibili.com/bili/isliving/$UID?callback=isliving").text isLiving = !isLvingURL.contains(/"data":""/) } if (!isLiving) { // 非直播状态下log直播情况 if (!worker.working) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 直播关闭了" worker.stopWorker() } } livingCheck++ return isLiving } def checkAndSplit(){ splitCheck++ if (splitCheck == SPLIT_INTERVAL) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 录播超出时长,分割中……" splitCheck = 0 worker.restart() println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 分割完毕" } } } class RecordWorker extends Thread{ volatile Process process volatile int retry = 0 volatile boolean working = false; def FFMPEG def ROOMID def OUTPUTDIR RecordWorker(FFMPEG, ROOMID, OUTPUTDIR){ this.FFMPEG = FFMPEG this.ROOMID = ROOMID this.OUTPUTDIR = OUTPUTDIR } @Override void run(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 准备启动 FFMPEG" working = true def h5play = new URL("http://live.bilibili.com/api/playurl?player=1&cid=${ROOMID}&quality=0").text def matcher = h5play =~ /<url><!\[CDATA\[(.+)\]\]><\/url>/ if (matcher.find()) { retry = 0 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在直播中" println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" def m3u8 = matcher.group 1 String[] command = [FFMPEG, "-i", "$m3u8", "-c", "copy", "-f", "flv", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.flv"] process = command.execute() println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] FFMPEG 启动完毕,等待输出" process.waitForProcessOutput System.out, System.err } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址" retry++ if (retry == 10) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址,重试已达上限" System.exit 1 } } } def restart(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 重启录制线程" stopWorker() this.start() this.join() } def stopWorker(){ println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 关闭录制线程" if(!working){ return false } quitFFmpeg() working = false return true } private synchronized setWorking(boolean flag){ working = flag } private void quitFFmpeg() { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在关闭 FFMPEG" if (process?.alive) { process.out.withWriter{ writer -> writer.write "q" writer.flush() } println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 已退出录制" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 进程不存在" } println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] FFMPEG 已停止活动" } } -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,7 +7,7 @@ def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def CHECKER = 60 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 10 // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.scheduleWithFixedDelay(new RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG, CHECKER, TRACKS), 0, 1, TimeUnit.SECONDS) @@ -50,8 +50,8 @@ class RecorderThread implements Runnable { if (isliving) { // 检测到正在直播 if (process == null) { // 当前没有ffmpeg进程 Thread thread = new Thread({ // 创建一个线程运行ffmpeg,防止阻塞监控线程 def h5play = new URL("http://live.bilibili.com/api/playurl?player=1&cid=${ROOMID}&quality=0").text def matcher = h5play =~ /<url><!\[CDATA\[(.+)\]\]><\\/url>/ if (matcher.find()) { retry = 0 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在直播中" @@ -60,8 +60,8 @@ class RecorderThread implements Runnable { String[] command = [FFMPEG, "-i", "$m3u8", "-c", "copy", "-f", "flv", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.flv"] process = command.execute() process.waitForProcessOutput System.out, System.err } else { -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 8 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -93,11 +93,15 @@ class RecorderThread implements Runnable { void quitFFmpeg() { if (process != null) { if (process.alive) { def writer = new BufferedWriter(new OutputStreamWriter(process.out)) writer.write "q" writer.flush() println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 已退出录制" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 进程不存在" } process = null } } } -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -58,7 +58,6 @@ class RecorderThread implements Runnable { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" def m3u8 = matcher.group 1 String[] command = [FFMPEG, "-i", "$m3u8", "-c", "copy", "-bsf:a", "aac_adtstoasc", @@ -93,7 +92,7 @@ class RecorderThread implements Runnable { } void quitFFmpeg() { if (process != null) { def writer = new BufferedWriter(new OutputStreamWriter(process.outputStream)) writer.write "q" writer.flush() -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -82,7 +82,7 @@ class RecorderThread implements Runnable { quitFFmpeg() } } } else if (check == 1) { if (process == null) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 0 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -10,7 +10,6 @@ def CHECKER = 60 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 15 // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.scheduleWithFixedDelay(new RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG, CHECKER, TRACKS), 0, 1, TimeUnit.SECONDS) class RecorderThread implements Runnable { -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,4 @@ #!/usr/bin/env groovy import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -6,7 +7,7 @@ def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def CHECKER = 60 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 15 // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.addShutdownHook { scheduledExecutorService.shutdown() } @@ -93,7 +94,7 @@ class RecorderThread implements Runnable { } void quitFFmpeg() { if (process != null && process.outputStream != null) { def writer = new BufferedWriter(new OutputStreamWriter(process.outputStream)) writer.write "q" writer.flush() -
sunny00123 revised this gist
Aug 20, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,7 @@ def UID = "276904" // B站UID def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def CHECKER = 60 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 30 // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() -
sunny00123 revised this gist
Aug 19, 2016 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,7 +6,7 @@ def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def CHECKER = 30 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 30 // 录制多长时间分割一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.addShutdownHook { scheduledExecutorService.shutdown() } @@ -75,7 +75,7 @@ class RecorderThread implements Runnable { } }) thread.start() // 启动录制线程 } else { // 当前正在录制,开始按时长分割,防止ffmpeg录制出错时无法检测到 split++ if (split == TRACKS) { split = 0 -
sunny00123 revised this gist
Aug 19, 2016 . 1 changed file with 26 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,34 +5,48 @@ def UID = "276904" // B站UID def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def CHECKER = 30 // 直播检测线程的调度间隔,单位:秒 def TRACKS = 60 * 30 // 录制多长时间分批一次,防止ffmpeg录制出错时无法检测到,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.addShutdownHook { scheduledExecutorService.shutdown() } scheduledExecutorService.scheduleWithFixedDelay(new RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG, CHECKER, TRACKS), 0, 1, TimeUnit.SECONDS) class RecorderThread implements Runnable { def UID def ROOMID def OUTPUTDIR def FFMPEG def CHECKER def TRACKS RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG, CHECKER, TRACKS) { this.UID = UID this.ROOMID = ROOMID this.OUTPUTDIR = OUTPUTDIR this.FFMPEG = FFMPEG this.CHECKER = CHECKER this.TRACKS = TRACKS Runtime.runtime.addShutdownHook { quitFFmpeg() } } volatile Process process volatile isliving = false volatile retry = 0 volatile split = 0 // 分割计时器 volatile check = 0 // 检测计时器 @Override void run() { if (check == 0) { def islivingurl = new URL("http://live.bilibili.com/bili/isliving/$UID?callback=isliving").text isliving = !islivingurl.contains(/"data":""/) } check++ if (check == CHECKER) { check = 0 } if (isliving) { // 检测到正在直播 if (process == null) { // 当前没有ffmpeg进程 Thread thread = new Thread({ // 创建一个线程运行ffmpeg,防止阻塞监控线程 @@ -48,7 +62,6 @@ class RecorderThread implements Runnable { "-i", "$m3u8", "-c", "copy", "-bsf:a", "aac_adtstoasc", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.mp4"] process = command.execute() process.waitForProcessOutput System.out, System.err @@ -62,6 +75,12 @@ class RecorderThread implements Runnable { } }) thread.start() // 启动录制线程 } else { // 当前正在录制,开始按时长分隔,防止ffmpeg录制出错时无法检测到 split++ if (split == TRACKS) { split = 0 quitFFmpeg() } } } else { if (process == null) { -
sunny00123 revised this gist
Aug 19, 2016 . 1 changed file with 4 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -44,12 +44,12 @@ class RecorderThread implements Runnable { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" def m3u8 = matcher.group 1 String[] command = [FFMPEG, "-re", "-i", "$m3u8", "-c", "copy", "-bsf:a", "aac_adtstoasc", // "-f", "flv", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.mp4"] process = command.execute() process.waitForProcessOutput System.out, System.err } else { -
sunny00123 revised this gist
Aug 18, 2016 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,11 +1,11 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit def UID = "276904" // B站UID def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def DELAY = 30 // 直播检测线程的调度间隔,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.addShutdownHook { scheduledExecutorService.shutdown() } @@ -33,9 +33,9 @@ class RecorderThread implements Runnable { void run() { def islivingurl = new URL("http://live.bilibili.com/bili/isliving/$UID?callback=isliving").text def isliving = !islivingurl.contains(/"data":""/) if (isliving) { // 检测到正在直播 if (process == null) { // 当前没有ffmpeg进程 Thread thread = new Thread({ // 创建一个线程运行ffmpeg,防止阻塞监控线程 def h5play = new URL("http://live.bilibili.com/api/h5playurl?roomid=$ROOMID").text def matcher = h5play =~ /"url":"(.+)"/ if (matcher.find()) { @@ -61,7 +61,7 @@ class RecorderThread implements Runnable { } } }) thread.start() // 启动录制线程 } } else { if (process == null) { -
sunny00123 revised this gist
Aug 18, 2016 . No changes.There are no files selected for viewing
-
sunny00123 revised this gist
Aug 18, 2016 . 1 changed file with 71 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,69 +3,83 @@ import java.util.concurrent.TimeUnit def UID = "276904" // B站用户UID def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def DELAY = 30 // 监控线程的间隔,单位:秒 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() scheduledExecutorService.addShutdownHook { scheduledExecutorService.shutdown() } scheduledExecutorService.scheduleWithFixedDelay(new RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG), 0, DELAY, TimeUnit.SECONDS) class RecorderThread implements Runnable { def UID def ROOMID def OUTPUTDIR def FFMPEG RecorderThread(UID, ROOMID, OUTPUTDIR, FFMPEG) { this.UID = UID this.ROOMID = ROOMID this.OUTPUTDIR = OUTPUTDIR this.FFMPEG = FFMPEG Runtime.runtime.addShutdownHook { quitFFmpeg() } } volatile Process process volatile retry = 0 @Override void run() { def islivingurl = new URL("http://live.bilibili.com/bili/isliving/$UID?callback=isliving").text def isliving = !islivingurl.contains(/"data":""/) if (isliving) { if (process == null) { Thread thread = new Thread({ def h5play = new URL("http://live.bilibili.com/api/h5playurl?roomid=$ROOMID").text def matcher = h5play =~ /"url":"(.+)"/ if (matcher.find()) { retry = 0 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在直播中" println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" def m3u8 = matcher.group 1 String[] command = [FFMPEG, "-i", "$m3u8", "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", "-f", "flv", "${OUTPUTDIR}${File.separator}${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.flv"] process = command.execute() process.waitForProcessOutput System.out, System.err } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址" retry++ if (retry == 10) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址,重试已达上限" System.exit 1 } } }) thread.start() } } else { if (process == null) { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 直播关闭了" quitFFmpeg() } } } void quitFFmpeg() { if (process != null && process.alive) { def writer = new BufferedWriter(new OutputStreamWriter(process.outputStream)) writer.write "q" writer.flush() process = null println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 已退出录制" } } } -
sunny00123 revised this gist
Aug 14, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -52,7 +52,7 @@ scheduledExecutorService.scheduleWithFixedDelay({ thread.start() } } else { // 未检测到直播 if (process == null || !process.alive) { // 如果当前没有ffmpeg的进程,则没有直播 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { // 如果当前有ffmpeg的进程,发送q到ffmpeg使其退出 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 直播关闭了" -
sunny00123 created this gist
Aug 14, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,71 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit def UID = "276904" // B站用户UID def ROOMID = "131985" // 直播间的房间编号,不是地址编号 def OUTPUTDIR = /D:\Users\Sun\Desktop\ffmpeg\bin/ // 录制文件输出目录 def FFMPEG = /D:\Users\Sun\Desktop\ffmpeg\bin\ffmpeg.exe/ // ffmpeg可执行程序位置 def DELAY = 30 // SECONDS // 监控线程的间隔,当检测到直播时开启一个ffmpeg进程进行录制 def scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() def counter = 0 def isliving = false def isrecording = false Process process scheduledExecutorService.scheduleWithFixedDelay({ def islivingurl = new URL("http://live.bilibili.com/bili/isliving/$UID").text isliving = !islivingurl.contains(/"data":""/) if (isliving) { // 开始直播了 if (!isrecording) { // 如果没有录制,则开始录制 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 正在直播中" println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 开始录制了" isrecording = true Thread thread = new Thread({ // 开启一个守护线程去启动ffmpeg的进程防止阻塞监控线程 def h5play = new URL("http://live.bilibili.com/api/h5playurl?roomid=$ROOMID").text def matcher = h5play =~ /"url":"(.+)"/ if (matcher.find()) { def m3u8 = matcher.group(1) String[] command = [FFMPEG, "-i", "$m3u8", "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", "-f", "mp4", "${Calendar.getInstance().format("yyyy-MM-dd-HH-mm-ss")}.mp4"] process = command.execute null, new File(OUTPUTDIR) process.addShutdownHook { // 监听ctrl+c的事件,退出时发送q到ffmpeg进程,避免出现 moov atom not found 错误 quitFFmpeg process scheduledExecutorService.shutdown() } process.waitForProcessOutput System.out, System.err } else { println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 无法获取直播流地址" counter++ if (counter == 10) { // 无法获取直播地址时,退出程序 quitFFmpeg process scheduledExecutorService.shutdown() } } }) thread.setDaemon(true) thread.start() } } else { // 未检测到直播 if (!process.alive) { // 如果当前没有ffmpeg的进程,则没有直播 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 还没有直播" } else { // 如果当前有ffmpeg的进程,发送q到ffmpeg使其退出 println "[${Calendar.getInstance().format("yyyy-MM-dd HH:mm:ss")}] 直播关闭了" quitFFmpeg process isrecording = false } } }, 0, DELAY, TimeUnit.SECONDS) static void quitFFmpeg(Process process) { if (process.alive) { def writer = new BufferedWriter(new OutputStreamWriter(process.outputStream)) writer.write("q") writer.flush() } }