dever 6 years ago
parent
commit
b987f17bb0

+ 14 - 0
video/sdk/Pili_v2.php

@@ -0,0 +1,14 @@
+<?php
+
+$root = dirname(__FILE__);
+
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Utils.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'HttpResponse.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'HttpRequest.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Mac.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Config.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Transport.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Hub.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Stream.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'Client.php')));
+require(join(DIRECTORY_SEPARATOR, array($root, 'Qiniu/Pili', 'RoomClient.php')));

+ 18 - 0
video/sdk/Qiniu/Pili/Client.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Qiniu\Pili;
+
+class Client
+{
+    private $_mac;
+
+    public function __construct($mac)
+    {
+        $this->_mac=$mac;
+    }
+
+    public function hub($hubname)
+    {
+        return new Hub($this->_mac, $hubname);
+    }
+}

+ 50 - 0
video/sdk/Qiniu/Pili/Config.php

@@ -0,0 +1,50 @@
+<?php
+namespace Qiniu\Pili;
+
+final class Config
+{
+    const SDK_VERSION = '2.1.1';
+    const SDK_USER_AGENT = 'pili-sdk-php';
+
+    public $USE_HTTPS = false;
+    public $API_HOST = 'pili.qiniuapi.com';
+    public $API_VERSION = 'v2';
+
+    public $RTCAPI_HOST = 'http://rtc.qiniuapi.com';
+    public $RTCAPI_VERSION = 'v2';   //连麦版本号,可以设置 "v1" 和 "v2"
+
+    protected static $_instance = null;
+
+    protected function __construct()
+    {
+    }
+
+    protected function __clone()
+    {
+    }
+
+    public static function getInstance()
+    {
+        if (!(self::$_instance instanceof self)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function __get($property)
+    {
+        if (property_exists(self::getInstance(), $property)) {
+            return self::getInstance()->$property;
+        } else {
+            return null;
+        }
+    }
+
+    public function __set($property, $value)
+    {
+        if (property_exists(self::getInstance(), $property)) {
+            self::getInstance()->$property = $value;
+        }
+        return self::getInstance();
+    }
+}

+ 178 - 0
video/sdk/Qiniu/Pili/HttpRequest.php

@@ -0,0 +1,178 @@
+<?php
+namespace Qiniu\Pili;
+
+use \Qiniu\Pili\HttpResponse;
+
+class HttpRequest
+{
+    const DELETE = "DELETE";
+    const GET    = "GET";
+    const POST   = "POST";
+
+    private static $verifyPeer = false;
+    private static $socketTimeout = null;
+    private static $defaultHeaders = array();
+
+    /**
+     * Verify SSL peer
+     * @param bool $enabled enable SSL verification, by default is false
+     */
+    public static function verifyPeer($enabled)
+    {
+        self::$verifyPeer = $enabled;
+    }
+
+    /**
+     * Set a timeout
+     * @param integer $seconds timeout value in seconds
+     */
+    public static function timeout($seconds)
+    {
+        self::$socketTimeout = $seconds;
+    }
+
+    /**
+     * Set a new default header to send on every request
+     * @param string $name header name
+     * @param string $value header value
+     */
+    public static function defaultHeader($name, $value)
+    {
+        self::$defaultHeaders[$name] = $value;
+    }
+
+    /**
+     * Clear all the default headers
+     */
+    public static function clearDefaultHeaders()
+    {
+        self::$defaultHeaders = array();
+    }
+
+    /**
+     * This function is useful for serializing multidimensional arrays, and avoid getting
+     * the "Array to string conversion" notice
+     */
+    public static function http_build_query_for_curl($arrays, &$new = array(), $prefix = null)
+    {
+        if (is_object($arrays)) {
+            $arrays = get_object_vars($arrays);
+        }
+        foreach ($arrays as $key => $value) {
+            $k = isset($prefix) ? $prefix . '[' . $key . ']' : $key;
+            if (!$value instanceof \CURLFile and (is_array($value) or is_object($value))) {
+                self::http_build_query_for_curl($value, $new, $k);
+            } else {
+                $new[$k] = $value;
+            }
+        }
+    }
+
+    private static function getArrayFromQuerystring($querystring)
+    {
+        $pairs = explode("&", $querystring);
+        $vars  = array();
+        foreach ($pairs as $pair) {
+            $nv          = explode("=", $pair, 2);
+            $name        = $nv[0];
+            $value       = $nv[1];
+            $vars[$name] = $value;
+        }
+        return $vars;
+    }
+
+    /**
+     * Ensure that a URL is encoded and safe to use with cURL
+     * @param  string $url URL to encode
+     * @return string
+     */
+    private static function encodeUrl($url)
+    {
+        $url_parsed = parse_url($url);
+        $scheme = $url_parsed['scheme'] . '://';
+        $host   = $url_parsed['host'];
+        $port   = (isset($url_parsed['port']) ? $url_parsed['port'] : null);
+        $path   = (isset($url_parsed['path']) ? $url_parsed['path'] : null);
+        $query  = (isset($url_parsed['query']) ? $url_parsed['query'] : null);
+        if ($query != null) {
+            $query = '?' . http_build_query(self::getArrayFromQuerystring($url_parsed['query']));
+        }
+        if ($port && $port[0] != ":") {
+            $port = ":" . $port;
+        }
+        $result = $scheme . $host . $port . $path . $query;
+        return $result;
+    }
+
+    private static function getHeader($key, $val)
+    {
+        $key = trim($key);
+        return $key . ": " . $val;
+    }
+
+    /**
+     * Send a cURL request
+     * @param string $httpMethod HTTP method to use
+     * @param string $url URL to send the request to
+     * @param mixed $body request body
+     * @param array $headers additional headers to send
+     * @throws Exception if a cURL error occurs
+     * @return HttpResponse
+     */
+    public static function send($httpMethod, $url, $body = null, $headers = array())
+    {
+        if ($headers == null) {
+            $headers = array();
+        }
+        $annexHeaders = array();
+        $finalHeaders = array_merge($headers, self::$defaultHeaders);
+        foreach ($finalHeaders as $key => $val) {
+            $annexHeaders[] = self::getHeader($key, $val);
+        }
+        $lowerCaseFinalHeaders = array_change_key_case($finalHeaders);
+        $ch = curl_init();
+        if ($httpMethod != self::GET) {
+            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
+            if (is_array($body) || $body instanceof Traversable) {
+                self::http_build_query_for_curl($body, $postBody);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
+            } else {
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+            }
+        } elseif (is_array($body)) {
+            if (strpos($url, '?') !== false) {
+                $url .= "&";
+            } else {
+                $url .= "?";
+            }
+            self::http_build_query_for_curl($body, $postBody);
+            $url .= urldecode(http_build_query($postBody));
+        }
+        curl_setopt($ch, CURLOPT_URL, self::encodeUrl($url));
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+        curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $annexHeaders);
+        curl_setopt($ch, CURLOPT_HEADER, true);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::$verifyPeer);
+        curl_setopt($ch, CURLOPT_ENCODING, ""); // If an empty string, "", is set, a header containing all supported encoding types is sent.
+        if (self::$socketTimeout != null) {
+            curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
+        }
+        $response = curl_exec($ch);
+        $error    = curl_error($ch);
+        if ($error) {
+            throw new \Exception($error);
+        }
+        // Split the full response in its headers and body
+        $curl_info   = curl_getinfo($ch);
+        $header_size = $curl_info["header_size"];
+        $header      = substr($response, 0, $header_size);
+        $body        = substr($response, $header_size);
+        $httpCode    = $curl_info["http_code"];
+        if ($httpCode >= 400) {
+            throw new \Exception($body);
+        }
+        return new HttpResponse($httpCode, $body, $header);
+    }
+}

