1 /*
2  * This file is part of mpv.
3  *
4  * mpv is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * mpv is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <assert.h>
21 
22 #include <libavutil/common.h>
23 
24 #include "common/common.h"
25 
26 #include "stream/stream.h"
27 
28 #include "osdep/timer.h"
29 
30 #include "mpv_talloc.h"
31 #include "options/m_config.h"
32 #include "options/options.h"
33 #include "common/global.h"
34 #include "common/msg.h"
35 #include "common/stats.h"
36 #include "player/client.h"
37 #include "player/command.h"
38 #include "osd.h"
39 #include "osd_state.h"
40 #include "dec_sub.h"
41 #include "img_convert.h"
42 #include "draw_bmp.h"
43 #include "video/mp_image.h"
44 #include "video/mp_image_pool.h"
45 
46 #define OPT_BASE_STRUCT struct osd_style_opts
47 static const m_option_t style_opts[] = {
48     {"font", OPT_STRING(font)},
49     {"font-size", OPT_FLOAT(font_size), M_RANGE(1, 9000)},
50     {"color", OPT_COLOR(color)},
51     {"border-color", OPT_COLOR(border_color)},
52     {"shadow-color", OPT_COLOR(shadow_color)},
53     {"back-color", OPT_COLOR(back_color)},
54     {"border-size", OPT_FLOAT(border_size)},
55     {"shadow-offset", OPT_FLOAT(shadow_offset)},
56     {"spacing", OPT_FLOAT(spacing), M_RANGE(-10, 10)},
57     {"margin-x", OPT_INT(margin_x), M_RANGE(0, 300)},
58     {"margin-y", OPT_INT(margin_y), M_RANGE(0, 600)},
59     {"align-x", OPT_CHOICE(align_x,
60         {"left", -1}, {"center", 0}, {"right", +1})},
61     {"align-y", OPT_CHOICE(align_y,
62         {"top", -1}, {"center", 0}, {"bottom", +1})},
63     {"blur", OPT_FLOAT(blur), M_RANGE(0, 20)},
64     {"bold", OPT_FLAG(bold)},
65     {"italic", OPT_FLAG(italic)},
66     {"justify", OPT_CHOICE(justify,
67         {"auto", 0}, {"left", 1}, {"center", 2}, {"right", 3})},
68     {"font-provider", OPT_CHOICE(font_provider,
69         {"auto", 0}, {"none", 1}, {"fontconfig", 2}), .flags = UPDATE_SUB_HARD},
70     {0}
71 };
72 
73 const struct m_sub_options osd_style_conf = {
74     .opts = style_opts,
75     .size = sizeof(struct osd_style_opts),
76     .defaults = &(const struct osd_style_opts){
77         .font = "sans-serif",
78         .font_size = 55,
79         .color = {255, 255, 255, 255},
80         .border_color = {0, 0, 0, 255},
81         .shadow_color = {240, 240, 240, 128},
82         .border_size = 3,
83         .shadow_offset = 0,
84         .margin_x = 25,
85         .margin_y = 22,
86         .align_x = -1,
87         .align_y = -1,
88     },
89     .change_flags = UPDATE_OSD,
90 };
91 
92 const struct m_sub_options sub_style_conf = {
93     .opts = style_opts,
94     .size = sizeof(struct osd_style_opts),
95     .defaults = &(const struct osd_style_opts){
96         .font = "sans-serif",
97         .font_size = 55,
98         .color = {255, 255, 255, 255},
99         .border_color = {0, 0, 0, 255},
100         .shadow_color = {240, 240, 240, 128},
101         .border_size = 3,
102         .shadow_offset = 0,
103         .margin_x = 25,
104         .margin_y = 22,
105         .align_x = 0,
106         .align_y = 1,
107     },
108     .change_flags = UPDATE_OSD,
109 };
110 
osd_res_equals(struct mp_osd_res a,struct mp_osd_res b)111 bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
112 {
113     return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt
114         && a.mr == b.mr && a.mb == b.mb
115         && a.display_par == b.display_par;
116 }
117 
osd_create(struct mpv_global * global)118 struct osd_state *osd_create(struct mpv_global *global)
119 {
120     assert(MAX_OSD_PARTS >= OSDTYPE_COUNT);
121 
122     struct osd_state *osd = talloc_zero(NULL, struct osd_state);
123     *osd = (struct osd_state) {
124         .opts_cache = m_config_cache_alloc(osd, global, &mp_osd_render_sub_opts),
125         .global = global,
126         .log = mp_log_new(osd, global->log, "osd"),
127         .force_video_pts = MP_NOPTS_VALUE,
128         .stats = stats_ctx_create(osd, global, "osd"),
129     };
130     pthread_mutex_init(&osd->lock, NULL);
131     osd->opts = osd->opts_cache->opts;
132 
133     for (int n = 0; n < MAX_OSD_PARTS; n++) {
134         struct osd_object *obj = talloc(osd, struct osd_object);
135         *obj = (struct osd_object) {
136             .type = n,
137             .text = talloc_strdup(obj, ""),
138             .progbar_state = {.type = -1},
139             .vo_change_id = 1,
140         };
141         osd->objs[n] = obj;
142     }
143 
144     osd->objs[OSDTYPE_SUB]->is_sub = true;
145     osd->objs[OSDTYPE_SUB2]->is_sub = true;
146 
147     osd_init_backend(osd);
148     return osd;
149 }
150 
osd_free(struct osd_state * osd)151 void osd_free(struct osd_state *osd)
152 {
153     if (!osd)
154         return;
155     osd_destroy_backend(osd);
156     talloc_free(osd->objs[OSDTYPE_EXTERNAL2]->external2);
157     pthread_mutex_destroy(&osd->lock);
158     talloc_free(osd);
159 }
160 
osd_set_text(struct osd_state * osd,const char * text)161 void osd_set_text(struct osd_state *osd, const char *text)
162 {
163     pthread_mutex_lock(&osd->lock);
164     struct osd_object *osd_obj = osd->objs[OSDTYPE_OSD];
165     if (!text)
166         text = "";
167     if (strcmp(osd_obj->text, text) != 0) {
168         talloc_free(osd_obj->text);
169         osd_obj->text = talloc_strdup(osd_obj, text);
170         osd_obj->osd_changed = true;
171         osd->want_redraw_notification = true;
172     }
173     pthread_mutex_unlock(&osd->lock);
174 }
175 
osd_set_sub(struct osd_state * osd,int index,struct dec_sub * dec_sub)176 void osd_set_sub(struct osd_state *osd, int index, struct dec_sub *dec_sub)
177 {
178     pthread_mutex_lock(&osd->lock);
179     if (index >= 0 && index < 2) {
180         struct osd_object *obj = osd->objs[OSDTYPE_SUB + index];
181         obj->sub = dec_sub;
182         obj->vo_change_id += 1;
183     }
184     osd->want_redraw_notification = true;
185     pthread_mutex_unlock(&osd->lock);
186 }
187 
osd_get_render_subs_in_filter(struct osd_state * osd)188 bool osd_get_render_subs_in_filter(struct osd_state *osd)
189 {
190     pthread_mutex_lock(&osd->lock);
191     bool r = osd->render_subs_in_filter;
192     pthread_mutex_unlock(&osd->lock);
193     return r;
194 }
195 
osd_set_render_subs_in_filter(struct osd_state * osd,bool s)196 void osd_set_render_subs_in_filter(struct osd_state *osd, bool s)
197 {
198     pthread_mutex_lock(&osd->lock);
199     if (osd->render_subs_in_filter != s) {
200         osd->render_subs_in_filter = s;
201 
202         int change_id = 0;
203         for (int n = 0; n < MAX_OSD_PARTS; n++)
204             change_id = MPMAX(change_id, osd->objs[n]->vo_change_id);
205         for (int n = 0; n < MAX_OSD_PARTS; n++)
206             osd->objs[n]->vo_change_id = change_id + 1;
207     }
208     pthread_mutex_unlock(&osd->lock);
209 }
210 
osd_set_force_video_pts(struct osd_state * osd,double video_pts)211 void osd_set_force_video_pts(struct osd_state *osd, double video_pts)
212 {
213     pthread_mutex_lock(&osd->lock);
214     osd->force_video_pts = video_pts;
215     pthread_mutex_unlock(&osd->lock);
216 }
217 
osd_get_force_video_pts(struct osd_state * osd)218 double osd_get_force_video_pts(struct osd_state *osd)
219 {
220     pthread_mutex_lock(&osd->lock);
221     double pts = osd->force_video_pts;
222     pthread_mutex_unlock(&osd->lock);
223     return pts;
224 }
225 
osd_set_progbar(struct osd_state * osd,struct osd_progbar_state * s)226 void osd_set_progbar(struct osd_state *osd, struct osd_progbar_state *s)
227 {
228     pthread_mutex_lock(&osd->lock);
229     struct osd_object *osd_obj = osd->objs[OSDTYPE_OSD];
230     osd_obj->progbar_state.type = s->type;
231     osd_obj->progbar_state.value = s->value;
232     osd_obj->progbar_state.num_stops = s->num_stops;
233     MP_TARRAY_GROW(osd_obj, osd_obj->progbar_state.stops, s->num_stops);
234     if (s->num_stops) {
235         memcpy(osd_obj->progbar_state.stops, s->stops,
236                sizeof(osd_obj->progbar_state.stops[0]) * s->num_stops);
237     }
238     osd_obj->osd_changed = true;
239     osd->want_redraw_notification = true;
240     pthread_mutex_unlock(&osd->lock);
241 }
242 
osd_set_external2(struct osd_state * osd,struct sub_bitmaps * imgs)243 void osd_set_external2(struct osd_state *osd, struct sub_bitmaps *imgs)
244 {
245     pthread_mutex_lock(&osd->lock);
246     struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL2];
247     talloc_free(obj->external2);
248     obj->external2 = sub_bitmaps_copy(NULL, imgs);
249     obj->vo_change_id += 1;
250     osd->want_redraw_notification = true;
251     pthread_mutex_unlock(&osd->lock);
252 }
253 
check_obj_resize(struct osd_state * osd,struct mp_osd_res res,struct osd_object * obj)254 static void check_obj_resize(struct osd_state *osd, struct mp_osd_res res,
255                              struct osd_object *obj)
256 {
257     if (!osd_res_equals(res, obj->vo_res)) {
258         obj->vo_res = res;
259         mp_client_broadcast_event_external(osd->global->client_api,
260                                            MP_EVENT_WIN_RESIZE, NULL);
261     }
262 }
263 
264 // Optional. Can be called for faster reaction of OSD-generating scripts like
265 // osc.lua. This can achieve that the resize happens first, so that the OSD is
266 // generated at the correct resolution the first time the resized frame is
267 // rendered. Since the OSD doesn't (and can't) wait for the script, this
268 // increases the time in which the script can react, and also gets rid of the
269 // unavoidable redraw delay (though it will still be racy).
270 // Unnecessary for anything else.
osd_resize(struct osd_state * osd,struct mp_osd_res res)271 void osd_resize(struct osd_state *osd, struct mp_osd_res res)
272 {
273     pthread_mutex_lock(&osd->lock);
274     int types[] = {OSDTYPE_OSD, OSDTYPE_EXTERNAL, OSDTYPE_EXTERNAL2, -1};
275     for (int n = 0; types[n] >= 0; n++)
276         check_obj_resize(osd, res, osd->objs[types[n]]);
277     pthread_mutex_unlock(&osd->lock);
278 }
279 
render_object(struct osd_state * osd,struct osd_object * obj,struct mp_osd_res osdres,double video_pts,const bool sub_formats[SUBBITMAP_COUNT])280 static struct sub_bitmaps *render_object(struct osd_state *osd,
281                                          struct osd_object *obj,
282                                          struct mp_osd_res osdres, double video_pts,
283                                          const bool sub_formats[SUBBITMAP_COUNT])
284 {
285     int format = SUBBITMAP_LIBASS;
286     if (!sub_formats[format] || osd->opts->force_rgba_osd)
287         format = SUBBITMAP_RGBA;
288 
289     struct sub_bitmaps *res = NULL;
290 
291     check_obj_resize(osd, osdres, obj);
292 
293     if (obj->type == OSDTYPE_SUB) {
294         if (obj->sub)
295             res = sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts);
296     } else if (obj->type == OSDTYPE_SUB2) {
297         if (obj->sub && sub_is_secondary_visible(obj->sub))
298             res = sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts);
299     } else if (obj->type == OSDTYPE_EXTERNAL2) {
300         if (obj->external2 && obj->external2->format) {
301             res = sub_bitmaps_copy(NULL, obj->external2); // need to be owner
302             obj->external2->change_id = 0;
303         }
304     } else {
305         res = osd_object_get_bitmaps(osd, obj, format);
306     }
307 
308     if (obj->vo_had_output != !!res) {
309         obj->vo_had_output = !!res;
310         obj->vo_change_id += 1;
311     }
312 
313     if (res) {
314         obj->vo_change_id += res->change_id;
315 
316         res->render_index = obj->type;
317         res->change_id = obj->vo_change_id;
318     }
319 
320     return res;
321 }
322 
323 // Render OSD to a list of bitmap and return it. The returned object is
324 // refcounted. Typically you should hold it only for a short time, and then
325 // release it.
326 // draw_flags is a bit field of OSD_DRAW_* constants
osd_render(struct osd_state * osd,struct mp_osd_res res,double video_pts,int draw_flags,const bool formats[SUBBITMAP_COUNT])327 struct sub_bitmap_list *osd_render(struct osd_state *osd, struct mp_osd_res res,
328                                    double video_pts, int draw_flags,
329                                    const bool formats[SUBBITMAP_COUNT])
330 {
331     pthread_mutex_lock(&osd->lock);
332 
333     struct sub_bitmap_list *list = talloc_zero(NULL, struct sub_bitmap_list);
334     list->change_id = 1;
335     list->w = res.w;
336     list->h = res.h;
337 
338     if (osd->force_video_pts != MP_NOPTS_VALUE)
339         video_pts = osd->force_video_pts;
340 
341     if (draw_flags & OSD_DRAW_SUB_FILTER)
342         draw_flags |= OSD_DRAW_SUB_ONLY;
343 
344     for (int n = 0; n < MAX_OSD_PARTS; n++) {
345         struct osd_object *obj = osd->objs[n];
346 
347         // Object is drawn into the video frame itself; don't draw twice
348         if (osd->render_subs_in_filter && obj->is_sub &&
349             !(draw_flags & OSD_DRAW_SUB_FILTER))
350             continue;
351         if ((draw_flags & OSD_DRAW_SUB_ONLY) && !obj->is_sub)
352             continue;
353         if ((draw_flags & OSD_DRAW_OSD_ONLY) && obj->is_sub)
354             continue;
355 
356         char *stat_type_render = obj->is_sub ? "sub-render" : "osd-render";
357         stats_time_start(osd->stats, stat_type_render);
358 
359         struct sub_bitmaps *imgs =
360             render_object(osd, obj, res, video_pts, formats);
361 
362         stats_time_end(osd->stats, stat_type_render);
363 
364         if (imgs && imgs->num_parts > 0) {
365             if (formats[imgs->format]) {
366                 talloc_steal(list, imgs);
367                 MP_TARRAY_APPEND(list, list->items, list->num_items, imgs);
368                 imgs = NULL;
369             } else {
370                 MP_ERR(osd, "Can't render OSD part %d (format %d).\n",
371                        obj->type, imgs->format);
372             }
373         }
374 
375         list->change_id += obj->vo_change_id;
376 
377         talloc_free(imgs);
378     }
379 
380     // If this is called with OSD_DRAW_SUB_ONLY or OSD_DRAW_OSD_ONLY set, assume
381     // it will always draw the complete OSD by doing multiple osd_draw() calls.
382     // OSD_DRAW_SUB_FILTER on the other hand is an evil special-case, and we
383     // must not reset the flag when it happens.
384     if (!(draw_flags & OSD_DRAW_SUB_FILTER))
385         osd->want_redraw_notification = false;
386 
387     pthread_mutex_unlock(&osd->lock);
388     return list;
389 }
390 
391 // Warning: this function should be considered legacy. Use osd_render() instead.
osd_draw(struct osd_state * osd,struct mp_osd_res res,double video_pts,int draw_flags,const bool formats[SUBBITMAP_COUNT],void (* cb)(void * ctx,struct sub_bitmaps * imgs),void * cb_ctx)392 void osd_draw(struct osd_state *osd, struct mp_osd_res res,
393               double video_pts, int draw_flags,
394               const bool formats[SUBBITMAP_COUNT],
395               void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx)
396 {
397     struct sub_bitmap_list *list =
398         osd_render(osd, res, video_pts, draw_flags, formats);
399 
400     stats_time_start(osd->stats, "draw");
401 
402     for (int n = 0; n < list->num_items; n++)
403         cb(cb_ctx, list->items[n]);
404 
405     stats_time_end(osd->stats, "draw");
406 
407     talloc_free(list);
408 }
409 
410 // Calls mp_image_make_writeable() on the dest image if something is drawn.
411 // draw_flags as in osd_render().
osd_draw_on_image(struct osd_state * osd,struct mp_osd_res res,double video_pts,int draw_flags,struct mp_image * dest)412 void osd_draw_on_image(struct osd_state *osd, struct mp_osd_res res,
413                        double video_pts, int draw_flags, struct mp_image *dest)
414 {
415     osd_draw_on_image_p(osd, res, video_pts, draw_flags, NULL, dest);
416 }
417 
418 // Like osd_draw_on_image(), but if dest needs to be copied to make it
419 // writeable, allocate images from the given pool. (This is a minor
420 // optimization to reduce "real" image sized memory allocations.)
osd_draw_on_image_p(struct osd_state * osd,struct mp_osd_res res,double video_pts,int draw_flags,struct mp_image_pool * pool,struct mp_image * dest)421 void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res,
422                          double video_pts, int draw_flags,
423                          struct mp_image_pool *pool, struct mp_image *dest)
424 {
425     struct sub_bitmap_list *list =
426         osd_render(osd, res, video_pts, draw_flags, mp_draw_sub_formats);
427 
428     if (!list->num_items) {
429         talloc_free(list);
430         return;
431     }
432 
433     if (!mp_image_pool_make_writeable(pool, dest))
434         return; // on OOM, skip
435 
436     // Need to lock for the dumb osd->draw_cache thing.
437     pthread_mutex_lock(&osd->lock);
438 
439     if (!osd->draw_cache)
440         osd->draw_cache = mp_draw_sub_alloc(osd, osd->global);
441 
442     stats_time_start(osd->stats, "draw-bmp");
443 
444     if (!mp_draw_sub_bitmaps(osd->draw_cache, dest, list))
445         MP_WARN(osd, "Failed rendering OSD.\n");
446     talloc_steal(osd, osd->draw_cache);
447 
448     stats_time_end(osd->stats, "draw-bmp");
449 
450     pthread_mutex_unlock(&osd->lock);
451 
452     talloc_free(list);
453 }
454 
455 // Setup the OSD resolution to render into an image with the given parameters.
456 // The interesting part about this is that OSD has to compensate the aspect
457 // ratio if the image does not have a 1:1 pixel aspect ratio.
osd_res_from_image_params(const struct mp_image_params * p)458 struct mp_osd_res osd_res_from_image_params(const struct mp_image_params *p)
459 {
460     return (struct mp_osd_res) {
461         .w = p->w,
462         .h = p->h,
463         .display_par = p->p_h / (double)p->p_w,
464     };
465 }
466 
467 // Typically called to react to OSD style changes.
osd_changed(struct osd_state * osd)468 void osd_changed(struct osd_state *osd)
469 {
470     pthread_mutex_lock(&osd->lock);
471     osd->objs[OSDTYPE_OSD]->osd_changed = true;
472     osd->want_redraw_notification = true;
473     // Done here for a lack of a better place.
474     m_config_cache_update(osd->opts_cache);
475     pthread_mutex_unlock(&osd->lock);
476 }
477 
osd_query_and_reset_want_redraw(struct osd_state * osd)478 bool osd_query_and_reset_want_redraw(struct osd_state *osd)
479 {
480     pthread_mutex_lock(&osd->lock);
481     bool r = osd->want_redraw_notification;
482     osd->want_redraw_notification = false;
483     pthread_mutex_unlock(&osd->lock);
484     return r;
485 }
486 
osd_get_vo_res(struct osd_state * osd)487 struct mp_osd_res osd_get_vo_res(struct osd_state *osd)
488 {
489     pthread_mutex_lock(&osd->lock);
490     // Any OSDTYPE is fine; but it mustn't be a subtitle one (can have lower res.)
491     struct mp_osd_res res = osd->objs[OSDTYPE_OSD]->vo_res;
492     pthread_mutex_unlock(&osd->lock);
493     return res;
494 }
495 
496 // Position the subbitmaps in imgs on the screen. Basically, this fits the
497 // subtitle canvas (of size frame_w x frame_h) onto the screen, such that it
498 // fills the whole video area (especially if the video is magnified, e.g. on
499 // fullscreen). If compensate_par is >0, adjust the way the subtitles are
500 // "stretched" on the screen, and letter-box the result. If compensate_par
501 // is <0, strictly letter-box the subtitles. If it is 0, stretch them.
osd_rescale_bitmaps(struct sub_bitmaps * imgs,int frame_w,int frame_h,struct mp_osd_res res,double compensate_par)502 void osd_rescale_bitmaps(struct sub_bitmaps *imgs, int frame_w, int frame_h,
503                          struct mp_osd_res res, double compensate_par)
504 {
505     int vidw = res.w - res.ml - res.mr;
506     int vidh = res.h - res.mt - res.mb;
507     double xscale = (double)vidw / frame_w;
508     double yscale = (double)vidh / frame_h;
509     if (compensate_par < 0)
510         compensate_par = xscale / yscale / res.display_par;
511     if (compensate_par > 0)
512         xscale /= compensate_par;
513     int cx = vidw / 2 - (int)(frame_w * xscale) / 2;
514     int cy = vidh / 2 - (int)(frame_h * yscale) / 2;
515     for (int i = 0; i < imgs->num_parts; i++) {
516         struct sub_bitmap *bi = &imgs->parts[i];
517         bi->x = (int)(bi->x * xscale) + cx + res.ml;
518         bi->y = (int)(bi->y * yscale) + cy + res.mt;
519         bi->dw = (int)(bi->w * xscale + 0.5);
520         bi->dh = (int)(bi->h * yscale + 0.5);
521     }
522 }
523 
524 // Copy *in and return a new allocation of it. Free with talloc_free(). This
525 // will contain a refcounted copy of the image data.
526 //
527 // in->packed must be set and must be a refcounted image, unless there is no
528 // data (num_parts==0).
529 //
530 //  p_cache: if not NULL, then this points to a struct sub_bitmap_copy_cache*
531 //           variable. The function may set this to an allocation and may later
532 //           read it. You have to free it with talloc_free() when done.
533 //  in: valid struct, or NULL (in this case it also returns NULL)
534 //  returns: new copy, or NULL if there was no data in the input
sub_bitmaps_copy(struct sub_bitmap_copy_cache ** p_cache,struct sub_bitmaps * in)535 struct sub_bitmaps *sub_bitmaps_copy(struct sub_bitmap_copy_cache **p_cache,
536                                      struct sub_bitmaps *in)
537 {
538     if (!in || !in->num_parts)
539         return NULL;
540 
541     struct sub_bitmaps *res = talloc(NULL, struct sub_bitmaps);
542     *res = *in;
543 
544     // Note: the p_cache thing is a lie and unused.
545 
546     // The bitmaps being refcounted is essential for performance, and for
547     // not invalidating in->parts[*].bitmap pointers.
548     assert(in->packed && in->packed->bufs[0]);
549 
550     res->packed = mp_image_new_ref(res->packed);
551     MP_HANDLE_OOM(res->packed);
552     talloc_steal(res, res->packed);
553 
554     res->parts = NULL;
555     MP_RESIZE_ARRAY(res, res->parts, res->num_parts);
556     memcpy(res->parts, in->parts, sizeof(res->parts[0]) * res->num_parts);
557 
558     return res;
559 }
560