树莓派实现24小时直播点歌功能

此文章已经十分久远,请直接去看
【重写】树莓派驱动的b站点播台

【重写】树莓派驱动的b站点播台


最新的文章已经把代码更新为弹幕点歌了,并且在GitHub持续更新。

在这篇文章发布之前,我在it水家已经投过一稿了:http://www.ithome.com/html/win10/311694.htm

另外上一篇不带点歌功能,只有推流功能的教程:https://www.chenxublog.com/2017/06/02/raspi-live-24h-bilibili.html

如果是零基础,建议先看一遍上面那两篇文章,如果是使用代码,请参考本文。这几天代码已经更新多次。

我写的代码只能保证可用,毕竟我是业余的233333欢迎大神拿我的思路进行优化

github:https://github.com/chenxuuu/24h-raspberry-live-on-bilibili

php端运行的代码(最后更新于2017.6.4晚):

<head>
<meta charset="utf-8">
</head>
<body>
<form action="" method="post">
<p>一个简陋的点歌台23333</p>
<p>搜索结果来自网易云</p>
<p>输入歌曲名搜索歌曲:<input type="text" name="song" /></p>
<p><input type="submit" name="sub" value="搜索" /></p>
</form>
<form action="" method="post">
<p>或者直接输入id点歌(推荐!)<br/>实例(红色部分为id):<br/>http://music.163.com/song/<font color="red">26489014</font>/?userid=261620056<br/>http://music.163.com/#/song?id=<font color="red">32477053</font></p>
<p>id:<input type="text" name="id" /></p>
<p><input type="submit" name="sub" value="查看" /></p>
</form>
by 晨旭/chenxublog.com | running on Raspberry Pi 2 Model B<br/>直播间地址:http://live.bilibili.com/16703<br/><br/><br/>
当前状态:<div id='t'></div>
<?php
require_once 'NeteaseMusicAPI_mini.php';

function get_url_id($id)
{
    $api = new NeteaseMusicAPI();
    $result = $api->url($id);
    $result = str_replace("[","",$result);
    $result = str_replace("]","",$result);
    $data=json_decode($result, true);
    return $data['data']['url'];
}
function urlcheck($url)
{
    $counttemp=0;
    for($counttemp=1;$counttemp<31;$counttemp++)
    {
        if (file_exists($counttemp.'.txt') && $url==file_get_contents($counttemp.'.txt'))   
        {   
            return false;
        }
    }
    return true;
}
function netease_http($url)
{
    $refer = "http://music.163.com/";
    $header[] = "Cookie: " . "appver=1.5.0.75771;";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
    curl_setopt($ch, CURLOPT_REFERER, $refer);
    $cexecute = curl_exec($ch);
    curl_close($ch);
    if ($cexecute)
    {
        $result = json_decode($cexecute, true);
        return $result;
    }
    else
    {
        return false;
    }
}
function getStatus($url){
    if($headers = @get_headers($url)){
            $status  = $headers[0];
            $statusno= false;
            if(preg_match_all('%HTTP/1\.1 ([\d]{3})%i',$status,$matches)){
                    $statusno = $matches[1][0];
            }
            return $statusno;
    }
        return false;
}

