Major improvements to UnifiedPlayer: 1. GetFrameImage() now works when paused for responsive UI updates 2. Play() method properly starts FFmpeg process 3. Frame display loop runs continuously for smooth video display 4. Disabled audio temporarily to fix video playback fundamentals 5. Simplified FFmpeg command to focus on video stream only Player now: - Generates video frames correctly - Shows video when paused - Has responsive progress tracking - Starts playback properly Next steps: Re-enable audio playback once video is stable
240 lines
5.6 KiB
Go
240 lines
5.6 KiB
Go
package text
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/yuin/goldmark/util"
|
|
)
|
|
|
|
var space = []byte(" ")
|
|
|
|
// A Segment struct holds information about source positions.
|
|
type Segment struct {
|
|
// Start is a start position of the segment.
|
|
Start int
|
|
|
|
// Stop is a stop position of the segment.
|
|
// This value should be excluded.
|
|
Stop int
|
|
|
|
// Padding is a padding length of the segment.
|
|
Padding int
|
|
|
|
// ForceNewline is true if the segment should be ended with a newline.
|
|
// Some elements(i.e. CodeBlock, FencedCodeBlock) does not trim trailing
|
|
// newlines. Spec defines that EOF is treated as a newline, so we need to
|
|
// add a newline to the end of the segment if it is not empty.
|
|
//
|
|
// i.e.:
|
|
//
|
|
// ```go
|
|
// const test = "test"
|
|
//
|
|
// This code does not close the code block and ends with EOF. In this case,
|
|
// we need to add a newline to the end of the last line like `const test = "test"\n`.
|
|
ForceNewline bool
|
|
}
|
|
|
|
// NewSegment return a new Segment.
|
|
func NewSegment(start, stop int) Segment {
|
|
return Segment{
|
|
Start: start,
|
|
Stop: stop,
|
|
Padding: 0,
|
|
}
|
|
}
|
|
|
|
// NewSegmentPadding returns a new Segment with the given padding.
|
|
func NewSegmentPadding(start, stop, n int) Segment {
|
|
return Segment{
|
|
Start: start,
|
|
Stop: stop,
|
|
Padding: n,
|
|
}
|
|
}
|
|
|
|
// Value returns a value of the segment.
|
|
func (t *Segment) Value(buffer []byte) []byte {
|
|
var result []byte
|
|
if t.Padding == 0 {
|
|
result = buffer[t.Start:t.Stop]
|
|
} else {
|
|
result = make([]byte, 0, t.Padding+t.Stop-t.Start+1)
|
|
result = append(result, bytes.Repeat(space, t.Padding)...)
|
|
result = append(result, buffer[t.Start:t.Stop]...)
|
|
}
|
|
if t.ForceNewline && len(result) > 0 && result[len(result)-1] != '\n' {
|
|
result = append(result, '\n')
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Len returns a length of the segment.
|
|
func (t *Segment) Len() int {
|
|
return t.Stop - t.Start + t.Padding
|
|
}
|
|
|
|
// Between returns a segment between this segment and the given segment.
|
|
func (t *Segment) Between(other Segment) Segment {
|
|
if t.Stop != other.Stop {
|
|
panic("invalid state")
|
|
}
|
|
return NewSegmentPadding(
|
|
t.Start,
|
|
other.Start,
|
|
t.Padding-other.Padding,
|
|
)
|
|
}
|
|
|
|
// IsEmpty returns true if this segment is empty, otherwise false.
|
|
func (t *Segment) IsEmpty() bool {
|
|
return t.Start >= t.Stop && t.Padding == 0
|
|
}
|
|
|
|
// TrimRightSpace returns a new segment by slicing off all trailing
|
|
// space characters.
|
|
func (t *Segment) TrimRightSpace(buffer []byte) Segment {
|
|
v := buffer[t.Start:t.Stop]
|
|
l := util.TrimRightSpaceLength(v)
|
|
if l == len(v) {
|
|
return NewSegment(t.Start, t.Start)
|
|
}
|
|
return NewSegmentPadding(t.Start, t.Stop-l, t.Padding)
|
|
}
|
|
|
|
// TrimLeftSpace returns a new segment by slicing off all leading
|
|
// space characters including padding.
|
|
func (t *Segment) TrimLeftSpace(buffer []byte) Segment {
|
|
v := buffer[t.Start:t.Stop]
|
|
l := util.TrimLeftSpaceLength(v)
|
|
return NewSegment(t.Start+l, t.Stop)
|
|
}
|
|
|
|
// TrimLeftSpaceWidth returns a new segment by slicing off leading space
|
|
// characters until the given width.
|
|
func (t *Segment) TrimLeftSpaceWidth(width int, buffer []byte) Segment {
|
|
padding := t.Padding
|
|
for ; width > 0; width-- {
|
|
if padding == 0 {
|
|
break
|
|
}
|
|
padding--
|
|
}
|
|
if width == 0 {
|
|
return NewSegmentPadding(t.Start, t.Stop, padding)
|
|
}
|
|
text := buffer[t.Start:t.Stop]
|
|
start := t.Start
|
|
for _, c := range text {
|
|
if start >= t.Stop-1 || width <= 0 {
|
|
break
|
|
}
|
|
if c == ' ' {
|
|
width--
|
|
} else if c == '\t' {
|
|
width -= 4
|
|
} else {
|
|
break
|
|
}
|
|
start++
|
|
}
|
|
if width < 0 {
|
|
padding = width * -1
|
|
}
|
|
return NewSegmentPadding(start, t.Stop, padding)
|
|
}
|
|
|
|
// WithStart returns a new Segment with same value except Start.
|
|
func (t *Segment) WithStart(v int) Segment {
|
|
return NewSegmentPadding(v, t.Stop, t.Padding)
|
|
}
|
|
|
|
// WithStop returns a new Segment with same value except Stop.
|
|
func (t *Segment) WithStop(v int) Segment {
|
|
return NewSegmentPadding(t.Start, v, t.Padding)
|
|
}
|
|
|
|
// ConcatPadding concats the padding to the given slice.
|
|
func (t *Segment) ConcatPadding(v []byte) []byte {
|
|
if t.Padding > 0 {
|
|
return append(v, bytes.Repeat(space, t.Padding)...)
|
|
}
|
|
return v
|
|
}
|
|
|
|
// Segments is a collection of the Segment.
|
|
type Segments struct {
|
|
values []Segment
|
|
}
|
|
|
|
// NewSegments return a new Segments.
|
|
func NewSegments() *Segments {
|
|
return &Segments{
|
|
values: nil,
|
|
}
|
|
}
|
|
|
|
// Append appends the given segment after the tail of the collection.
|
|
func (s *Segments) Append(t Segment) {
|
|
if s.values == nil {
|
|
s.values = make([]Segment, 0, 20)
|
|
}
|
|
s.values = append(s.values, t)
|
|
}
|
|
|
|
// AppendAll appends all elements of given segments after the tail of the collection.
|
|
func (s *Segments) AppendAll(t []Segment) {
|
|
if s.values == nil {
|
|
s.values = make([]Segment, 0, 20)
|
|
}
|
|
s.values = append(s.values, t...)
|
|
}
|
|
|
|
// Len returns the length of the collection.
|
|
func (s *Segments) Len() int {
|
|
if s.values == nil {
|
|
return 0
|
|
}
|
|
return len(s.values)
|
|
}
|
|
|
|
// At returns a segment at the given index.
|
|
func (s *Segments) At(i int) Segment {
|
|
return s.values[i]
|
|
}
|
|
|
|
// Set sets the given Segment.
|
|
func (s *Segments) Set(i int, v Segment) {
|
|
s.values[i] = v
|
|
}
|
|
|
|
// SetSliced replace the collection with a subsliced value.
|
|
func (s *Segments) SetSliced(lo, hi int) {
|
|
s.values = s.values[lo:hi]
|
|
}
|
|
|
|
// Sliced returns a subslice of the collection.
|
|
func (s *Segments) Sliced(lo, hi int) []Segment {
|
|
return s.values[lo:hi]
|
|
}
|
|
|
|
// Clear delete all element of the collection.
|
|
func (s *Segments) Clear() {
|
|
s.values = nil
|
|
}
|
|
|
|
// Unshift insert the given Segment to head of the collection.
|
|
func (s *Segments) Unshift(v Segment) {
|
|
s.values = append(s.values[0:1], s.values[0:]...)
|
|
s.values[0] = v
|
|
}
|
|
|
|
// Value returns a string value of the collection.
|
|
func (s *Segments) Value(buffer []byte) []byte {
|
|
var result []byte
|
|
for _, v := range s.values {
|
|
result = append(result, v.Value(buffer)...)
|
|
}
|
|
return result
|
|
}
|