Unlike av_frame_is_writable(), it uses the link's alloc callback, making direct rendering possible. The code comes from ff_filter_frame_framed(), moved with mostly trivial changes.
Unified · Split
| @@ -1107,49 +1107,12 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame) · 1106 unmodified lines | ||
| 1107 | 1107 | if (!(filter_frame = dst->filter_frame)) |
| 1108 | 1108 | filter_frame = default_filter_frame; |
| 1109 | 1109 | |
| 1110 | − /* copy the frame if needed */ | |
| 1111 | − if (dst->needs_writable && !av_frame_is_writable(frame)) { | |
| 1112 | − av_log(link->dst, AV_LOG_DEBUG, "Copying data in avfilter.\n"); | |
| 1113 | − | |
| 1114 | − switch (link->type) { | |
| 1115 | − case AVMEDIA_TYPE_VIDEO: | |
| 1116 | − out = ff_get_video_buffer(link, link->w, link->h); | |
| 1117 | − break; | |
| 1118 | − case AVMEDIA_TYPE_AUDIO: | |
| 1119 | − out = ff_get_audio_buffer(link, frame->nb_samples); | |
| 1120 | − break; | |
| 1121 | − default: | |
| 1122 | − ret = AVERROR(EINVAL); | |
| 1123 | − goto fail; | |
| 1124 | − } | |
| 1125 | − if (!out) { | |
| 1126 | − ret = AVERROR(ENOMEM); | |
| 1127 | − goto fail; | |
| 1128 | − } | |
| 1129 | − | |
| 1130 | − ret = av_frame_copy_props(out, frame); | |
| 1110 | + if (dst->needs_writable) { | |
| 1111 | + ret = ff_inlink_make_frame_writable(link, &frame); | |
| 1131 | 1112 | if (ret < 0) |
| 1132 | 1113 | goto fail; |
| 1133 | − | |
| 1134 | − switch (link->type) { | |
| 1135 | − case AVMEDIA_TYPE_VIDEO: | |
| 1136 | − av_image_copy(out->data, out->linesize, (const uint8_t **)frame->data, frame->linesize, | |
| 1137 | − frame->format, frame->width, frame->height); | |
| 1138 | − break; | |
| 1139 | − case AVMEDIA_TYPE_AUDIO: | |
| 1140 | − av_samples_copy(out->extended_data, frame->extended_data, | |
| 1141 | − 0, 0, frame->nb_samples, | |
| 1142 | − av_frame_get_channels(frame), | |
| 1143 | − frame->format); | |
| 1144 | − break; | |
| 1145 | − default: | |
| 1146 | − ret = AVERROR(EINVAL); | |
| 1147 | − goto fail; | |
| 1148 | − } | |
| 1149 | − | |
| 1150 | − av_frame_free(&frame); | |
| 1151 | − } else | |
| 1152 | − out = frame; | |
| 1114 | + } | |
| 1115 | + out = frame; /* TODO rename */ | |
| 1153 | 1116 | |
| 1154 | 1117 | while(cmd && cmd->time <= out->pts * av_q2d(link->time_base)){ |
| 1155 | 1118 | av_log(link->dst, AV_LOG_DEBUG, |
| @@ -1559,6 +1522,55 @@ int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts · 403 unmodified lines | ||
| 1559 | 1522 | return 1; |
| 1560 | 1523 | } |
| 1561 | 1524 | |
| 1525 | +int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe) | |
| 1526 | +{ | |
| 1527 | + AVFrame *frame = *rframe; | |
| 1528 | + AVFrame *out; | |
| 1529 | + int ret; | |
| 1530 | + | |
| 1531 | + if (av_frame_is_writable(frame)) | |
| 1532 | + return 0; | |
| 1533 | + av_log(link->dst, AV_LOG_DEBUG, "Copying data in avfilter.\n"); | |
| 1534 | + | |
| 1535 | + switch (link->type) { | |
| 1536 | + case AVMEDIA_TYPE_VIDEO: | |
| 1537 | + out = ff_get_video_buffer(link, link->w, link->h); | |
| 1538 | + break; | |
| 1539 | + case AVMEDIA_TYPE_AUDIO: | |
| 1540 | + out = ff_get_audio_buffer(link, frame->nb_samples); | |
| 1541 | + break; | |
| 1542 | + default: | |
| 1543 | + return AVERROR(EINVAL); | |
| 1544 | + } | |
| 1545 | + if (!out) | |
| 1546 | + return AVERROR(ENOMEM); | |
| 1547 | + | |
| 1548 | + ret = av_frame_copy_props(out, frame); | |
| 1549 | + if (ret < 0) { | |
| 1550 | + av_frame_free(&out); | |
| 1551 | + return ret; | |
| 1552 | + } | |
| 1553 | + | |
| 1554 | + switch (link->type) { | |
| 1555 | + case AVMEDIA_TYPE_VIDEO: | |
| 1556 | + av_image_copy(out->data, out->linesize, (const uint8_t **)frame->data, frame->linesize, | |
| 1557 | + frame->format, frame->width, frame->height); | |
| 1558 | + break; | |
| 1559 | + case AVMEDIA_TYPE_AUDIO: | |
| 1560 | + av_samples_copy(out->extended_data, frame->extended_data, | |
| 1561 | + 0, 0, frame->nb_samples, | |
| 1562 | + av_frame_get_channels(frame), | |
| 1563 | + frame->format); | |
| 1564 | + break; | |
| 1565 | + default: | |
| 1566 | + av_assert0(!"reached"); | |
| 1567 | + } | |
| 1568 | + | |
| 1569 | + av_frame_free(&frame); | |
| 1570 | + *rframe = out; | |
| 1571 | + return 0; | |
| 1572 | +} | |
| 1573 | + | |
| 1562 | 1574 | const AVClass *avfilter_get_class(void) |
| 1563 | 1575 | { |
| 1564 | 1576 | return &avfilter_class; |
| @@ -39,6 +39,13 @@ · 38 unmodified lines | ||
| 39 | 39 | */ |
| 40 | 40 | void ff_filter_set_ready(AVFilterContext *filter, unsigned priority); |
| 41 | 41 | |
| 42 | +/** | |
| 43 | + * Make sure a frame is writable. | |
| 44 | + * This is similar to av_frame_make_writable() except it uses the link's | |
| 45 | + * buffer allocation callback, and therefore allows direct rendering. | |
| 46 | + */ | |
| 47 | +int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe); | |
| 48 | + | |
| 42 | 49 | /** |
| 43 | 50 | * Test and acknowledge the change of status on the link. |
| 44 | 51 | * |