FFmpeg
Loading...
Searching...
No Matches
iterm2enc.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * Copyright (c) 2026 Zhao Zhili <quinkblack@foxmail.com>
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <string.h>
22
23#include "libavutil/base64.h"
24#include "libavutil/macros.h"
25#include "libavutil/mem.h"
26#include "libavutil/opt.h"
27#include "avformat.h"
28#include "mux.h"
29
30/* iTerm2 inline image protocol: https://iterm2.com/documentation-images.html */
31
32#define ESC "\033"
33
34#define SYNC_BEGIN ESC "[?2026h"
35#define SYNC_END ESC "[?2026l"
36#define CURSOR_SAVE ESC "7"
37#define CURSOR_RESTORE ESC "8"
38#define CURSOR_HOME ESC "[H"
39#define CURSOR_BOTTOM ESC "[999H"
40
41#define OSC_START ESC "]1337;"
42#define BEL "\a"
43#define ST ESC "\\"
44
45/* tmux requires DCS passthrough with ST termination and ESC doubling */
46#define TMUX_DCS ESC "Ptmux;"
47
48/* iTerm2 and tmux silently drop a single OSC sequence >= 1 MiB, so split the
49 * image into chunks below that limit. Old tmux capped a sequence at 256 bytes,
50 * but the tiny chunks that would require flood the tmux parser and freeze the
51 * terminal, so we do not support such versions. */
52#define FILEPART_CHUNK ((1 << 20) - 4096)
53
54#define WRITE_LITERAL(pb, str) avio_write(pb, (const unsigned char *)(str), \
55 sizeof(str) - 1)
56
57typedef struct ITerm2Context {
58 const AVClass *class;
62 int tmux;
63 char *b64;
64 unsigned b64_size;
66
68{
69 if (c->tmux)
72}
73
75{
76 WRITE_LITERAL(pb, BEL);
77 if (c->tmux)
78 WRITE_LITERAL(pb, ST);
79}
80
82{
83 size_t b64_len = strlen(c->b64);
84
85 osc_open(c, pb);
86 WRITE_LITERAL(pb, "MultipartFile=");
87 avio_printf(pb, "inline=1;size=%d", size);
88 if (c->display_width && c->display_width[0])
89 avio_printf(pb, ";width=%s", c->display_width);
90 if (c->display_height && c->display_height[0])
91 avio_printf(pb, ";height=%s", c->display_height);
92 if (!c->keep_aspect)
93 WRITE_LITERAL(pb, ";preserveAspectRatio=0");
94 osc_close(c, pb);
95
96 for (size_t off = 0; off < b64_len; off += FILEPART_CHUNK) {
97 size_t n = FFMIN(FILEPART_CHUNK, b64_len - off);
98
99 osc_open(c, pb);
100 WRITE_LITERAL(pb, "FilePart=");
101 avio_write(pb, c->b64 + off, n);
102 osc_close(c, pb);
103 }
104
105 osc_open(c, pb);
106 WRITE_LITERAL(pb, "FileEnd");
107 osc_close(c, pb);
108}
109
111{
112 ITerm2Context *c = s->priv_data;
113
114 av_fast_malloc(&c->b64, &c->b64_size, AV_BASE64_SIZE(pkt->size));
115 if (!c->b64)
116 return AVERROR(ENOMEM);
117 if (!av_base64_encode(c->b64, c->b64_size, pkt->data, pkt->size))
118 return AVERROR(EINVAL);
119
120 /* Synchronized output swaps the frame in atomically. */
122
123 write_image(c, s->pb, pkt->size);
124
126
127 avio_flush(s->pb);
128
129 return 0;
130}
131
133{
134 WRITE_LITERAL(s->pb, CURSOR_BOTTOM "\n");
135
136 return 0;
137}
138
140{
141 ITerm2Context *c = s->priv_data;
142 av_freep(&c->b64);
143}
144
145#define OFFSET(x) offsetof(ITerm2Context, x)
146#define ENC AV_OPT_FLAG_ENCODING_PARAM
147
148static const AVOption options[] = {
149 { "display_width", "on-screen width (auto, N cells, Npx, N%%)",
150 OFFSET(display_width), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC },
151 { "display_height", "on-screen height (auto, N cells, Npx, N%%)",
152 OFFSET(display_height), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC },
153 { "keep_aspect", "preserve aspect ratio when scaling",
154 OFFSET(keep_aspect), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
155 { "tmux", "wrap image in tmux DCS passthrough, requires tmux set -g allow-passthrough on",
156 OFFSET(tmux), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
157 { NULL },
158};
159
160static const AVClass iterm2_class = {
161 .class_name = "iTerm2 muxer",
162 .item_name = av_default_item_name,
163 .option = options,
164 .version = LIBAVUTIL_VERSION_INT,
165 .category = AV_CLASS_CATEGORY_MUXER,
166};
167
169 .p.name = "iterm2",
170 .p.long_name = NULL_IF_CONFIG_SMALL("iTerm2 inline image protocol"),
171 .priv_data_size = sizeof(ITerm2Context),
172 .p.audio_codec = AV_CODEC_ID_NONE,
173 .p.video_codec = AV_CODEC_ID_MJPEG,
174 .write_packet = iterm2_write_packet,
175 .write_trailer = iterm2_write_trailer,
176 .deinit = iterm2_deinit,
177 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
179 .p.priv_class = &iterm2_class,
180};
const FFOutputFormat ff_iterm2_muxer
Definition iterm2enc.c:168
Main libavformat public API header.
#define AVFMT_NODIMENSIONS
Format does not need width/height.
Definition avformat.h:502
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition avformat.h:498
int avio_printf(AVIOContext *s, const char *fmt,...) av_printf_format(2
Writes a formatted string to the context.
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition aviobuf.c:228
#define ENC
Definition caca.c:198
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
static AVPacket * pkt
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_MJPEG
Definition codec_id.h:57
char * av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
Encode data to base64 and null-terminate.
Definition base64.c:147
#define AV_BASE64_SIZE(x)
Calculate the output size needed to base64-encode x bytes to a null-terminated string.
Definition base64.h:66
#define AVERROR(e)
Definition error.h:45
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
Allocate a buffer, reusing the given one if large enough.
Definition mem.c:555
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define CURSOR_HOME
Definition iterm2enc.c:38
static int iterm2_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition iterm2enc.c:110
static av_cold void iterm2_deinit(AVFormatContext *s)
Definition iterm2enc.c:139
static void osc_open(ITerm2Context *c, AVIOContext *pb)
Definition iterm2enc.c:67
static const AVClass iterm2_class
Definition iterm2enc.c:160
#define SYNC_END
Definition iterm2enc.c:35
#define ESC
Definition iterm2enc.c:32
static void write_image(ITerm2Context *c, AVIOContext *pb, int size)
Definition iterm2enc.c:81
#define OSC_START
Definition iterm2enc.c:41
#define FILEPART_CHUNK
Definition iterm2enc.c:52
#define CURSOR_BOTTOM
Definition iterm2enc.c:39
#define SYNC_BEGIN
Definition iterm2enc.c:34
static void osc_close(ITerm2Context *c, AVIOContext *pb)
Definition iterm2enc.c:74
#define ST
Definition iterm2enc.c:43
#define BEL
Definition iterm2enc.c:42
static int iterm2_write_trailer(AVFormatContext *s)
Definition iterm2enc.c:132
#define WRITE_LITERAL(pb, str)
Definition iterm2enc.c:54
#define TMUX_DCS
Definition iterm2enc.c:46
#define OFFSET(x)
Definition iterm2enc.c:145
#define CURSOR_SAVE
Definition iterm2enc.c:36
#define CURSOR_RESTORE
Definition iterm2enc.c:37
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
@ AV_CLASS_CATEGORY_MUXER
Definition log.h:32
Utility Preprocessor macros.
#define FFMIN(a, b)
Definition macros.h:49
Memory handling functions.
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
AVOptions.
Describe the class of an AVClass context structure.
Definition log.h:76
Format I/O context.
Definition avformat.h:1333
Bytestream IO Context.
Definition avio.h:160
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
char * display_height
Definition iterm2enc.c:60
char * display_width
Definition iterm2enc.c:59
unsigned b64_size
Definition iterm2enc.c:64
#define av_freep(p)
int size
static double c[64]