if(!empty($_POST['song'])){

$song=$_POST['song'];
echo '搜索关键词:'.$song.'<br/>=========================================================<br/>';
$url = "http://s.music.163.com/search/get/?type=1&s=".$song;
$response = netease_http($url);
$counttemp=0;
for($counttemp=0;$counttemp<10;$counttemp++)
{
    if(getStatus($response['result']['songs'][$counttemp]['audio'])=='200')
    {
        echo $response['result']['songs'][$counttemp]['name'].'<img src="'.$response['result']['songs'][$counttemp]['album']['picUrl'].'" height="100"/><a href="?down='.$response['result']['songs'][$counttemp]['audio'].'">就选这首了</a><br/>试听:<audio src="'.$response['result']['songs'][$counttemp]['audio'].'" controls="controls"></audio><br/>=========================================================<br/>';
    }
    else
    {
        echo '第'.($counttemp+1).'首歌曲获取失败(歌曲失效/版权问题/网络抽风)<br/>=========================================================<br/>';
    }
}

}
elseif(!empty($_POST['id'])){
    echo '试听:<audio src="'.get_url_id($_POST['id']).'" controls="controls"></audio><br/><a href="?down='.get_url_id($_POST['id']).'">就选这首了</a>';
}
elseif(!empty($_GET['down']) && empty($_GET['write']) && strpos($_GET['down'],"music.126.net")!=false && getStatus($_GET['down'])=='200')
{
    echo '当前可供选择的空闲的序列(注意,直播是从第1首到第30首按顺序播放的。歌曲长度最大限制六分钟,超过将丢弃。):<br/>';
    $counttemp=0;
    for($counttemp=1;$counttemp<31;$counttemp++)
    {
        if($counttemp%5==0)
        {
            echo '<br/>';
        }
        if (!file_exists($counttemp.'.txt'))
        {
            echo '<a href="?write='.$counttemp.'&down='.$_GET['down'].'">换掉第'.$counttemp.'首歌</a>(<font color="green">可以换歌</font>)|';
        }
        else
        {
            echo '第'.$counttemp.'首歌(<font color="red">排队渲染中</font>)|';
        }
    }
}
elseif(!empty($_GET['write']) && !empty($_GET['down']) && urlcheck($_GET['down']) && getStatus($_GET['down'])=='200')
{
    if(!file_exists($_GET['write'].'.txt'))
    {
        //file_put_contents('songs/'.$_GET['write'].'.txt', $_GET['down']);
        $myfile = fopen($_GET['write'].'.txt', "w") or die("Unable to open file!");
        fwrite($myfile, $_GET['down']);
        fclose($myfile);
        echo '第'.$_GET['write'].'首歌曲的渲染请求提交成功!<a href="index.php">返回首页</a>';
    }
    else
    {
        echo '啊哦!这个序号已经有人选中了!正在渲染!';
    }
}
else
{
    echo '树莓当前负载状态(注意,直播是从第1首到第30首按顺序播放的):<br/>';
    $counttemp=0;
    for($counttemp=1;$counttemp<31;$counttemp++)
    {
        if($counttemp%5==0)
        {
            echo '<br/>';
        }
        if (!file_exists($counttemp.'.txt'))
        {
            echo '第'.$counttemp.'首歌(<font color="green">可以换掉</font>)|';
        }
        else
        {
            echo '第'.$counttemp.'首歌(<font color="red">排队渲染中</font>)|';
        }
    }
}

?>
<script type="text/javascript" src="now.js"></script>
</body>

同目录下需要新建一个NeteaseMusicAPI_mini.php,文件取自:

https://github.com/metowolf/NeteaseCloudMusicApi/blob/master/weapi/NeteaseMusicAPI_mini.php

php文件我全部放到了/usr/share/nginx/www/songs目录下。

渲染处理部分:

ff.py(路径/home/pi/songs/)

# -*- coding:utf-8 -*-
import os
import urllib
import eyed3
import time
for i in range(1, 30+1):
  if(os.path.exists(str(i)+'.mp3')):
    os.remove(str(i)+'.mp3')
    os.remove('/usr/share/nginx/www/songs/'+str(i)+'.txt')
  if(os.path.exists('/usr/share/nginx/www/songs/'+str(i)+'.txt')):
    f = open('/usr/share/nginx/www/songs/'+str(i)+'.txt')
    content = f.read()
    fileout = file('/usr/share/nginx/www/songs/now.js','w')
    fileout.write('t.innerHTML=("正在下载'+str(i)+'.mp3")')
    fileout.close()
    urllib.urlretrieve(content, str(i)+'.mp3')
    print('download success')
    xx=eyed3.load(str(i)+'.mp3')
    seconds=xx.info.time_secs
    if(seconds<600):
      fileout = file('/usr/share/nginx/www/songs/now.js','w')
      fileout.write('t.innerHTML=("正在生成'+str(i)+'.mp4的一图流视频 第一步/共两步")')
      fileout.close()
      os.system('ffmpeg -loop 1 -r 1 -t '+str(seconds)+' -f image2 -i '+str(i)+'.png -vcodec libx264 -pix_fmt yuv420p -crf 24 -y SinglePictureVideo.mp4')
      fileout = file('/usr/share/nginx/www/songs/now.js','w')
      fileout.write('t.innerHTML=("正在将'+str(i)+'.flv的视频与音频合为一体 第二步/共两步")')
      fileout.close()
      os.system('ffmpeg -i SinglePictureVideo.mp4 -i '+str(i)+'.mp3 -c:v copy -c:a aac -y '+str(i)+'.flv')
      os.remove(str(i)+'.mp3')
      os.remove('SinglePictureVideo.mp4')
      os.remove('/usr/share/nginx/www/songs/'+str(i)+'.txt')
      fileout = file('/usr/share/nginx/www/songs/now.js','w')
      fileout.write('t.innerHTML=("成功渲染'+str(i)+'.flv!60秒后会开始渲染下一个视频")')
      fileout.close()
    else:
      os.remove(str(i)+'.mp3')
      os.remove('/usr/share/nginx/www/songs/'+str(i)+'.txt')
    time.sleep(60)
time.sleep(10)

ff.sh(路径/home/pi/songs/)

#!/bin/bash
while true
do
 python ff.py
done

路径/home/pi/songs/下还有1-30.png文件用于作为视频内容

启动ffmpeg部分:

playlist.txt请自己写吧,规则见https://trac.ffmpeg.org/wiki/Concatenate

live.sh(与playlist.txt同级)