+ 71 - 0
video/sdk/Qiniu/Pili/HttpResponse.php

@@ -0,0 +1,71 @@
+<?php
+namespace Qiniu\Pili;
+
+class HttpResponse
+{
+    private $code;
+    private $raw_body;
+    private $body;
+    private $headers;
+
+    /**
+     * @param int $code response code of the cURL request
+     * @param string $raw_body the raw body of the cURL response
+     * @param string $headers raw header string from cURL response
+     */
+    public function __construct($code, $raw_body, $headers)
+    {
+        $this->code     = $code;
+        $this->headers  = $this->get_headers_from_curl_response($headers);
+        $this->raw_body = $raw_body;
+        $this->body     = $raw_body;
+        $json           = json_decode($raw_body, true);
+        if (json_last_error() == JSON_ERROR_NONE) {
+            $this->body = $json;
+        }
+    }
+
+    /**
+     * Return a property of the response if it exists.
+     * Possibilities include: code, raw_body, headers, body (if the response is json-decodable)
+     * @return mixed
+     */
+    public function __get($property)
+    {
+        if (property_exists($this, $property)) {
+            return $this->$property;
+        }
+    }
+
+    /**
+     * Set the properties of this object
+     * @param string $property the property name
+     * @param mixed $value the property value
+     */
+    public function __set($property, $value)
+    {
+        if (property_exists($this, $property)) {
+            $this->$property = $value;
+        }
+        return $this;
+    }
+
+    /**
+     * Retrieve the cURL response headers from the
+     * header string and convert it into an array
+     * @param  string $headers header string from cURL response
+     * @return array
+     */
+    private function get_headers_from_curl_response($headers)
+    {
+        $headers = explode("\r\n", $headers);
+        array_shift($headers);
+        foreach ($headers as $line) {
+            if (strstr($line, ': ')) {
+                list($key, $value) = explode(': ', $line);
+                $result[$key] = $value;
+            }
+        }
+        return $result;
+    }
+}

