package main import ( "encoding/binary" "log" flowdef "mxl-pattern-generator/internal/flow-def" "mxl-pattern-generator/internal/generator" "os" "os/signal" "syscall" "github.com/qvest-digital/go-mxl/mxl" ) func main() { var width, height uint = 1920, 1080 var fps_num, fps_den uint = 25, 1 flowUUID := "5fbec3b1-1b0f-417d-9059-8b94a47197ed" log.Printf("Go Pattern Gen") log.Printf("Video: %dx%dp%d/%d", width, height, fps_num, fps_den) log.Printf("UUID: %s", flowUUID) domain := "/dev/shm/mxl" inst, err := mxl.NewInstance(domain, "") if err != nil { log.Fatalf("MXL Init Failed: %v", err) } defer inst.Close() flowDef, err := flowdef.NewFlowDefJSON( flowdef.TYPE_VIDEO, flowUUID, width, height, fps_num, fps_den, ) if err != nil { log.Fatalf("Could not create Flow Definition: %v", err) } writer, isCreated, err := inst.NewWriter(flowDef) if err != nil { log.Fatalf("Failed to create MXL writer: %v", err) } if !isCreated { log.Printf("reusing existing flow: %s, domain: %s", flowUUID, domain) } defer writer.Close() var gen *generator.OpenCLGenerator gen, err = generator.NewOpenCLGenerator(width, height, "/home/itten/codeproj/go-mxl-pattern-gen/kernels/v210_pack.cl") gen, err = generator.NewOpenCLGenerator(width, height, "/home/itten/codeproj/go-mxl-pattern-gen/kernels/v210_pack.cl") if err != nil { log.Fatalf("OpenCL init failed: %v", err) } defer gen.Close() gen.SetText(generator.TextConfig{ Str: "MXL PATTERN GEN", X: 0, Y: 0, Scale: 8, FG: [3]uint32{940, 512, 512}, BG: [3]uint32{64, 512, 512}, Boxed: true, }) flowCfg := writer.Config() rate := flowCfg.Common.GrainRate idx := mxl.CurrentIndex(rate) log.Printf("writing flow grainRate=%d/%d starting at idx=%d", rate.Num, rate.Den, idx) stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) var written int64 for { select { case <-stop: log.Printf("stopping after %d grains", written) return default: } gwa, err := writer.OpenGrain(idx) if err != nil { log.Fatalf("OpenGrain(%d): %v", idx, err) } // fillGrainRamp(gwa.Payload, idx) // smpteBars(gwa.Payload) gen.GenerateFrame(gwa.Payload, int(idx)) // if err != nil { // log.Fatalf("Frame Generator: %v", err) // } if err := gwa.Commit(gwa.TotalSlices, 0); err != nil { log.Fatalf("Commit(%d): %v", idx, err) } written++ idx++ if written%100 == 0 { log.Printf("grains written=%d, index=%d", written, idx) } // Pace ourselves to roughly the grain rate mxl.SleepNs(mxl.NsUntilIndex(idx, rate)) } } // fillGrainRamp writes byte((i+start)&0xFF) across buf. A matching reader // uses the same pattern to verify grain continuit func fillGrainRamp(buf []byte, start uint64) { for i := range buf { buf[i] = byte((uint64(i) + start) & 0xFF) } } func smpteBars(dest []byte) { const width = 1920 // SMPTE 75% bars, Rec.709, 10-bit Y'CbCr: {Y, Cb, Cr} bars := [7][3]uint32{ {721, 512, 512}, // 75% white {674, 176, 543}, // 75% yellow {581, 589, 176}, // 75% cyan {534, 253, 207}, // 75% green {251, 771, 817}, // 75% magenta {204, 435, 848}, // 75% red {111, 848, 481}, // 75% blue } barIdx := func(x int) int { idx := x * len(bars) / width if idx >= len(bars) { return len(bars) - 1 } return idx } // v210: each 16-byte block carries 6 pixels (Y0..Y5, chroma co-sited at 0/2/4) for block := 0; block*16+16 <= len(dest); block++ { baseX := (block * 6) % width var y [6]uint32 for i := range y { y[i] = bars[barIdx(baseX+i)][0] } cb0, cr0 := bars[barIdx(baseX)][1], bars[barIdx(baseX)][2] cb2, cr2 := bars[barIdx(baseX+2)][1], bars[barIdx(baseX+2)][2] cb4, cr4 := bars[barIdx(baseX+4)][1], bars[barIdx(baseX+4)][2] w0 := (cb0 & 0x3FF) | ((y[0] & 0x3FF) << 10) | ((cr0 & 0x3FF) << 20) w1 := (y[1] & 0x3FF) | ((cb2 & 0x3FF) << 10) | ((y[2] & 0x3FF) << 20) w2 := (cr2 & 0x3FF) | ((y[3] & 0x3FF) << 10) | ((cb4 & 0x3FF) << 20) w3 := (y[4] & 0x3FF) | ((cr4 & 0x3FF) << 10) | ((y[5] & 0x3FF) << 20) byteIdx := block * 16 binary.LittleEndian.PutUint32(dest[byteIdx+0:], w0) binary.LittleEndian.PutUint32(dest[byteIdx+4:], w1) binary.LittleEndian.PutUint32(dest[byteIdx+8:], w2) binary.LittleEndian.PutUint32(dest[byteIdx+12:], w3) } }