#!/bin/bash
while true
do
  ffmpeg -re -f concat -safe 0 -i playlist.txt -vcodec copy -acodec aac -b:a 192k -f flv "你的直播地址和码"
done

理论上到这里就结束了,但是网络老断,那我只能暴力检测和强制重启推流来解决了

断网自动重推部分:

net.py

#coding:utf8
'''python3 code
author's email: chenyan@feling.net
通过统计ifconfig命令的输出,计算当前网速
'''

import logging
logging.basicConfig(level=logging.INFO,
                format='%(message)s',
                #filename='speed',
                #filemod='w'
                )

import os, sys, time
import re

def get_total_tx_bytes(interface, isCN):
    grep = '发送字节' if isCN else '"TX bytes"'
    r = os.popen('ifconfig '+interface+' | grep '+grep).read()
    total_bytes = re.sub('(.+:)| \(.+','',r)
    return int(total_bytes)

def get_total_rx_bytes(interface, isCN):
    grep = '接收字节' if isCN else '"RX bytes"'
    r = os.popen('ifconfig '+interface+' | grep '+grep).read()
    total_bytes = re.sub(' \(.+','',r)
    total_bytes = re.sub('.+?:','',total_bytes)
    return int(total_bytes)


if __name__=='__main__':
    interface = sys.argv[1]
    get_total_bytes = get_total_tx_bytes if sys.argv[2]=='tx' else get_total_rx_bytes
    isCN = True if sys.argv[3]=='cn' else False
    freq = int(sys.argv[4])
    low_count = 0
    for i in range(1, 10 + 1):
        last = get_total_bytes(interface, isCN)
        time.sleep(freq)
        increase = get_total_bytes(interface, isCN) - last
        logging.info(str(increase/freq/1000))
        speed_now = increase/freq/1000
        if(speed_now < 5):
            low_count = low_count +1
    if(low_count > 5):
        os.system('killall ffmpeg')
        time.sleep(1)
        os.system('killall ffmpeg')
        time.sleep(1)
        os.system('killall ffmpeg')
        logging.info('666')
    else:
        logging.info('ok!')

net.sh

#!/bin/bash
while true
do
python net.py eth0 tx false 1
done

这样所有我用的脚本都贴在这里了,启动方式就是扔几个screen就行了

screen sh live.sh
#按ctrl+a,ctrl+d
screen sh net.sh
#按ctrl+a,ctrl+d
cd songs/
screen sh ff.sh
#按ctrl+a,ctrl+d

完毕~

技术不高,如有错误,请指出,谢谢o( ̄▽ ̄)ブ

22 Comments

          1. Google Chrome 58.0.3029.110 Google Chrome 58.0.3029.110 Windows 10 x64 Edition Windows 10 x64 Edition

            我换了很多个系统,Debian和Ubuntu都用过了,就是在安装解码器时执行
            ./configure –host=arm-unknown-linux-gnueabi –enable-static –disable-opencl
            会提示No working C compiler found.,我谷歌了也找不到有效的解决方法,请问你有没有遇到这种情况?

              1. Google Chrome 58.0.3029.110 Google Chrome 58.0.3029.110 Windows 10 x64 Edition Windows 10 x64 Edition

                我用的centos 安装上了ffmpeg 可以的 但在配置nginx的时候一直出各种各样的问题

      1. Google Chrome 58.0.3029.110 Google Chrome 58.0.3029.110 Mac OS X  10.12.0 Mac OS X 10.12.0

        好像有个h264_omx硬解码,可当初编译FFmpeg时候没有开启。用重编FFmpeg心好累啊。
        h264_omx有帮助的吧。看到有人树莓派直播CPU占用只用了5%。

  1. Google Chrome 59.0.3071.86 Google Chrome 59.0.3071.86 Windows 10 x64 Edition Windows 10 x64 Edition

    参考博主的搭建了一个能点播MV的:https://aoaoao.me/live/index.php 直播地址:http://live.bilibili.com/3368800

  2. Google Chrome 56.0.2924.87 Google Chrome 56.0.2924.87 Windows 10 x64 Edition Windows 10 x64 Edition

    代码高亮体验极差,无法复制,只能复制单行,因为每行代码是个单独的标签,平板、电脑均为此状

      1. Google Chrome 56.0.2924.87 Google Chrome 56.0.2924.87 Windows 10 x64 Edition Windows 10 x64 Edition

        双击没提示,而且也很奇怪,不顺手,像其他网站右上角一键复制比较好。点到别的页面我无力吐槽。关了js我才复制的。真心不推荐一行一个标签

        1. Google Chrome 61.0.3159.5 Google Chrome 61.0.3159.5 Windows 10 x64 Edition Windows 10 x64 Edition

          反正双击过后会出现一个单独的文本框,默认全选
          代码高亮是编辑器自带的,我没装过代码高亮插件

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注