Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
JPEG streaming in Go
Search
Harukasan
PRO
March 14, 2017
Technology
2
2.8k
JPEG streaming in Go
pixiv Night #02 - 画像処理技術(go, blender, C++ライブラリ等)
2017-03-14
https://pixiv.connpass.com/event/50284/
Harukasan
PRO
March 14, 2017
Tweet
Share
More Decks by Harukasan
See All by Harukasan
Successor to PicoRabbit: Ruby Programming Envorinment / RubyKaigi 2025 follow up
harukasan
PRO
1
190
Write your own mrbgem, Create your own device
harukasan
PRO
1
120
PicoRabbit: a Tiny Presentation Device Powered by Ruby
harukasan
PRO
2
520
pixivを支える技術 / 技育CAMPアカデミア
harukasan
PRO
3
530
20240401 新卒研修 - ピクシブにおける技術領域
harukasan
PRO
1
850
ピクシブのコンテンツ配信基盤技術 / pixiv TECH SALON
harukasan
PRO
5
5.7k
Goにおける画像ファイル処理 / golang.tokyo #19
harukasan
PRO
7
6.7k
WebRTC動画をトランスコードする / Transcoding video streams from WebRTC
harukasan
PRO
5
1.6k
ImageFluxを支えるリモート開発 / 20171202
harukasan
PRO
2
1.9k
Other Decks in Technology
See All in Technology
ガバメントクラウド(AWS)へのデータ移行戦略の立て方【虎の巻】 / 20251011 Mitsutosi Matsuo
shift_evolve
PRO
2
200
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
930
セキュアな認可付きリモートMCPサーバーをAWSマネージドサービスでつくろう! / Let's build an OAuth protected remote MCP server based on AWS managed services
kaminashi
3
340
AgentCon Accra: Ctrl + Alt + Assist: AI Agents Edition
bethany
0
110
Geospatialの世界最前線を探る [2025年版]
dayjournal
1
230
フレームワークを意識させないワークショップづくり
keigosuda
0
200
AWSでAgentic AIを開発するための前提知識の整理
nasuvitz
2
170
生成AI時代のセキュアコーディングとDevSecOps
yuriemori
0
120
ビズリーチ求職者検索におけるPLMとLLMの活用 / Search Engineering MEET UP_2-1
visional_engineering_and_design
1
140
2025-10-09_プロジェクトマネージャーAIチャンス
taukami
0
150
それでも私が品質保証プロセスを作り続ける理由 #テストラジオ / Why I still continue to create QA process
pineapplecandy
0
110
研究開発部メンバーの働き⽅ / Sansan R&D Profile
sansan33
PRO
3
20k
Featured
See All Featured
Why Our Code Smells
bkeepers
PRO
340
57k
Side Projects
sachag
455
43k
Site-Speed That Sticks
csswizardry
13
910
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
Practical Orchestrator
shlominoach
190
11k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
190
55k
Rails Girls Zürich Keynote
gr2m
95
14k
How to Think Like a Performance Engineer
csswizardry
27
2k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Designing for humans not robots
tammielis
254
26k
Faster Mobile Websites
deanohume
310
31k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.1k
Transcript
JPEG streaming in Go !IBSVLBTBO4IVOTVLF.JDIJJ ͋Δ͍+1&(ͷϒϩοΫϊΠζͱͳʹ͔
Harukasan Shunsuke MICHII Lead engineer of pixiv Inc., Product manager
of ImageFlux, Infrastructure engineer, Kibana, Gopher
None
© pixiv Inc., SAKURA Internet Inc. ImageFlux service ͓٬༷ετϨʔδ ίϯςϯπΩϟογϡ
ը૾มॲཧ ΦϦδφϧը૾Λऔಘ มޙΩϟογϡΛ৴ ΤϯυϢʔβ ։ൃऀ ৴URLͷઃఆ ৴ݩΦϦδϯͷઃఆ ϨϙʔτͷӾཡ طଘετϨʔδΛͦͷ··ར༻Մೳ ը૾σʔλΛطଘͷετϨʔδαʔό͔ ΒҠಈ͢Δඞཁ͋Γ·ͤΜɻཧը໘͔ ΒΦϦδϯΛઃఆ͢Δ͚ͩͰ͙͢ʹը૾ม ͕ར༻ՄೳͰ͢ɻ URLΛม͑Δ͚ͩͰը૾ม ৴URLʹύϥϝʔλΛՃ͢Δ͚ͩͰ ҙͷը૾มΛߦ͑·͢ɻ εϚʔτϑΥϯɺPCͰผʑͷURLΛ͏͜ ͱͰɺ1ຕͷը૾͔Β༷ʑͳαΠζͷը૾ Λ৴͢Δ͜ͱ͕ՄೳͰ͢ɻ ը૾มॲཧ
© pixiv Inc., SAKURA Internet Inc. ImageFlux service ͓٬༷ετϨʔδ ίϯςϯπΩϟογϡ
ը૾มॲཧ ΦϦδφϧը૾Λऔಘ มޙΩϟογϡΛ৴ ΤϯυϢʔβ ։ൃऀ ৴URLͷઃఆ ৴ݩΦϦδϯͷઃఆ ϨϙʔτͷӾཡ طଘετϨʔδΛͦͷ··ར༻Մೳ ը૾σʔλΛطଘͷετϨʔδαʔό͔ ΒҠಈ͢Δඞཁ͋Γ·ͤΜɻཧը໘͔ ΒΦϦδϯΛઃఆ͢Δ͚ͩͰ͙͢ʹը૾ม ͕ར༻ՄೳͰ͢ɻ URLΛม͑Δ͚ͩͰը૾ม ৴URLʹύϥϝʔλΛՃ͢Δ͚ͩͰ ҙͷը૾มΛߦ͑·͢ɻ εϚʔτϑΥϯɺPCͰผʑͷURLΛ͏͜ ͱͰɺ1ຕͷը૾͔Β༷ʑͳαΠζͷը૾ Λ৴͢Δ͜ͱ͕ՄೳͰ͢ɻ ը૾มॲཧ
Decoding a JPEG image Resizing Encoding into a new JPEG
image Fetching from an origin Writing content into a client http.Transport libjpeg-turbo libswscale from ffmpeg http.ResponseWriter libjpeg-turbo
streaming in Go • io.Reader: reading content into the given
buffer • io.Writer: writing content from the given buffer typedef Reader interface { func Read(p []byte) (n int, err error) } typedef Writer interface { func Write(p []byte) (n int, err error) }
• Copying content from a reader into a writer io.Copy(dst,
src) 32KB buffer io.Reader io.Writer Read(buffer) Write(buffer) : 32KBͷόοϑΝ෦Ͱຖճmake
• Copying content from a reader into a writer •
To avoid allocating a buffer, use io.CopyBuffer: io.Copy(dst, src) 32KB buffer io.Reader io.Writer Read(buffer) Write(buffer) io.CopyBuffer(dst, src, buf) : 32KBͷόοϑΝ෦Ͱຖճmake
func ServeHTTP(w *http.ResponseWriter, r *http.Request) { req, _ := http.NewRequest("GET",
"http://www.example.com", nil) resp, _ := http.DefaultTransport.RoundTrip(req) io.CopyBuffer(w, resp.Body, buf) } HTTPίϐʔ͢Δ͚ͩͰετϦʔϛϯάͰ͖ΔʢΤϥʔॲཧ……ʣ
Decoding a JPEG image Resizing Encoding into a new JPEG
image Fetching from an origin Writing content into a client http.Transport libjpeg-turbo libswscale from ffmpeg http.ResponseWriter libjpeg-turbo
Streaming image scanlines • ScanlineReader interface typedef ScanlineReader interface {
func Config() *Config → plane sizes, pixel format,... func ReadScanLine(p [][]uint8) (n int, err error) → reading scanlines of planes into each buffer → PNG: JPEG: } R G B Y Cb Cr *blue difference chroma *red difference chroma
Decoding a JPEG image Resizing Encoding into a new JPEG
image Fetching from an origin Writing content into a client http.Transport libjpeg-turbo libswscale from ffmpeg http.ResponseWriter libjpeg-turbo
Decoding a JPEG image Resizing Encoding into a new JPEG
image Fetching from an origin Writing content into a client http.Transport Decoder.ReadScanLines Scaler.ReadScanLines http.ResponseWriter Encoder.ReadScanLines
func ServeHTTP(w *http.ResponseWriter, r *http.Request) { req, _ := http.NewRequest("GET",
"http://www.example.com", nil) resp, _ := http.DefaultTransport.RoundTrip(req) dec := jpeg.NewDecoder(resp.Body io.Reader) → ScanlineReader dec := scale.NewResize(dec ScanlineReader) → ScanlineReader enc := jpeg.NewEncoder() enc.Write(w io.Writer, dec ScanlineReader) }
streaming image scanlines • ScanlineReader interfaceΛ͏͜ͱͰը૾σʔλετϦʔϜͱ͠ ͯѻ͏͜ͱ͕Ͱ͖Δ • interfaceΛ͏͜ͱͰ֤ॲཧͷར༻ํ๏Λ౷ҰͰ͖Δ •
ඪ४ϥΠϒϥϦͷΠϯλʔϑΣʔεΑ͘ߟ͑ΒΕͯΔͷͰϚω͢Δ ͱྑ͍ײ͡ʹͳΔ
ͳͥετϦʔϛϯά͢Δඞཁ͕͋Δͷ͔ • ը૾σʔλΛ͍ͬΜʹॲཧ͢ΔͱશըૉͷϝϞϦ͕ඞཁ͕ͩ ετϦʔϜॲཧ͢ΕόοϑΝΛҰ֬อ͢Δ͚ͩͰࡁΉ • ֤৭ۭؒຖʹॲཧΛهड़Ͱ͖ɺແବͳ৭ม͕ൃੜ͠ͳ͍ JPEGಉ࢜ͷϦαΠζͷ߹Y'CbCr৭ۭؒͷ··ѻ͏͜ͱͰɺ RGBͷมʹΑΔϩε͕ൃੜ͠ͳ͍ • cgoͰϒϩοΫ͢Δ࣌ؒΛ͘͢Δ͜ͱ͕Ͱ͖Δ
Streaming JPEG scanlines typedef ScanlineReader interface { func Config() *Config
→ plane sizes, pixel format,... func ReadScanLine(p [][]uint8) (n int, err error) → JPEG: } Y Cb Cr *blue difference chroma *red difference chroma ͭ·Γɺ͜ͷΠϯλʔϑΣʔεͰJPEGΛσίʔυ͢ΕJPEGΛετϦʔϜॲཧͰ͖Δ
color conversion chroma subsampling huffman coding quantization DCT transformation ReadScanline
io.Reader RGB→Y'CbCrͷ৭ۭؒม ΫϩϚαϒαϯϓϦϯάॲཧ DCTม ྔࢠԽ ϋϑϚϯѹॖ
8x8ϐΫηϧ͝ͱʹDCTʢࢄίαΠϯมʣͰ ྲྀͱप͝ͱͷަྲྀʹม →ߴप΄Ͳߥ͘ͳΔΑ͏ʹྔࢠԽ͢Δ͜ͱͰใྔΛݮ →ϋϑϚϯූ߸Խͯ͠ѹॖ ΊͬͪΌࡶʹJPEGͷѹॖΛઆ໌͢Δͱ ߴप प
8x8ϐΫηϧ͝ͱʹDCTʢࢄίαΠϯมʣͰ ྲྀͱप͝ͱͷަྲྀʹม →ߴप΄Ͳߥ͘ͳΔΑ͏ʹྔࢠԽ͢Δ͜ͱͰใྔΛݮ →ϋϑϚϯූ߸Խͯ͠ѹॖ ΊͬͪΌࡶʹJPEGͷѹॖΛઆ໌͢Δͱ ߴप प
• JPEGͰ8x8ϐΫηϧʢ1ϒϩοΫʣ͝ͱʹDCTɺྔࢠԽΛߦ͏ • MCU: 8x8ըૉ͝ͱʹY'CbCrΛฒͨͷ • ͜ͷ8x8ըૉ͝ͱʹDCT/ྔࢠԽͰใྔΛམͱ͢ͷͰϒϩοΫڥք ʹϊΠζ͕ൃੜˠϒϩοΫϊΠζ MCU
color conversion chroma subsampling huffman coding quantization DCT transformation ReadScanline
io.Reader 8x8ըૉ͝ͱॲཧ͢Δ →ετϦʔϛϯάॲཧͰ͖Δʂ 1ըૉ͝ͱʹॲཧͰ͖Δ
/* ... static JDIMENSION read_scanlines(j_decompress_ptr dinfo, unsigned char *buf, int
stride, int height) { JSAMPROW *rows = alloca(sizeof(JSAMPROW) * height); int i; for (i = 0; i < height; i++) { rows[i] = &buf[i * stride]; } return jpeg_read_scanlines(dinfo, rows, height); } */ import "c" func (dec *Decoder) ReadScanline(p [][]uint8) (n int, err error) { n := C.read_scanline(dec.dinfo, p, dec.Stride, dec.Height); return n, nil } libjpegͷ߹jpeg_read_scanlinesΛݺͿ͚ͩ
Conclusion • GoͰඇৗʹ؆୯ʹετϦʔϜॲཧΛ࣮ݱͰ͖Δ • JPEGͷ߹8ߦͣͭσίʔυ/Τϯίʔυ͢Δ͜ͱ͕Ͱ͖Δ →ετϦʔϜͰ͖ΔΠϯλʔϑΣʔε͢ΕͪΐͬͱͣͭॲཧͰ͖Δ →libjpegετϦʔϜͰ͖ΔAPIΛ࣋ͬͯΔͷͰcgoͰݺͼग़͚ͩ͢ • ϒϩοΫϊΠζͱ8x8ըૉ͝ͱʹॲཧ͢ΔͷͰڥքʹग़ΔϊΠζ