+ 159 - 0
video/sdk/Qiniu/Pili/Hub.php

@@ -0,0 +1,159 @@
+<?php
+namespace Qiniu\Pili;
+
+use \Qiniu\Pili\HttpRequest;
+use \Qiniu\Pili\Utils;
+
+class Hub
+{
+    private $_hub;
+    private $_baseURL;
+    private $_transport;
+
+    public function __construct($mac, $hubName)
+    {
+        $this->_hub = $hubName;
+        $this->_transport = new Transport($mac);
+
+        $cfg = Config::getInstance();
+        $protocal = $cfg->USE_HTTPS === true ? "https" : "http";
+        $this->_baseURL = $protocal . "://" . $cfg->API_HOST . "/v2/hubs/" . $this->_hub;
+    }
+
+    //创建一个流对象.
+    /*
+     * PARAM
+     * @streamKey: 流名.
+     * RETURN
+     * 返回一个流对象.
+     */
+    public function create($streamKey)
+    {
+        $url = $this->_baseURL . "/streams";
+        $params['key'] = $streamKey;
+        $body = json_encode($params);
+        try {
+            $this->_transport->send(HttpRequest::POST, $url, $body);
+        } catch (\Exception $e) {
+            return $e;
+        }
+
+        return new Stream($this->_transport, $this->_hub, $streamKey);
+    }
+
+    //初始化一个流对象
+    /*
+     * PARAM
+     * @streamKey: 流名.
+     * RETURN
+     * 返回一个流对象.
+     */
+    public function stream($streamKey)
+    {
+        return new Stream($this->_transport, $this->_hub, $streamKey);
+    }
+
+    private function _list($live, $prefix, $limit, $marker)
+    {
+        $url = sprintf("%s/streams?liveonly=%s&prefix=%s&limit=%d&marker=%s", $this->_baseURL, $live, $prefix, $limit, $marker);
+        try {
+            $ret = $this->_transport->send(HttpRequest::GET, $url);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        $keys = array();
+        foreach ($ret["items"] as $item) {
+            array_push($keys, $item["key"]);
+        }
+        $marker = $ret["marker"];
+        $ret = array();
+        $ret["keys"] = $keys;
+        $ret["omarker"] = $marker;
+        return $ret;
+    }
+
+    //根据 prefix 遍历 Hub 的正在直播的流列表.
+    /*
+     * PARAM
+     * @prefix: 流名的前缀.
+     * @limit: 限定了一次最多可以返回的流个数, 实际返回的流个数可能小于这个 limit 值.
+     * @marker: 是上一次遍历得到的流标.
+     * RETURN
+     * @keys: 流名的数组.
+     * @omarker: 记录了此次遍历到的游标, 在下次请求时应该带上, 如果 omarker 为 "" 表示已经遍历完所有流.
+     */
+    public function listLiveStreams($prefix, $limit, $marker)
+    {
+        return $this->_list("true", $prefix, $limit, $marker);
+    }
+
+    //根据 prefix 遍历 Hub 的流列表.
+    /*
+     * PARAM
+     * @prefix: 流名的前缀.
+     * @limit: 限定了一次最多可以返回的流个数, 实际返回的流个数可能小于这个 limit 值.
+     * @marker: 是上一次遍历得到的流标.
+     * RETURN
+     * @keys: 流名的数组.
+     * @omarker: 记录了此次遍历到的游标, 在下次请求时应该带上, 如果 omarker 为 "" 表示已经遍历完所有流.
+     */
+    public function listStreams($prefix, $limit, $marker)
+    {
+        return $this->_list("false", $prefix, $limit, $marker);
+    }
+
+    //批量查询流直播信息.
+    /*
+     * PARAM
+     * @streamKeys: 流名数组, 最大长度为100.
+     * RETURN
+     * @items: 数组. 每个item包含一个流的直播信息.
+     *   @key: 流名.
+     *   @startAt: 直播开始的 Unix 时间戳, 0 表示当前没在直播.
+     *   @clientIP: 直播的客户端 IP.
+     *   @bps: 直播的码率.
+     *   @fps: 直播的帧率.
+     */
+    public function batchLiveStatus($streamKeys)
+    {
+        $url = $this->_baseURL . "/livestreams";
+        $params['items'] = $streamKeys;
+        $body = json_encode($params);
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+}
+
+//----------------url
+//生成 RTMP 推流地址.
+//expireAfterSeconds 表示 URL 在多久之后失效.
+function RTMPPublishURL($domain, $hub, $streamKey, $expireAfterSeconds, $accessKey, $secretKey)
+{
+    $expire = time() + $expireAfterSeconds;
+    $path = sprintf("/%s/%s?e=%d", $hub, $streamKey, $expire);
+    $token = $accessKey . ":" . Utils::sign($secretKey, $path);
+    return sprintf("rtmp://%s%s&token=%s", $domain, $path, $token);
+}
+
+//生成 RTMP 直播地址.
+function RTMPPlayURL($domain, $hub, $streamKey)
+{
+    return sprintf("rtmp://%s/%s/%s", $domain, $hub, $streamKey);
+}
+
+//生成 HLS 直播地址.
+function HLSPlayURL($domain, $hub, $streamKey)
+{
+    return sprintf("http://%s/%s/%s.m3u8", $domain, $hub, $streamKey);
+}
+
+//生成 HDL 直播地址.
+function HDLPlayURL($domain, $hub, $streamKey)
+{
+    return sprintf("http://%s/%s/%s.flv", $domain, $hub, $streamKey);
+}
+
+//生成直播封面地址.
+function SnapshotPlayURL($domain, $hub, $streamKey)
+{
+    return sprintf("http://%s/%s/%s.jpg", $domain, $hub, $streamKey);
+}

+ 40 - 0
video/sdk/Qiniu/Pili/Mac.php

@@ -0,0 +1,40 @@
+<?php
+namespace Qiniu\Pili;
+
+class Mac
+{
+    public $_accessKey;
+    public $_secretKey;
+
+    public function __construct($accessKey, $secretKey)
+    {
+        $this->_accessKey = $accessKey;
+        $this->_secretKey = $secretKey;
+    }
+
+    public function MACToken($method, $url, $contentType, $body)
+    {
+        $url = parse_url($url);
+        $data = '';
+        if (!empty($url['path'])) {
+            $data = $method . ' ' . $url['path'];
+        }
+        if (!empty($url['query'])) {
+            $data .= '?' . $url['query'];
+        }
+        if (!empty($url['host'])) {
+            $data .= "\nHost: " . $url['host'];
+            if (isset($url['port'])) {
+                $data .= ':' . $url['port'];
+            }
+        }
+        if (!empty($contentType)) {
+            $data .= "\nContent-Type: " . $contentType;
+        }
+        $data .= "\n\n";
+        if (!empty($body)) {
+            $data .= $body;
+        }
+        return 'Qiniu ' . $this->_accessKey . ':' . Utils::sign($this->_secretKey, $data);
+    }
+}

+ 119 - 0
video/sdk/Qiniu/Pili/RoomClient.php

@@ -0,0 +1,119 @@
+<?php
+namespace Qiniu\Pili;
+
+class RoomClient
+{
+    private $_transport;
+    private $_mac;
+    private $_baseURL;
+
+    public function __construct($mac)
+    {
+        $this->_mac = $mac;
+        $this->_transport = new Transport($mac);
+
+        $cfg = Config::getInstance();
+        $this->_baseURL = sprintf("%s/%s/rooms", $cfg->RTCAPI_HOST, $cfg->RTCAPI_VERSION);
+    }
+
+    /*
+     * ownerId: 要创建房间的所有者
+     * roomName: 房间名称
+     */
+    public function createRoom($ownerId, $roomName = null)
+    {
+        $params['owner_id'] = $ownerId;
+        if (!empty($roomName)) {
+            $params['room_name'] = $roomName;
+        }
+        $body = json_encode($params);
+        try {
+            $ret = $this->_transport->send(HttpRequest::POST, $this->_baseURL, $body);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        return $ret;
+    }
+
+    /*
+     * roomName: 房间名称
+     */
+    public function getRoom($roomName)
+    {
+        $url = $this->_baseURL . '/' . $roomName;
+        try {
+            $ret = $this->_transport->send(HttpRequest::GET, $url);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        return $ret;
+    }
+
+    /*
+     * roomName: 房间名称
+     */
+    public function deleteRoom($roomName)
+    {
+        $url = $this->_baseURL . '/' . $roomName;
+        try {
+            $ret = $this->_transport->send(HttpRequest::DELETE, $url);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        return $ret;
+    }
+
+    /*
+     * 获取房间的人数
+     * roomName: 房间名称
+     */
+    public function getRoomUserNum($roomName)
+    {
+        $url = sprintf("%s/%s/users", $this->_baseURL, $roomName);
+        try {
+            $ret = $this->_transport->send(HttpRequest::GET, $url);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        return $ret;
+    }
+
+   /*
+    * 踢出玩家
+    * roomName: 房间名称
+    * userId: 请求加入房间的用户ID
+    */
+    public function kickingPlayer($roomName, $UserId)
+    {
+        $url = sprintf("%s/%s/users/%s", $this->_baseURL, $roomName, $UserId);
+        try {
+            $ret = $this->_transport->send(HttpRequest::DELETE, $url);
+        } catch (\Exception $e) {
+            return $e;
+        }
+        return $ret;
+    }
+
+    /*
+     * roomName: 房间名称
+     * userId: 请求加入房间的用户ID
+     * perm: 该用户的房间管理权限,"admin""user",房间主播为"admin",拥有将其他用户移除出房间等特权。
+     * expireAt: int64类型,鉴权的有效时间,传入秒为单位的64位Unix时间,token将在该时间后失效。
+     */
+    public function roomToken($roomName, $userId, $perm, $expireAt)
+    {
+        $ver = Config::getInstance()->RTCAPI_VERSION;
+        if ($ver === 'v2') {
+            $params['version']="2.0";
+        }
+        $params['room_name'] = $roomName;
+        $params['user_id'] = $userId;
+        $params['perm'] = $perm;
+        $params['expire_at'] = $expireAt;
+        $roomAccessString = json_encode($params);
+        $encodedRoomAccess = Utils::base64UrlEncode($roomAccessString);
+        $sign = hash_hmac('sha1', $encodedRoomAccess, $this->_mac->_secretKey, true);
+        $encodedSign = Utils::base64UrlEncode($sign);
+        return $this->_mac->_accessKey . ":" . $encodedSign . ":" . $encodedRoomAccess;
+    }
+}

+ 189 - 0
video/sdk/Qiniu/Pili/Stream.php

@@ -0,0 +1,189 @@
+<?php
+namespace Qiniu\Pili;
+
+use \Qiniu\Pili\Utils;
+use \Qiniu\Pili\HttpRequest;
+
+class Stream
+{
+    private $_transport;
+    private $_hub;
+    private $_key;
+    private $_baseURL;
+
+    public function __construct($transport, $hub, $key)
+    {
+        $this->_transport = $transport;
+        $this->_hub = $hub;
+        $this->_key = $key;
+
+        $cfg = Config::getInstance();
+        $protocal = $cfg->USE_HTTPS === true ? "https" : "http";
+        $this->_baseURL = sprintf("%s://%s/%s/hubs/%s/streams/%s", $protocal, $cfg->API_HOST, $cfg->API_VERSION, $this->_hub, Utils::base64UrlEncode($this->_key));
+    }
+
+    //获得流信息.
+    /*
+     * RETURN
+     * @hub: Hub名.
+     * @key: 流名.
+     * @disableTill: 禁用结束的时间, 0 表示不禁用, -1 表示永久禁用.
+     * @converts: 实时转码规格.
+    */
+    public function info()
+    {
+        $resp=$this->_transport->send(HttpRequest::GET, $this->_baseURL);
+        $ret = array();
+        $ret["hub"] = $this->_hub;
+        $ret["key"] = $this->_key;
+        $ret["disabledTill"] = $resp["disabledTill"];
+        $ret["converts"] = $resp["converts"];
+        return $ret;
+    }
+
+    //在一定期限内禁止一个流.
+    /*
+     * PARAM
+     * @till: Unix 时间戳, 在这之前流均不可用.
+     */
+    public function disable($till = null)
+    {
+        $url = $this->_baseURL . "/disabled";
+        if (empty($till)) {
+            $params['disabledTill'] = -1;
+        } else {
+            $params['disabledTill'] = $till;
+        }
+        $body = json_encode($params);
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+
+    //启用一个流.
+    public function enable()
+    {
+        $url = $this->_baseURL . "/disabled";
+        $params['disabledTill'] = 0;
+        $body = json_encode($params);
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+
+    //查询直播状态.
+    /*
+     * RETURN
+     * @startAt: 直播开始的 Unix 时间戳, 0 表示当前没在直播.
+     * @clientIP: 直播的客户端 IP.
+     * @bps: 直播的码率.
+     * @fps: 直播的帧率.
+     */
+    public function liveStatus()
+    {
+        $url = $this->_baseURL . "/live";
+        return $this->_transport->send(HttpRequest::GET, $url);
+    }
+
+    //查询直播历史.
+    /*
+     * PARAM
+     * @start: Unix 时间戳, 限定了查询的时间范围, 0 值表示不限定, 系统会返回所有时间的直播历史.
+     * @end: Unix 时间戳, 限定了查询的时间范围, 0 值表示不限定, 系统会返回所有时间的直播历史.
+     * RETURN
+     * @items: 数组. 每个item包含一次推流的开始及结束时间.
+     *   @start: Unix 时间戳, 直播开始时间.
+     *   @end: Unix 时间戳, 直播结束时间.
+     */
+    public function historyActivity($start = null, $end = null)
+    {
+        $url = $this->_baseURL . "/historyrecord";
+        $flag = "?";
+        if (!empty($start)) {
+            $url = $url . $flag . "start=" . $start;
+            $flag = "&";
+        }
+        if (!empty($end)) {
+            $url = $url . $flag . "end=" . $end;
+        }
+        return $this->_transport->send(HttpRequest::GET, $url);
+    }
+
+    //保存直播回放.
+    /*
+     * PARAM
+     * @start: Unix 时间戳, 起始时间, 0 值表示不指定, 则不限制起始时间.
+     * @end: Unix 时间戳, 结束时间, 0 值表示当前时间.
+     * RETURN
+     * @fname: 保存到bucket里的文件名, 由系统生成.
+     */
+    public function save($start = null, $end = null)
+    {
+        $url = $this->_baseURL . "/saveas";
+        if (!empty($start)) {
+            $params['start'] = $start;
+        }
+        if (!empty($end)) {
+            $params['end'] = $end;
+        }
+        $body = json_encode($params);
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+
+    //灵活度更高的保存直播回放.
+    /*
+     * PARAM
+     * @fname: 保存的文件名, 不指定会随机生成.
+     * @start: Unix 时间戳, 起始时间, 0 值表示不指定, 则不限制起始时间.
+     * @end: Unix 时间戳, 结束时间, 0 值表示当前时间.
+     * @format: 保存的文件格式, 默认为m3u8.
+     * @pipeline: dora 的私有队列, 不指定则用默认队列.
+     * @notify: 保存成功后的回调地址.
+     * @expireDays: 对应ts文件的过期时间.
+     *   -1 表示不修改ts文件的expire属性.
+     *   0  表示修改ts文件生命周期为永久保存.
+     *   >0 表示修改ts文件的的生命周期为ExpireDays.
+     * RETURN
+     * @fname: 保存到bucket里的文件名.
+     * @persistentID: 异步模式时,持久化异步处理任务ID,通常用不到该字段.
+     */
+    public function saveas($options = null)
+    {
+        $url = $this->_baseURL . "/saveas";
+        if (!empty($options)) {
+            $body = json_encode($options);
+        } else {
+            $body = null;
+        }
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+
+    //对流进行截图并保存.
+    /*
+     * PARAM
+     * @fname: 保存的文件名, 不指定会随机生成.
+     * @time: Unix 时间戳, 保存的时间点, 默认为当前时间.
+     * @format: 保存的文件格式, 默认为jpg.
+     * RETURN
+     * @fname: 保存到bucket里的文件名.
+     */
+    public function snapshot($options = null)
+    {
+        $url = $this->_baseURL . "/snapshot";
+        if (!empty($options)) {
+            $body = json_encode($options);
+        } else {
+            $body = null;
+        }
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+
+    //更改流的实时转码规格
+    /*
+     * PARAM
+     * @profiles: 实时转码规格. array("480p", "720p")
+     */
+    public function updateConverts($profiles)
+    {
+        $url = $this->_baseURL . "/converts";
+        $params['converts'] = $profiles;
+        $body = json_encode($params);
+        return $this->_transport->send(HttpRequest::POST, $url, $body);
+    }
+}

+ 38 - 0
video/sdk/Qiniu/Pili/Transport.php

@@ -0,0 +1,38 @@
+<?php
+namespace Qiniu\Pili;
+
+use \Qiniu\Pili\Utils;
+use \Qiniu\Pili\HttpRequest;
+
+final class Transport
+{
+    private $_mac;
+
+    public function __construct($mac)
+    {
+        $this->_mac = $mac;
+    }
+
+    public function send($method, $url, $body = null)
+    {
+        $headers = $this->_setHeaders($method, $url, $body);
+        $response = HttpRequest::send($method, $url, $body, $headers);
+        return $response->body;
+    }
+
+    private function _setHeaders($method, $url, $body = null)
+    {
+        if ($method != HttpRequest::GET) {
+            $cType = 'application/json';
+        } else {
+            $cType = null;
+        }
+        $macToken = $this->_mac->MACToken($method, $url, $cType, $body);
+        $ua = Utils::getUserAgent(Config::SDK_USER_AGENT, Config::SDK_VERSION);
+        return array(
+            'Content-Type'  => $cType,
+            'User-Agent'    => $ua,
+            'Authorization' => $macToken,
+        );
+    }
+}

+ 40 - 0
video/sdk/Qiniu/Pili/Utils.php

@@ -0,0 +1,40 @@
+<?php
+namespace Qiniu\Pili;
+
+final class Utils
+{
+    public static function base64UrlEncode($str)
+    {
+        $find = array('+', '/');
+        $replace = array('-', '_');
+        return str_replace($find, $replace, base64_encode($str));
+    }
+
+    public static function base64UrlDecode($str)
+    {
+        $find = array('-', '_');
+        $replace = array('+', '/');
+        return base64_decode(str_replace($find, $replace, $str));
+    }
+
+    public static function digest($secret, $data)
+    {
+        return hash_hmac('sha1', $data, $secret, true);
+    }
+
+    public static function sign($secret, $data)
+    {
+        return self::base64UrlEncode(self::digest($secret, $data));
+    }
+
+    public static function getUserAgent($sdkName, $sdkVersion)
+    {
+        $ua = sprintf("%s/%s", $sdkName, $sdkVersion);
+        if (extension_loaded('curl')) {
+            $curlVersion = curl_version();
+            $ua .= ' curl/' . $curlVersion['version'];
+        }
+        $ua .= ' PHP/' . PHP_VERSION;
+        return $ua;
+    }
+}