You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	lavc: add MicroDVD decoder.
Based on my MicroDVD->ASS conversion code from MPlayer (sub/subassconvert.c).
This commit is contained in:
		| @@ -27,6 +27,7 @@ version next: | ||||
| - ffmpeg -benchmark_all option | ||||
| - super2xsai filter ported from libmpcodecs | ||||
| - add libavresample audio conversion library for compatibility | ||||
| - MicroDVD decoder | ||||
|  | ||||
|  | ||||
| version 0.10: | ||||
|   | ||||
| @@ -837,7 +837,7 @@ performance on systems without hardware floating point support). | ||||
| @item SSA/ASS      @tab X @tab X @tab X @tab X | ||||
| @item DVB          @tab X @tab X @tab X @tab X | ||||
| @item DVD          @tab X @tab X @tab X @tab X | ||||
| @item MicroDVD     @tab X @tab X @tab   @tab | ||||
| @item MicroDVD     @tab X @tab X @tab   @tab X | ||||
| @item PGS          @tab   @tab   @tab   @tab X | ||||
| @item SubRip (SRT) @tab X @tab X @tab X @tab X | ||||
| @item XSUB         @tab   @tab   @tab X @tab X | ||||
|   | ||||
| @@ -239,6 +239,7 @@ OBJS-$(CONFIG_MACE3_DECODER)           += mace.o | ||||
| OBJS-$(CONFIG_MACE6_DECODER)           += mace.o | ||||
| OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o \ | ||||
|                                           mpegvideo.o error_resilience.o | ||||
| OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o | ||||
| OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o | ||||
| OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpeg.o | ||||
| OBJS-$(CONFIG_MJPEG_ENCODER)           += mjpegenc.o mjpeg.o           \ | ||||
|   | ||||
| @@ -397,6 +397,7 @@ void avcodec_register_all(void) | ||||
|     REGISTER_ENCDEC  (ASS, ass); | ||||
|     REGISTER_ENCDEC  (DVBSUB, dvbsub); | ||||
|     REGISTER_ENCDEC  (DVDSUB, dvdsub); | ||||
|     REGISTER_DECODER (MICRODVD, microdvd); | ||||
|     REGISTER_DECODER (PGSSUB, pgssub); | ||||
|     REGISTER_ENCDEC  (SRT, srt); | ||||
|     REGISTER_ENCDEC  (XSUB, xsub); | ||||
|   | ||||
							
								
								
									
										317
									
								
								libavcodec/microdvddec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								libavcodec/microdvddec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,317 @@ | ||||
