AndrisBB Posted July 17 Share Posted July 17 Tākā ārā pamatīgs karstums un neko citu interesantu darīt tāpat nav ko, tad jāieposto šodienas un vakardienas mini prototip-projekts. Problēma: Esu ievērijis ka ir pamatīgs trūkums softwārei, kas var paņemt video un uzrenderēt pa virsu kautkādus vidžetus ar sirdsdarbību, ātrumu, lokāciju utt no GPX faila. Itkā ir Garmin Virb Edit, bet tas jau pamests ntos gadus, plus softwāre ar tāda jancīga un neko nopietnu tur izdarīt tāpat nevar. Nekādus citus jēdzīgus variantus redzējis neesu. Risinājums: Uztaisīt Gstreamer pluginu, kas var nolasīt GPX failu un uzrenderēt widžetus pa virsu video. Widžeti varētu būt SVG faili ar kautkādu grafiku un tad uz katra kadra palaižas JavaScript funkcija kura saņem GPX datus, kas atbilst tam laikam, modificē SVG un atgriež pluginam, lai var uzrenderēt SVG pa virsu. Reāli pielietojums varētu būt ka vienkārši norenderē to video uz kautkāda zaļā fona, ko pēctam var izmantot jebkurā video editēšanas softā, graizīt utt. Bet var arī renderēt pa virsu jebkuram video. Arhitektūra: Lai renderētu SVG izmantoju RSVG bibliotēku no Glib. Kā JavaScript dziēju izmantoju Duktape. Ļoti minimāls dzinējs kas neko nezin par dokumentiem, tīklu utt, var izpildīt tik parastu JavaScript. Uz katru kadru Gstreamer plugins padod GPX datus, JavaScript nolasa, modificē SVG un atgriež. Tad plugins uzrenderē to SVG kadram pa virsu. Lai ierakstītu piemēram sirdsdarbību uz video un iekodētu iekš h264 var izmantot ļoti pronitīvu pipelainu. gst-launch-1.0 videotestsrc num-buffers=600 \ ! video/x-raw,width=1280,height=960 \ ! videoconvert \ ! gpxoverlay \ location=../src/gpxoverlay/assets/atom.svg \ script=../src/gpxoverlay/assets/test.js \ gpx-location=../src/gpxoverlay/assets/route.gpx \ ! videoconvert \ ! queue \ ! x264enc \ ! mp4mux \ ! filesink \ location=../video.mp4 GPX izskatās apmēram tā, kur vienkārši XML datu punkti (koordinātas padzēsu): <?xml version="1.0" encoding="UTF-8"?> <gpx creator="Garmin Connect" version="1.1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/11.xsd" xmlns:ns3="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://www.garmin.com/xmlschemas/GpxExtensions/v3"> <metadata> <link href="connect.garmin.com"> <text>Garmin Connect</text> </link> <time>2022-07-16T14:42:51.000Z</time> </metadata> <trk> <name>South Gloucestershire Cycling</name> <type>cycling</type> <trkseg> <trkpt lat="" lon=""> <ele>77.59999847412109375</ele> <time>2022-07-16T14:44:21.000Z</time> <extensions> <ns3:TrackPointExtension> <ns3:atemp>30.0</ns3:atemp> <ns3:hr>103</ns3:hr> <ns3:cad>78</ns3:cad> </ns3:TrackPointExtension> </extensions> </trkpt> <trkpt lat="" lon=""> <ele>77.8000030517578125</ele> <time>2022-07-16T14:44:22.000Z</time> <extensions> <ns3:TrackPointExtension> <ns3:atemp>30.0</ns3:atemp> <ns3:hr>103</ns3:hr> <ns3:cad>78</ns3:cad> </ns3:TrackPointExtension> </extensions> </trkpt> <trkpt lat="" lon=""> <ele>78</ele> <time>2022-07-16T14:44:24.000Z</time> <extensions> <ns3:TrackPointExtension> <ns3:atemp>30.0</ns3:atemp> <ns3:hr>102</ns3:hr> <ns3:cad>78</ns3:cad> </ns3:TrackPointExtension> </extensions> </trkpt> <trkpt lat="" lon=""> <ele>78.59999847412109375</ele> <time>2022-07-16T14:44:27.000Z</time> <extensions> <ns3:TrackPointExtension> <ns3:atemp>30.0</ns3:atemp> <ns3:hr>104</ns3:hr> <ns3:cad>78</ns3:cad> </ns3:TrackPointExtension> </extensions> </trkpt> <trkpt lat="" lon=""> <ele>79.1999969482421875</ele> <time>2022-07-16T14:44:31.000Z</time> <extensions> <ns3:TrackPointExtension> <ns3:atemp>30.0</ns3:atemp> <ns3:hr>105</ns3:hr> <ns3:cad>77</ns3:cad> </ns3:TrackPointExtension> </extensions> </trkpt> Loti vienkāršs SVG widžets (jātrod kāds mākslinkies, kas sazīmē ko interesantāku) <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="312" height="120" viewBox="0 0 78 30"> <text xml:id="timestamp_id" x="2" y="29" fill="blue" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:6px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#1a00ff;fill-opacity:1;stroke-width:0.265;stroke-dasharray:none"> </text> <rect style="fill:#494949;fill-opacity:0.3;stroke:none;" width="78" height="30"/> <text xml:id="label_id" x="20" y="8" fill="blue" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:6px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffffff;fill-opacity:1;stroke-width:0.265;stroke-dasharray:none"> HEART RATE </text> <text xml:id="hr_id" x="20" y="20" fill="blue" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#1ad93e;fill-opacity:1;stroke-width:0.265;stroke-dasharray:none"> 140 BPM </text> <path xml:id="heart_id" transform="scale(1.2 1.2)" d="m 14,4 a 4.0366464,4.1745268 0.0194517 0 0 -2.745618,-1.114394 4.0366464,4.1745268 0.0194517 0 0 -2.907338,1.281633 l -0.0016,-0.0017 -0.0016,0.0017 a 4.0366464,4.1745268 0.0194517 0 0 -2.907286,-1.28159 4.0366464,4.1745268 0.0194517 0 0 -4.036694,4.174578 4.0366464,4.1745268 0.0194517 0 0 1.239257,3.00659 l -0.0016,0.002 5.707812,5.90276 5.708275,-5.90325 -3.96e-4,-3.8e-4 a 4.0366464,4.1745268 0.0194517 0 0 1.23801,-3.00797 4.0366464,4.1745268 0.0194517 0 0 -1.290981,-3.060131 z" fill="#ff00ff"/> </svg> Un pats JavaScripts var document = new Document("../src/gpxoverlay/assets/atom.svg"); this.Document = document; var counter = 0; function start() { print("JS start called"); } function render(point) { counter = counter + 1; if(point != undefined) { // print(point.timestamp); var el = Document.getElementById("timestamp_id"); if(el != undefined) { el.innerHTML = point.timestamp; } el = Document.getElementById("hr_id"); if(el != undefined) { el.innerHTML = point.hr.toString() + " BPM"; } el = Document.getElementById("heart_id"); if(el != undefined) { var fill = "#ffffff" if(counter >= 0 && counter < 15) { fill = "#ff0000"; } if(counter > 30) { counter = 0; } el.setAttribute("fill", fill); } } return document.stringify(); } Gstreamera pipeline sanāk tā (kautkā baigi garais grafiks): Rezultātā video (ja forums rādīs): video.mp4 1 Link to comment Share on other sites More sharing options...
AndrisBB Posted July 17 Author Share Posted July 17 (edited) Kods te https://github.com/AndrisBB/gstreamer-gpx-overlay Pagaidām knapi turās kopā, bet strādā. Kas jādara: * Jāuzraksta labāks parsers priekš GPX failiem. Pagaidām nolasa tikai dažās vērtības, atbalsta tikai vienu segmentu un ielasa vienkārši punktus listē. Renderēšanas laikā iet cauri visai listei kamēr atrod vajadzīgo. Kautko gudrāku tur vajag. * Duktape JavaScript dzinējs ir diezgan interesants veidojums, ar kuru komunicēt nav tas vieglākais uzdevums. Jāstumda mainīgos uz no/staka. Jādomā kā kautkādā assamblerī. Domu var saprast te https://github.com/AndrisBB/gstreamer-gpx-overlay/blob/master/src/gpxoverlay/gstduktape.c Dokumentācija te. Kā izsaukt funckijas no C uz Javascript, vai no JavasScript uz C. https://duktape.org/index.html * Duktape ir pliks JavaScript dzinējs, nav tur ne Document, ne Element, nu nekā ko piedāvā browzers. Pat ne Console.log(). Nākas visu rakstīt pašam. Piemēram pagaidām no 'Document' ir viena funkcija -> getElementByID. Tāpat arī elementam ir tikai 'innerHTML' propertijs un 'setAttribute'. Izmantoju GDOMe bibliotēku SVG/HTML parsēšanai un modificēšanai. * Renderēšana pagaidām diezgan vārga. Izmantoju rSVG bibliotēku, lai uzrenderētu SVG uz kadra. Tas pagaidām knapi turas kopā. Krāsas ar var redzēt ka nav pareizās. RGBA sajaukti vietām, bet slinkums tagad risināt. * Nav nekādu widžetu. Jāpasūta kādam dizainerim iekš Fiverr.com vai kur līdzīgi, lai sazīmē ko interesantu. Īsumā - var teikt ka kautkas darbojas, bet nu tālu līdz paberigšanai, cerams nepazudīs interese un beigās kas lietderīgs sanāks. Edited July 17 by AndrisBB Link to comment Share on other sites More sharing options...
AndrisBB Posted July 17 Author Share Posted July 17 Garmin Virb Edit var uztaisīt ko tādu Link to comment Share on other sites More sharing options...
AndrisBB Posted July 24 Author Share Posted July 24 (edited) Šodien mazliet pačakarējos un uztaisiju primitīvu 'elavation' widžetu. Venkārši sākumā savāc visus punktus un tad uzrenderē pligonu un pašreizējo punktu. SVG <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="480" height="240" viewBox="0 0 480 240"> <rect style="fill:#494949;fill-opacity:0.3;stroke:none;" width="480" height="240"/> <text xml:id="label_id" x="8" y="24" fill="blue" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#00ff00;fill-opacity:1;stroke-width:0.265;stroke-dasharray:none"> ELEVATION </text> <polygon xml:id="elevation_graph" points="480,120 480,240 0,240 0,48 48,96 96,72 144,100 192,144 240,100 288,84 336,73 384,65 432,55" style="fill:#6adb5c;fill-opacity:0.9" transform=""></polygon> <circle xml:id="current_pos" stroke="#000000" stroke-width="3px" cx="-10" cy="-10" fill="#ffffff" r="10" transform=""></circle> </svg> JavaScripts var document = new Document("../src/gpxoverlay/assets/elevation.svg"); this.Document = document; var counter = 0; var _segment = null; var _elevation_min = Number.MAX_VALUE; var _elevation_max = Number.MIN_VALUE; var MAX_POINTS_TO_RENDER = 480; var GRAPH_WIDTH = 480; var GRAPH_HEIGHT = 240; var _points_to_render = 0; function start(segment) { _segment = segment; // Find min and max elevation values var trkPoints = segment.trkPoints; for (var i = 0; i < trkPoints.length; i++) { if(trkPoints[i].elevation > _elevation_max) { _elevation_max = trkPoints[i].elevation; } if(trkPoints[i].elevation < _elevation_min) { _elevation_min = trkPoints[i].elevation; } } if(trkPoints.length >= MAX_POINTS_TO_RENDER) { _points_to_render = MAX_POINTS_TO_RENDER; } else { _points_to_render = trkPoints.length; } } function filter_points(points, idx) { var first_point = 0; if(idx < _points_to_render/2) { first_point = 0; } else { first_point = idx - _points_to_render/2; } return points.slice(first_point, first_point + _points_to_render); } function render(point) { var points = filter_points(_segment.trkPoints, point.idx); var polygon = "480,240 0,240"; for(var i = 0; i < points.length; i++) { var x = i * (GRAPH_WIDTH/points.length); var y = GRAPH_HEIGHT - parseInt(((_elevation_max - _elevation_min)/GRAPH_HEIGHT) * points[i].elevation); polygon = polygon.concat(" " + x + "," + y); if(point.idx == points[i].idx) { var el_pos = Document.getElementById("current_pos"); if(el_pos != undefined) { el_pos.setAttribute("cx", x); el_pos.setAttribute("cy", y); } } } var el = Document.getElementById("elevation_graph"); if(el != undefined) { el.setAttribute("points", polygon); } el = Document.getElementById("label_id"); if(el != undefined) { el.innerHTML = "ELEVATION " + point.elevation.toFixed(2).toString() + "M"; } return document.stringify(); } Edited July 24 by AndrisBB Link to comment Share on other sites More sharing options...
AndrisBB Posted July 24 Author Share Posted July 24 Divi widžeti uz parasta testa fona gst-launch-1.0 videotestsrc \ ! video/x-raw,width=1280,height=960 \ ! videoconvert \ ! gpxoverlay \ script-location=../src/gpxoverlay/assets/elevation.js \ gpx-location=../src/gpxoverlay/assets/activity_9090497773.gpx \ x=790 \ y=10 \ ! gpxoverlay \ script-location=../src/gpxoverlay/assets/test.js \ gpx-location=../src/gpxoverlay/assets/activity_9090497773.gpx \ x=10 \ y=10 \ ! videoconvert \ ! gtksink 222522974_ScreenRecording2022-07-24at16_45_36.mp4 Pa virsu video failam gst-launch-1.0 filesrc location=../rec.mp4 \ ! decodebin name=dec \ ! videoconvert \ ! gpxoverlay \ script-location=../src/gpxoverlay/assets/elevation.js \ gpx-location=../src/gpxoverlay/assets/activity_9090497773.gpx \ x=790 \ y=10 \ ! gpxoverlay \ script-location=../src/gpxoverlay/assets/test.js \ gpx-location=../src/gpxoverlay/assets/activity_9090497773.gpx \ x=10 \ y=10 \ ! videoconvert \ ! gtksink 2147137468_ScreenRecording2022-07-24at16_54_12.mp4 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now