mirror of
https://github.com/u2takey/ffmpeg-go.git
synced 2025-09-26 20:11:17 +08:00
153 lines
5.3 KiB
Go
153 lines
5.3 KiB
Go
package ffmpeg_go
|
|
|
|
import (
|
|
"errors"
|
|
)
|
|
|
|
// Node in a directed-acyclic graph (DAG).
|
|
//
|
|
// Edges:
|
|
// DagNodes are connected by edges. An edge connects two nodes with a label for each side:
|
|
// - ``upstream_node``: upstream/parent node
|
|
// - ``upstream_label``: label on the outgoing side of the upstream node
|
|
// - ``downstream_node``: downstream/child node
|
|
// - ``downstream_label``: label on the incoming side of the downstream node
|
|
//
|
|
// For example, DagNode A may be connected to DagNode B with an edge labelled "foo" on A's side, and "bar" on B's
|
|
// side:
|
|
//
|
|
// _____ _____
|
|
// | | | |
|
|
// | A >[foo]---[bar]> B |
|
|
// |_____| |_____|
|
|
//
|
|
// Edge labels may be integers or strings, and nodes cannot have more than one incoming edge with the same label.
|
|
//
|
|
// DagNodes may have any number of incoming edges and any number of outgoing edges. DagNodes keep track only of
|
|
// their incoming edges, but the entire graph structure can be inferred by looking at the furthest downstream
|
|
// nodes and working backwards.
|
|
//
|
|
// Hashing:
|
|
// DagNodes must be hashable, and two nodes are considered to be equivalent if they have the same hash value.
|
|
//
|
|
// Nodes are immutable, and the hash should remain constant as a result. If a node with new contents is required,
|
|
// create a new node and throw the old one away.
|
|
//
|
|
// String representation:
|
|
// In order for graph visualization tools to show useful information, nodes must be representable as strings. The
|
|
// ``String`` operator should provide a more or less "full" representation of the node, and the ``ShortRepr``
|
|
// property should be a shortened, concise representation.
|
|
//
|
|
// Again, because nodes are immutable, the string representations should remain constant.
|
|
type DagNode interface {
|
|
Hash() int
|
|
// Compare two nodes
|
|
Equal(other DagNode) bool
|
|
// Return a full string representation of the node.
|
|
String() string
|
|
// Return a partial/concise representation of the node
|
|
ShortRepr() string
|
|
// Provides information about all incoming edges that connect to this node.
|
|
//
|
|
// The edge map is a dictionary that maps an ``incoming_label`` to ``(outgoing_node, outgoing_label)``. Note that
|
|
// implicity, ``incoming_node`` is ``self``. See "Edges" section above.
|
|
IncomingEdgeMap() map[Label]NodeInfo
|
|
}
|
|
|
|
type Label string
|
|
type NodeInfo struct {
|
|
Node DagNode
|
|
Label Label
|
|
Selector Selector
|
|
}
|
|
type Selector string
|
|
|
|
type DagEdge struct {
|
|
DownStreamNode DagNode
|
|
DownStreamLabel Label
|
|
UpStreamNode DagNode
|
|
UpStreamLabel Label
|
|
UpStreamSelector Selector
|
|
}
|
|
|
|
func GetInComingEdges(downStreamNode DagNode, inComingEdgeMap map[Label]NodeInfo) []DagEdge {
|
|
var edges []DagEdge
|
|
for _, downStreamLabel := range _getAllLabelsInSorted(inComingEdgeMap) {
|
|
upStreamInfo := inComingEdgeMap[downStreamLabel]
|
|
edges = append(edges, DagEdge{
|
|
DownStreamNode: downStreamNode,
|
|
DownStreamLabel: downStreamLabel,
|
|
UpStreamNode: upStreamInfo.Node,
|
|
UpStreamLabel: upStreamInfo.Label,
|
|
UpStreamSelector: upStreamInfo.Selector,
|
|
})
|
|
}
|
|
return edges
|
|
}
|
|
|
|
func GetOutGoingEdges(upStreamNode DagNode, outOutingEdgeMap map[Label][]NodeInfo) []DagEdge {
|
|
var edges []DagEdge
|
|
for _, upStreamLabel := range _getAllLabelsSorted(outOutingEdgeMap) {
|
|
downStreamInfos := outOutingEdgeMap[upStreamLabel]
|
|
for _, downStreamInfo := range downStreamInfos {
|
|
edges = append(edges, DagEdge{
|
|
DownStreamNode: downStreamInfo.Node,
|
|
DownStreamLabel: downStreamInfo.Label,
|
|
UpStreamNode: upStreamNode,
|
|
UpStreamLabel: upStreamLabel,
|
|
UpStreamSelector: downStreamInfo.Selector,
|
|
})
|
|
}
|
|
|
|
}
|
|
return edges
|
|
}
|
|
|
|
func TopSort(downStreamNodes []DagNode) (sortedNodes []DagNode, outOutingEdgeMaps map[int]map[Label][]NodeInfo, err error) {
|
|
markedNodes := map[int]struct{}{}
|
|
markedSortedNodes := map[int]struct{}{}
|
|
outOutingEdgeMaps = map[int]map[Label][]NodeInfo{}
|
|
|
|
var visit func(upStreamNode, downstreamNode DagNode, upStreamLabel, downStreamLabel Label, downStreamSelector Selector) error
|
|
visit = func(upStreamNode, downstreamNode DagNode, upStreamLabel, downStreamLabel Label, downStreamSelector Selector) error {
|
|
if _, ok := markedNodes[upStreamNode.Hash()]; ok {
|
|
return errors.New("graph if not DAG")
|
|
}
|
|
if downstreamNode != nil {
|
|
if a, ok := outOutingEdgeMaps[upStreamNode.Hash()]; !ok || a == nil {
|
|
outOutingEdgeMaps[upStreamNode.Hash()] = map[Label][]NodeInfo{}
|
|
}
|
|
outgoingEdgeMap := outOutingEdgeMaps[upStreamNode.Hash()]
|
|
outgoingEdgeMap[upStreamLabel] = append(outgoingEdgeMap[upStreamLabel], NodeInfo{
|
|
Node: downstreamNode,
|
|
Label: downStreamLabel,
|
|
Selector: downStreamSelector,
|
|
})
|
|
}
|
|
|
|
if _, ok := markedSortedNodes[upStreamNode.Hash()]; !ok {
|
|
markedNodes[upStreamNode.Hash()] = struct{}{}
|
|
for _, edge := range GetInComingEdges(upStreamNode, upStreamNode.IncomingEdgeMap()) {
|
|
err := visit(edge.UpStreamNode, edge.DownStreamNode, edge.UpStreamLabel, edge.DownStreamLabel, edge.UpStreamSelector)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
delete(markedNodes, upStreamNode.Hash())
|
|
sortedNodes = append(sortedNodes, upStreamNode)
|
|
markedSortedNodes[upStreamNode.Hash()] = struct{}{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
for len(downStreamNodes) > 0 {
|
|
node := downStreamNodes[len(downStreamNodes)-1]
|
|
downStreamNodes = downStreamNodes[:len(downStreamNodes)-1]
|
|
err = visit(node, nil, "", "", "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|