| /* | ||||
|  * Copyright (c) 2012 Clément Bœsch | ||||
|  * | ||||
|  * This file is part of FFmpeg. | ||||
|  * | ||||
|  * FFmpeg is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * FFmpeg is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with FFmpeg; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * @file | ||||
|  * MicroDVD subtitle decoder | ||||
|  * | ||||
|  * Based on the specifications found here: | ||||
|  * https://trac.videolan.org/vlc/ticket/1825#comment:6 | ||||
|  */ | ||||
|  | ||||
| #include "libavutil/avstring.h" | ||||
| #include "libavutil/parseutils.h" | ||||
| #include "libavutil/bprint.h" | ||||
| #include "avcodec.h" | ||||
| #include "ass.h" | ||||
|  | ||||
| static int indexof(const char *s, int c) | ||||
| { | ||||
|     char *f = strchr(s, c); | ||||
|     return f ? (f - s) : -1; | ||||
| } | ||||
|  | ||||
| struct microdvd_tag { | ||||
|     char key; | ||||
|     int persistent; | ||||
|     uint32_t data1; | ||||
|     uint32_t data2; | ||||
|     char *data_string; | ||||
|     int data_string_len; | ||||
| }; | ||||
|  | ||||
| #define MICRODVD_PERSISTENT_OFF     0 | ||||
| #define MICRODVD_PERSISTENT_ON      1 | ||||
| #define MICRODVD_PERSISTENT_OPENED  2 | ||||
|  | ||||
| // Color, Font, Size, cHarset, stYle, Position, cOordinate | ||||
| #define MICRODVD_TAGS "cfshyYpo" | ||||
|  | ||||
| static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag) | ||||
| { | ||||
|     int tag_index = indexof(MICRODVD_TAGS, tag.key); | ||||
|  | ||||
|     if (tag_index < 0) | ||||
|         return; | ||||
|     memcpy(&tags[tag_index], &tag, sizeof(tag)); | ||||
| } | ||||
|  | ||||
| // italic, bold, underline, strike-through | ||||
| #define MICRODVD_STYLES "ibus" | ||||
|  | ||||
| static char *microdvd_load_tags(struct microdvd_tag *tags, char *s) | ||||
| { | ||||
|     while (*s == '{') { | ||||
|         char *start = s; | ||||
|         char tag_char = *(s + 1); | ||||
|         struct microdvd_tag tag = {0}; | ||||
|  | ||||
|         if (!tag_char || *(s + 2) != ':') | ||||
|             break; | ||||
|         s += 3; | ||||
|  | ||||
|         switch (tag_char) { | ||||
|  | ||||
|         /* Style */ | ||||
|         case 'Y': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|         case 'y': | ||||
|             while (*s && *s != '}') { | ||||
|                 int style_index = indexof(MICRODVD_STYLES, *s); | ||||
|  | ||||
|                 if (style_index >= 0) | ||||
|                     tag.data1 |= (1 << style_index); | ||||
|                 s++; | ||||
|             } | ||||
|             if (*s != '}') | ||||
|                 break; | ||||
|             /* We must distinguish persistent and non-persistent styles | ||||
|              * to handle this kind of style tags: {y:ib}{Y:us} */ | ||||
|             tag.key = tag_char; | ||||
|             break; | ||||
|  | ||||
|         /* Color */ | ||||
|         case 'C': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|         case 'c': | ||||
|             tag.data1 = strtol(s, &s, 16) & 0x00ffffff; | ||||
|             if (*s != '}') | ||||
|                 break; | ||||
|             tag.key = 'c'; | ||||
|             break; | ||||
|  | ||||
|         /* Font name */ | ||||
|         case 'F': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|         case 'f': { | ||||
|             int len = indexof(s, '}'); | ||||
|             if (len < 0) | ||||
|                 break; | ||||
|             tag.data_string = s; | ||||
|             tag.data_string_len = len; | ||||
|             s += len; | ||||
|             tag.key = 'f'; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         /* Font size */ | ||||
|         case 'S': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|         case 's': | ||||
|             tag.data1 = strtol(s, &s, 10); | ||||
|             if (*s != '}') | ||||
|                 break; | ||||
|             tag.key = 's'; | ||||
|             break; | ||||
|  | ||||
|         /* Charset */ | ||||
|         case 'H': { | ||||
|             //TODO: not yet handled, just parsed. | ||||
|             int len = indexof(s, '}'); | ||||
|             if (len < 0) | ||||
|                 break; | ||||
|             tag.data_string = s; | ||||
|             tag.data_string_len = len; | ||||
|             s += len; | ||||
|             tag.key = 'h'; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         /* Position */ | ||||
|         case 'P': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|             tag.data1 = (*s++ == '1'); | ||||
|             if (*s != '}') | ||||
|                 break; | ||||
|             tag.key = 'p'; | ||||
|             break; | ||||
|  | ||||
|         /* Coordinates */ | ||||
|         case 'o': | ||||
|             tag.persistent = MICRODVD_PERSISTENT_ON; | ||||
|             tag.data1 = strtol(s, &s, 10); | ||||
|             if (*s != ',') | ||||
|                 break; | ||||
|             s++; | ||||
|             tag.data2 = strtol(s, &s, 10); | ||||
|             if (*s != '}') | ||||
|                 break; | ||||
|             tag.key = 'o'; | ||||
|             break; | ||||
|  | ||||
|         default:    /* Unknown tag, we consider it's text */ | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (tag.key == 0) | ||||
|             return start; | ||||
|  | ||||
|         microdvd_set_tag(tags, tag); | ||||
|         s++; | ||||
|     } | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags) | ||||
| { | ||||
|     int i, sidx; | ||||
|     for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { | ||||
|         if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED) | ||||
|             continue; | ||||
|         switch (tags[i].key) { | ||||
|         case 'Y': | ||||
|         case 'y': | ||||
|             for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) | ||||
|                 if (tags[i].data1 & (1 << sidx)) | ||||
|                     av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]); | ||||
|             break; | ||||
|  | ||||
|         case 'c': | ||||
|             av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1); | ||||
|             break; | ||||
|  | ||||
|         case 'f': | ||||
|             av_bprintf(new_line, "{\\fn%.*s}", | ||||
|                        tags[i].data_string_len, tags[i].data_string); | ||||
|             break; | ||||
|  | ||||
|         case 's': | ||||
|             av_bprintf(new_line, "{\\fs%d}", tags[i].data1); | ||||
|             break; | ||||
|  | ||||
|         case 'p': | ||||
|             if (tags[i].data1 == 0) | ||||
|                 av_bprintf(new_line, "{\\an8}"); | ||||
|             break; | ||||
|  | ||||
|         case 'o': | ||||
|             av_bprintf(new_line, "{\\pos(%d,%d)}", | ||||
|                        tags[i].data1, tags[i].data2); | ||||
|             break; | ||||
|         } | ||||
|         if (tags[i].persistent == MICRODVD_PERSISTENT_ON) | ||||
|             tags[i].persistent = MICRODVD_PERSISTENT_OPENED; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void microdvd_close_no_persistent_tags(AVBPrint *new_line, | ||||
|                                               struct microdvd_tag *tags) | ||||
| { | ||||
|     int i, sidx; | ||||
|  | ||||
|     for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) { | ||||
|         if (tags[i].persistent != MICRODVD_PERSISTENT_OFF) | ||||
|             continue; | ||||
|         switch (tags[i].key) { | ||||
|  | ||||
|         case 'y': | ||||
|             for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--) | ||||
|                 if (tags[i].data1 & (1 << sidx)) | ||||
|                     av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]); | ||||
|             break; | ||||
|  | ||||
|         case 'c': | ||||
|             av_bprintf(new_line, "{\\c}"); | ||||
|             break; | ||||
|  | ||||
|         case 'f': | ||||
|             av_bprintf(new_line, "{\\fn}"); | ||||
|             break; | ||||
|  | ||||
|         case 's': | ||||
|             av_bprintf(new_line, "{\\fs}"); | ||||
|             break; | ||||
|         } | ||||
|         tags[i].key = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int microdvd_decode_frame(AVCodecContext *avctx, | ||||
|                                  void *data, int *got_sub_ptr, AVPacket *avpkt) | ||||
| { | ||||
|     AVSubtitle *sub = data; | ||||
|     AVBPrint new_line; | ||||
|     char *decoded_sub; | ||||
|     char *line = avpkt->data; | ||||
|     char *end = avpkt->data + avpkt->size; | ||||
|     int64_t frame_start = avpkt->pts; | ||||
|     int64_t frame_end   = avpkt->pts + avpkt->duration; | ||||
|     int ts_start = av_rescale_q(frame_start, avctx->time_base, (AVRational){1,100}); | ||||
|     int ts_end   = av_rescale_q(frame_end,   avctx->time_base, (AVRational){1,100}); | ||||
|     struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; | ||||
|  | ||||
|     if (avpkt->size <= 0) | ||||
|         return avpkt->size; | ||||
|  | ||||
|     av_bprint_init(&new_line, 0, 2048); | ||||
|  | ||||
|     // skip {frame_start}{frame_end} | ||||
|     line = strchr(line, '}'); if (!line) goto end; line++; | ||||
|     line = strchr(line, '}'); if (!line) goto end; line++; | ||||
|  | ||||
|     // subtitle content | ||||
|     while (line < end && *line) { | ||||
|  | ||||
|         // parse MicroDVD tags, and open them in ASS | ||||
|         line = microdvd_load_tags(tags, line); | ||||
|         microdvd_open_tags(&new_line, tags); | ||||
|  | ||||
|         // simple copy until EOL or forced carriage return | ||||
|         while (line < end && *line && *line != '|') { | ||||
|             av_bprint_chars(&new_line, *line, 1); | ||||
|             line++; | ||||
|         } | ||||
|  | ||||
|         // line split | ||||
|         if (line < end && *line == '|') { | ||||
|             microdvd_close_no_persistent_tags(&new_line, tags); | ||||
|             av_bprintf(&new_line, "\\N"); | ||||
|             line++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| end: | ||||
|     av_bprint_finalize(&new_line, &decoded_sub); | ||||
|     if (*decoded_sub) | ||||
|         ff_ass_add_rect(sub, decoded_sub, ts_start, ts_end, 0); | ||||
|     av_free(decoded_sub); | ||||
|  | ||||
|     *got_sub_ptr = sub->num_rects > 0; | ||||
|     return avpkt->size; | ||||
| } | ||||
|  | ||||
| AVCodec ff_microdvd_decoder = { | ||||
|     .name         = "microdvd", | ||||
|     .long_name    = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"), | ||||
|     .type         = AVMEDIA_TYPE_SUBTITLE, | ||||
|     .id           = CODEC_ID_MICRODVD, | ||||
|     .init         = ff_ass_subtitle_header_default, | ||||
|     .decode       = microdvd_decode_frame, | ||||
| }; | ||||
| @@ -27,7 +27,7 @@ | ||||
|  */ | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_MAJOR 54 | ||||
| #define LIBAVCODEC_VERSION_MINOR  15 | ||||
| #define LIBAVCODEC_VERSION_MINOR  16 | ||||
| #define LIBAVCODEC_VERSION_MICRO 100 | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user