mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
feature: support playback
This commit is contained in:
@@ -9,12 +9,15 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"net/url"
|
||||
|
||||
"github.com/emiago/sipgo"
|
||||
"github.com/emiago/sipgo/sip"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"m7s.live/v5/pkg/config"
|
||||
"m7s.live/v5/pkg/util"
|
||||
"m7s.live/v5/plugin/gb28181pro/pb"
|
||||
gb28181 "m7s.live/v5/plugin/gb28181pro/pkg"
|
||||
)
|
||||
@@ -450,6 +453,90 @@ func (gb *GB28181ProPlugin) StartPlay(ctx context.Context, req *pb.PlayRequest)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StartPlayback 处理回放请求
|
||||
func (gb *GB28181ProPlugin) StartPlayback(ctx context.Context, req *pb.PlaybackRequest) (*pb.PlayResponse, error) {
|
||||
resp := &pb.PlayResponse{}
|
||||
gb.Info("StartPlayback request", "deviceId", req.DeviceId, "channelId", req.ChannelId, "start", req.Start, "end", req.End, "range", req.Range)
|
||||
|
||||
// 先从内存中获取设备
|
||||
device, ok := gb.devices.Get(req.DeviceId)
|
||||
if !ok && gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbDevice Device
|
||||
if err := gb.DB.Where("device_id = ?", req.DeviceId).First(&dbDevice).Error; err == nil {
|
||||
// 恢复设备的必要字段
|
||||
dbDevice.Logger = gb.With("id", req.DeviceId)
|
||||
dbDevice.channels.L = new(sync.RWMutex)
|
||||
dbDevice.plugin = gb
|
||||
device = &dbDevice
|
||||
} else {
|
||||
gb.Error("StartPlayback failed", "error", "device not found", "deviceId", req.DeviceId)
|
||||
resp.Code = 404
|
||||
resp.Message = "device not found"
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 先从内存中获取通道
|
||||
channel, ok := device.channels.Get(req.ChannelId)
|
||||
if !ok && gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbChannel gb28181.DeviceChannel
|
||||
if err := gb.DB.Where("device_id = ? AND device_db_id = ?", req.ChannelId, device.ID).First(&dbChannel).Error; err == nil {
|
||||
channel = &Channel{
|
||||
Device: device,
|
||||
Logger: device.Logger.With("channel", req.ChannelId),
|
||||
DeviceChannel: dbChannel,
|
||||
}
|
||||
device.channels.Set(channel)
|
||||
} else {
|
||||
gb.Error("StartPlayback failed", "error", "channel not found", "channelId", req.ChannelId)
|
||||
resp.Code = 404
|
||||
resp.Message = "channel not found"
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 处理时间范围
|
||||
startTime, endTime, err := util.TimeRangeQueryParse(url.Values{
|
||||
"range": []string{req.Range},
|
||||
"start": []string{req.Start},
|
||||
"end": []string{req.End},
|
||||
})
|
||||
if err != nil {
|
||||
gb.Error("StartPlayback failed", "error", "invalid time format", "err", err)
|
||||
resp.Code = 400
|
||||
resp.Message = fmt.Sprintf("invalid time format: %v", err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 构建流路径,加入时间信息以区分不同的回放请求
|
||||
streamPath := fmt.Sprintf("%s/%s/playback_%s_%s", req.DeviceId, req.ChannelId,
|
||||
startTime.Format("20060102150405"), endTime.Format("20060102150405"))
|
||||
|
||||
// 调用 Pull 方法开始拉流
|
||||
gb.Pull(streamPath, config.Pull{
|
||||
URL: streamPath,
|
||||
Args: config.HTTPValues{
|
||||
"start": []string{startTime.Format(time.RFC3339)},
|
||||
"end": []string{endTime.Format(time.RFC3339)},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
// 设置响应信息
|
||||
resp.Code = 0
|
||||
resp.Message = "success"
|
||||
resp.StreamInfo = &pb.StreamInfo{
|
||||
Stream: streamPath,
|
||||
App: "gb28181",
|
||||
Ip: device.IP,
|
||||
}
|
||||
|
||||
gb.Info("StartPlayback success", "deviceId", req.DeviceId, "channelId", req.ChannelId,
|
||||
"start", startTime.Format(time.RFC3339), "end", endTime.Format(time.RFC3339))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AddPlatform 实现添加平台信息
|
||||
func (gb *GB28181ProPlugin) AddPlatform(ctx context.Context, req *pb.Platform) (*pb.BaseResponse, error) {
|
||||
resp := &pb.BaseResponse{}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/emiago/sipgo"
|
||||
"github.com/emiago/sipgo/sip"
|
||||
"m7s.live/v5"
|
||||
"m7s.live/v5/pkg/task"
|
||||
"m7s.live/v5/pkg/util"
|
||||
gb28181 "m7s.live/v5/plugin/gb28181pro/pkg"
|
||||
@@ -240,7 +239,8 @@ func (d *Device) CreateRequest(Method sip.RequestMethod) *sip.Request {
|
||||
req := sip.NewRequest(Method, d.Recipient)
|
||||
req.AppendHeader(&d.fromHDR)
|
||||
contentType := sip.ContentTypeHeader("Application/MANSCDP+xml")
|
||||
req.AppendHeader(sip.NewHeader("User-Agent", "M7S/"+m7s.Version))
|
||||
//req.AppendHeader(sip.NewHeader("User-Agent", "M7S/"+m7s.Version))
|
||||
req.AppendHeader(sip.NewHeader("User-Agent", "asdf"))
|
||||
req.AppendHeader(&contentType)
|
||||
req.AppendHeader(&d.contactHDR)
|
||||
return req
|
||||
|
||||
@@ -2,19 +2,16 @@ package plugin_gb28181pro
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/emiago/sipgo"
|
||||
"github.com/emiago/sipgo/sip"
|
||||
"github.com/rs/zerolog"
|
||||
m7s "m7s.live/v5"
|
||||
"m7s.live/v5/pkg/task"
|
||||
"m7s.live/v5/pkg/util"
|
||||
gb28181 "m7s.live/v5/plugin/gb28181pro/pkg"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Dialog struct {
|
||||
@@ -24,6 +21,8 @@ type Dialog struct {
|
||||
gb *GB28181ProPlugin
|
||||
session *sipgo.DialogClientSession
|
||||
pullCtx m7s.PullJob
|
||||
start string
|
||||
end string
|
||||
}
|
||||
|
||||
func (d *Dialog) GetCallID() string {
|
||||
@@ -44,82 +43,84 @@ func (d *Dialog) Start() (err error) {
|
||||
}
|
||||
sss := strings.Split(d.pullCtx.RemoteURL, "/")
|
||||
deviceId, channelId := sss[0], sss[1]
|
||||
if len(sss) == 2 {
|
||||
// 先从内存中获取设备
|
||||
device, ok := d.gb.devices.Get(deviceId)
|
||||
if !ok && d.gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbDevice Device
|
||||
if err := d.gb.DB.Where("device_id = ?", deviceId).First(&dbDevice).Error; err == nil {
|
||||
// 恢复设备的必要字段
|
||||
dbDevice.Logger = d.gb.With("id", deviceId)
|
||||
dbDevice.channels.L = new(sync.RWMutex)
|
||||
dbDevice.plugin = d.gb
|
||||
dbDevice.eventChan = make(chan any, 10)
|
||||
//if len(sss) == 2 {
|
||||
// 先从内存中获取设备
|
||||
device, ok := d.gb.devices.Get(deviceId)
|
||||
if !ok && d.gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbDevice Device
|
||||
if err := d.gb.DB.Where("device_id = ?", deviceId).First(&dbDevice).Error; err == nil {
|
||||
|
||||
// 初始化 SIP 相关字段
|
||||
dbDevice.fromHDR = sip.FromHeader{
|
||||
Address: sip.Uri{
|
||||
User: d.gb.Serial,
|
||||
Host: d.gb.Realm,
|
||||
},
|
||||
Params: sip.NewParams(),
|
||||
}
|
||||
dbDevice.fromHDR.Params.Add("tag", sip.GenerateTagN(16))
|
||||
|
||||
dbDevice.contactHDR = sip.ContactHeader{
|
||||
Address: sip.Uri{
|
||||
User: d.gb.Serial,
|
||||
Host: dbDevice.LocalIP,
|
||||
Port: dbDevice.Port,
|
||||
},
|
||||
}
|
||||
|
||||
dbDevice.Recipient = sip.Uri{
|
||||
Host: dbDevice.IP,
|
||||
Port: dbDevice.Port,
|
||||
User: dbDevice.DeviceID,
|
||||
}
|
||||
|
||||
// 初始化 SIP 客户端
|
||||
dbDevice.client, _ = sipgo.NewClient(d.gb.ua, sipgo.WithClientLogger(zerolog.New(os.Stdout)), sipgo.WithClientHostname(dbDevice.LocalIP))
|
||||
if dbDevice.client != nil {
|
||||
dbDevice.dialogClient = sipgo.NewDialogClient(dbDevice.client, dbDevice.contactHDR)
|
||||
} else {
|
||||
return fmt.Errorf("failed to create sip client for device %s", deviceId)
|
||||
}
|
||||
|
||||
device = &dbDevice
|
||||
} else {
|
||||
return fmt.Errorf("device %s not found", deviceId)
|
||||
}
|
||||
} else if !ok {
|
||||
device = &dbDevice
|
||||
} else {
|
||||
return fmt.Errorf("device %s not found", deviceId)
|
||||
}
|
||||
} else if !ok {
|
||||
return fmt.Errorf("device %s not found", deviceId)
|
||||
}
|
||||
|
||||
// 先从内存中获取通道
|
||||
channel, ok := device.channels.Get(channelId)
|
||||
if !ok && d.gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbChannel gb28181.DeviceChannel
|
||||
if err := d.gb.DB.Where("device_id = ? AND device_db_id = ?", channelId, device.ID).First(&dbChannel).Error; err == nil {
|
||||
channel = &Channel{
|
||||
Device: device,
|
||||
Logger: device.Logger.With("channel", channelId),
|
||||
DeviceChannel: dbChannel,
|
||||
}
|
||||
device.channels.Set(channel)
|
||||
} else {
|
||||
return fmt.Errorf("channel %s not found", channelId)
|
||||
// 先从内存中获取通道
|
||||
channel, ok := device.channels.Get(channelId)
|
||||
if !ok && d.gb.DB != nil {
|
||||
// 如果内存中没有且数据库存在,则从数据库查询
|
||||
var dbChannel gb28181.DeviceChannel
|
||||
if err := d.gb.DB.Where("device_id = ? AND device_db_id = ?", channelId, device.ID).First(&dbChannel).Error; err == nil {
|
||||
channel = &Channel{
|
||||
Device: device,
|
||||
Logger: device.Logger.With("channel", channelId),
|
||||
DeviceChannel: dbChannel,
|
||||
}
|
||||
} else if !ok {
|
||||
device.channels.Set(channel)
|
||||
} else {
|
||||
return fmt.Errorf("channel %s not found", channelId)
|
||||
}
|
||||
} else if !ok {
|
||||
return fmt.Errorf("channel %s not found", channelId)
|
||||
}
|
||||
|
||||
d.Channel = channel
|
||||
} else if len(sss) == 3 {
|
||||
var recordRange util.Range[int]
|
||||
err = recordRange.Resolve(sss[2])
|
||||
d.Channel = channel
|
||||
//} else if len(sss) == 3 {
|
||||
// var recordRange util.Range[int]
|
||||
// err = recordRange.Resolve(sss[2])
|
||||
//}
|
||||
|
||||
if device != nil && channel != nil {
|
||||
// 初始化 SIP 相关字段
|
||||
device.fromHDR = sip.FromHeader{
|
||||
Address: sip.Uri{
|
||||
User: d.gb.Serial,
|
||||
Host: d.gb.Realm,
|
||||
},
|
||||
Params: sip.NewParams(),
|
||||
}
|
||||
device.fromHDR.Params.Add("tag", sip.GenerateTagN(16))
|
||||
|
||||
device.contactHDR = sip.ContactHeader{
|
||||
Address: sip.Uri{
|
||||
User: d.gb.Serial,
|
||||
Host: device.LocalIP,
|
||||
Port: device.Port,
|
||||
},
|
||||
}
|
||||
|
||||
device.Recipient = sip.Uri{
|
||||
Host: device.IP,
|
||||
Port: device.Port,
|
||||
User: channelId, // 使用通道的 DeviceID
|
||||
}
|
||||
// 恢复设备的必要字段
|
||||
device.Logger = d.gb.With("id", deviceId)
|
||||
device.channels.L = new(sync.RWMutex)
|
||||
device.plugin = d.gb
|
||||
device.eventChan = make(chan any, 10)
|
||||
// 初始化 SIP 客户端
|
||||
device.client, _ = sipgo.NewClient(d.gb.ua, sipgo.WithClientLogger(zerolog.New(os.Stdout)), sipgo.WithClientHostname(device.LocalIP))
|
||||
if device.client != nil {
|
||||
device.dialogClient = sipgo.NewDialogClient(device.client, device.contactHDR)
|
||||
} else {
|
||||
d.gb.Error("failed to create sip client for device", "error", "deviceId", deviceId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
d.gb.dialogs.Set(d)
|
||||
@@ -138,7 +139,7 @@ func (d *Dialog) Start() (err error) {
|
||||
}
|
||||
|
||||
// 调用 PlayStreamCmd
|
||||
d.session, err = d.gb.PlayStreamCmd(d.Channel.Device, d.Channel, d.MediaPort)
|
||||
d.session, err = d.gb.PlayStreamCmd(device, channel, d.MediaPort, d.start, d.end)
|
||||
if err != nil {
|
||||
return fmt.Errorf("play stream failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ type PositionConfig struct {
|
||||
}
|
||||
|
||||
// PlayStreamCmd 请求预览视频流
|
||||
func (gb *GB28181ProPlugin) PlayStreamCmd(device *Device, channel *Channel, mediaPort uint16) (*sipgo.DialogClientSession, error) {
|
||||
func (gb *GB28181ProPlugin) PlayStreamCmd(device *Device, channel *Channel, mediaPort uint16, start, end string) (*sipgo.DialogClientSession, error) {
|
||||
if channel == nil {
|
||||
return nil, fmt.Errorf("channel is nil")
|
||||
}
|
||||
@@ -60,11 +60,26 @@ func (gb *GB28181ProPlugin) PlayStreamCmd(device *Device, channel *Channel, medi
|
||||
// 构建 SDP 内容
|
||||
sdpInfo := []string{
|
||||
"v=0",
|
||||
fmt.Sprintf("o=%s 0 0 IN IP4 %s", device.ID, sdpIP),
|
||||
"s=Play",
|
||||
fmt.Sprintf("o=%s 0 0 IN IP4 %s", device.DeviceID, sdpIP),
|
||||
fmt.Sprintf("s=%s", map[bool]string{true: "Playback", false: "Play"}[start != "" && end != ""]), // 根据是否有时间参数决定
|
||||
//"u=" + device.ID + ":0",
|
||||
"u=" + channel.DeviceID + ":0",
|
||||
"c=IN IP4 " + sdpIP,
|
||||
"t=0 0",
|
||||
}
|
||||
|
||||
// 如果有时间参数,添加时间行
|
||||
if start != "" && end != "" {
|
||||
startTime, err := time.Parse(time.RFC3339, start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse start time failed: %v", err)
|
||||
}
|
||||
endTime, err := time.Parse(time.RFC3339, end)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse end time failed: %v", err)
|
||||
}
|
||||
sdpInfo = append(sdpInfo, fmt.Sprintf("t=%d %d", startTime.Unix(), endTime.Unix()))
|
||||
} else {
|
||||
sdpInfo = append(sdpInfo, "t=0 0")
|
||||
}
|
||||
|
||||
// 添加媒体行和相关属性
|
||||
@@ -127,6 +142,7 @@ func (gb *GB28181ProPlugin) PlayStreamCmd(device *Device, channel *Channel, medi
|
||||
toHeader := sip.ToHeader{
|
||||
Address: sip.Uri{User: channel.DeviceID, Host: device.HostAddress},
|
||||
}
|
||||
userAgentHeader := sip.NewHeader("User-Agent", "M7S/"+m7s.Version)
|
||||
|
||||
request.AppendHeader(&contentTypeHeader)
|
||||
request.AppendHeader(subjectHeader)
|
||||
@@ -135,7 +151,8 @@ func (gb *GB28181ProPlugin) PlayStreamCmd(device *Device, channel *Channel, medi
|
||||
request.SetBody([]byte(strings.Join(sdpInfo, "\r\n") + "\r\n"))
|
||||
|
||||
// 创建会话
|
||||
return device.dialogClient.Invite(gb, device.Recipient, request.Body(), &contentTypeHeader, subjectHeader, &device.fromHDR, allowHeader, &toHeader)
|
||||
return device.dialogClient.Invite(gb, device.Recipient, request.Body(), &contentTypeHeader, subjectHeader, &device.fromHDR, &toHeader, userAgentHeader, allowHeader)
|
||||
//return device.dialogClient.Invite(gb, device.Recipient, request.Body(), &contentTypeHeader, subjectHeader, &device.fromHDR, allowHeader, &toHeader)
|
||||
}
|
||||
|
||||
type GB28181ProPlugin struct {
|
||||
@@ -680,6 +697,14 @@ func (gb *GB28181ProPlugin) Pull(streamPath string, conf config.Pull, pubConf *c
|
||||
dialog := Dialog{
|
||||
gb: gb,
|
||||
}
|
||||
if conf.Args != nil {
|
||||
if starts, ok := conf.Args["start"]; ok && len(starts) > 0 {
|
||||
dialog.start = starts[0]
|
||||
}
|
||||
if ends, ok := conf.Args["end"]; ok && len(ends) > 0 {
|
||||
dialog.end = ends[0]
|
||||
}
|
||||
}
|
||||
dialog.GetPullJob().Init(&dialog, &gb.Plugin, streamPath, conf, pubConf)
|
||||
}
|
||||
|
||||
@@ -848,18 +873,4 @@ func (gb *GB28181ProPlugin) OnInvite(req *sip.Request, tx sip.ServerTransaction)
|
||||
|
||||
gb.Info("OnInvite", "action", "complete", "deviceId", inviteInfo.RequesterId, "channelId", inviteInfo.TargetChannelId,
|
||||
"ip", inviteInfo.IP, "port", inviteInfo.Port, "tcp", inviteInfo.TCP, "tcpActive", inviteInfo.TCPActive)
|
||||
|
||||
// TODO: 实现媒体流处理
|
||||
// 1. 创建合适的Publisher
|
||||
// 2. 创建PS流接收器
|
||||
// 3. 配置接收参数:
|
||||
// - 使用解析出的 inviteInfo.IP 和 inviteInfo.Port 作为目标地址
|
||||
// - 使用 inviteInfo.TCP 和 inviteInfo.TCPActive 确定传输模式
|
||||
// - 使用本地分配的 mediaPort 作为监听端口
|
||||
// 4. 启动接收任务
|
||||
// 5. 处理媒体流解复用
|
||||
// 6. 根据 inviteInfo.SessionName 判断是实时点播还是历史回放
|
||||
// - 如果是回放,使用 inviteInfo.StartTime 和 inviteInfo.StopTime
|
||||
// 7. 使用 inviteInfo.SSRC 标识流
|
||||
// 8. 如果指定了 inviteInfo.DownloadSpeed,控制下载速度
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1534,6 +1534,86 @@ func local_request_Api_ListPlatforms_0(ctx context.Context, marshaler runtime.Ma
|
||||
|
||||
}
|
||||
|
||||
func request_Api_StartPlayback_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq PlaybackRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["deviceId"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "deviceId")
|
||||
}
|
||||
|
||||
protoReq.DeviceId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "deviceId", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["channelId"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channelId")
|
||||
}
|
||||
|
||||
protoReq.ChannelId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channelId", err)
|
||||
}
|
||||
|
||||
msg, err := client.StartPlayback(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Api_StartPlayback_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq PlaybackRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["deviceId"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "deviceId")
|
||||
}
|
||||
|
||||
protoReq.DeviceId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "deviceId", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["channelId"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channelId")
|
||||
}
|
||||
|
||||
protoReq.ChannelId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channelId", err)
|
||||
}
|
||||
|
||||
msg, err := server.StartPlayback(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterApiHandlerServer registers the http handlers for service Api to "mux".
|
||||
// UnaryRPC :call ApiServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
@@ -2266,6 +2346,31 @@ func RegisterApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, server
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Api_StartPlayback_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/gb28181pro.Api/StartPlayback", runtime.WithHTTPPathPattern("/gb28181/api/playback/start/{deviceId}/{channelId}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Api_StartPlayback_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Api_StartPlayback_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2945,6 +3050,28 @@ func RegisterApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, client
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Api_StartPlayback_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/gb28181pro.Api/StartPlayback", runtime.WithHTTPPathPattern("/gb28181/api/playback/start/{deviceId}/{channelId}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Api_StartPlayback_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Api_StartPlayback_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3006,6 +3133,8 @@ var (
|
||||
pattern_Api_DeletePlatform_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"gb28181", "api", "platform", "id"}, ""))
|
||||
|
||||
pattern_Api_ListPlatforms_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"gb28181", "api", "platform", "list"}, ""))
|
||||
|
||||
pattern_Api_StartPlayback_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"gb28181", "api", "playback", "start", "deviceId", "channelId"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -3066,4 +3195,6 @@ var (
|
||||
forward_Api_DeletePlatform_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Api_ListPlatforms_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Api_StartPlayback_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
||||
@@ -221,6 +221,13 @@ service api {
|
||||
get: "/gb28181/api/platform/list"
|
||||
};
|
||||
}
|
||||
|
||||
// 开始回放
|
||||
rpc StartPlayback (PlaybackRequest) returns (PlayResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/gb28181/api/playback/start/{deviceId}/{channelId}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 请求和响应消息定义
|
||||
@@ -424,6 +431,14 @@ message PlayRequest {
|
||||
string channelId = 2;
|
||||
}
|
||||
|
||||
message PlaybackRequest {
|
||||
string deviceId = 1;
|
||||
string channelId = 2;
|
||||
string start = 3; // 开始时间
|
||||
string end = 4; // 结束时间
|
||||
string range = 5; // 时间范围,可选
|
||||
}
|
||||
|
||||
message PlayResponse {
|
||||
int32 code = 1;
|
||||
string message = 2;
|
||||
|
||||
@@ -51,6 +51,7 @@ const (
|
||||
Api_UpdatePlatform_FullMethodName = "/gb28181pro.api/UpdatePlatform"
|
||||
Api_DeletePlatform_FullMethodName = "/gb28181pro.api/DeletePlatform"
|
||||
Api_ListPlatforms_FullMethodName = "/gb28181pro.api/ListPlatforms"
|
||||
Api_StartPlayback_FullMethodName = "/gb28181pro.api/StartPlayback"
|
||||
)
|
||||
|
||||
// ApiClient is the client API for Api service.
|
||||
@@ -115,6 +116,8 @@ type ApiClient interface {
|
||||
DeletePlatform(ctx context.Context, in *DeletePlatformRequest, opts ...grpc.CallOption) (*BaseResponse, error)
|
||||
// 获取平台列表
|
||||
ListPlatforms(ctx context.Context, in *ListPlatformsRequest, opts ...grpc.CallOption) (*PlatformsPageInfo, error)
|
||||
// 开始回放
|
||||
StartPlayback(ctx context.Context, in *PlaybackRequest, opts ...grpc.CallOption) (*PlayResponse, error)
|
||||
}
|
||||
|
||||
type apiClient struct {
|
||||
@@ -415,6 +418,16 @@ func (c *apiClient) ListPlatforms(ctx context.Context, in *ListPlatformsRequest,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *apiClient) StartPlayback(ctx context.Context, in *PlaybackRequest, opts ...grpc.CallOption) (*PlayResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PlayResponse)
|
||||
err := c.cc.Invoke(ctx, Api_StartPlayback_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ApiServer is the server API for Api service.
|
||||
// All implementations must embed UnimplementedApiServer
|
||||
// for forward compatibility.
|
||||
@@ -477,6 +490,8 @@ type ApiServer interface {
|
||||
DeletePlatform(context.Context, *DeletePlatformRequest) (*BaseResponse, error)
|
||||
// 获取平台列表
|
||||
ListPlatforms(context.Context, *ListPlatformsRequest) (*PlatformsPageInfo, error)
|
||||
// 开始回放
|
||||
StartPlayback(context.Context, *PlaybackRequest) (*PlayResponse, error)
|
||||
mustEmbedUnimplementedApiServer()
|
||||
}
|
||||
|
||||
@@ -574,6 +589,9 @@ func (UnimplementedApiServer) DeletePlatform(context.Context, *DeletePlatformReq
|
||||
func (UnimplementedApiServer) ListPlatforms(context.Context, *ListPlatformsRequest) (*PlatformsPageInfo, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListPlatforms not implemented")
|
||||
}
|
||||
func (UnimplementedApiServer) StartPlayback(context.Context, *PlaybackRequest) (*PlayResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method StartPlayback not implemented")
|
||||
}
|
||||
func (UnimplementedApiServer) mustEmbedUnimplementedApiServer() {}
|
||||
func (UnimplementedApiServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -1117,6 +1135,24 @@ func _Api_ListPlatforms_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Api_StartPlayback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PlaybackRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ApiServer).StartPlayback(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Api_StartPlayback_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ApiServer).StartPlayback(ctx, req.(*PlaybackRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Api_ServiceDesc is the grpc.ServiceDesc for Api service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -1240,6 +1276,10 @@ var Api_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListPlatforms",
|
||||
Handler: _Api_ListPlatforms_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "StartPlayback",
|
||||
Handler: _Api_StartPlayback_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "gb28181.proto",
|
||||
|
||||
Reference in New Issue
Block a user