mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 16:07:07 +08:00
Add stream mapping to process state, adjust addresses and indexes for HLS outputs
This commit is contained in:
89
docs/docs.go
89
docs/docs.go
@@ -5656,6 +5656,75 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.GraphElement": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"dst_filter": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dst_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"inpad": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"outpad": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sampling": {
|
||||||
|
"description": "Hz",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"src_filter": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"src_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timebase": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "audio or video",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api.GraphMapping": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"copy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.GraphQuery": {
|
"api.GraphQuery": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -6510,6 +6579,9 @@ const docTemplate = `{
|
|||||||
"$ref": "#/definitions/api.ProgressIO"
|
"$ref": "#/definitions/api.ProgressIO"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mapping": {
|
||||||
|
"$ref": "#/definitions/api.StreamMapping"
|
||||||
|
},
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -8009,6 +8081,23 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.StreamMapping": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"graphs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/api.GraphElement"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mapping": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/api.GraphMapping"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.Version": {
|
"api.Version": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@@ -5648,6 +5648,75 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.GraphElement": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"dst_filter": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dst_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"inpad": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"outpad": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sampling": {
|
||||||
|
"description": "Hz",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"src_filter": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"src_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timebase": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "audio or video",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api.GraphMapping": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"copy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.GraphQuery": {
|
"api.GraphQuery": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -6502,6 +6571,9 @@
|
|||||||
"$ref": "#/definitions/api.ProgressIO"
|
"$ref": "#/definitions/api.ProgressIO"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mapping": {
|
||||||
|
"$ref": "#/definitions/api.StreamMapping"
|
||||||
|
},
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -8001,6 +8073,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.StreamMapping": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"graphs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/api.GraphElement"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mapping": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/api.GraphMapping"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.Version": {
|
"api.Version": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@@ -747,6 +747,52 @@ definitions:
|
|||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
api.GraphElement:
|
||||||
|
properties:
|
||||||
|
dst_filter:
|
||||||
|
type: string
|
||||||
|
dst_name:
|
||||||
|
type: string
|
||||||
|
format:
|
||||||
|
type: string
|
||||||
|
height:
|
||||||
|
type: integer
|
||||||
|
index:
|
||||||
|
type: integer
|
||||||
|
inpad:
|
||||||
|
type: string
|
||||||
|
layout:
|
||||||
|
type: string
|
||||||
|
outpad:
|
||||||
|
type: string
|
||||||
|
sampling:
|
||||||
|
description: Hz
|
||||||
|
type: integer
|
||||||
|
src_filter:
|
||||||
|
type: string
|
||||||
|
src_name:
|
||||||
|
type: string
|
||||||
|
timebase:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: audio or video
|
||||||
|
type: string
|
||||||
|
width:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
api.GraphMapping:
|
||||||
|
properties:
|
||||||
|
copy:
|
||||||
|
type: boolean
|
||||||
|
index:
|
||||||
|
type: integer
|
||||||
|
input:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
output:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
api.GraphQuery:
|
api.GraphQuery:
|
||||||
properties:
|
properties:
|
||||||
query:
|
query:
|
||||||
@@ -1326,6 +1372,8 @@ definitions:
|
|||||||
items:
|
items:
|
||||||
$ref: '#/definitions/api.ProgressIO'
|
$ref: '#/definitions/api.ProgressIO'
|
||||||
type: array
|
type: array
|
||||||
|
mapping:
|
||||||
|
$ref: '#/definitions/api.StreamMapping'
|
||||||
outputs:
|
outputs:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/api.ProgressIO'
|
$ref: '#/definitions/api.ProgressIO'
|
||||||
@@ -2399,6 +2447,17 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
api.StreamMapping:
|
||||||
|
properties:
|
||||||
|
graphs:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/api.GraphElement'
|
||||||
|
type: array
|
||||||
|
mapping:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/api.GraphMapping'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
api.Version:
|
api.Version:
|
||||||
properties:
|
properties:
|
||||||
arch:
|
arch:
|
||||||
|
@@ -252,6 +252,8 @@ func (p *parser) Parse(line string) uint64 {
|
|||||||
"error": err,
|
"error": err,
|
||||||
}).Error().Log("Failed parsing HSL stream mapping")
|
}).Error().Log("Failed parsing HSL stream mapping")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFFmpegOutputs {
|
if isFFmpegOutputs {
|
||||||
|
@@ -939,95 +939,68 @@ func TestParserStreamMapping(t *testing.T) {
|
|||||||
require.Equal(t, 4, len(progress.Output))
|
require.Equal(t, 4, len(progress.Output))
|
||||||
|
|
||||||
require.Equal(t, StreamMapping{
|
require.Equal(t, StreamMapping{
|
||||||
Graphs: []Graph{
|
Graphs: []GraphElement{
|
||||||
{
|
{Index: 0, Name: "Parsed_null_0", Filter: "null", DstName: "format", DstFilter: "format", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
||||||
Index: 0,
|
{Index: 0, Name: "graph 0 input from stream 0:0", Filter: "buffer", DstName: "Parsed_null_0", DstFilter: "null", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
||||||
Graph: []GraphElement{
|
{Index: 0, Name: "format", Filter: "format", DstName: "out_0_0", DstFilter: "buffersink", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
||||||
{SrcName: "Parsed_null_0", SrcFilter: "null", DstName: "format", DstFilter: "format", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
{Index: 1, Name: "Parsed_anull_0", Filter: "anull", DstName: "auto_aresample_0", DstFilter: "aresample", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
{SrcName: "graph 0 input from stream 0:0", SrcFilter: "buffer", DstName: "Parsed_null_0", DstFilter: "null", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
{Index: 1, Name: "graph_1_in_2_0", Filter: "abuffer", DstName: "Parsed_anull_0", DstFilter: "anull", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
{SrcName: "format", SrcFilter: "format", DstName: "out_0_0", DstFilter: "buffersink", Inpad: "default", Outpad: "default", Timebase: "1/90000", Type: "video", Format: "yuvj420p", Sampling: 0, Layout: "", Width: 1280, Height: 720},
|
{Index: 1, Name: "format_out_0_2", Filter: "aformat", DstName: "out_0_2", DstFilter: "abuffersink", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
},
|
{Index: 1, Name: "auto_aresample_0", Filter: "aresample", DstName: "format_out_0_2", DstFilter: "aformat", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
}, {
|
{Index: 2, Name: "Parsed_anull_0", Filter: "anull", DstName: "auto_aresample_0", DstFilter: "aresample", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
Index: 1,
|
{Index: 2, Name: "graph_2_in_2_0", Filter: "abuffer", DstName: "Parsed_anull_0", DstFilter: "anull", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
Graph: []GraphElement{
|
{Index: 2, Name: "format_out_0_3", Filter: "aformat", DstName: "out_0_3", DstFilter: "abuffersink", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
{SrcName: "Parsed_anull_0", SrcFilter: "anull", DstName: "auto_aresample_0", DstFilter: "aresample", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
{Index: 2, Name: "auto_aresample_0", Filter: "aresample", DstName: "format_out_0_3", DstFilter: "aformat", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
||||||
{SrcName: "graph_1_in_2_0", SrcFilter: "abuffer", DstName: "Parsed_anull_0", DstFilter: "anull", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
{SrcName: "format_out_0_2", SrcFilter: "aformat", DstName: "out_0_2", DstFilter: "abuffersink", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
{SrcName: "auto_aresample_0", SrcFilter: "aresample", DstName: "format_out_0_2", DstFilter: "aformat", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Index: 2,
|
|
||||||
Graph: []GraphElement{
|
|
||||||
{SrcName: "Parsed_anull_0", SrcFilter: "anull", DstName: "auto_aresample_0", DstFilter: "aresample", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
{SrcName: "graph_2_in_2_0", SrcFilter: "abuffer", DstName: "Parsed_anull_0", DstFilter: "anull", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "u8", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
{SrcName: "format_out_0_3", SrcFilter: "aformat", DstName: "out_0_3", DstFilter: "abuffersink", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
{SrcName: "auto_aresample_0", SrcFilter: "aresample", DstName: "format_out_0_3", DstFilter: "aformat", Inpad: "default", Outpad: "default", Timebase: "1/44100", Type: "audio", Format: "fltp", Sampling: 44100, Layout: "mono", Width: 0, Height: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Mapping: []Mapping{
|
Mapping: []GraphMapping{
|
||||||
{
|
{
|
||||||
Input: 0,
|
Input: 0,
|
||||||
Output: -1,
|
Output: -1,
|
||||||
Graph: MappingGraph{
|
Index: 0,
|
||||||
Index: 0,
|
Name: "graph 0 input from stream 0:0",
|
||||||
Name: "graph 0 input from stream 0:0",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: 2,
|
Input: 2,
|
||||||
Output: -1,
|
Output: -1,
|
||||||
Graph: MappingGraph{
|
Index: 1,
|
||||||
Index: 1,
|
Name: "graph_1_in_2_0",
|
||||||
Name: "graph_1_in_2_0",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: 2,
|
Input: 2,
|
||||||
Output: -1,
|
Output: -1,
|
||||||
Graph: MappingGraph{
|
Index: 2,
|
||||||
Index: 2,
|
Name: "graph_2_in_2_0",
|
||||||
Name: "graph_2_in_2_0",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: -1,
|
Input: -1,
|
||||||
Output: 0,
|
Output: 0,
|
||||||
Graph: MappingGraph{
|
Index: 0,
|
||||||
Index: 0,
|
Name: "out_0_0",
|
||||||
Name: "out_0_0",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: 1,
|
Input: 1,
|
||||||
Output: 1,
|
Output: 1,
|
||||||
Graph: MappingGraph{
|
Index: -1,
|
||||||
Index: -1,
|
Name: "",
|
||||||
Name: "",
|
Copy: true,
|
||||||
},
|
|
||||||
Copy: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: -1,
|
Input: -1,
|
||||||
Output: 2,
|
Output: 2,
|
||||||
Graph: MappingGraph{
|
Index: 1,
|
||||||
Index: 1,
|
Name: "out_0_2",
|
||||||
Name: "out_0_2",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Input: -1,
|
Input: -1,
|
||||||
Output: 3,
|
Output: 3,
|
||||||
Graph: MappingGraph{
|
Index: 2,
|
||||||
Index: 2,
|
Name: "out_0_3",
|
||||||
Name: "out_0_3",
|
Copy: false,
|
||||||
},
|
|
||||||
Copy: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, progress.Mapping)
|
}, progress.Mapping)
|
||||||
|
@@ -242,46 +242,16 @@ type ffmpegGraphElement struct {
|
|||||||
Height uint64 `json:"height"`
|
Height uint64 `json:"height"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ffmpegGraphElement) Export() GraphElement {
|
|
||||||
return GraphElement{
|
|
||||||
SrcName: f.SrcName,
|
|
||||||
SrcFilter: f.SrcFilter,
|
|
||||||
DstName: f.DstName,
|
|
||||||
DstFilter: f.DstFilter,
|
|
||||||
Inpad: f.Inpad,
|
|
||||||
Outpad: f.Outpad,
|
|
||||||
Timebase: f.Timebase,
|
|
||||||
Type: f.Type,
|
|
||||||
Format: f.Format,
|
|
||||||
Sampling: f.Sampling,
|
|
||||||
Layout: f.Layout,
|
|
||||||
Width: f.Width,
|
|
||||||
Height: f.Height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ffmpegGraph struct {
|
type ffmpegGraph struct {
|
||||||
Index uint64 `json:"index"`
|
Index int `json:"index"`
|
||||||
Graph []ffmpegGraphElement `json:"graph"`
|
Graph []ffmpegGraphElement `json:"graph"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ffmpegGraph) Export() Graph {
|
type ffmpegGraphMapping struct {
|
||||||
g := Graph{
|
|
||||||
Index: f.Index,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range f.Graph {
|
|
||||||
g.Graph = append(g.Graph, e.Export())
|
|
||||||
}
|
|
||||||
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
type ffmpegMapping struct {
|
|
||||||
Input *ffmpegMappingIO `json:"input"`
|
Input *ffmpegMappingIO `json:"input"`
|
||||||
Output *ffmpegMappingIO `json:"output"`
|
Output *ffmpegMappingIO `json:"output"`
|
||||||
Graph struct {
|
Graph struct {
|
||||||
Index uint64 `json:"index"`
|
Index int `json:"index"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
} `json:"graph"`
|
} `json:"graph"`
|
||||||
Copy bool `json:"copy"`
|
Copy bool `json:"copy"`
|
||||||
@@ -293,8 +263,8 @@ type ffmpegMappingIO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ffmpegStreamMapping struct {
|
type ffmpegStreamMapping struct {
|
||||||
Graphs []ffmpegGraph `json:"graphs"`
|
Graphs []ffmpegGraph `json:"graphs"`
|
||||||
Mapping []ffmpegMapping `json:"mapping"`
|
Mapping []ffmpegGraphMapping `json:"mapping"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ffmpegProcess struct {
|
type ffmpegProcess struct {
|
||||||
@@ -307,23 +277,40 @@ type ffmpegProcess struct {
|
|||||||
func (f *ffmpegProcess) ExportMapping() StreamMapping {
|
func (f *ffmpegProcess) ExportMapping() StreamMapping {
|
||||||
sm := StreamMapping{}
|
sm := StreamMapping{}
|
||||||
|
|
||||||
for _, g := range f.mapping.Graphs {
|
for _, graph := range f.mapping.Graphs {
|
||||||
sm.Graphs = append(sm.Graphs, g.Export())
|
for _, g := range graph.Graph {
|
||||||
|
e := GraphElement{
|
||||||
|
Index: graph.Index,
|
||||||
|
Name: g.SrcName,
|
||||||
|
Filter: g.SrcFilter,
|
||||||
|
DstName: g.DstName,
|
||||||
|
DstFilter: g.DstFilter,
|
||||||
|
Inpad: g.Inpad,
|
||||||
|
Outpad: g.Outpad,
|
||||||
|
Timebase: g.Timebase,
|
||||||
|
Type: g.Type,
|
||||||
|
Format: g.Format,
|
||||||
|
Sampling: g.Sampling,
|
||||||
|
Layout: g.Layout,
|
||||||
|
Width: g.Width,
|
||||||
|
Height: g.Height,
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Graphs = append(sm.Graphs, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fm := range f.mapping.Mapping {
|
for _, fm := range f.mapping.Mapping {
|
||||||
m := Mapping{
|
m := GraphMapping{
|
||||||
Input: -1,
|
Input: -1,
|
||||||
Output: -1,
|
Output: -1,
|
||||||
Graph: MappingGraph{
|
Index: fm.Graph.Index,
|
||||||
Index: int(fm.Graph.Index),
|
Name: fm.Graph.Name,
|
||||||
Name: fm.Graph.Name,
|
Copy: fm.Copy,
|
||||||
},
|
|
||||||
Copy: fm.Copy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.Graph.Name) == 0 {
|
if len(m.Name) == 0 {
|
||||||
m.Graph.Index = -1
|
m.Index = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if fm.Input != nil {
|
if fm.Input != nil {
|
||||||
@@ -496,8 +483,9 @@ type Usage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GraphElement struct {
|
type GraphElement struct {
|
||||||
SrcName string
|
Index int
|
||||||
SrcFilter string
|
Name string
|
||||||
|
Filter string
|
||||||
DstName string
|
DstName string
|
||||||
DstFilter string
|
DstFilter string
|
||||||
Inpad string
|
Inpad string
|
||||||
@@ -511,24 +499,15 @@ type GraphElement struct {
|
|||||||
Height uint64
|
Height uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Graph struct {
|
type GraphMapping struct {
|
||||||
Index uint64
|
|
||||||
Graph []GraphElement
|
|
||||||
}
|
|
||||||
|
|
||||||
type MappingGraph struct {
|
|
||||||
Index int
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mapping struct {
|
|
||||||
Input int
|
Input int
|
||||||
Output int
|
Output int
|
||||||
Graph MappingGraph
|
Index int
|
||||||
|
Name string
|
||||||
Copy bool
|
Copy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type StreamMapping struct {
|
type StreamMapping struct {
|
||||||
Graphs []Graph
|
Graphs []GraphElement
|
||||||
Mapping []Mapping
|
Mapping []GraphMapping
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/atrox/haikunatorgo/v2 v2.0.1
|
github.com/atrox/haikunatorgo/v2 v2.0.1
|
||||||
github.com/caddyserver/certmagic v0.19.2
|
github.com/caddyserver/certmagic v0.19.2
|
||||||
github.com/casbin/casbin/v2 v2.77.2
|
github.com/casbin/casbin/v2 v2.77.2
|
||||||
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230920220938-7a37b8ea80ef
|
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230926123431-2fdbec157292
|
||||||
github.com/datarhei/gosrt v0.5.4
|
github.com/datarhei/gosrt v0.5.4
|
||||||
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
|
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
|
||||||
github.com/fujiwara/shapeio v1.0.0
|
github.com/fujiwara/shapeio v1.0.0
|
||||||
|
4
go.sum
4
go.sum
@@ -46,8 +46,8 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230920220938-7a37b8ea80ef h1:6uBGPAbVZpg6/BxJQ/5sM35YbyXVKI+6oD30eEX0V2k=
|
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230926123431-2fdbec157292 h1:/GV5wClf40U23jhwMIyoq0hgyCqmN2kRWPyRZe6Lf+Y=
|
||||||
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230920220938-7a37b8ea80ef/go.mod h1:3eKfwhPKoW7faTn+luShRVNMqcIskvlIKjRJ7ShjyL8=
|
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230926123431-2fdbec157292/go.mod h1:3eKfwhPKoW7faTn+luShRVNMqcIskvlIKjRJ7ShjyL8=
|
||||||
github.com/datarhei/gosrt v0.5.4 h1:dE3mmSB+n1GeviGM8xQAW3+UD3mKeFmd84iefDul5Vs=
|
github.com/datarhei/gosrt v0.5.4 h1:dE3mmSB+n1GeviGM8xQAW3+UD3mKeFmd84iefDul5Vs=
|
||||||
github.com/datarhei/gosrt v0.5.4/go.mod h1:MiUCwCG+LzFMzLM/kTA+3wiTtlnkVvGbW/F0XzyhtG8=
|
github.com/datarhei/gosrt v0.5.4/go.mod h1:MiUCwCG+LzFMzLM/kTA+3wiTtlnkVvGbW/F0XzyhtG8=
|
||||||
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
|
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
|
||||||
|
@@ -91,18 +91,19 @@ func (i *ProgressIO) Unmarshal(io *app.ProgressIO) {
|
|||||||
|
|
||||||
// Progress represents the progress of an ffmpeg process
|
// Progress represents the progress of an ffmpeg process
|
||||||
type Progress struct {
|
type Progress struct {
|
||||||
Input []ProgressIO `json:"inputs"`
|
Input []ProgressIO `json:"inputs"`
|
||||||
Output []ProgressIO `json:"outputs"`
|
Output []ProgressIO `json:"outputs"`
|
||||||
Frame uint64 `json:"frame" format:"uint64"`
|
Mapping StreamMapping `json:"mapping"`
|
||||||
Packet uint64 `json:"packet" format:"uint64"`
|
Frame uint64 `json:"frame" format:"uint64"`
|
||||||
FPS json.Number `json:"fps" swaggertype:"number" jsonschema:"type=number"`
|
Packet uint64 `json:"packet" format:"uint64"`
|
||||||
Quantizer json.Number `json:"q" swaggertype:"number" jsonschema:"type=number"`
|
FPS json.Number `json:"fps" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
|
Quantizer json.Number `json:"q" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Time json.Number `json:"time" swaggertype:"number" jsonschema:"type=number"`
|
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
|
||||||
Bitrate json.Number `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
|
Time json.Number `json:"time" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Speed json.Number `json:"speed" swaggertype:"number" jsonschema:"type=number"`
|
Bitrate json.Number `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
|
||||||
Drop uint64 `json:"drop" format:"uint64"`
|
Speed json.Number `json:"speed" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Dup uint64 `json:"dup" format:"uint64"`
|
Drop uint64 `json:"drop" format:"uint64"`
|
||||||
|
Dup uint64 `json:"dup" format:"uint64"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal converts a restreamer Progress to a Progress in API representation
|
// Unmarshal converts a restreamer Progress to a Progress in API representation
|
||||||
@@ -134,4 +135,74 @@ func (progress *Progress) Unmarshal(p *app.Progress) {
|
|||||||
for i, io := range p.Output {
|
for i, io := range p.Output {
|
||||||
progress.Output[i].Unmarshal(&io)
|
progress.Output[i].Unmarshal(&io)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progress.Mapping.Unmarshal(&p.Mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GraphElement struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Filter string `json:"filter"`
|
||||||
|
DstName string `json:"dst_name"`
|
||||||
|
DstFilter string `json:"dst_filter"`
|
||||||
|
Inpad string `json:"inpad"`
|
||||||
|
Outpad string `json:"outpad"`
|
||||||
|
Timebase string `json:"timebase"`
|
||||||
|
Type string `json:"type"` // audio or video
|
||||||
|
Format string `json:"format"`
|
||||||
|
Sampling uint64 `json:"sampling"` // Hz
|
||||||
|
Layout string `json:"layout"`
|
||||||
|
Width uint64 `json:"width"`
|
||||||
|
Height uint64 `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GraphMapping struct {
|
||||||
|
Input int `json:"input"`
|
||||||
|
Output int `json:"output"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Copy bool `json:"copy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamMapping struct {
|
||||||
|
Graphs []GraphElement `json:"graphs"`
|
||||||
|
Mapping []GraphMapping `json:"mapping"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal converts a restreamer StreamMapping to a StreamMapping in API representation
|
||||||
|
func (s *StreamMapping) Unmarshal(m *app.StreamMapping) {
|
||||||
|
s.Graphs = make([]GraphElement, 0, len(m.Graphs))
|
||||||
|
for _, mge := range m.Graphs {
|
||||||
|
ge := GraphElement{
|
||||||
|
Index: mge.Index,
|
||||||
|
Name: mge.Name,
|
||||||
|
Filter: mge.Filter,
|
||||||
|
DstName: mge.DstName,
|
||||||
|
DstFilter: mge.DstFilter,
|
||||||
|
Inpad: mge.Inpad,
|
||||||
|
Outpad: mge.Outpad,
|
||||||
|
Timebase: mge.Timebase,
|
||||||
|
Type: mge.Type,
|
||||||
|
Format: mge.Format,
|
||||||
|
Sampling: mge.Sampling,
|
||||||
|
Layout: mge.Layout,
|
||||||
|
Width: mge.Width,
|
||||||
|
Height: mge.Height,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Graphs = append(s.Graphs, ge)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Mapping = make([]GraphMapping, 0, len(m.Mapping))
|
||||||
|
for _, mmapping := range m.Mapping {
|
||||||
|
mapping := GraphMapping{
|
||||||
|
Input: mmapping.Input,
|
||||||
|
Output: mmapping.Output,
|
||||||
|
Index: mmapping.Index,
|
||||||
|
Name: mmapping.Name,
|
||||||
|
Copy: mmapping.Copy,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Mapping = append(s.Mapping, mapping)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,7 @@ type ProgressIO struct {
|
|||||||
type Progress struct {
|
type Progress struct {
|
||||||
Input []ProgressIO
|
Input []ProgressIO
|
||||||
Output []ProgressIO
|
Output []ProgressIO
|
||||||
|
Mapping StreamMapping
|
||||||
Frame uint64 // counter
|
Frame uint64 // counter
|
||||||
Packet uint64 // counter
|
Packet uint64 // counter
|
||||||
FPS float64 // rate, frames per second
|
FPS float64 // rate, frames per second
|
||||||
@@ -55,3 +56,33 @@ type Progress struct {
|
|||||||
Drop uint64 // counter
|
Drop uint64 // counter
|
||||||
Dup uint64 // counter
|
Dup uint64 // counter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GraphElement struct {
|
||||||
|
Index int
|
||||||
|
Name string
|
||||||
|
Filter string
|
||||||
|
DstName string
|
||||||
|
DstFilter string
|
||||||
|
Inpad string
|
||||||
|
Outpad string
|
||||||
|
Timebase string
|
||||||
|
Type string // audio or video
|
||||||
|
Format string
|
||||||
|
Sampling uint64 // Hz
|
||||||
|
Layout string
|
||||||
|
Width uint64
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type GraphMapping struct {
|
||||||
|
Input int // Index of input stream, negative if output element
|
||||||
|
Output int // Index of output stream, negative if input element
|
||||||
|
Index int // Index of the graph, negative if streamcopy
|
||||||
|
Name string // Name of the source resp. destination, empty if streamcopy
|
||||||
|
Copy bool // Whether it's a streamcopy i.e. there's no graph
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamMapping struct {
|
||||||
|
Graphs []GraphElement
|
||||||
|
Mapping []GraphMapping
|
||||||
|
}
|
||||||
|
@@ -1713,6 +1713,39 @@ func convertProgressFromParser(progress *app.Progress, pprogress parse.Progress)
|
|||||||
|
|
||||||
progress.Output = append(progress.Output, output)
|
progress.Output = append(progress.Output, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, pgraph := range pprogress.Mapping.Graphs {
|
||||||
|
graph := app.GraphElement{
|
||||||
|
Index: pgraph.Index,
|
||||||
|
Name: pgraph.Name,
|
||||||
|
Filter: pgraph.Filter,
|
||||||
|
DstName: pgraph.DstName,
|
||||||
|
DstFilter: pgraph.DstFilter,
|
||||||
|
Inpad: pgraph.Inpad,
|
||||||
|
Outpad: pgraph.Outpad,
|
||||||
|
Timebase: pgraph.Timebase,
|
||||||
|
Type: pgraph.Type,
|
||||||
|
Format: pgraph.Format,
|
||||||
|
Sampling: pgraph.Sampling,
|
||||||
|
Layout: pgraph.Layout,
|
||||||
|
Width: pgraph.Width,
|
||||||
|
Height: pgraph.Height,
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Mapping.Graphs = append(progress.Mapping.Graphs, graph)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pmapping := range pprogress.Mapping.Mapping {
|
||||||
|
mapping := app.GraphMapping{
|
||||||
|
Input: pmapping.Input,
|
||||||
|
Output: pmapping.Output,
|
||||||
|
Index: pmapping.Index,
|
||||||
|
Name: pmapping.Name,
|
||||||
|
Copy: pmapping.Copy,
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Mapping.Mapping = append(progress.Mapping.Mapping, mapping)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *restream) GetProcessLog(id app.ProcessID) (*app.Log, error) {
|
func (r *restream) GetProcessLog(id app.ProcessID) (*app.Log, error) {
|
||||||
|
55
vendor/github.com/datarhei/core-client-go/v16/api/progress.go
generated
vendored
55
vendor/github.com/datarhei/core-client-go/v16/api/progress.go
generated
vendored
@@ -45,16 +45,47 @@ type ProgressIO struct {
|
|||||||
|
|
||||||
// Progress represents the progress of an ffmpeg process
|
// Progress represents the progress of an ffmpeg process
|
||||||
type Progress struct {
|
type Progress struct {
|
||||||
Input []ProgressIO `json:"inputs"`
|
Input []ProgressIO `json:"inputs"`
|
||||||
Output []ProgressIO `json:"outputs"`
|
Output []ProgressIO `json:"outputs"`
|
||||||
Frame uint64 `json:"frame" format:"uint64"`
|
Mapping StreamMapping `json:"mapping"`
|
||||||
Packet uint64 `json:"packet" format:"uint64"`
|
Frame uint64 `json:"frame" format:"uint64"`
|
||||||
FPS float64 `json:"fps" swaggertype:"number" jsonschema:"type=number"`
|
Packet uint64 `json:"packet" format:"uint64"`
|
||||||
Quantizer float64 `json:"q" swaggertype:"number" jsonschema:"type=number"`
|
FPS float64 `json:"fps" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
|
Quantizer float64 `json:"q" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Time float64 `json:"time" swaggertype:"number" jsonschema:"type=number"`
|
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
|
||||||
Bitrate float64 `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
|
Time float64 `json:"time" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Speed float64 `json:"speed" swaggertype:"number" jsonschema:"type=number"`
|
Bitrate float64 `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
|
||||||
Drop uint64 `json:"drop" format:"uint64"`
|
Speed float64 `json:"speed" swaggertype:"number" jsonschema:"type=number"`
|
||||||
Dup uint64 `json:"dup" format:"uint64"`
|
Drop uint64 `json:"drop" format:"uint64"`
|
||||||
|
Dup uint64 `json:"dup" format:"uint64"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GraphElement struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Filter string `json:"filter"`
|
||||||
|
DstName string `json:"dst_name"`
|
||||||
|
DstFilter string `json:"dst_filter"`
|
||||||
|
Inpad string `json:"inpad"`
|
||||||
|
Outpad string `json:"outpad"`
|
||||||
|
Timebase string `json:"timebase"`
|
||||||
|
Type string `json:"type"` // audio or video
|
||||||
|
Format string `json:"format"`
|
||||||
|
Sampling uint64 `json:"sampling"` // Hz
|
||||||
|
Layout string `json:"layout"`
|
||||||
|
Width uint64 `json:"width"`
|
||||||
|
Height uint64 `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GraphMapping struct {
|
||||||
|
Input int `json:"input"`
|
||||||
|
Output int `json:"output"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Copy bool `json:"copy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamMapping struct {
|
||||||
|
Graphs []GraphElement `json:"graphs"`
|
||||||
|
Mapping []GraphMapping `json:"mapping"`
|
||||||
}
|
}
|
||||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -78,7 +78,7 @@ github.com/cespare/xxhash/v2
|
|||||||
# github.com/cpuguy83/go-md2man/v2 v2.0.2
|
# github.com/cpuguy83/go-md2man/v2 v2.0.2
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/cpuguy83/go-md2man/v2/md2man
|
github.com/cpuguy83/go-md2man/v2/md2man
|
||||||
# github.com/datarhei/core-client-go/v16 v16.11.1-0.20230920220938-7a37b8ea80ef
|
# github.com/datarhei/core-client-go/v16 v16.11.1-0.20230926123431-2fdbec157292
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/datarhei/core-client-go/v16
|
github.com/datarhei/core-client-go/v16
|
||||||
github.com/datarhei/core-client-go/v16/api
|
github.com/datarhei/core-client-go/v16/api
|
||||||
|
Reference in New Issue
Block a user