@@ -25,67 +25,192 @@
#include "avcodec.h"
#include "codec_internal.h"
+#include "encode.h"
#include "libavutil/ass_internal.h"
#include "libavutil/avstring.h"
#include "libavutil/internal.h"
#include "libavutil/mem.h"
+typedef struct {
+ AVCodecContext *avctx;
+ AVFrame* current_frame;
+ int have_frame;
+ int current_area;
+} AssEncContext;
+
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+ if (avctx->extradata_size)
+ return;
+
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avctx->extradata_size = strlen(subtitle_header);
+ avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+ memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+ avctx->extradata[avctx->extradata_size] = 0;
+ }
+
+ if (!avctx->extradata_size) {
+ const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ avctx->extradata_size = strlen(subtitle_header);
+ avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+ memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+ avctx->extradata[avctx->extradata_size] = 0;
+ av_freep(&subtitle_header);
+ }
+}
+
static av_cold int ass_encode_init(AVCodecContext *avctx)
{
- avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
- if (!avctx->extradata)
- return AVERROR(ENOMEM);
- memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
- avctx->extradata_size = avctx->subtitle_header_size;
- avctx->extradata[avctx->extradata_size] = 0;
+ AssEncContext *s = avctx->priv_data;
+
+ if (avctx->subtitle_header_size) {
+ avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+ memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+ avctx->extradata_size = avctx->subtitle_header_size;
+ avctx->extradata[avctx->extradata_size] = 0;
+ }
+
+ s->current_frame = av_frame_alloc();
+ return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+ AssEncContext *s = avctx->priv_data;
+ av_frame_free(&s->current_frame);
return 0;
}
-static int ass_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize,
- const AVSubtitle *sub)
+////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+//// const AVFrame* frame, int* got_packet)
+////{
+//// int ret;
+//// size_t req_len = 0, total_len = 0;
+////
+//// check_write_header(avctx, frame);
+////
+//// for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+//// const char *ass = frame->subtitle_areas[i]->ass;
+////
+//// if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+//// av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
+//// return AVERROR(EINVAL);
+//// }
+////
+//// if (ass)
+//// req_len += strlen(ass);
+//// }
+////
+//// ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+//// if (ret < 0) {
+//// av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+//// return ret;
+//// }
+////
+//// for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+//// const char *ass = frame->subtitle_areas[i]->ass;
+////
+//// if (ass) {
+//// size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+//// total_len += len;
+//// }
+//// }
+////
+//// avpkt->size = total_len;
+//// *got_packet = total_len > 0;
+////
+//// return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
- int i, len, total_len = 0;
+ AssEncContext *s = avctx->priv_data;
+ int ret;
+
+ if (!s->have_frame) {
+ s->current_area = 0;
+ ret = ff_encode_get_frame(avctx, s->current_frame);
+
+ if (ret < 0) {
+ av_frame_unref(s->current_frame);
+ return ret;
+ }
+
+ s->have_frame = 1;
+ }
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ check_write_header(avctx, s->current_frame);
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (s->current_frame->repeat_sub) {
+ av_frame_unref(s->current_frame);
+ s->have_frame = 0;
+ return AVERROR(EAGAIN);
+ }
+
+ if (s->current_area < s->current_frame->num_subtitle_areas) {
+ const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+ const char *ass = area->ass;
+
+ if (area->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+ if (ass) {
+ size_t len = strlen(ass);
+
+ ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
- if (len > bufsize-total_len-1) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ avpkt->size = len;
}
- total_len += len;
+ s->current_area++;
}
- return total_len;
+ if (s->current_area < s->current_frame->num_subtitle_areas)
+ return 0;
+
+ av_frame_unref(s->current_frame);
+ s->have_frame = 0;
+
+ return 0;
}
#if CONFIG_SSA_ENCODER
const FFCodec ff_ssa_encoder = {
- .p.name = "ssa",
- CODEC_LONG_NAME("ASS (Advanced SubStation Alpha) subtitle"),
- .p.type = AVMEDIA_TYPE_SUBTITLE,
- .p.id = AV_CODEC_ID_ASS,
- .init = ass_encode_init,
- FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+ .p.name = "ssa",
+ .p.long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+ .p.type = AVMEDIA_TYPE_SUBTITLE,
+ .p.id = AV_CODEC_ID_ASS,
+ .priv_data_size = sizeof(AssEncContext),
+ .init = ass_encode_init,
+ .close = ass_encode_close,
+ FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
};
#endif
#if CONFIG_ASS_ENCODER
const FFCodec ff_ass_encoder = {
- .p.name = "ass",
- CODEC_LONG_NAME("ASS (Advanced SubStation Alpha) subtitle"),
- .p.type = AVMEDIA_TYPE_SUBTITLE,
- .p.id = AV_CODEC_ID_ASS,
- .init = ass_encode_init,
- FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+ .p.name = "ass",
+ .p.long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+ .p.type = AVMEDIA_TYPE_SUBTITLE,
+ .priv_data_size = sizeof(AssEncContext),
+ .p.id = AV_CODEC_ID_ASS,
+ .init = ass_encode_init,
+ .close = ass_encode_close,
+ FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
};
#endif
@@ -3024,10 +3024,13 @@ void av_parser_close(AVCodecParserContext *s);
* @{
*/
+ /**
+ * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+ */
+attribute_deprecated
int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
const AVSubtitle *sub);
-
/**
* @}
*/
@@ -108,9 +108,6 @@ enum FFCodecType {
/* The codec is an encoder using the encode callback;
* audio and video codecs only. */
FF_CODEC_CB_TYPE_ENCODE,
- /* The codec is an encoder using the encode_sub callback;
- * subtitle codecs only. */
- FF_CODEC_CB_TYPE_ENCODE_SUB,
/* The codec is an encoder using the receive_packet callback;
* audio and video codecs only. */
FF_CODEC_CB_TYPE_RECEIVE_PACKET,
@@ -213,12 +210,6 @@ typedef struct FFCodec {
*/
int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
const struct AVFrame *frame, int *got_packet_ptr);
- /**
- * Encode subtitles to a raw buffer.
- * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
- */
- int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
- int buf_size, const struct AVSubtitle *sub);
/**
* Encode API with decoupled frame/packet dataflow.
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
@@ -307,9 +298,6 @@ typedef struct FFCodec {
#define FF_CODEC_ENCODE_CB(func) \
.cb_type = FF_CODEC_CB_TYPE_ENCODE, \
.cb.encode = (func)
-#define FF_CODEC_ENCODE_SUB_CB(func) \
- .cb_type = FF_CODEC_CB_TYPE_ENCODE_SUB, \
- .cb.encode_sub = (func)
#define FF_CODEC_RECEIVE_PACKET_CB(func) \
.cb_type = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
.cb.receive_packet = (func)
@@ -21,6 +21,7 @@
#include "avcodec.h"
#include "bytestream.h"
#include "codec_internal.h"
+#include "encode.h"
#include "libavutil/colorspace.h"
typedef struct DVBSubtitleContext {
@@ -269,21 +270,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
return len;
}
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
- const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
DVBSubtitleContext *s = avctx->priv_data;
uint8_t *q, *pseg_len;
int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
-
-
- q = outbuf;
+ size_t buf_size;
+ int ret;
page_id = 1;
- if (h->num_rects && !h->rects)
+ if (frame->num_subtitle_areas && !frame->subtitle_areas)
return AVERROR(EINVAL);
+ ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ buf_size = avpkt->size;
+ q = avpkt->data;
+
if (avctx->width > 0 && avctx->height > 0) {
if (buf_size < 11)
return AVERROR_BUFFER_TOO_SMALL;
@@ -302,7 +311,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
/* page composition segment */
- if (buf_size < 8 + h->num_rects * 6)
+ if (buf_size < 8 + frame->num_subtitle_areas * 6)
return AVERROR_BUFFER_TOO_SMALL;
*q++ = 0x0f; /* sync_byte */
*q++ = 0x10; /* segment_type */
@@ -314,30 +323,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
/* page_version = 0 + page_state */
*q++ = (s->object_version << 4) | (page_state << 2) | 3;
- for (region_id = 0; region_id < h->num_rects; region_id++) {
+ for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
*q++ = region_id;
*q++ = 0xff; /* reserved */
- bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
- bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
}
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
- buf_size -= 8 + h->num_rects * 6;
+ buf_size -= 8 + frame->num_subtitle_areas * 6;
- if (h->num_rects) {
- for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
- if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+ if (frame->num_subtitle_areas) {
+ for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+ if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
return AVERROR_BUFFER_TOO_SMALL;
/* CLUT segment */
- if (h->rects[clut_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
- } else if (h->rects[clut_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
bpp_index = 1;
- } else if (h->rects[clut_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@@ -354,12 +363,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
*q++ = clut_id;
*q++ = (0 << 4) | 0xf; /* version = 0 */
- for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+ for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
*q++ = i; /* clut_entry_id */
*q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
{
int a, r, g, b;
- uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+ uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
a = (x >> 24) & 0xff;
r = (x >> 16) & 0xff;
g = (x >> 8) & 0xff;
@@ -373,22 +382,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
}
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
- buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+ buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
}
- if (buf_size < h->num_rects * 22)
+ if (buf_size < frame->num_subtitle_areas * 22)
return AVERROR_BUFFER_TOO_SMALL;
- for (region_id = 0; region_id < h->num_rects; region_id++) {
+ for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
/* region composition segment */
- if (h->rects[region_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
- } else if (h->rects[region_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
bpp_index = 1;
- } else if (h->rects[region_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@@ -402,8 +411,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
q += 2; /* segment length */
*q++ = region_id;
*q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
- bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
- bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
*q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
*q++ = region_id; /* clut_id == region_id */
*q++ = 0; /* 8 bit fill colors */
@@ -417,9 +426,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
}
- buf_size -= h->num_rects * 22;
+ buf_size -= frame->num_subtitle_areas * 22;
- for (object_id = 0; object_id < h->num_rects; object_id++) {
+ for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
const uint8_t *bitmap, int linesize,
int w, int h);
@@ -428,13 +437,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
return AVERROR_BUFFER_TOO_SMALL;
/* bpp_index maths */
- if (h->rects[object_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
dvb_encode_rle = dvb_encode_rle2;
- } else if (h->rects[object_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle4;
- } else if (h->rects[object_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle8;
} else {
@@ -464,19 +473,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
top_ptr = q;
ret = dvb_encode_rle(&q, buf_size,
- h->rects[object_id]->data[0],
- h->rects[object_id]->w * 2,
- h->rects[object_id]->w,
- h->rects[object_id]->h >> 1);
+ frame->subtitle_areas[object_id]->buf[0]->data,
+ frame->subtitle_areas[object_id]->w * 2,
+ frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->h >> 1);
if (ret < 0)
return ret;
buf_size -= ret;
bottom_ptr = q;
ret = dvb_encode_rle(&q, buf_size,
- h->rects[object_id]->data[0] + h->rects[object_id]->w,
- h->rects[object_id]->w * 2,
- h->rects[object_id]->w,
- h->rects[object_id]->h >> 1);
+ frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->w * 2,
+ frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->h >> 1);
if (ret < 0)
return ret;
buf_size -= ret;
@@ -503,7 +512,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
buf_size -= 6;
s->object_version = (s->object_version + 1) & 0xf;
- return q - outbuf;
+ avpkt->size = q - avpkt->data;
+ *got_packet = 1;
+
+ return 0;
}
const FFCodec ff_dvbsub_encoder = {
@@ -512,5 +524,5 @@ const FFCodec ff_dvbsub_encoder = {
.p.type = AVMEDIA_TYPE_SUBTITLE,
.p.id = AV_CODEC_ID_DVB_SUBTITLE,
.priv_data_size = sizeof(DVBSubtitleContext),
- FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
+ FF_CODEC_ENCODE_CB(dvbsub_encode),
};
@@ -22,6 +22,8 @@
#include "bytestream.h"
#include "codec_internal.h"
#include "dvdsub.h"
+#include "encode.h"
+#include "internal.h"
#include "libavutil/avassert.h"
#include "libavutil/bprint.h"
#include "libavutil/imgutils.h"
@@ -115,15 +117,14 @@ static int color_distance(uint32_t a, uint32_t b)
* Count colors used in a rectangle, quantizing alpha and grouping by
* nearest global palette entry.
*/
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
- const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+ const AVSubtitleArea *r)
{
DVDSubtitleContext *dvdc = avctx->priv_data;
unsigned count[256] = { 0 };
- uint32_t *palette = (uint32_t *)r->data[1];
uint32_t color;
int x, y, i, j, match, d, best_d, av_uninit(best_j);
- uint8_t *p = r->data[0];
+ uint8_t *p = r->buf[0]->data;
for (y = 0; y < r->h; y++) {
for (x = 0; x < r->w; x++)
@@ -133,7 +134,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
for (i = 0; i < 256; i++) {
if (!count[i]) /* avoid useless search */
continue;
- color = palette[i];
+ color = r->pal[i];
/* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
if (match) {
@@ -233,13 +234,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
}
}
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
{
int x, y;
uint8_t *p, *q;
- p = src->data[0];
- q = dst->data[0] + (src->x - dst->x) +
+ p = src->buf[0]->data;
+ q = dst->buf[0]->data + (src->x - dst->x) +
(src->y - dst->y) * dst->linesize[0];
for (y = 0; y < src->h; y++) {
for (x = 0; x < src->w; x++)
@@ -249,51 +250,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
}
}
-static int encode_dvd_subtitles(AVCodecContext *avctx,
- uint8_t *outbuf, int outbuf_size,
- const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
DVDSubtitleContext *dvdc = avctx->priv_data;
uint8_t *q, *qq;
int offset1, offset2;
- int i, rects = h->num_rects, ret;
+ int ret = 0;
+ unsigned i, rects = frame->num_subtitle_areas;
unsigned global_palette_hits[33] = { 0 };
int cmap[256];
int out_palette[4];
int out_alpha[4];
- AVSubtitleRect vrect;
- uint8_t *vrect_data = NULL;
+ AVSubtitleArea vrect;
+ uint8_t* vrect_data = NULL, *outbuf;
int x2, y2;
int forced = 0;
+ int outbuf_size;
+ int64_t duration_ms;
- if (rects == 0 || !h->rects)
+ if (rects == 0)
+ return 0;
+
+ if (!frame->subtitle_areas)
return AVERROR(EINVAL);
+
for (i = 0; i < rects; i++)
- if (h->rects[i]->type != AV_SUBTITLE_FMT_BITMAP) {
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) {
av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
return AVERROR(EINVAL);
}
/* Mark this subtitle forced if any of the rectangles is forced. */
for (i = 0; i < rects; i++)
- if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+ if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
forced = 1;
break;
}
- vrect = *h->rects[0];
+ vrect = *frame->subtitle_areas[0];
if (rects > 1) {
/* DVD subtitles can have only one rectangle: build a virtual
rectangle containing all actual rectangles.
The data of the rectangles will be copied later, when the palette
is decided, because the rectangles may have different palettes. */
- int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
- int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+ int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+ int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
for (i = 1; i < rects; i++) {
- xmin = FFMIN(xmin, h->rects[i]->x);
- ymin = FFMIN(ymin, h->rects[i]->y);
- xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
- ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+ xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+ ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+ xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+ ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
}
vrect.x = xmin;
vrect.y = ymin;
@@ -305,27 +312,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
/* Count pixels outside the virtual rectangle as transparent */
global_palette_hits[0] = vrect.w * vrect.h;
for (i = 0; i < rects; i++)
- global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+ global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
}
for (i = 0; i < rects; i++)
- count_colors(avctx, global_palette_hits, h->rects[i]);
+ count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
select_palette(avctx, out_palette, out_alpha, global_palette_hits);
if (rects > 1) {
- if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+ vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+ if (!vrect.buf[0])
return AVERROR(ENOMEM);
- vrect.data [0] = vrect_data;
+
vrect.linesize[0] = vrect.w;
for (i = 0; i < rects; i++) {
- build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+ build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
out_palette, out_alpha);
- copy_rectangle(&vrect, h->rects[i], cmap);
+ copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
}
for (i = 0; i < 4; i++)
cmap[i] = i;
} else {
- build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+ build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
out_palette, out_alpha);
}
@@ -336,6 +345,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
out_palette[i], out_alpha[i] >> 4);
av_log(avctx, AV_LOG_DEBUG, "\n");
+
+ ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ outbuf_size = avpkt->size;
+ outbuf = avpkt->data;
+
// encode data block
q = outbuf + 4;
offset1 = q - outbuf;
@@ -345,10 +364,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
ret = AVERROR_BUFFER_TOO_SMALL;
goto fail;
}
- dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+ dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
vrect.w, (vrect.h + 1) >> 1, cmap);
offset2 = q - outbuf;
- dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+ dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
vrect.w, vrect.h >> 1, cmap);
if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -363,7 +382,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
bytestream_put_be16(&qq, q - outbuf);
// send start display command
- bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+ bytestream_put_be16(&q, 0);
bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
*q++ = 0x03; // palette - 4 nibbles
*q++ = (out_palette[3] << 4) | out_palette[2];
@@ -401,7 +420,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
*q++ = 0xff; // terminating command
// send stop display command last
- bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+ duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+ bytestream_put_be16(&q, (duration_ms*90) >> 10);
bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
*q++ = 0x02; // set end
*q++ = 0xff; // terminating command
@@ -410,7 +430,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
bytestream_put_be16(&qq, q - outbuf);
av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
- ret = q - outbuf;
+ avpkt->size = q - outbuf;
+ ret = 0;
+ *got_packet = 1;
fail:
av_free(vrect_data);
@@ -474,14 +496,13 @@ static int dvdsub_init(AVCodecContext *avctx)
return 0;
}
-static int dvdsub_encode(AVCodecContext *avctx,
- unsigned char *buf, int buf_size,
- const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+ const struct AVFrame* frame, int* got_packet)
{
//DVDSubtitleContext *s = avctx->priv_data;
int ret;
- ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+ ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
return ret;
}
@@ -506,7 +527,7 @@ const FFCodec ff_dvdsub_encoder = {
.p.type = AVMEDIA_TYPE_SUBTITLE,
.p.id = AV_CODEC_ID_DVD_SUBTITLE,
.init = dvdsub_init,
- FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
+ FF_CODEC_ENCODE_CB(dvdsub_encode),
.p.priv_class = &dvdsubenc_class,
.priv_data_size = sizeof(DVDSubtitleContext),
};
@@ -161,17 +161,70 @@ fail:
return ret;
}
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
- const AVSubtitle *sub)
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub)
{
- int ret;
+ int ret = 0;
+ AVFrame *frame = NULL;
+ AVPacket* avpkt = NULL;
+
if (sub->start_display_time) {
av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
return -1;
}
- ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub);
+ memset(buf, 0, buf_size);
+ // Create a temporary frame for calling the regular api:
+ frame = av_frame_alloc();
+ if (!frame) {
+ ret = AVERROR(ENOMEM);
+ goto exit;
+ }
+
+ frame->format = sub->format;
+ frame->type = AVMEDIA_TYPE_SUBTITLE;
+ ret = av_frame_get_buffer2(frame, 0);
+ if (ret < 0)
+ goto exit;
+
+ // Create a temporary packet
+ avpkt = av_packet_alloc();
+ if (!avpkt) {
+ ret = AVERROR(ENOMEM);
+ goto exit;
+ }
+
+ // Copy legacy subtitle data to temp frame
+ ret = ff_frame_put_subtitle(frame, sub);
+ if (ret < 0)
+ goto exit;
+
+ ret = avcodec_send_frame(avctx, frame);
+ if (ret < 0)
+ goto exit;
+
+ ret = avcodec_receive_packet(avctx, avpkt);
+
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ goto exit;
+
+ //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
avctx->frame_number++;
+
+ if (avpkt->size) {
+ if (avpkt->size > buf_size) {
+ ret = AVERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ memcpy(buf, avpkt->data, avpkt->size);
+ ret = avpkt->size;
+ }
+
+exit:
+
+ av_packet_free(&avpkt);
+ av_frame_free(&frame);
return ret;
}
@@ -30,6 +30,7 @@
#include "libavutil/ass_internal.h"
#include "bytestream.h"
#include "codec_internal.h"
+#include "encode.h"
#define STYLE_FLAG_BOLD (1<<0)
#define STYLE_FLAG_ITALIC (1<<1)
@@ -73,6 +74,7 @@ typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
ASSStyle *ass_dialog_style;
StyleBox *style_attributes;
unsigned count;
@@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
if (!s->ass_ctx)
return AVERROR_INVALIDDATA;
ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = {
.end = mov_text_end_cb,
};
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
- int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+ MovTextContext* s = avctx->priv_data;
+ int ret;
+
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+
+ if (s->ass_ctx && !avctx->extradata_size) {
+ ret = encode_sample_description(avctx);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+ }
+}
+
+static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+ const AVFrame *frame, int *got_packet)
{
MovTextContext *s = avctx->priv_data;
ASSDialog *dialog;
- int i, length;
+ int i, ret = 0;
+ size_t j;
+ uint8_t* buf;
+
+ ensure_ass_context(avctx, frame);
s->text_pos = 0;
s->count = 0;
s->box_flags = 0;
av_bprint_clear(&s->buffer);
- for (i = 0; i < sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i = 0; i < frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- mov_text_dialog(s, dialog);
- avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+
+ if (i > 0)
+ mov_text_new_line_cb(s, 0);
+
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ mov_text_dialog(s, dialog);
+ avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
+
+ }
}
- if (s->buffer.len > UINT16_MAX)
- return AVERROR(ERANGE);
- AV_WB16(buf, s->buffer.len);
- buf += 2;
+ if (!av_bprint_is_complete(&s->buffer)) {
+ return AVERROR(ENOMEM);
+ }
- for (size_t j = 0; j < box_count; j++)
+ for (j = 0; j < box_count; j++) {
box_types[j].encode(s);
+ }
- if (!av_bprint_is_complete(&s->buffer))
- return AVERROR(ENOMEM);
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
- if (!s->buffer.len)
- return 0;
+ buf = avpkt->data;
- if (s->buffer.len > bufsize - 3) {
+ AV_WB16(buf, s->buffer.len);
+ buf += 2;
+
+ if (s->buffer.len > avpkt->size - 3) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ ret = AVERROR_BUFFER_TOO_SMALL;
+ goto exit;
}
memcpy(buf, s->buffer.str, s->buffer.len);
- length = s->buffer.len + 2;
+ avpkt->size = s->buffer.len + 2;
+ *got_packet = 1;
- return length;
+exit:
+ return ret;
}
#define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,7 @@ const FFCodec ff_movtext_encoder = {
.priv_data_size = sizeof(MovTextContext),
.p.priv_class = &mov_text_encoder_class,
.init = mov_text_encode_init,
- FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
+ FF_CODEC_ENCODE_CB(mov_text_encode_frame),
.close = mov_text_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include "avcodec.h"
+#include "encode.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "codec_internal.h"
@@ -35,6 +36,7 @@
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
AVBPrint buffer;
char stack[SRT_STACK_SIZE];
int stack_ptr;
@@ -132,14 +134,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
}
}
-
static av_cold int srt_encode_init(AVCodecContext *avctx)
{
SRTContext *s = avctx->priv_data;
s->avctx = avctx;
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+ s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+ return 0;
}
static void srt_text_cb(void *priv, const char *text, int len)
@@ -229,58 +230,95 @@ static const ASSCodesCallbacks text_callbacks = {
.new_line = srt_new_line_cb,
};
-static int encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub,
- const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
{
SRTContext *s = avctx->priv_data;
ASSDialog *dialog;
- int i;
+ int i, ret;
+
+ ensure_ass_context(s, frame);
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- s->alignment_applied = 0;
- if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
- srt_style_apply(s, dialog->style);
- avpriv_ass_split_override_codes(cb, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+
+ if (i > 0)
+ srt_new_line_cb(s, 0);
+
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ s->alignment_applied = 0;
+ if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+ srt_style_apply(s, dialog->style);
+ avpriv_ass_split_override_codes(cb, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
+ }
}
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
- if (s->buffer.len > bufsize) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- memcpy(buf, s->buffer.str, s->buffer.len);
- return s->buffer.len;
+ memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+ avpkt->size = s->buffer.len;
+ *got_packet = 1;
+
+ return 0;
}
-static int srt_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+ return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
}
-static int text_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+ return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
}
static int srt_encode_close(AVCodecContext *avctx)
@@ -300,7 +338,7 @@ const FFCodec ff_srt_encoder = {
.p.id = AV_CODEC_ID_SUBRIP,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+ FF_CODEC_ENCODE_CB(srt_encode_frame),
.close = srt_encode_close,
};
#endif
@@ -313,7 +351,7 @@ const FFCodec ff_subrip_encoder = {
.p.id = AV_CODEC_ID_SUBRIP,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+ FF_CODEC_ENCODE_CB(srt_encode_frame),
.close = srt_encode_close,
};
#endif
@@ -326,7 +364,7 @@ const FFCodec ff_text_encoder = {
.p.id = AV_CODEC_ID_TEXT,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
+ FF_CODEC_ENCODE_CB(text_encode_frame),
.close = srt_encode_close,
};
#endif
@@ -109,7 +109,6 @@ int main(void){
is_decoder = 1;
break;
case FF_CODEC_CB_TYPE_ENCODE:
- case FF_CODEC_CB_TYPE_ENCODE_SUB:
case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
is_encoder = 1;
break;
@@ -125,15 +124,13 @@ int main(void){
#define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
CHECK(RECEIVE_PACKET, receive_packet) ||
- CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
+ CHECK(ENCODE, encode) ||
CHECK(RECEIVE_FRAME, receive_frame)) {
ERR_EXT("Codec %s does not implement its %s callback.\n",
is_decoder ? "decoding" : "encoding");
}
#undef CHECK
if (is_encoder) {
- if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
- ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
ERR("Encoder %s has decoder-only thread functions or bsf.\n");
if (codec->type == AVMEDIA_TYPE_AUDIO) {
@@ -33,11 +33,17 @@
#include "libavutil/bprint.h"
#include "libavutil/internal.h"
#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
#include "ttmlenc.h"
+#include "encode.h"
+
+
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
+ int extradata_written;
AVBPrint buffer;
} TTMLContext;
@@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = {
.new_line = ttml_new_line_cb,
};
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
- int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+ TTMLContext* s = avctx->priv_data;
+ int ret;
+
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+
+ if (s->ass_ctx && !s->extradata_written) {
+ s->extradata_written = 1;
+ if ((ret = ttml_write_header_content(avctx)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+ }
+ }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
TTMLContext *s = avctx->priv_data;
ASSDialog *dialog;
- int i;
+ int i, ret;
+
+ ensure_ass_context(avctx, frame);
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
- int ret;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
+ if (!ass)
+ continue;
+
dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
if (!dialog)
return AVERROR(ENOMEM);
+ if (i > 0)
+ ttml_new_line_cb(s, 0);
+
if (dialog->style) {
av_bprintf(&s->buffer, "<span region=\"");
av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
-
- // force null-termination, so in case our destination buffer is
- // too small, the return value is larger than bufsize minus null.
- if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- return s->buffer.len;
+ av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+ avpkt->size = s->buffer.len;
+ *got_packet = 1;
+
+ return 0;
}
static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
s->avctx = avctx;
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
- if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
- return AVERROR_INVALIDDATA;
- }
+ if (s->ass_ctx) {
+ if (ret = ttml_write_header_content(avctx) < 0)
+ return ret;
- if ((ret = ttml_write_header_content(avctx)) < 0) {
- return ret;
+ s->extradata_written = 1;
}
return 0;
@@ -389,7 +444,7 @@ const FFCodec ff_ttml_encoder = {
.p.id = AV_CODEC_ID_TTML,
.priv_data_size = sizeof(TTMLContext),
.init = ttml_encode_init,
- FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
+ FF_CODEC_ENCODE_CB(ttml_encode_frame),
.close = ttml_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};
@@ -76,7 +76,6 @@ int av_codec_is_encoder(const AVCodec *avcodec)
{
const FFCodec *const codec = ffcodec(avcodec);
return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE ||
- codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
}
@@ -22,6 +22,7 @@
#include <stdarg.h>
#include "avcodec.h"
+#include "encode.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "codec_internal.h"
@@ -32,6 +33,7 @@
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
AVBPrint buffer;
unsigned timestamp_end;
int count;
@@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = {
.end = webvtt_end_cb,
};
-static int webvtt_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
WebVTTContext *s = avctx->priv_data;
ASSDialog *dialog;
- int i;
+ int ret, i;
+
+ ensure_ass_context(s, frame);
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- webvtt_style_apply(s, dialog->style);
- avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+
+ if (i > 0)
+ webvtt_new_line_cb(s, 0);
+
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ webvtt_style_apply(s, dialog->style);
+ avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
+ }
}
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
- if (s->buffer.len > bufsize) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- memcpy(buf, s->buffer.str, s->buffer.len);
- return s->buffer.len;
+ memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+ avpkt->size = s->buffer.len;
+ *got_packet = s->buffer.len > 0;
+
+ return 0;
}
static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
{
WebVTTContext *s = avctx->priv_data;
s->avctx = avctx;
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+ return 0;
}
const FFCodec ff_webvtt_encoder = {
@@ -218,6 +258,6 @@ const FFCodec ff_webvtt_encoder = {
.p.id = AV_CODEC_ID_WEBVTT,
.priv_data_size = sizeof(WebVTTContext),
.init = webvtt_encode_init,
- FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
+ FF_CODEC_ENCODE_CB(webvtt_encode_frame),
.close = webvtt_encode_close,
};
@@ -23,6 +23,7 @@
#include "avcodec.h"
#include "bytestream.h"
#include "codec_internal.h"
+#include "encode.h"
#include "put_bits.h"
/**
@@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc)
return ms > 99;
}
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
- int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
- uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+ const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+ const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+ const uint64_t endTime = startTime + duration_ms;
int start_tc[4], end_tc[4];
- uint8_t *hdr = buf + 27; // Point behind the timestamp
+ uint8_t *hdr;
uint8_t *rlelenptr;
uint16_t width, height;
- int i;
+ int i, ret;
PutBitContext pb;
+ uint8_t* buf;
+ int64_t req_size;
- if (bufsize < 27 + 7*2 + 4*3) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ if (!frame->num_subtitle_areas) {
+ // Don't encode empty sub events
+ return 0;
}
+ // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+ req_size = 27 + 7*2 + 4*3 + 10;
+ req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+ req_size += 256; // Palette
+
+ ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ buf = avpkt->data;
+ hdr = avpkt->data + 27; // Point behind the timestamp
+
// TODO: support multiple rects
- if (h->num_rects != 1)
- av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+ if (frame->num_subtitle_areas != 1)
+ av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
// TODO: render text-based subtitles into bitmaps
- if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+ if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
return AVERROR(EINVAL);
}
// TODO: color reduction, similar to dvdsub encoder
- if (h->rects[0]->nb_colors > 4)
- av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+ if (frame->subtitle_areas[0]->nb_colors > 4)
+ av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
// TODO: Palette swapping if color zero is not transparent
- if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+ if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
return AVERROR(EINVAL);
}
- snprintf(buf, 28,
+ snprintf((char *)avpkt->data, 28,
"[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
start_tc[3], start_tc[2], start_tc[1], start_tc[0],
end_tc[3], end_tc[2], end_tc[1], end_tc[0]);
@@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
// 2 pixels required on either side of subtitle.
// Possibly due to limitations of hardware renderers.
// TODO: check if the bitmap is already padded
- width = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
- height = FFALIGN(h->rects[0]->h, 2);
+ width = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+ height = FFALIGN(frame->subtitle_areas[0]->h, 2);
bytestream_put_le16(&hdr, width);
bytestream_put_le16(&hdr, height);
- bytestream_put_le16(&hdr, h->rects[0]->x);
- bytestream_put_le16(&hdr, h->rects[0]->y);
- bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
- bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
rlelenptr = hdr; // Will store length of first field here later.
hdr+=2;
// Palette
for (i=0; i<4; i++)
- bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+ bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
// Bitmap
// RLE buffer. Reserve 2 bytes for possible padding after the last row.
- init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
- if (xsub_encode_rle(&pb, h->rects[0]->data[0],
- h->rects[0]->linesize[0] * 2,
- h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+ init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+ if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+ frame->subtitle_areas[0]->linesize[0] * 2,
+ frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
return AVERROR_BUFFER_TOO_SMALL;
bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
- if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
- h->rects[0]->linesize[0] * 2,
- h->rects[0]->w, h->rects[0]->h >> 1))
+ if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+ frame->subtitle_areas[0]->linesize[0] * 2,
+ frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
return AVERROR_BUFFER_TOO_SMALL;
// Enforce total height to be a multiple of 2
- if (h->rects[0]->h & 1) {
- put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+ if (frame->subtitle_areas[0]->h & 1) {
+ put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
}
flush_put_bits(&pb);
- return hdr - buf + put_bytes_output(&pb);
+ avpkt->size = hdr - buf + put_bytes_output(&pb);
+ *got_packet = 1;
+ return 0;
}
static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,5 +237,5 @@ const FFCodec ff_xsub_encoder = {
.p.type = AVMEDIA_TYPE_SUBTITLE,
.p.id = AV_CODEC_ID_XSUB,
.init = xsub_encoder_init,
- FF_CODEC_ENCODE_SUB_CB(xsub_encode),
+ FF_CODEC_ENCODE_CB(xsub_encode),
};