feature: support playback

This commit is contained in:
pg
2025-02-16 20:18:06 +08:00
committed by pggiroro
parent 2cb60d5a9c
commit a2dcb8a3ef
8 changed files with 957 additions and 576 deletions

View File

@@ -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{}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
)

View File

@@ -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;

View File

@@ -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",