static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_lavc_priv *priv = sd->priv; AVCodecContext *avctx = priv->avctx; double ts = av_q2d(av_inv_q(avctx->time_base)); AVSubtitle sub = {0}; AVPacket pkt; int ret, got_sub; mp_set_av_packet(&pkt, packet); pkt.pts = packet->pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : packet->pts * ts; pkt.duration = packet->duration * ts; ret = avcodec_decode_subtitle2(avctx, &sub, &got_sub, &pkt); if (ret < 0) { mp_msg(MSGT_OSD, MSGL_ERR, "Error decoding subtitle\n"); } else if (got_sub) { for (int i = 0; i < sub.num_rects; i++) { char *ass_line = sub.rects[i]->ass; if (!ass_line) break; // This might contain embedded timestamps, using the "old" ffmpeg // ASS packet format, in which case pts/duration might be ignored // at a later point. sd_conv_add_packet(sd, ass_line, strlen(ass_line), packet->pts, packet->duration); } } avsubtitle_free(&sub); }
void CDVDOverlayCodecFFmpeg::Flush() { avsubtitle_free(&m_Subtitle); m_SubtitleIndex = -1; avcodec_flush_buffers(m_pCodecContext); }
void dvbsub_ass_clear(void) { OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(ass_mutex); while(ass_queue.size()) { ass_data *a = (ass_data *) ass_queue.pop(); if (a) { avsubtitle_free(&a->sub); delete a; } } while(!sem_trywait(&ass_sem)); ass_track = NULL; for(std::map<int,ASS_Track*>::iterator it = ass_map.begin(); it != ass_map.end(); ++it) ass_free_track(it->second); ass_map.clear(); if (ass_renderer) { ass_renderer_done(ass_renderer); ass_renderer = NULL; } if (ass_library) { ass_library_done(ass_library); ass_library = NULL; } clear_queue(); }
void dvbsub_ass_write(AVCodecContext *c, AVSubtitle *sub, int pid) { if (ass_reader_running) { ass_queue.push((uint8_t *)new ass_data(c, sub, pid)); sem_post(&ass_sem); memset(sub, 0, sizeof(AVSubtitle)); } else avsubtitle_free(sub); }
static void clear_sub(struct sub *sub) { sub->count = 0; sub->pts = MP_NOPTS_VALUE; sub->endpts = MP_NOPTS_VALUE; if (sub->valid) avsubtitle_free(&sub->avsub); sub->valid = false; }
static int subtitle_handler(AVCodecContext *avctx, void *frame, int *got_sub_ptr, AVPacket *avpkt) { AVSubtitle sub; int ret = avcodec_decode_subtitle2(avctx, &sub, got_sub_ptr, avpkt); if (ret >= 0 && *got_sub_ptr) avsubtitle_free(&sub); return ret; }
int CDVDOverlayCodecFFmpeg::Decode(DemuxPacket *pPacket) { if (!m_pCodecContext || !pPacket) return 1; int gotsub = 0, len = 0; avsubtitle_free(&m_Subtitle); AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = pPacket->pData; avpkt.size = pPacket->iSize; avpkt.pts = pPacket->pts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->pts; avpkt.dts = pPacket->dts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->dts; len = avcodec_decode_subtitle2(m_pCodecContext, &m_Subtitle, &gotsub, &avpkt); if (len < 0) { CLog::Log(LOGERROR, "%s - avcodec_decode_subtitle returned failure", __FUNCTION__); Flush(); return OC_ERROR; } if (len != avpkt.size) CLog::Log(LOGWARNING, "%s - avcodec_decode_subtitle didn't consume the full packet", __FUNCTION__); if (!gotsub) return OC_BUFFER; double pts_offset = 0.0; if (m_pCodecContext->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && m_Subtitle.format == 0) { // for pgs subtitles the packet pts of the end_segments are wrong // instead use the subtitle pts to calc the offset here // see http://git.videolan.org/?p=ffmpeg.git;a=commit;h=2939e258f9d1fff89b3b68536beb931b54611585 if (m_Subtitle.pts != AV_NOPTS_VALUE && pPacket->pts != DVD_NOPTS_VALUE) { pts_offset = m_Subtitle.pts - pPacket->pts ; } } m_StartTime = DVD_MSEC_TO_TIME(m_Subtitle.start_display_time); m_StopTime = DVD_MSEC_TO_TIME(m_Subtitle.end_display_time); //adapt start and stop time to our packet pts bool dummy = false; CDVDOverlayCodec::GetAbsoluteTimes(m_StartTime, m_StopTime, pPacket, dummy, pts_offset); m_SubtitleIndex = 0; return OC_OVERLAY; }
static void decode(struct sd *sd, struct demux_packet *packet) { struct sd_lavc_priv *priv = sd->priv; AVCodecContext *avctx = priv->avctx; AVSubtitle sub = {0}; AVPacket pkt; AVPacket parsed_pkt = {0}; int ret, got_sub; mp_set_av_packet(&pkt, packet, &avctx->time_base); if (sd->codec && strcmp(sd->codec, "webvtt-webm") == 0) { if (parse_webvtt(&pkt, &parsed_pkt) < 0) { MP_ERR(sd, "Error parsing subtitle\n"); goto done; } pkt = parsed_pkt; } ret = avcodec_decode_subtitle2(avctx, &sub, &got_sub, &pkt); if (ret < 0) { MP_ERR(sd, "Error decoding subtitle\n"); } else if (got_sub) { for (int i = 0; i < sub.num_rects; i++) { char *ass_line = sub.rects[i]->ass; if (!ass_line) break; // This might contain embedded timestamps, using the "old" ffmpeg // ASS packet format, in which case pts/duration might be ignored // at a later point. sd_conv_add_packet(sd, ass_line, strlen(ass_line), packet->pts, packet->duration); } } done: avsubtitle_free(&sub); av_free_packet(&parsed_pkt); }
void plex_link_subtitles_to_graph(AVFilterGraph* g) { #if CONFIG_INLINEASS_FILTER int contextId = 0; for (int i = 0; i < nb_filtergraphs && contextId < plexContext.nb_inlineass_ctxs; i++) { AVFilterGraph* graph = filtergraphs[i]->graph; if (!graph) continue; for (int i = 0; i < graph->nb_filters && contextId < plexContext.nb_inlineass_ctxs; i++) { const AVFilterContext* filterCtx = graph->filters[i]; if (strcmp(filterCtx->filter->name, "inlineass") == 0) { AVFilterContext *ctx = graph->filters[i]; InlineAssContext *assCtx = &plexContext.inlineass_ctxs[contextId++]; assCtx->ctx = ctx; for (int j = 0; j < nb_input_streams; j++) { InputStream *ist = input_streams[j]; if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT) avfilter_inlineass_add_attachment(ctx, ist->st); if (ist->file_index == assCtx->file_index && ist->st->index == assCtx->stream_index && ist->sub2video.sub_queue) { while (av_fifo_size(ist->sub2video.sub_queue)) { AVSubtitle tmp; av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL); plex_process_subtitles(ist, &tmp); avsubtitle_free(&tmp); } } } avfilter_inlineass_set_fonts(ctx); } } } #endif }
int _tmain(int argc, _TCHAR* argv[]) { ISubtitles* subtitle = NULL; if (!ISubtitles::create(&subtitle)) { printf("failed to create subtitle instance.\n"); return 1; } #ifdef EMBEDDING_SUBTITLE av_register_all(); avformat_network_init(); AVFormatContext *fmt_ctx = NULL; char *url = LOCAL_FILE; int subtitle_stream_idx = -1; AVStream* subtitle_stream; AVCodecContext* subtitle_dec_ctx; AVPacket pkt; AVSubtitle sub; int got_sub; int ret; int index = 0; /* open input file, and allocate format context */ if (avformat_open_input(&fmt_ctx, url, NULL, NULL) < 0) { LOGE("Could not open source file"); return 1; } /* retrieve stream information */ if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { LOGE("Could not find stream information\n"); return 1; } if (open_codec_context(fmt_ctx, &subtitle_stream_idx, AVMEDIA_TYPE_SUBTITLE) < 0) { LOGE("failed to find subttile track"); return 1; } subtitle_stream = fmt_ctx->streams[subtitle_stream_idx]; subtitle_dec_ctx = subtitle_stream->codec; /* dump input information to stderr */ av_dump_format(fmt_ctx, 0, url, 0); SubtitleCodecId codec_id; if (subtitle_dec_ctx->codec_id == AV_CODEC_ID_ASS || subtitle_dec_ctx->codec_id == AV_CODEC_ID_SSA) codec_id = SUBTITLE_CODEC_ID_ASS; else codec_id = SUBTITLE_CODEC_ID_TEXT; ret = subtitle->addEmbeddingSubtitle(codec_id, "chs", "chs", (const char *)subtitle_dec_ctx->extradata, subtitle_dec_ctx->extradata_size); if (ret < 0) { LOGE("failed to addEmbeddingSubtitle"); return 1; } /* initialize packet, set data to NULL, let the demuxer fill it */ av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; /* read frames from the file */ while (av_read_frame(fmt_ctx, &pkt) >= 0 && index < 10 ) { if (pkt.stream_index == subtitle_stream_idx) { AVPacket orig_pkt = pkt; do { ret = avcodec_decode_subtitle2(subtitle_dec_ctx, &sub, &got_sub, &pkt); if (ret < 0) { break; } if (got_sub) { LOGI("got subtitle"); for (int i=0;i<sub.num_rects;i++) { if (sub.rects[i]->ass) { int64_t start_time ,stop_time; AVRational ra; ra.num = 1; ra.den = AV_TIME_BASE; start_time = av_rescale_q(sub.pts + sub.start_display_time * 1000, ra, subtitle_stream->time_base); stop_time = av_rescale_q(sub.pts + sub.end_display_time * 1000, ra, subtitle_stream->time_base); subtitle->addEmbeddingSubtitleEntity(0, start_time, stop_time - start_time, sub.rects[i]->ass, strlen(sub.rects[i]->ass)); // my_strlen_utf8_c index++; } } avsubtitle_free(&sub); } pkt.data += ret; pkt.size -= ret; } while (pkt.size > 0); av_free_packet(&orig_pkt); } else { av_free_packet(&pkt); } } #else if (!subtitle->loadSubtitle(SUB_FILE_PATH, false)) { printf("failed to load subtitle: %s", SUB_FILE_PATH); return 1; } #endif STSSegment* segment = NULL; char subtitleText[1024] = {0}; int line = 0; while(line < 20 && subtitle->getNextSubtitleSegment(&segment)) { int64_t startTime = segment->getStartTime(); int64_t stopTime = segment->getStopTime(); segment->getSubtitleText(subtitleText, 1024); LOGI("%01d:%02d:%02d.%02d --> %01d:%02d:%02d.%02d %s", int(startTime/1000/3600), int(startTime/1000%3600/60), int(startTime/1000%60), int(startTime%1000)/10, int(stopTime/1000/3600), int(stopTime/1000%3600/60), int(stopTime/1000%60), int(stopTime%1000)/10, CW2A(CA2W(subtitleText, CP_UTF8))); //getchar(); line++; } subtitle->close(); return 0; }
void CDVDOverlayCodecFFmpeg::Dispose() { avsubtitle_free(&m_Subtitle); avcodec_free_context(&m_pCodecContext); }
/** * Decode a subtitle packet via libavcodec. * \return < 0 on error, > 0 if further processing is needed */ int decode_avsub(struct sh_sub *sh, uint8_t **data, int *size, double *pts, double *endpts) { AVCodecContext *ctx = sh->context; enum CodecID cid = CODEC_ID_NONE; int new_type = 0; int res; int got_sub; AVSubtitle sub; AVPacket pkt; switch (sh->type) { case 'b': cid = CODEC_ID_DVB_SUBTITLE; break; case 'p': cid = CODEC_ID_HDMV_PGS_SUBTITLE; break; case 'x': cid = CODEC_ID_XSUB; break; } av_init_packet(&pkt); pkt.data = *data; pkt.size = *size; pkt.pts = *pts * 1000; if (*pts != MP_NOPTS_VALUE && *endpts != MP_NOPTS_VALUE) pkt.convergence_duration = (*endpts - *pts) * 1000; if (!ctx) { AVCodec *sub_codec; init_avcodec(); ctx = avcodec_alloc_context3(NULL); sub_codec = avcodec_find_decoder(cid); if (!ctx || !sub_codec || avcodec_open2(ctx, sub_codec, NULL) < 0) { mp_msg(MSGT_SUBREADER, MSGL_FATAL, "Could not open subtitle decoder\n"); av_freep(&ctx); return -1; } sh->context = ctx; } res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0) return res; if (*pts != MP_NOPTS_VALUE) { if (sub.end_display_time > sub.start_display_time) *endpts = *pts + sub.end_display_time / 1000.0; *pts += sub.start_display_time / 1000.0; } if (got_sub && vo_spudec && sub.num_rects == 0) spudec_set_paletted(vo_spudec, NULL, 0, NULL, 0, 0, 0, 0, *pts, *endpts); if (got_sub && sub.num_rects > 0) { switch (sub.rects[0]->type) { case SUBTITLE_BITMAP: if (!vo_spudec) vo_spudec = spudec_new_scaled(NULL, ctx->width, ctx->height, NULL, 0); spudec_set_paletted(vo_spudec, sub.rects[0]->pict.data[0], sub.rects[0]->pict.linesize[0], sub.rects[0]->pict.data[1], sub.rects[0]->x, sub.rects[0]->y, sub.rects[0]->w, sub.rects[0]->h, *pts, *endpts); vo_osd_changed(OSDTYPE_SPU); break; case SUBTITLE_TEXT: *data = strdup(sub.rects[0]->text); *size = strlen(*data); new_type = 't'; break; case SUBTITLE_ASS: *data = strdup(sub.rects[0]->ass); *size = strlen(*data); new_type = 'a'; break; } } if (got_sub) avsubtitle_free(&sub); return new_type; }
static int decode_packet(AVCodecContext *dec_ctx, FILE *dst_file, AVFrame *frame, int *got_frame, int *frame_count, AVPacket *pkt) { int ret = -1; *got_frame = 0; AVSubtitle sub; unsigned i, j, k, l; if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { ret = 0; /* decode video frame */ ret = avcodec_decode_video2(dec_ctx, frame, got_frame, pkt); if (ret < 0) { fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret)); return ret; } if (*got_frame) { if (frame->width != width || frame->height != height || frame->format != pix_fmt) { fprintf(stderr, "Error: input video width/height/format changed:\n" "old: width = %d, height = %d, format = %s\n" "new: width = %d, height = %d, format = %s\n", width, height, av_get_pix_fmt_name(pix_fmt), dec_ctx->width, dec_ctx->height, av_get_pix_fmt_name(dec_ctx->pix_fmt)); return -1; } printf("video_frame n:%d coded_n:%d pts:%s\n", *frame_count, frame->coded_picture_number, av_ts2timestr(frame->pts, &dec_ctx->time_base)); /* copy decoded frame to destination buffer: * this is required since rawvideo expects non aligned data */ av_image_copy(video_dst_data, video_dst_linesize, (const uint8_t **)(frame->data), frame->linesize, pix_fmt, width, height); *frame_count += 1; /* write to rawvideo file */ fwrite(video_dst_data[0], 1, video_dst_bufsize, dst_file); } } else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { ret = 0; /* decode audio frame */ ret = avcodec_decode_audio4(dec_ctx, frame, got_frame, pkt); if (ret < 0) { fprintf(stderr, "Error decoding audio frame (%s)\n", av_err2str(ret)); return ret; } /* Some audio decoders decode only part of the packet, and have to be * called again with the remainder of the packet data. * Sample: fate-suite/lossless-audio/luckynight-partial.shn * Also, some decoders might over-read the packet. */ ret = FFMIN(ret, pkt->size); if (*got_frame) { size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format); printf("audio_frame n:%d nb_samples:%d pts:%s\n", *frame_count, frame->nb_samples, av_ts2timestr(frame->pts, &dec_ctx->time_base)); *frame_count += 1; /* Write the raw audio data samples of the first plane. This works * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, * most audio decoders output planar audio, which uses a separate * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). * In other words, this code will write only the first audio channel * in these cases. * You should use libswresample or libavfilter to convert the frame * to packed data. */ fwrite(frame->extended_data[0], 1, unpadded_linesize, dst_file); } } else if (dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { ret = 0; /* decode video frame */ ret = avcodec_decode_subtitle2(dec_ctx, &sub, got_frame, pkt); if (ret < 0) { fprintf(stderr, "Error decoding subtitle (%s)\n", av_err2str(ret)); return ret; } if (*got_frame) { printf("subtitle n:%d format:%u pts:%s start_time:%u end_time:%u num_recs:%u\n", *frame_count, sub.format, av_ts2timestr(sub.pts, &dec_ctx->time_base), sub.start_display_time, sub.end_display_time, sub.num_rects); *frame_count += 1; /* write to text file */ for (i = 0; i < sub.num_rects; i += 1) { fprintf(dst_file, "x:%d y:%d w:%d h:%d nb_colors:%d flags:%x linesizes:%d,%d,%d,%d,%d,%d,%d,%d\n" "text:%s\nass:%s\n", sub.rects[i]->x, sub.rects[i]->y, sub.rects[i]->w, sub.rects[i]->h, sub.rects[i]->nb_colors, sub.rects[i]->flags, sub.rects[i]->pict.linesize[0], sub.rects[i]->pict.linesize[1], sub.rects[i]->pict.linesize[2], sub.rects[i]->pict.linesize[3], sub.rects[i]->pict.linesize[4], sub.rects[i]->pict.linesize[5], sub.rects[i]->pict.linesize[6], sub.rects[i]->pict.linesize[7], sub.rects[i]->text, sub.rects[i]->ass); for (j = 0; j < AV_NUM_DATA_POINTERS; j += 1) { if (sub.rects[i]->pict.linesize[j]) { fprintf(dst_file, "data:%d\n", j); for (k = 0; k < sub.rects[i]->h; k += 1) { for (l = 0; l < sub.rects[i]->w; l += 1) { fprintf(dst_file, "%x", sub.rects[i]->pict.data[j][l + k * sub.rects[i]->pict.linesize[j]]); } fprintf(dst_file, "\n"); } } } } avsubtitle_free(&sub); } } /* de-reference the frame, which is not used anymore */ if (*got_frame) av_frame_unref(frame); return ret; }
void Scheduler::process_subtitle(Display::Ptr vid) { unsigned disp_x, disp_y; vid->get_rect(disp_x, disp_y); sub_renderer->set_dimensions(disp_x, disp_y); for (;;) { avlock.lock(); if (sub_pkt_queue.size() == 0) { avlock.unlock(); break; } auto packet = sub_pkt_queue.pull(); avlock.unlock(); auto& pkt = packet.get(); uint8_t *data = pkt.data; size_t size = pkt.size; int finished = 0; AVSubtitle sub; int ret; while (pkt.size > 0) { gfx_lock.lock(); ret = avcodec_decode_subtitle2(file->sub().ctx, &sub, &finished, &pkt); gfx_lock.unlock(); if (ret <= 0) { std::cerr << "Decode subtitle failed." << std::endl; break; } pkt.data += ret; pkt.size -= ret; } pkt.data = data; pkt.size = size; if (finished) { for (unsigned i = 0; i < sub.num_rects; i++) { if (sub.rects[i]->ass) { // Push our new message to handler queue. We just handle ASS atm, but hey ;) gfx_lock.lock(); sub_renderer->push_msg(sub.rects[i]->ass, video_pts); gfx_lock.unlock(); } } } else { std::cout << "Did not finish a subtitle frame." << std::endl; } avsubtitle_free(&sub); } // Print all subtitle currently active in this PTS to screen. gfx_lock.lock(); auto& list = sub_renderer->msg_list(video_pts); for (auto& msg : list) vid->subtitle(msg); gfx_lock.unlock(); }
void AbstractStream::deleteSubtitle(AVSubtitle *subtitle) { avsubtitle_free(subtitle); delete subtitle; }
bool SubtitleProcessorFFmpeg::processSubtitle() { m_frames.clear(); int ss = m_reader.subtitleStream(); if (ss < 0) { qWarning("no subtitle stream found"); return false; } AVCodecContext *ctx = m_reader.subtitleCodecContext(); AVCodec *dec = avcodec_find_decoder(ctx->codec_id); if (!dec) { qWarning("Failed to find subtitle codec %s", avcodec_get_name(ctx->codec_id)); return false; } const AVCodecDescriptor *dec_desc = avcodec_descriptor_get(ctx->codec_id); if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { qWarning("Only text based subtitles are currently supported"); return false; } AVDictionary *codec_opts = NULL; int ret = avcodec_open2(ctx, dec, &codec_opts); if (ret < 0) { qWarning("open subtitle codec error: %s", av_err2str(ret)); av_dict_free(&codec_opts); return false; } while (!m_reader.atEnd()) { if (!m_reader.readFrame()) continue; if (!m_reader.packet()->isValid()) continue; if (m_reader.stream() != ss) continue; AVPacket packet; av_init_packet(&packet); packet.size = m_reader.packet()->data.size(); packet.data = (uint8_t*)m_reader.packet()->data.constData(); int got_subtitle = 0; AVSubtitle sub; memset(&sub, 0, sizeof(sub)); ret = avcodec_decode_subtitle2(ctx, &sub, &got_subtitle, &packet); if (ret < 0 || !got_subtitle) { av_free_packet(&packet); avsubtitle_free(&sub); continue; } qreal pts = m_reader.packet()->pts; SubtitleFrame frame; frame.begin = pts + qreal(sub.start_display_time)/1000.0; frame.end = pts + qreal(sub.end_display_time)/1000.0; // qDebug() << QTime(0, 0, 0).addMSecs(frame.begin*1000.0) << "-" << QTime(0, 0, 0).addMSecs(frame.end*1000.0) << " fmt: " << sub.format << " pts: " << m_reader.packet()->pts //sub.pts // << " rects: " << sub.num_rects; for (unsigned i = 0; i < sub.num_rects; i++) { switch (sub.rects[i]->type) { case SUBTITLE_ASS: frame.text.append(PlainText::fromAss(sub.rects[i]->ass)).append('\n'); break; case SUBTITLE_TEXT: frame.text.append(sub.rects[i]->text).append('\n'); break; case SUBTITLE_BITMAP: break; default: break; } } m_frames.append(frame); av_free_packet(&packet); avsubtitle_free(&sub); } avcodec_close(ctx); return true; }
static av_cold int init_subtitles(AVFilterContext *ctx) { int j, ret, sid; int k = 0; AVDictionary *codec_opts = NULL; AVFormatContext *fmt = NULL; AVCodecContext *dec_ctx = NULL; AVCodec *dec = NULL; const AVCodecDescriptor *dec_desc; AVStream *st; AVPacket pkt; AssContext *ass = ctx->priv; /* Init libass */ ret = init(ctx); if (ret < 0) return ret; ass->track = ass_new_track(ass->library); if (!ass->track) { av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n"); return AVERROR(EINVAL); } /* Open subtitles file */ ret = avformat_open_input(&fmt, ass->filename, NULL, NULL); if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename); goto end; } ret = avformat_find_stream_info(fmt, NULL); if (ret < 0) goto end; /* Locate subtitles stream */ if (ass->stream_index < 0) ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0); else { ret = -1; if (ass->stream_index < fmt->nb_streams) { for (j = 0; j < fmt->nb_streams; j++) { if (fmt->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { if (ass->stream_index == k) { ret = j; break; } k++; } } } } if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Unable to locate subtitle stream in %s\n", ass->filename); goto end; } sid = ret; st = fmt->streams[sid]; /* Load attached fonts */ for (j = 0; j < fmt->nb_streams; j++) { AVStream *st = fmt->streams[j]; if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) { const AVDictionaryEntry *tag = NULL; tag = av_dict_get(st->metadata, "filename", NULL, AV_DICT_MATCH_CASE); if (tag) { av_log(ctx, AV_LOG_DEBUG, "Loading attached font: %s\n", tag->value); ass_add_font(ass->library, tag->value, st->codecpar->extradata, st->codecpar->extradata_size); } else { av_log(ctx, AV_LOG_WARNING, "Font attachment has no filename, ignored.\n"); } } } /* Initialize fonts */ ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1); /* Open decoder */ dec = avcodec_find_decoder(st->codecpar->codec_id); if (!dec) { av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n", avcodec_get_name(st->codecpar->codec_id)); return AVERROR(EINVAL); } dec_desc = avcodec_descriptor_get(st->codecpar->codec_id); if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { av_log(ctx, AV_LOG_ERROR, "Only text based subtitles are currently supported\n"); return AVERROR_PATCHWELCOME; } if (ass->charenc) av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0); if (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,26,100)) av_dict_set(&codec_opts, "sub_text_format", "ass", 0); dec_ctx = avcodec_alloc_context3(dec); if (!dec_ctx) return AVERROR(ENOMEM); ret = avcodec_parameters_to_context(dec_ctx, st->codecpar); if (ret < 0) goto end; /* * This is required by the decoding process in order to rescale the * timestamps: in the current API the decoded subtitles have their pts * expressed in AV_TIME_BASE, and thus the lavc internals need to know the * stream time base in order to achieve the rescaling. * * That API is old and needs to be reworked to match behaviour with A/V. */ av_codec_set_pkt_timebase(dec_ctx, st->time_base); ret = avcodec_open2(dec_ctx, NULL, &codec_opts); if (ret < 0) goto end; if (ass->force_style) { char **list = NULL; char *temp = NULL; char *ptr = av_strtok(ass->force_style, ",", &temp); int i = 0; while (ptr) { av_dynarray_add(&list, &i, ptr); if (!list) { ret = AVERROR(ENOMEM); goto end; } ptr = av_strtok(NULL, ",", &temp); } av_dynarray_add(&list, &i, NULL); if (!list) { ret = AVERROR(ENOMEM); goto end; } ass_set_style_overrides(ass->library, list); av_free(list); } /* Decode subtitles and push them into the renderer (libass) */ if (dec_ctx->subtitle_header) ass_process_codec_private(ass->track, dec_ctx->subtitle_header, dec_ctx->subtitle_header_size); av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; while (av_read_frame(fmt, &pkt) >= 0) { int i, got_subtitle; AVSubtitle sub = {0}; if (pkt.stream_index == sid) { ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt); if (ret < 0) { av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n", av_err2str(ret)); } else if (got_subtitle) { const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); const int64_t duration = sub.end_display_time; for (i = 0; i < sub.num_rects; i++) { char *ass_line = sub.rects[i]->ass; if (!ass_line) break; if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,25,100)) ass_process_data(ass->track, ass_line, strlen(ass_line)); else ass_process_chunk(ass->track, ass_line, strlen(ass_line), start_time, duration); } } } av_packet_unref(&pkt); avsubtitle_free(&sub); } end: av_dict_free(&codec_opts); avcodec_close(dec_ctx); avcodec_free_context(&dec_ctx); avformat_close_input(&fmt); return ret; }
void SubtitleReader::FreeAVSubtitle(const AVSubtitle &subtitle) { avsubtitle_free(const_cast<AVSubtitle*>(&subtitle)); }
// Initialize sub from sub->avsub. static void read_sub_bitmaps(struct sd *sd, struct sub *sub) { struct MPOpts *opts = sd->opts; struct sd_lavc_priv *priv = sd->priv; AVSubtitle *avsub = &sub->avsub; MP_TARRAY_GROW(priv, sub->inbitmaps, avsub->num_rects); packer_set_size(priv->packer, avsub->num_rects); // If we blur, we want a transparent region around the bitmap data to // avoid "cut off" artifacts on the borders. bool apply_blur = opts->sub_gauss != 0.0f; int extend = apply_blur ? 5 : 0; // Assume consumers may use bilinear scaling on it (2x2 filter) int padding = 1 + extend; priv->packer->padding = padding; // For the sake of libswscale, which in some cases takes sub-rects as // source images, and wants 16 byte start pointer and stride alignment. int align = 4; for (int i = 0; i < avsub->num_rects; i++) { struct AVSubtitleRect *r = avsub->rects[i]; struct sub_bitmap *b = &sub->inbitmaps[sub->count]; if (r->type != SUBTITLE_BITMAP) { MP_ERR(sd, "unsupported subtitle type from libavcodec\n"); continue; } if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) && opts->forced_subs_only) continue; if (r->w <= 0 || r->h <= 0) continue; b->bitmap = r; // save for later (dumb hack to avoid more complexity) priv->packer->in[sub->count] = (struct pos){r->w + (align - 1), r->h}; sub->count++; } priv->packer->count = sub->count; if (packer_pack(priv->packer) < 0) { MP_ERR(sd, "Unable to pack subtitle bitmaps.\n"); sub->count = 0; } if (!sub->count) return; struct pos bb[2]; packer_get_bb(priv->packer, bb); sub->bound_w = bb[1].x; sub->bound_h = bb[1].y; if (!sub->data || sub->data->w < sub->bound_w || sub->data->h < sub->bound_h) { talloc_free(sub->data); sub->data = mp_image_alloc(IMGFMT_BGRA, priv->packer->w, priv->packer->h); if (!sub->data) { sub->count = 0; return; } talloc_steal(priv, sub->data); } for (int i = 0; i < sub->count; i++) { struct sub_bitmap *b = &sub->inbitmaps[i]; struct pos pos = priv->packer->result[i]; struct AVSubtitleRect *r = b->bitmap; #if HAVE_AV_SUBTITLE_NOPICT uint8_t **data = r->data; int *linesize = r->linesize; #else uint8_t **data = r->pict.data; int *linesize = r->pict.linesize; #endif b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; // Choose such that the extended start position is aligned. pos.x = MP_ALIGN_UP(pos.x - extend, align) + extend; b->src_x = pos.x; b->src_y = pos.y; b->stride = sub->data->stride[0]; b->bitmap = sub->data->planes[0] + pos.y * b->stride + pos.x * 4; sub->src_w = FFMAX(sub->src_w, b->x + b->w); sub->src_h = FFMAX(sub->src_h, b->y + b->h); assert(r->nb_colors > 0); assert(r->nb_colors <= 256); uint32_t pal[256] = {0}; memcpy(pal, data[1], r->nb_colors * 4); convert_pal(pal, 256, opts->sub_gray); for (int y = -padding; y < b->h + padding; y++) { uint32_t *out = (uint32_t*)((char*)b->bitmap + y * b->stride); int start = 0; for (int x = -padding; x < 0; x++) out[x] = 0; if (y >= 0 && y < b->h) { uint8_t *in = data[0] + y * linesize[0]; for (int x = 0; x < b->w; x++) *out++ = pal[*in++]; start = b->w; } for (int x = start; x < b->w + padding; x++) *out++ = 0; } b->bitmap = (char*)b->bitmap - extend * b->stride - extend * 4; b->src_x -= extend; b->src_y -= extend; b->x -= extend; b->y -= extend; b->w += extend * 2; b->h += extend * 2; if (apply_blur) mp_blur_rgba_sub_bitmap(b, opts->sub_gauss); } } static void decode(struct sd *sd, struct demux_packet *packet) { struct MPOpts *opts = sd->opts; struct sd_lavc_priv *priv = sd->priv; AVCodecContext *ctx = priv->avctx; double pts = packet->pts; double endpts = MP_NOPTS_VALUE; double duration = packet->duration; AVSubtitle sub; AVPacket pkt; // libavformat sets duration==0, even if the duration is unknown. Some files // also have actually subtitle packets with duration explicitly set to 0 // (yes, at least some of such mkv files were muxed by libavformat). // Assume there are no bitmap subs that actually use duration==0 for // hidden subtitle events. if (duration == 0) duration = -1; if (pts == MP_NOPTS_VALUE) MP_WARN(sd, "Subtitle with unknown start time.\n"); mp_set_av_packet(&pkt, packet, &priv->pkt_timebase); int got_sub; int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0 || !got_sub) return; if (sub.pts != AV_NOPTS_VALUE) pts = sub.pts / (double)AV_TIME_BASE; if (pts != MP_NOPTS_VALUE) { if (sub.end_display_time > sub.start_display_time && sub.end_display_time != UINT32_MAX) { duration = (sub.end_display_time - sub.start_display_time) / 1000.0; } pts += sub.start_display_time / 1000.0; if (duration >= 0) endpts = pts + duration; // set end time of previous sub struct sub *prev = &priv->subs[0]; if (prev->valid) { if (prev->endpts == MP_NOPTS_VALUE || prev->endpts > pts) prev->endpts = pts; if (opts->sub_fix_timing && pts - prev->endpts <= SUB_GAP_THRESHOLD) prev->endpts = pts; for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == prev->pts) { priv->seekpoints[n].endpts = prev->endpts; break; } } } // This subtitle packet only signals the end of subtitle display. if (!sub.num_rects) { avsubtitle_free(&sub); return; } } alloc_sub(priv); struct sub *current = &priv->subs[0]; current->valid = true; current->pts = pts; current->endpts = endpts; current->avsub = sub; read_sub_bitmaps(sd, current); if (pts != MP_NOPTS_VALUE) { for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == pts) goto skip; } // Set arbitrary limit as safe-guard against insane files. if (priv->num_seekpoints >= 10000) MP_TARRAY_REMOVE_AT(priv->seekpoints, priv->num_seekpoints, 0); MP_TARRAY_APPEND(priv, priv->seekpoints, priv->num_seekpoints, (struct seekpoint){.pts = pts, .endpts = endpts}); skip: ; } }
static void *ass_reader_thread(void *) { set_threadname("ass_reader_thread"); while (!sem_wait(&ass_sem)) { if (!ass_reader_running) break; ass_data *a = (ass_data *) ass_queue.pop(); if (!a) { if (!ass_reader_running) break; continue; } OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(ass_mutex); std::map<int,ASS_Track*>::iterator it = ass_map.find(a->pid); ASS_Track *track; if (it == ass_map.end()) { CFrameBuffer *fb = CFrameBuffer::getInstance(); int xres = fb->getScreenWidth(true); int yres = fb->getScreenHeight(true); if (!ass_library) { ass_library = ass_library_init(); ass_set_extract_fonts(ass_library, 1); ass_set_style_overrides(ass_library, NULL); ass_renderer = ass_renderer_init(ass_library); ass_set_frame_size(ass_renderer, xres, yres); ass_set_margins(ass_renderer, 3 * yres / 100, 3 * yres / 100, 3 * xres / 100, 3 * xres / 100); ass_set_use_margins(ass_renderer, 1); ass_set_hinting(ass_renderer, ASS_HINTING_LIGHT); ass_set_aspect_ratio(ass_renderer, 1.0, 1.0); ass_font = *sub_font_file; ass_set_fonts(ass_renderer, ass_font.c_str(), "Arial", 0, NULL, 1); } track = ass_new_track(ass_library); track->PlayResX = xres; track->PlayResY = yres; ass_size = sub_font_size; ass_set_font_scale(ass_renderer, ((double) ass_size)/ASS_CUSTOM_FONT_SIZE); if (a->c->subtitle_header) { std::string ass_hdr = ass_subtitle_header_default(); if (ass_hdr.compare((char*) a->c->subtitle_header)) { ass_process_codec_private(track, (char *) a->c->subtitle_header, a->c->subtitle_header_size); } else { // This is the FFMPEG default ASS header. Use something more suitable instead: ass_hdr = ass_subtitle_header_custom(); ass_process_codec_private(track, (char *) ass_hdr.c_str(), ass_hdr.length()); } } ass_map[a->pid] = track; if (a->pid == dvbsub_pid) ass_track = track; //fprintf(stderr, "### got subtitle track %d, subtitle header: \n---\n%s\n---\n", pid, c->subtitle_header); } else track = it->second; for (unsigned int i = 0; i < a->sub.num_rects; i++) if (a->sub.rects[i]->ass) ass_process_data(track, a->sub.rects[i]->ass, strlen(a->sub.rects[i]->ass)); avsubtitle_free(&a->sub); delete a; } ass_reader_running = false; pthread_exit(NULL); }
int SubtitleDecoder::subtitle_thread(void *arg) { VideoState *is = (VideoState*)arg; Frame *sp; int got_subtitle; double pts; int i; for (;;) { if (!(sp = is->subpq().peek_writable())) return 0; if ((got_subtitle = is->subdec(). decode_frame((AVFrame*) &sp->sub)) < 0) break; pts = 0; if (got_subtitle && sp->sub.format == 0) { if (sp->sub.pts != AV_NOPTS_VALUE) pts = sp->sub.pts / (double)AV_TIME_BASE; sp->pts = pts; sp->serial = is->subdec().pkt_serial; for (i = 0; i < sp->sub.num_rects; i++) { int in_w = sp->sub.rects[i]->w; int in_h = sp->sub.rects[i]->h; int subw = is->subdec().avctx->width ? is->subdec().avctx->width : is->decoders.viddec_width; int subh = is->subdec().avctx->height ? is->subdec().avctx->height : is->decoders.viddec_height; int out_w = is->decoders.viddec_width ? in_w * is->decoders.viddec_width / subw : in_w; int out_h = is->decoders.viddec_height ? in_h * is->decoders.viddec_height / subh : in_h; AVPicture newpic; //can not use avpicture_alloc as it is not compatible with avsubtitle_free() av_image_fill_linesizes(newpic.linesize, AV_PIX_FMT_YUVA420P, out_w); newpic.data[0] = (uint8_t*) av_malloc(newpic.linesize[0] * out_h); newpic.data[3] = (uint8_t*) av_malloc(newpic.linesize[3] * out_h); newpic.data[1] = (uint8_t*) av_malloc(newpic.linesize[1] * ((out_h+1)/2)); newpic.data[2] = (uint8_t*) av_malloc(newpic.linesize[2] * ((out_h+1)/2)); is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx, in_w, in_h, AV_PIX_FMT_PAL8, out_w, out_h, AV_PIX_FMT_YUVA420P, sws_flags, NULL, NULL, NULL); if (!is->sub_convert_ctx || !newpic.data[0] || !newpic.data[3] || !newpic.data[1] || !newpic.data[2] ) { av_log(NULL, AV_LOG_FATAL, "Cannot initialize the sub conversion context\n"); exit(1); } sws_scale(is->sub_convert_ctx, (const uint8_t* const*)sp->sub.rects[i]->pict.data, sp->sub.rects[i]->pict.linesize, 0, in_h, newpic.data, newpic.linesize); av_free(sp->sub.rects[i]->pict.data[0]); av_free(sp->sub.rects[i]->pict.data[1]); sp->sub.rects[i]->pict = newpic; sp->sub.rects[i]->w = out_w; sp->sub.rects[i]->h = out_h; sp->sub.rects[i]->x = sp->sub.rects[i]->x * out_w / in_w; sp->sub.rects[i]->y = sp->sub.rects[i]->y * out_h / in_h; } /* now we can update the picture count */ is->subpq().push(); } else if (got_subtitle) { avsubtitle_free(&sp->sub); } } return 0; }
bool FFDecSW::decodeSubtitle( const QByteArray &encoded, double pts, double pos, QMPlay2_OSD *&osd, int w, int h ) { if ( codec_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE ) return false; if ( encoded.isEmpty() ) { getFromBitmapSubsBuffer( osd, pos ); return true; } AVPacket packet; av_new_packet( &packet, encoded.size() ); memcpy( packet.data, encoded.data(), encoded.size() ); bool ret = true; int got_sub_ptr; AVSubtitle subtitle; if ( avcodec_decode_subtitle2( codec_ctx, &subtitle, &got_sub_ptr, &packet ) && got_sub_ptr ) { for ( unsigned i = 0 ; i < subtitle.num_rects ; i++ ) { AVSubtitleRect *rect = subtitle.rects[ i ]; switch ( rect->type ) { case SUBTITLE_BITMAP: { BitmapSubBuffer *buff = new BitmapSubBuffer; buff->duration = ( subtitle.end_display_time - subtitle.start_display_time ) / 1000.0; buff->pts = subtitle.start_display_time ? subtitle.start_display_time / 1000.0 : ( pts < 0.0 ? 0.0 : pts ); buff->w = av_clip( rect->w, 0, w ); buff->h = av_clip( rect->h, 0, h ); buff->x = av_clip( rect->x, 0, w - buff->w ); buff->y = av_clip( rect->y, 0, h - buff->h ); buff->bitmap.resize( ( buff->w * buff->h ) << 2 ); uint8_t *source = ( uint8_t * )rect->pict.data[ 0 ]; uint32_t *palette = ( uint32_t * )rect->pict.data[ 1 ]; uint32_t *dest = ( uint32_t * )buff->bitmap.data(); for ( int y = 0 ; y < buff->h ; y++ ) for ( int x = 0 ; x < buff->w ; x++ ) dest[ y * buff->w + x ] = palette[ source[ y * rect->pict.linesize[ 0 ] + x ] ]; if ( buff->pts <= pos ) while ( bitmapSubBuffer.size() ) delete bitmapSubBuffer.takeFirst(); bitmapSubBuffer += buff; getFromBitmapSubsBuffer( osd, pos ); } break; default: ret = false; break; } } avsubtitle_free( &subtitle ); } av_free_packet( &packet ); return ret; }
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration) { struct sd_lavc_priv *priv = sh->context; AVCodecContext *ctx = priv->avctx; AVSubtitle sub; AVPacket pkt; clear(priv); av_init_packet(&pkt); pkt.data = data; pkt.size = data_len; pkt.pts = pts * 1000; if (duration >= 0) pkt.convergence_duration = duration * 1000; int got_sub; int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0 || !got_sub) return; if (pts != MP_NOPTS_VALUE) { if (sub.end_display_time > sub.start_display_time) duration = (sub.end_display_time - sub.start_display_time) / 1000.0; pts += sub.start_display_time / 1000.0; } double endpts = MP_NOPTS_VALUE; if (pts != MP_NOPTS_VALUE && duration >= 0) endpts = pts + duration; if (vo_spudec && sub.num_rects == 0) spudec_set_paletted(vo_spudec, NULL, 0, NULL, 0, 0, 0, 0, pts, endpts); if (sub.num_rects > 0) { switch (sub.rects[0]->type) { case SUBTITLE_BITMAP: // Assume resolution heuristics only work for PGS and DVB if (!osd->support_rgba || sh->type != 'p' && sh->type != 'b') { if (!vo_spudec) vo_spudec = spudec_new_scaled(NULL, ctx->width, ctx->height, NULL, 0); old_avsub_to_spudec(sub.rects, sub.num_rects, pts, endpts); vo_osd_changed(OSDTYPE_SPU); break; } priv->inbitmaps = talloc_array(priv, struct sub_bitmap, sub.num_rects); for (int i = 0; i < sub.num_rects; i++) { struct AVSubtitleRect *r = sub.rects[i]; struct sub_bitmap *b = &priv->inbitmaps[i]; uint32_t *outbmp = talloc_size(priv->inbitmaps, r->w * r->h * 4); b->bitmap = outbmp; b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; uint8_t *inbmp = r->pict.data[0]; uint32_t *palette = (uint32_t *) r->pict.data[1]; for (int y = 0; y < r->h; y++) { for (int x = 0; x < r->w; x++) *outbmp++ = palette[*inbmp++]; inbmp += r->pict.linesize[0] - r->w; }; } priv->count = sub.num_rects; priv->endpts = endpts; break; default: mp_msg(MSGT_SUBREADER, MSGL_ERR, "sd_lavc: unsupported subtitle " "type from libavcodec\n"); break; } } avsubtitle_free(&sub); }
cDvbSubtitleBitmaps::~cDvbSubtitleBitmaps() { // dbgconverter("cDvbSubtitleBitmaps::delete: PTS: %lld rects %d\n", pts, Count()); avsubtitle_free(&sub); }
void lavc_conv_uninit(struct lavc_conv *priv) { avsubtitle_free(&priv->cur); avcodec_free_context(&priv->avctx); talloc_free(priv); }
static void decode(struct sd *sd, struct demux_packet *packet) { struct MPOpts *opts = sd->opts; struct sd_lavc_priv *priv = sd->priv; AVCodecContext *ctx = priv->avctx; double pts = packet->pts; double endpts = MP_NOPTS_VALUE; double duration = packet->duration; AVSubtitle sub; AVPacket pkt; // libavformat sets duration==0, even if the duration is unknown. Some files // also have actually subtitle packets with duration explicitly set to 0 // (yes, at least some of such mkv files were muxed by libavformat). // Assume there are no bitmap subs that actually use duration==0 for // hidden subtitle events. if (duration == 0) duration = -1; if (pts == MP_NOPTS_VALUE) MP_WARN(sd, "Subtitle with unknown start time.\n"); mp_set_av_packet(&pkt, packet, &priv->pkt_timebase); int got_sub; int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0 || !got_sub) return; if (sub.pts != AV_NOPTS_VALUE) pts = sub.pts / (double)AV_TIME_BASE; if (pts != MP_NOPTS_VALUE) { if (sub.end_display_time > sub.start_display_time && sub.end_display_time != UINT32_MAX) { duration = (sub.end_display_time - sub.start_display_time) / 1000.0; } pts += sub.start_display_time / 1000.0; if (duration >= 0) endpts = pts + duration; // set end time of previous sub struct sub *prev = &priv->subs[0]; if (prev->valid) { if (prev->endpts == MP_NOPTS_VALUE || prev->endpts > pts) prev->endpts = pts; if (opts->sub_fix_timing && pts - prev->endpts <= SUB_GAP_THRESHOLD) prev->endpts = pts; for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == prev->pts) { priv->seekpoints[n].endpts = prev->endpts; break; } } } // This subtitle packet only signals the end of subtitle display. if (!sub.num_rects) { avsubtitle_free(&sub); return; } } alloc_sub(priv); struct sub *current = &priv->subs[0]; current->valid = true; current->pts = pts; current->endpts = endpts; current->avsub = sub; MP_TARRAY_GROW(priv, current->inbitmaps, sub.num_rects); MP_TARRAY_GROW(priv, current->imgs, sub.num_rects); for (int i = 0; i < sub.num_rects; i++) { struct AVSubtitleRect *r = sub.rects[i]; struct sub_bitmap *b = ¤t->inbitmaps[current->count]; struct osd_bmp_indexed *img = ¤t->imgs[current->count]; if (r->type != SUBTITLE_BITMAP) { MP_ERR(sd, "unsupported subtitle type from libavcodec\n"); continue; } if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) && opts->forced_subs_only) continue; if (r->w <= 0 || r->h <= 0) continue; #if HAVE_AV_SUBTITLE_NOPICT uint8_t **data = r->data; int *linesize = r->linesize; #else uint8_t **data = r->pict.data; int *linesize = r->pict.linesize; #endif img->bitmap = data[0]; assert(r->nb_colors > 0); assert(r->nb_colors * 4 <= sizeof(img->palette)); memcpy(img->palette, data[1], r->nb_colors * 4); b->bitmap = img; b->stride = linesize[0]; b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; current->count++; } if (pts != MP_NOPTS_VALUE) { for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == pts) goto skip; } // Set arbitrary limit as safe-guard against insane files. if (priv->num_seekpoints >= 10000) MP_TARRAY_REMOVE_AT(priv->seekpoints, priv->num_seekpoints, 0); MP_TARRAY_APPEND(priv, priv->seekpoints, priv->num_seekpoints, (struct seekpoint){.pts = pts, .endpts = endpts}); skip: ; }
SubtitleFrame SubtitleProcessorFFmpeg::processLine(const QByteArray &data, qreal pts, qreal duration) { // AV_CODEC_ID_xxx and srt, subrip are available for ffmpeg >= 1.0. AV_CODEC_ID_xxx // TODO: what about other formats? // libav-9: packet data from demuxer contains time and but duration is 0, must decode if (duration > 0 && (!codec_ctx #if QTAV_USE_FFMPEG(LIBAVCODEC) || codec_ctx->codec_id == AV_CODEC_ID_SUBRIP #endif || codec_ctx->codec_id == AV_CODEC_ID_SRT )) { SubtitleFrame f; f.begin = pts; f.end = pts + duration; if (data.startsWith("Dialogue:")) // e.g. decoding embedded subtitles f.text = PlainText::fromAss(data.constData()); else f.text = QString::fromUtf8(data.constData(), data.size()); //utf-8 is required return f; } AVPacket packet; av_init_packet(&packet); packet.size = data.size(); packet.data = (uint8_t*)data.constData(); /* * ffmpeg <2.5: AVPakcet.data has original text including time info, decoder use that info to get correct time * "Dialogue: 0,0:00:20.21,0:00:22.96,*Default,NTP,0000,0000,0000,blablabla * ffmpeg >=2.5: AVPakcet.data changed, we have to set correct pts & duration for packet to be decoded * 16,0,*Default,NTP,0000,0000,0000,,blablabla */ const double unit = 1.0/av_q2d(codec_ctx->time_base); //time_base is deprecated, use framerate since 17085a0, check FF_API_AVCTX_TIMEBASE packet.pts = pts * unit; packet.duration = duration * unit; AVSubtitle sub; memset(&sub, 0, sizeof(sub)); int got_subtitle = 0; int ret = avcodec_decode_subtitle2(codec_ctx, &sub, &got_subtitle, &packet); if (ret < 0 || !got_subtitle) { av_free_packet(&packet); avsubtitle_free(&sub); return SubtitleFrame(); } SubtitleFrame frame; // start_display_time and duration are in ms frame.begin = pts + qreal(sub.start_display_time)/1000.0; frame.end = pts + qreal(sub.end_display_time)/1000.0; //qDebug() << QTime(0, 0, 0).addMSecs(frame.begin*1000.0) << "-" << QTime(0, 0, 0).addMSecs(frame.end*1000.0) << " fmt: " << sub.format << " pts: " << m_reader.packet().pts //sub.pts // << " rects: " << sub.num_rects << " end: " << sub.end_display_time; for (unsigned i = 0; i < sub.num_rects; i++) { switch (sub.rects[i]->type) { case SUBTITLE_ASS: //qDebug("frame: %s", sub.rects[i]->ass); frame.text.append(PlainText::fromAss(sub.rects[i]->ass)).append('\n'); break; case SUBTITLE_TEXT: //qDebug("frame: %s", sub.rects[i]->text); frame.text.append(sub.rects[i]->text).append('\n'); break; case SUBTITLE_BITMAP: break; default: break; } } av_free_packet(&packet); avsubtitle_free(&sub); return frame; }
/** * Decode subtitles from LAVC */ static void video_subtitles_lavc(media_pipe_t *mp, media_buf_t *mb, AVCodecContext *ctx) { AVSubtitle sub; int got_sub = 0, i, x, y; video_overlay_t *vo; AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = mb->mb_data; avpkt.size = mb->mb_size; if(avcodec_decode_subtitle2(ctx, &sub, &got_sub, &avpkt) < 1 || !got_sub) return; if(sub.num_rects == 0) { // Flush screen vo = calloc(1, sizeof(video_overlay_t)); vo->vo_type = VO_TIMED_FLUSH; vo->vo_start = mb->mb_pts + sub.start_display_time * 1000; video_overlay_enqueue(mp, vo); } else { for(i = 0; i < sub.num_rects; i++) { AVSubtitleRect *r = sub.rects[i]; switch(r->type) { case SUBTITLE_BITMAP: vo = calloc(1, sizeof(video_overlay_t)); vo->vo_start = mb->mb_pts + sub.start_display_time * 1000; vo->vo_stop = mb->mb_pts + sub.end_display_time * 1000; vo->vo_canvas_width = ctx->width; vo->vo_canvas_height = ctx->height; vo->vo_x = r->x; vo->vo_y = r->y; vo->vo_pixmap = pixmap_create(r->w, r->h, PIXMAP_BGR32, 0); if(vo->vo_pixmap == NULL) { free(vo); break; } const uint8_t *src = r->pict.data[0]; const uint32_t *clut = (uint32_t *)r->pict.data[1]; for(y = 0; y < r->h; y++) { uint32_t *dst = (uint32_t *)(vo->vo_pixmap->pm_pixels + y * vo->vo_pixmap->pm_linesize); for(x = 0; x < r->w; x++) *dst++ = clut[src[x]]; src += r->pict.linesize[0]; } video_overlay_enqueue(mp, vo); break; case SUBTITLE_ASS: sub_ass_render(mp, r->ass, ctx->subtitle_header, ctx->subtitle_header_size, mb->mb_font_context); break; default: break; } } } avsubtitle_free(&sub); }