mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-libstb-hal.git
synced 2025-08-26 15:02:43 +02:00
libeplayer3: cleanup
Origin commit data
------------------
Branch: master
Commit: 9e00f61071
Author: martii <m4rtii@gmx.de>
Date: 2014-03-21 (Fri, 21 Mar 2014)
------------------
No further description and justification available within origin commit message!
------------------
This commit was generated by Migit
This commit is contained in:
@@ -1,765 +0,0 @@
|
||||
/*
|
||||
* Container handling for subtitles handled by libass
|
||||
* konfetti 2010; based on code from crow
|
||||
*
|
||||
* The subtitle handling as container is not a very proper solution, in
|
||||
* a proper architecture this should be handled as subcontainer or something
|
||||
* like that. But we dont want to make more effort as necessary here ;)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* ***************************** */
|
||||
/* Includes */
|
||||
/* ***************************** */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <ass/ass.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "misc.h"
|
||||
#include "subtitle.h"
|
||||
#include "writer.h"
|
||||
|
||||
/* ***************************** */
|
||||
/* Makros/Constants */
|
||||
/* ***************************** */
|
||||
|
||||
#define ASS_DEBUG
|
||||
|
||||
#ifdef ASS_DEBUG
|
||||
|
||||
static short debug_level = 0;
|
||||
|
||||
#define ass_printf(level, fmt, x...) do { \
|
||||
if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define ass_printf(level, fmt, x...)
|
||||
#endif
|
||||
|
||||
#ifndef ASS_SILENT
|
||||
#define ass_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define ass_err(fmt, x...)
|
||||
#endif
|
||||
|
||||
/* Error Constants */
|
||||
#define cERR_CONTAINER_ASS_NO_ERROR 0
|
||||
#define cERR_CONTAINER_ASS_ERROR -1
|
||||
|
||||
#define ASS_RING_SIZE 5
|
||||
|
||||
#define ASS_FONT "/share/fonts/neutrino.ttf"
|
||||
|
||||
/* ***************************** */
|
||||
/* Types */
|
||||
/* ***************************** */
|
||||
|
||||
typedef struct ass_s {
|
||||
unsigned char *data;
|
||||
int len;
|
||||
unsigned char *extradata;
|
||||
int extralen;
|
||||
|
||||
long long int pts;
|
||||
float duration;
|
||||
} ass_t;
|
||||
|
||||
typedef struct region_s {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int w;
|
||||
unsigned int h;
|
||||
time_t undisplay;
|
||||
|
||||
struct region_s *next;
|
||||
} region_t;
|
||||
|
||||
/* ***************************** */
|
||||
/* Varaibles */
|
||||
/* ***************************** */
|
||||
|
||||
static pthread_mutex_t mutex;
|
||||
|
||||
static pthread_t PlayThread;
|
||||
static int hasPlayThreadStarted = 0;
|
||||
|
||||
static unsigned char isContainerRunning = 0;
|
||||
|
||||
static ASS_Library *ass_library;
|
||||
static ASS_Renderer *ass_renderer;
|
||||
|
||||
//static float ass_font_scale = 0.4; // was: 0.7
|
||||
//static float ass_line_spacing = 0.4; // was: 0.7
|
||||
|
||||
static unsigned int screen_width = 0;
|
||||
static unsigned int screen_height = 0;
|
||||
static uint32_t *destination = NULL;
|
||||
static int destStride = 0;
|
||||
static void (*framebufferBlit) (void) = NULL;
|
||||
|
||||
static int needsBlit = 0;
|
||||
|
||||
static ASS_Track *ass_track = NULL;
|
||||
|
||||
static region_t *firstRegion = NULL;
|
||||
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* MISC Functions */
|
||||
/* ***************************** */
|
||||
|
||||
void ass_msg_callback(int level
|
||||
__attribute__ ((unused)), const char *format,
|
||||
va_list va, void *ctx __attribute__ ((unused)))
|
||||
{
|
||||
int n;
|
||||
char *str;
|
||||
va_list dst;
|
||||
|
||||
va_copy(dst, va);
|
||||
n = vsnprintf(NULL, 0, format, va);
|
||||
if (n > 0 && (str = malloc(n + 1))) {
|
||||
vsnprintf(str, n + 1, format, dst);
|
||||
ass_printf(100, "%s\n", str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
|
||||
static void getMutex(int line)
|
||||
{
|
||||
ass_printf(150, "%d requesting mutex\n", line);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
ass_printf(150, "%d received mutex\n", line);
|
||||
}
|
||||
|
||||
static void releaseMutex(int line)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ass_printf(150, "%d released mutex\n", line);
|
||||
}
|
||||
|
||||
/* ********************************* */
|
||||
/* Region Undisplay handling */
|
||||
/* ********************************* */
|
||||
|
||||
/* release and undisplay all saved regions
|
||||
*/
|
||||
void releaseRegions()
|
||||
{
|
||||
region_t *next, *old;
|
||||
Writer_t *writer;
|
||||
|
||||
if (firstRegion == NULL)
|
||||
return;
|
||||
|
||||
writer = getDefaultFramebufferWriter();
|
||||
|
||||
if (writer == NULL) {
|
||||
ass_err("no framebuffer writer found!\n");
|
||||
}
|
||||
|
||||
next = firstRegion;
|
||||
while (next) {
|
||||
if (writer) {
|
||||
WriterFBCallData_t out;
|
||||
|
||||
ass_printf(100, "release: w %d h %d x %d y %d\n",
|
||||
next->w, next->h, next->x, next->y);
|
||||
|
||||
out.data = NULL;
|
||||
out.Width = next->w;
|
||||
out.Height = next->h;
|
||||
out.x = next->x;
|
||||
out.y = next->y;
|
||||
|
||||
out.Screen_Width = screen_width;
|
||||
out.Screen_Height = screen_height;
|
||||
out.destination = destination;
|
||||
out.destStride = destStride;
|
||||
|
||||
writer->writeData(&out);
|
||||
needsBlit = 1;
|
||||
}
|
||||
old = next;
|
||||
next = next->next;
|
||||
free(old);
|
||||
}
|
||||
|
||||
firstRegion = NULL;
|
||||
}
|
||||
|
||||
/* check for regions which should be undisplayed.
|
||||
* we are very tolerant on time here, because
|
||||
* regions are also released when new regions are
|
||||
* detected (see ETSI EN 300 743 Chapter Page Composition)
|
||||
*/
|
||||
void checkRegions()
|
||||
{
|
||||
#define cDeltaTime 2
|
||||
region_t *next, *old, *prev;
|
||||
Writer_t *writer;
|
||||
time_t now = time(NULL);
|
||||
|
||||
if (firstRegion == NULL)
|
||||
return;
|
||||
|
||||
writer = getDefaultFramebufferWriter();
|
||||
|
||||
if (!writer) {
|
||||
ass_err("no framebuffer writer found!\n");
|
||||
}
|
||||
|
||||
prev = next = firstRegion;
|
||||
while (next) {
|
||||
if (now > next->undisplay + cDeltaTime) {
|
||||
ass_printf(100, "undisplay: %ld > %ld\n", now,
|
||||
next->undisplay + cDeltaTime);
|
||||
|
||||
if (writer) {
|
||||
WriterFBCallData_t out;
|
||||
|
||||
ass_printf(100, "release: w %d h %d x %d y %d\n",
|
||||
next->w, next->h, next->x, next->y);
|
||||
|
||||
out.data = NULL;
|
||||
out.Width = next->w;
|
||||
out.Height = next->h;
|
||||
out.x = next->x;
|
||||
out.y = next->y;
|
||||
|
||||
out.Screen_Width = screen_width;
|
||||
out.Screen_Height = screen_height;
|
||||
out.destination = destination;
|
||||
out.destStride = destStride;
|
||||
|
||||
writer->writeData(&out);
|
||||
needsBlit = 1;
|
||||
}
|
||||
|
||||
old = next;
|
||||
next = prev->next = next->next;
|
||||
|
||||
if (old == firstRegion)
|
||||
firstRegion = next;
|
||||
free(old);
|
||||
} else {
|
||||
prev = next;
|
||||
next = next->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* store a display region for later release */
|
||||
void storeRegion(unsigned int x, unsigned int y, unsigned int w,
|
||||
unsigned int h, time_t undisplay)
|
||||
{
|
||||
region_t **new = &firstRegion;
|
||||
|
||||
ass_printf(100, "%d %d %d %d %ld\n", x, y, w, h, undisplay);
|
||||
|
||||
while (*new)
|
||||
new = &(*new)->next;
|
||||
|
||||
*new = malloc(sizeof(region_t));
|
||||
|
||||
(*new)->next = NULL;
|
||||
(*new)->x = x;
|
||||
(*new)->y = y;
|
||||
(*new)->w = w;
|
||||
(*new)->h = h;
|
||||
(*new)->undisplay = undisplay;
|
||||
}
|
||||
|
||||
/* **************************** */
|
||||
/* Worker Thread */
|
||||
/* **************************** */
|
||||
|
||||
static void ASSThread(Context_t * context)
|
||||
{
|
||||
char threadname[17];
|
||||
strncpy(threadname, __func__, sizeof(threadname));
|
||||
threadname[16] = 0;
|
||||
prctl(PR_SET_NAME, (unsigned long) &threadname);
|
||||
Writer_t *writer;
|
||||
|
||||
ass_printf(10, "\n");
|
||||
|
||||
while (context->playback->isCreationPhase) {
|
||||
ass_err("Thread waiting for end of init phase...\n");
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
ass_printf(10, "Running!\n");
|
||||
|
||||
writer = getDefaultFramebufferWriter();
|
||||
|
||||
if (writer == NULL) {
|
||||
ass_err("no framebuffer writer found!\n");
|
||||
}
|
||||
|
||||
while (context && context->playback && context->playback->isPlaying) {
|
||||
|
||||
//IF MOVIE IS PAUSED, WAIT
|
||||
if (context->playback->isPaused) {
|
||||
ass_printf(20, "paused\n");
|
||||
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (context->playback->isSeeking) {
|
||||
ass_printf(10, "seeking\n");
|
||||
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((isContainerRunning) && (ass_track)) {
|
||||
ASS_Image *img = NULL;
|
||||
int change = 0;
|
||||
unsigned long int playPts;
|
||||
|
||||
if (context && context->playback) {
|
||||
if (context->playback->
|
||||
Command(context, PLAYBACK_PTS, &playPts) < 0)
|
||||
continue;
|
||||
}
|
||||
//FIXME: durch den sleep bleibt die cpu usage zw. 5 und 13%, ohne
|
||||
// steigt sie bei Verwendung von subtiteln bis auf 95%.
|
||||
// ich hoffe dadurch gehen keine subtitle verloren, wenn die playPts
|
||||
// durch den sleep verschlafen wird. Besser w<>re es den n<>chsten
|
||||
// subtitel zeitpunkt zu bestimmen und solange zu schlafen.
|
||||
usleep(10000);
|
||||
if (!context->playback->mayWriteToFramebuffer)
|
||||
continue;
|
||||
getMutex(__LINE__);
|
||||
checkRegions();
|
||||
|
||||
if (ass_renderer && ass_track)
|
||||
img =
|
||||
ass_render_frame(ass_renderer, ass_track,
|
||||
playPts / 90.0, &change);
|
||||
|
||||
ass_printf(150, "img %p pts %lu %f\n", img, playPts,
|
||||
playPts / 90.0);
|
||||
|
||||
if (img && ass_renderer && ass_track) {
|
||||
/* the spec says, that if a new set of regions is present
|
||||
* the complete display switches to the new state. So lets
|
||||
* release the old regions on display.
|
||||
*/
|
||||
if (change != 0)
|
||||
releaseRegions();
|
||||
|
||||
time_t now = time(NULL);
|
||||
time_t undisplay = now + 10;
|
||||
|
||||
if (ass_track && ass_track->events)
|
||||
undisplay =
|
||||
now + (ass_track->events->Duration + 500) / 90000;
|
||||
|
||||
ASS_Image *it;
|
||||
int x0 = screen_width - 1;
|
||||
int y0 = screen_height - 1;
|
||||
int x1 = 0;
|
||||
int y1 = 0;
|
||||
for (it = img; it; it = it->next) {
|
||||
if (it->w && it->h) {
|
||||
if (it->dst_x < x0)
|
||||
x0 = it->dst_x;
|
||||
if (it->dst_y < y0)
|
||||
y0 = it->dst_y;
|
||||
if (it->dst_x + it->w > x1)
|
||||
x1 = it->dst_x + it->w;
|
||||
if (it->dst_y + it->h > y1)
|
||||
y1 = it->dst_y + it->h;
|
||||
}
|
||||
}
|
||||
if (x1 > 0 && y1 > 0) {
|
||||
x1++;
|
||||
y1++;
|
||||
int x, y;
|
||||
uint32_t *dst =
|
||||
destination + y0 * destStride / sizeof(uint32_t) +
|
||||
x0;
|
||||
int destStrideDiff =
|
||||
destStride / sizeof(uint32_t) - (x1 - x0);
|
||||
for (y = y0; y < y1; y++) {
|
||||
for (x = x0; x < x1; x++)
|
||||
*dst++ = 0x80808080;
|
||||
dst += destStrideDiff;
|
||||
}
|
||||
storeRegion(x0, y0, x1 - x0, y1 - y0, undisplay);
|
||||
needsBlit = 1;
|
||||
}
|
||||
|
||||
while (context && context->playback
|
||||
&& context->playback->isPlaying && img) {
|
||||
WriterFBCallData_t out;
|
||||
|
||||
ass_printf(100,
|
||||
"w %d h %d s %d x %d y %d c %d chg %d now %ld und %ld\n",
|
||||
img->w, img->h, img->stride, img->dst_x,
|
||||
img->dst_y, img->color, change, now,
|
||||
undisplay);
|
||||
|
||||
/* api docu said w and h can be zero which
|
||||
* means image should not be rendered
|
||||
*/
|
||||
if ((img->w != 0) && (img->h != 0) && writer) {
|
||||
out.data = img->bitmap;
|
||||
out.Width = img->w;
|
||||
out.Height = img->h;
|
||||
out.Stride = img->stride;
|
||||
out.x = img->dst_x;
|
||||
out.y = img->dst_y;
|
||||
out.color = img->color;
|
||||
|
||||
out.Screen_Width = screen_width;
|
||||
out.Screen_Height = screen_height;
|
||||
out.destination = destination;
|
||||
out.destStride = destStride;
|
||||
|
||||
if (context && context->playback
|
||||
&& context->playback->isPlaying && writer)
|
||||
writer->writeData(&out);
|
||||
|
||||
needsBlit = 1;
|
||||
}
|
||||
|
||||
/* Next image */
|
||||
img = img->next;
|
||||
}
|
||||
}
|
||||
releaseMutex(__LINE__);
|
||||
} else {
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
if (needsBlit && framebufferBlit)
|
||||
framebufferBlit();
|
||||
needsBlit = 0;
|
||||
|
||||
/* cleanup no longer used but not overwritten regions */
|
||||
getMutex(__LINE__);
|
||||
checkRegions();
|
||||
releaseMutex(__LINE__);
|
||||
} /* while */
|
||||
|
||||
if (needsBlit && framebufferBlit)
|
||||
framebufferBlit();
|
||||
needsBlit = 0;
|
||||
|
||||
hasPlayThreadStarted = 0;
|
||||
|
||||
ass_printf(10, "terminating\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* **************************** */
|
||||
/* Container part for ass */
|
||||
/* **************************** */
|
||||
|
||||
int container_ass_init(Context_t * context)
|
||||
{
|
||||
SubtitleOutputDef_t output;
|
||||
|
||||
ass_printf(10, ">\n");
|
||||
|
||||
ass_library = ass_library_init();
|
||||
|
||||
if (!ass_library) {
|
||||
ass_err("ass_library_init failed!\n");
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
if (debug_level >= 100)
|
||||
ass_set_message_cb(ass_library, ass_msg_callback, NULL);
|
||||
|
||||
ass_set_extract_fonts(ass_library, 1);
|
||||
ass_set_style_overrides(ass_library, NULL);
|
||||
|
||||
ass_renderer = ass_renderer_init(ass_library);
|
||||
|
||||
if (!ass_renderer) {
|
||||
ass_err("ass_renderer_init failed!\n");
|
||||
|
||||
if (ass_library)
|
||||
ass_library_done(ass_library);
|
||||
ass_library = NULL;
|
||||
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
context->output->subtitle->Command(context, OUTPUT_GET_SUBTITLE_OUTPUT,
|
||||
&output);
|
||||
|
||||
screen_width = output.screen_width;
|
||||
screen_height = output.screen_height;
|
||||
destination = output.destination;
|
||||
destStride = output.destStride;
|
||||
framebufferBlit = output.framebufferBlit;
|
||||
|
||||
ass_printf(10, "width %d, height %d\n", screen_width, screen_height);
|
||||
|
||||
ass_set_frame_size(ass_renderer, screen_width, screen_height);
|
||||
ass_set_margins(ass_renderer, (int) (0.03 * screen_height),
|
||||
(int) (0.03 * screen_height),
|
||||
(int) (0.03 * screen_width),
|
||||
(int) (0.03 * screen_width));
|
||||
|
||||
ass_set_use_margins(ass_renderer, 1);
|
||||
// ass_set_font_scale(ass_renderer, (ass_font_scale * screen_height) / 240.0);
|
||||
|
||||
ass_set_hinting(ass_renderer, ASS_HINTING_LIGHT);
|
||||
// ass_set_line_spacing(ass_renderer, (ass_line_spacing * screen_height) / 240.0);
|
||||
ass_set_fonts(ass_renderer, ASS_FONT, "Arial", 0, NULL, 1);
|
||||
|
||||
ass_set_aspect_ratio(ass_renderer, 1.0, 1.0);
|
||||
|
||||
isContainerRunning = 1;
|
||||
|
||||
return cERR_CONTAINER_ASS_NO_ERROR;
|
||||
}
|
||||
|
||||
int container_ass_process_data(Context_t * context
|
||||
__attribute__ ((unused)),
|
||||
SubtitleData_t * data)
|
||||
{
|
||||
int first_kiss;
|
||||
|
||||
ass_printf(20, ">\n");
|
||||
|
||||
if (!isContainerRunning) {
|
||||
ass_err("Container not running\n");
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
if (ass_track == NULL) {
|
||||
first_kiss = 1;
|
||||
ass_track = ass_new_track(ass_library);
|
||||
|
||||
if (ass_track == NULL) {
|
||||
ass_err("error creating ass_track\n");
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
ass_track->PlayResX = screen_width;
|
||||
ass_track->PlayResY = screen_height;
|
||||
}
|
||||
|
||||
if ((data->extradata) && (first_kiss)) {
|
||||
ass_printf(30, "processing private %d bytes\n", data->extralen);
|
||||
ass_process_codec_private(ass_track, (char *) data->extradata,
|
||||
data->extralen);
|
||||
ass_printf(30, "processing private done\n");
|
||||
}
|
||||
|
||||
if (data->data) {
|
||||
ass_printf(30, "processing data %d bytes\n", data->len);
|
||||
ass_process_data(ass_track, (char *) data->data, data->len);
|
||||
ass_printf(30, "processing data done\n");
|
||||
}
|
||||
|
||||
return cERR_CONTAINER_ASS_NO_ERROR;
|
||||
}
|
||||
|
||||
static int container_ass_stop(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int ret = cERR_CONTAINER_ASS_NO_ERROR;
|
||||
int wait_time = 20;
|
||||
Writer_t *writer;
|
||||
|
||||
ass_printf(10, "\n");
|
||||
|
||||
if (!isContainerRunning) {
|
||||
ass_err("Container not running\n");
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
while ((hasPlayThreadStarted != 0) && (--wait_time) > 0) {
|
||||
ass_printf(10,
|
||||
"Waiting for ass thread to terminate itself, will try another %d times\n",
|
||||
wait_time);
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
if (wait_time == 0) {
|
||||
ass_err("Timeout waiting for thread!\n");
|
||||
|
||||
ret = cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
releaseRegions();
|
||||
|
||||
if (ass_track)
|
||||
ass_free_track(ass_track);
|
||||
|
||||
ass_track = NULL;
|
||||
|
||||
if (ass_renderer)
|
||||
ass_renderer_done(ass_renderer);
|
||||
ass_renderer = NULL;
|
||||
|
||||
if (ass_library)
|
||||
ass_library_done(ass_library);
|
||||
ass_library = NULL;
|
||||
|
||||
isContainerRunning = 0;
|
||||
|
||||
hasPlayThreadStarted = 0;
|
||||
|
||||
writer = getDefaultFramebufferWriter();
|
||||
|
||||
if (writer) {
|
||||
writer->reset();
|
||||
}
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
ass_printf(10, "ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int container_ass_switch_subtitle(Context_t * context, int *arg
|
||||
__attribute__ ((unused)))
|
||||
{
|
||||
int error;
|
||||
int ret = cERR_CONTAINER_ASS_NO_ERROR;
|
||||
pthread_attr_t attr;
|
||||
|
||||
ass_printf(10, "\n");
|
||||
|
||||
if (!isContainerRunning) {
|
||||
ass_err("Container not running\n");
|
||||
return cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
if (context && context->playback && context->playback->isPlaying) {
|
||||
ass_printf(10, "is Playing\n");
|
||||
} else {
|
||||
ass_printf(10, "is NOT Playing\n");
|
||||
}
|
||||
|
||||
if (hasPlayThreadStarted == 0) {
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if ((error =
|
||||
pthread_create(&PlayThread, &attr, (void *) &ASSThread,
|
||||
context)) != 0) {
|
||||
ass_printf(10, "Error creating thread, error:%d:%s\n", error,
|
||||
strerror(error));
|
||||
|
||||
hasPlayThreadStarted = 0;
|
||||
ret = cERR_CONTAINER_ASS_ERROR;
|
||||
} else {
|
||||
ass_printf(10, "Created thread\n");
|
||||
|
||||
hasPlayThreadStarted = 1;
|
||||
}
|
||||
} else {
|
||||
ass_printf(10, "A thread already exists!\n");
|
||||
|
||||
ret = cERR_CONTAINER_ASS_ERROR;
|
||||
}
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
releaseRegions();
|
||||
|
||||
/* free the track so extradata will be written next time
|
||||
* process_data is called.
|
||||
*/
|
||||
if (ass_track)
|
||||
ass_free_track(ass_track);
|
||||
|
||||
ass_track = NULL;
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
ass_printf(10, "exiting with value %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int Command(void *_context, ContainerCmd_t command, void *argument)
|
||||
{
|
||||
Context_t *context = (Context_t *) _context;
|
||||
int ret = cERR_CONTAINER_ASS_NO_ERROR;
|
||||
|
||||
ass_printf(50, "Command %d\n", command);
|
||||
|
||||
switch (command) {
|
||||
case CONTAINER_INIT:{
|
||||
ret = container_ass_init(context);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_STOP:{
|
||||
ret = container_ass_stop(context);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_SWITCH_SUBTITLE:{
|
||||
ret = container_ass_switch_subtitle(context, (int *) argument);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_DATA:{
|
||||
SubtitleData_t *data = (SubtitleData_t *) argument;
|
||||
ret = container_ass_process_data(context, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ass_err("ContainerCmd %d not supported!\n", command);
|
||||
ret = cERR_CONTAINER_ASS_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ass_printf(50, "exiting with value %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *ASS_Capabilities[] = { "ass", NULL };
|
||||
|
||||
Container_t ASSContainer = {
|
||||
"ASS",
|
||||
&Command,
|
||||
ASS_Capabilities
|
||||
};
|
@@ -1,517 +0,0 @@
|
||||
/*
|
||||
* subtitle handling for srt files.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* ***************************** */
|
||||
/* Includes */
|
||||
/* ***************************** */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "misc.h"
|
||||
#include "subtitle.h"
|
||||
|
||||
/* ***************************** */
|
||||
/* Makros/Constants */
|
||||
/* ***************************** */
|
||||
|
||||
#define SRT_DEBUG
|
||||
|
||||
#ifdef SRT_DEBUG
|
||||
|
||||
static short debug_level = 0;
|
||||
|
||||
#define srt_printf(level, fmt, x...) do { \
|
||||
if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define srt_printf(level, fmt, x...)
|
||||
#endif
|
||||
|
||||
#ifndef SRT_SILENT
|
||||
#define srt_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define srt_err(fmt, x...)
|
||||
#endif
|
||||
|
||||
/* Error Constants */
|
||||
#define cERR_SRT_NO_ERROR 0
|
||||
#define cERR_SRT_ERROR -1
|
||||
|
||||
#define TRACKWRAP 20
|
||||
#define MAXLINELENGTH 80
|
||||
|
||||
static const char FILENAME[] = "text_srt.c";
|
||||
|
||||
/* ***************************** */
|
||||
/* Types */
|
||||
/* ***************************** */
|
||||
|
||||
typedef struct {
|
||||
char *File;
|
||||
int Id;
|
||||
} SrtTrack_t;
|
||||
|
||||
static pthread_t thread_sub;
|
||||
|
||||
/* ***************************** */
|
||||
/* Varaibles */
|
||||
/* ***************************** */
|
||||
|
||||
static SrtTrack_t *Tracks;
|
||||
static int TrackCount = 0;
|
||||
static int CurrentTrack = -1; //no as default.
|
||||
|
||||
FILE *fsub = NULL;
|
||||
|
||||
static int hasThreadStarted = 0;
|
||||
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* MISC Functions */
|
||||
/* ***************************** */
|
||||
|
||||
void data_to_manager(Context_t * context, char *Text,
|
||||
unsigned long long int Pts, double Duration)
|
||||
{
|
||||
srt_printf(20, "--> Text= \"%s\"\n", Text);
|
||||
|
||||
if (context && context->playback && context->playback->isPlaying) {
|
||||
int sl = strlen(Text) - 1;
|
||||
while (sl && (Text[sl] == '\n' || Text[sl] == '\r'))
|
||||
Text[sl--] = '\0'; /*Delete last \n or \r */
|
||||
unsigned char *line = text_to_ass(Text, Pts, Duration);
|
||||
srt_printf(50, "Sub text is %s\n", Text);
|
||||
srt_printf(50, "Sub line is %s\n", line);
|
||||
SubtitleData_t data;
|
||||
data.data = line;
|
||||
data.len = strlen((char *) line);
|
||||
data.extradata = (unsigned char *) DEFAULT_ASS_HEAD;
|
||||
data.extralen = strlen(DEFAULT_ASS_HEAD);
|
||||
data.pts = Pts * 90;
|
||||
data.duration = Duration;
|
||||
|
||||
context->container->assContainer->Command(context, CONTAINER_DATA,
|
||||
&data);
|
||||
free(line);
|
||||
}
|
||||
|
||||
srt_printf(20, "<-- Text= \"%s\"\n", Text);
|
||||
}
|
||||
|
||||
/* ***************************** */
|
||||
/* Worker Thread */
|
||||
/* ***************************** */
|
||||
|
||||
static void *SrtSubtitleThread(void *data)
|
||||
{
|
||||
int pos = 0;
|
||||
char Data[MAXLINELENGTH];
|
||||
unsigned long long int Pts = 0;
|
||||
double Duration = 0;
|
||||
char *Text = NULL;
|
||||
|
||||
Context_t *context = (Context_t *) data;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
while (context && context->playback && context->playback->isPlaying
|
||||
&& fsub && fgets(Data, MAXLINELENGTH, fsub)) {
|
||||
srt_printf(20, "pos=%d\n", pos);
|
||||
|
||||
if (pos == 0) {
|
||||
if (Data[0] == '\n' || Data[0] == '\0'
|
||||
|| Data[0] == 13 /* ^M */ )
|
||||
continue; /* Empty line not allowed here */
|
||||
pos++;
|
||||
} else if (pos == 1) {
|
||||
int ret, horIni, minIni, secIni, milIni, horFim, minFim,
|
||||
secFim, milFim;
|
||||
|
||||
ret =
|
||||
sscanf(Data, "%d:%d:%d,%d --> %d:%d:%d,%d", &horIni,
|
||||
&minIni, &secIni, &milIni, &horFim, &minFim,
|
||||
&secFim, &milFim);
|
||||
if (ret != 8)
|
||||
continue; /* Data is not in correct format */
|
||||
|
||||
Pts = (horIni * 3600 + minIni * 60 + secIni) * 1000 + milIni;
|
||||
Duration =
|
||||
((horFim * 3600 + minFim * 60 + secFim) * 1000 + milFim -
|
||||
Pts) / 1000.0;
|
||||
|
||||
pos++;
|
||||
|
||||
} else if (pos == 2) {
|
||||
srt_printf(20, "Data[0] = %d \'%c\'\n", Data[0], Data[0]);
|
||||
|
||||
if (Data[0] == '\n' || Data[0] == '\0'
|
||||
|| Data[0] == 13 /* ^M */ ) {
|
||||
if (Text == NULL)
|
||||
Text = strdup(" \n"); /* better to display at least one character */
|
||||
|
||||
/*Hellmaster 1024 since we have waited, we have to check if we are still paying */
|
||||
data_to_manager(context, Text, Pts, Duration);
|
||||
free(Text);
|
||||
Text = NULL;
|
||||
pos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Text) {
|
||||
Text = strdup(Data);
|
||||
} else {
|
||||
int length =
|
||||
strlen(Text) /* \0 -> \n */ +strlen(Data) +
|
||||
2 /* \0 */ ;
|
||||
char *tmpText = Text;
|
||||
Text = (char *) malloc(length);
|
||||
|
||||
strcpy(Text, tmpText);
|
||||
strcat(Text, Data);
|
||||
free(tmpText);
|
||||
}
|
||||
}
|
||||
} /* while */
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
if (Text) {
|
||||
data_to_manager(context, Text, Pts, Duration);
|
||||
free(Text);
|
||||
Text = NULL;
|
||||
}
|
||||
|
||||
srt_printf(0, "thread has ended\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ***************************** */
|
||||
/* Functions */
|
||||
/* ***************************** */
|
||||
|
||||
static void SrtManagerAdd(Context_t * context
|
||||
__attribute__ ((unused)), SrtTrack_t track)
|
||||
{
|
||||
srt_printf(10, "%s %d\n", track.File, track.Id);
|
||||
|
||||
if (Tracks == NULL) {
|
||||
Tracks = malloc(sizeof(SrtTrack_t) * TRACKWRAP);
|
||||
}
|
||||
|
||||
if (TrackCount < TRACKWRAP) {
|
||||
Tracks[TrackCount].File = strdup(track.File);
|
||||
Tracks[TrackCount].Id = track.Id;
|
||||
TrackCount++;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static char **SrtManagerList(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
char **tracklist = NULL;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
if (Tracks != NULL) {
|
||||
char help[256];
|
||||
int i = 0, j = 0;
|
||||
|
||||
tracklist = malloc(sizeof(char *) * ((TrackCount * 2) + 1));
|
||||
|
||||
for (i = 0, j = 0; i < TrackCount; i++, j += 2) {
|
||||
|
||||
sprintf(help, "%d", Tracks[i].Id);
|
||||
tracklist[j] = strdup(help);
|
||||
tracklist[j + 1] = strdup(Tracks[i].File);
|
||||
}
|
||||
tracklist[j] = NULL;
|
||||
}
|
||||
|
||||
return tracklist;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SrtManagerDel(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
if (Tracks != NULL) {
|
||||
for (i = 0; i < TrackCount; i++) {
|
||||
if (Tracks[i].File != NULL)
|
||||
free(Tracks[i].File);
|
||||
Tracks[i].File = NULL;
|
||||
}
|
||||
free(Tracks);
|
||||
Tracks = NULL;
|
||||
}
|
||||
|
||||
TrackCount = 0;
|
||||
CurrentTrack = -1;
|
||||
}
|
||||
|
||||
|
||||
static int SrtGetSubtitle(Context_t * context, char *Filename)
|
||||
{
|
||||
struct dirent *dirzeiger;
|
||||
DIR *dir;
|
||||
int i = TEXTSRTOFFSET;
|
||||
char *copyFilename = NULL;
|
||||
char *FilenameExtension = NULL;
|
||||
char *FilenameFolder = NULL;
|
||||
char *FilenameShort = NULL;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
if (Filename == NULL) {
|
||||
srt_err("Filename NULL\n");
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
|
||||
srt_printf(10, "file: %s\n", Filename);
|
||||
|
||||
copyFilename = strdup(Filename);
|
||||
|
||||
if (copyFilename == NULL) {
|
||||
srt_err("copyFilename NULL\n");
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
|
||||
FilenameFolder = dirname(copyFilename);
|
||||
|
||||
srt_printf(10, "folder: %s\n", FilenameFolder);
|
||||
|
||||
FilenameExtension = getExtension(copyFilename);
|
||||
|
||||
if (FilenameExtension == NULL) {
|
||||
srt_err("FilenameExtension NULL\n");
|
||||
free(copyFilename);
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
|
||||
srt_printf(10, "ext: %s\n", FilenameExtension);
|
||||
|
||||
FilenameShort = basename(copyFilename);
|
||||
|
||||
/* cut extension */
|
||||
FilenameShort[strlen(FilenameShort) - strlen(FilenameExtension) - 1] =
|
||||
'\0';
|
||||
|
||||
srt_printf(10, "basename: %s\n", FilenameShort);
|
||||
srt_printf(10, "%s\n%s | %s | %s\n", copyFilename, FilenameFolder,
|
||||
FilenameShort, FilenameExtension);
|
||||
|
||||
if ((dir = opendir(FilenameFolder)) != NULL) {
|
||||
while ((dirzeiger = readdir(dir)) != NULL) {
|
||||
char subtitleFilename[PATH_MAX];
|
||||
char *subtitleExtension = NULL;
|
||||
|
||||
srt_printf(20, "%s\n", (*dirzeiger).d_name);
|
||||
|
||||
strcpy(subtitleFilename, (*dirzeiger).d_name);
|
||||
|
||||
// Extension of Relativ Subtitle File Name
|
||||
subtitleExtension = getExtension(subtitleFilename);
|
||||
|
||||
if (subtitleExtension == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(subtitleExtension, "srt") != 0)
|
||||
continue;
|
||||
|
||||
/* cut extension */
|
||||
subtitleFilename[strlen(subtitleFilename) -
|
||||
strlen(subtitleExtension) - 1] = '\0';
|
||||
|
||||
srt_printf(10, "%s %s\n", FilenameShort, subtitleFilename);
|
||||
|
||||
if (strncmp
|
||||
(FilenameShort, subtitleFilename,
|
||||
strlen(FilenameShort)) == 0) {
|
||||
char absSubtitleFileName[PATH_MAX];
|
||||
/* found something of interest, so now make an absolut path name */
|
||||
|
||||
sprintf(absSubtitleFileName, "%s/%s.%s", FilenameFolder,
|
||||
subtitleFilename, subtitleExtension);
|
||||
|
||||
srt_printf(10, "SRT: %s [%s]\n", subtitleExtension,
|
||||
subtitleFilename);
|
||||
srt_printf(10, "\t->%s\n", absSubtitleFileName);
|
||||
|
||||
SrtTrack_t SrtSubtitle = {
|
||||
absSubtitleFileName,
|
||||
i,
|
||||
};
|
||||
|
||||
SrtManagerAdd(context, SrtSubtitle);
|
||||
|
||||
Track_t Subtitle;
|
||||
memset(&Subtitle, 0, sizeof(Subtitle));
|
||||
Subtitle.Name = subtitleExtension;
|
||||
Subtitle.Encoding = "S_TEXT/SRT";
|
||||
Subtitle.Id = i++;
|
||||
Subtitle.is_static = 1;
|
||||
context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle);
|
||||
}
|
||||
} /* while */
|
||||
closedir(dir);
|
||||
}
|
||||
/* if dir */
|
||||
free(copyFilename);
|
||||
|
||||
srt_printf(10, "<\n");
|
||||
return cERR_SRT_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SrtOpenSubtitle(Context_t * context
|
||||
__attribute__ ((unused)), int pid)
|
||||
{
|
||||
srt_printf(10, "\n");
|
||||
|
||||
if (pid < TEXTSRTOFFSET) {
|
||||
srt_err("trackid not for us\n");
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
|
||||
int trackid;
|
||||
for (trackid = 0; trackid < TrackCount; trackid++)
|
||||
if (Tracks[trackid].Id == pid)
|
||||
break;
|
||||
|
||||
if (trackid == TrackCount) {
|
||||
srt_err("trackid not for us\n");
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
|
||||
srt_printf(10, "%s\n", Tracks[trackid].File);
|
||||
|
||||
fsub = fopen(Tracks[trackid].File, "rb");
|
||||
|
||||
srt_printf(10, "%s\n", fsub ? "fsub!=NULL" : "fsub==NULL");
|
||||
|
||||
if (!fsub) {
|
||||
srt_err("cannot open file %s\n", Tracks[trackid].File);
|
||||
return cERR_SRT_ERROR;
|
||||
}
|
||||
return cERR_SRT_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SrtCloseSubtitle(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
srt_printf(10, "\n");
|
||||
|
||||
if (fsub)
|
||||
fclose(fsub);
|
||||
|
||||
/* this closes the thread! */
|
||||
fsub = NULL;
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
return cERR_SRT_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SrtSwitchSubtitle(Context_t * context, int *arg)
|
||||
{
|
||||
int ret = cERR_SRT_NO_ERROR;
|
||||
|
||||
srt_printf(10, "arg:%d\n", *arg);
|
||||
|
||||
ret = SrtCloseSubtitle(context);
|
||||
|
||||
if (((ret |= SrtOpenSubtitle(context, *arg)) == cERR_SRT_NO_ERROR)
|
||||
&& (!hasThreadStarted)) {
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thread_sub, &attr, &SrtSubtitleThread, context);
|
||||
|
||||
hasThreadStarted = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int SrtDel(Context_t * context)
|
||||
{
|
||||
int ret = cERR_SRT_NO_ERROR;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
ret = SrtCloseSubtitle(context);
|
||||
SrtManagerDel(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int Command(void *_context, ContainerCmd_t command, void *argument)
|
||||
{
|
||||
Context_t *context = (Context_t *) _context;
|
||||
int ret = cERR_SRT_NO_ERROR;
|
||||
|
||||
srt_printf(10, "\n");
|
||||
|
||||
switch (command) {
|
||||
case CONTAINER_INIT:{
|
||||
char *filename = (char *) argument;
|
||||
SrtDel(context);
|
||||
ret = SrtGetSubtitle(context, filename);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_DEL:{
|
||||
ret = SrtDel(context);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_SWITCH_SUBTITLE:{
|
||||
ret = SrtSwitchSubtitle(context, (int *) argument);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
srt_err("ConatinerCmd not supported! %d\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
srt_printf(10, "ret = %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *SrtCapabilities[] = { "srt", NULL };
|
||||
|
||||
Container_t SrtContainer = {
|
||||
"SRT",
|
||||
&Command,
|
||||
SrtCapabilities
|
||||
};
|
@@ -1,511 +0,0 @@
|
||||
/*
|
||||
* subtitle handling for ssa files.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* ***************************** */
|
||||
/* Includes */
|
||||
/* ***************************** */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "misc.h"
|
||||
#include "subtitle.h"
|
||||
|
||||
/* ***************************** */
|
||||
/* Makros/Constants */
|
||||
/* ***************************** */
|
||||
|
||||
#define SSA_DEBUG
|
||||
|
||||
#ifdef SSA_DEBUG
|
||||
|
||||
static short debug_level = 10;
|
||||
|
||||
#define ssa_printf(level, fmt, x...) do { \
|
||||
if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define ssa_printf(level, fmt, x...)
|
||||
#endif
|
||||
|
||||
#ifndef SSA_SILENT
|
||||
#define ssa_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define ssa_err(fmt, x...)
|
||||
#endif
|
||||
|
||||
/* Error Constants */
|
||||
#define cERR_SSA_NO_ERROR 0
|
||||
#define cERR_SSA_ERROR -1
|
||||
|
||||
#define TRACKWRAP 20
|
||||
#define MAXLINELENGTH 1000
|
||||
|
||||
static const char FILENAME[] = "text_ssa.c";
|
||||
|
||||
//Buffer size used in getLine function. Do not set to value less than 1 !!!
|
||||
#define SSA_BUFFER_SIZE 14
|
||||
|
||||
/* ***************************** */
|
||||
/* Types */
|
||||
/* ***************************** */
|
||||
|
||||
typedef struct {
|
||||
char *File;
|
||||
int Id;
|
||||
} SsaTrack_t;
|
||||
|
||||
/* ***************************** */
|
||||
/* Varaibles */
|
||||
/* ***************************** */
|
||||
|
||||
static pthread_t thread_sub;
|
||||
|
||||
static SsaTrack_t *Tracks;
|
||||
static int TrackCount = 0;
|
||||
FILE *fssa = NULL;
|
||||
|
||||
static int hasThreadStarted = 0;
|
||||
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* MISC Functions */
|
||||
/* ***************************** */
|
||||
char *SSAgetLine()
|
||||
{
|
||||
char *strAux = NULL, *strInput;
|
||||
char c[SSA_BUFFER_SIZE];
|
||||
int ch;
|
||||
int k, tam, tamAux;
|
||||
|
||||
k = tamAux = 0;
|
||||
|
||||
if (SSA_BUFFER_SIZE > 0) {
|
||||
|
||||
strInput = (char *) malloc(1 * sizeof(char));
|
||||
strInput[0] = '\0';
|
||||
|
||||
while (tamAux != 1) {
|
||||
|
||||
if ((ch = fgetc(fssa)) != EOF) {
|
||||
ungetc(ch, fssa);
|
||||
fgets(c, SSA_BUFFER_SIZE, fssa);
|
||||
strAux = (char *) strchr(c, '\n');
|
||||
tam = strlen(c);
|
||||
if (strAux != NULL) {
|
||||
tamAux = strlen(strAux);
|
||||
tam--;
|
||||
}
|
||||
|
||||
k = k + tam;
|
||||
strInput =
|
||||
(char *) realloc(strInput, (k + 1) * sizeof(char));
|
||||
|
||||
if (k != tam)
|
||||
strncat(strInput, c, tam);
|
||||
else
|
||||
strncpy(strInput, c, tam);
|
||||
|
||||
strInput[k] = '\0';
|
||||
|
||||
} else {
|
||||
tamAux = 1;
|
||||
fclose(fssa);
|
||||
fssa = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return strInput;
|
||||
|
||||
}
|
||||
|
||||
/* ***************************** */
|
||||
/* Worker Thread */
|
||||
/* ***************************** */
|
||||
static void *SsaSubtitleThread(void *Data)
|
||||
{
|
||||
Context_t *context = (Context_t *) Data;
|
||||
char *head = malloc(sizeof(char) * 1);
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
head[0] = '\0';
|
||||
|
||||
|
||||
while (context && context->playback && context->playback->isPlaying
|
||||
&& fssa) {
|
||||
char *line = NULL;
|
||||
|
||||
do {
|
||||
line = SSAgetLine();
|
||||
if (strncmp(line, "Dialogue: ", 10)) {
|
||||
int head_len = strlen(head);
|
||||
int line_len = strlen(line);
|
||||
head = realloc(head, line_len + head_len + 2);
|
||||
memcpy(head + head_len, line, sizeof(char) * line_len + 1);
|
||||
head[head_len + line_len] = '\n';
|
||||
head[head_len + line_len + 1] = '\0';
|
||||
}
|
||||
} while (strncmp(line, "Dialogue: ", 10) != 0 && fssa);
|
||||
|
||||
/*Hellmaster 1024 since we have waited, we have to check if we are still paying */
|
||||
if (context && context->playback && context->playback->isPlaying) {
|
||||
SubtitleData_t data;
|
||||
|
||||
data.data = (unsigned char *) line;
|
||||
data.len = strlen(line);
|
||||
data.extradata = (unsigned char *) head;
|
||||
data.extralen = strlen(head);
|
||||
data.pts = 0;
|
||||
data.duration = 0.0;
|
||||
context->container->assContainer->Command(context,
|
||||
CONTAINER_DATA,
|
||||
&data);
|
||||
}
|
||||
free(line);
|
||||
line = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
if (head) {
|
||||
free(head);
|
||||
head = NULL;
|
||||
}
|
||||
ssa_printf(0, "thread has ended\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ***************************** */
|
||||
/* Functions */
|
||||
/* ***************************** */
|
||||
|
||||
static void SsaManagerAdd(Context_t * context
|
||||
__attribute__ ((unused)), SsaTrack_t track)
|
||||
{
|
||||
ssa_printf(10, "%s %d\n", track.File, track.Id);
|
||||
|
||||
if (Tracks == NULL) {
|
||||
Tracks = malloc(sizeof(SsaTrack_t) * TRACKWRAP);
|
||||
}
|
||||
|
||||
if (TrackCount < TRACKWRAP) {
|
||||
Tracks[TrackCount].File = strdup(track.File);
|
||||
Tracks[TrackCount].Id = track.Id;
|
||||
TrackCount++;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static char **SsaManagerList(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
char **tracklist = NULL;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
if (Tracks != NULL) {
|
||||
char help[256];
|
||||
int i = 0, j = 0;
|
||||
|
||||
tracklist = malloc(sizeof(char *) * ((TrackCount * 2) + 1));
|
||||
|
||||
for (i = 0, j = 0; i < TrackCount; i++, j += 2) {
|
||||
|
||||
sprintf(help, "%d", Tracks[i].Id);
|
||||
tracklist[j] = strdup(help);
|
||||
tracklist[j + 1] = strdup(Tracks[i].File);
|
||||
}
|
||||
tracklist[j] = NULL;
|
||||
}
|
||||
|
||||
return tracklist;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SsaManagerDel(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
if (Tracks != NULL) {
|
||||
for (i = 0; i < TrackCount; i++) {
|
||||
if (Tracks[i].File != NULL)
|
||||
free(Tracks[i].File);
|
||||
Tracks[i].File = NULL;
|
||||
}
|
||||
free(Tracks);
|
||||
Tracks = NULL;
|
||||
}
|
||||
|
||||
TrackCount = 0;
|
||||
}
|
||||
|
||||
static int SsaGetSubtitle(Context_t * context, char *Filename)
|
||||
{
|
||||
struct dirent *dirzeiger;
|
||||
DIR *dir;
|
||||
int i = TEXTSSAOFFSET;
|
||||
char *copyFilename = NULL;
|
||||
char *FilenameExtension = NULL;
|
||||
char *FilenameFolder = NULL;
|
||||
char *FilenameShort = NULL;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
if (Filename == NULL) {
|
||||
ssa_err("Filename NULL\n");
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
|
||||
ssa_printf(10, "file: %s\n", Filename);
|
||||
|
||||
copyFilename = strdup(Filename);
|
||||
|
||||
if (copyFilename == NULL) {
|
||||
ssa_err("copyFilename NULL\n");
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
|
||||
FilenameFolder = dirname(copyFilename);
|
||||
|
||||
ssa_printf(10, "folder: %s\n", FilenameFolder);
|
||||
|
||||
FilenameExtension = getExtension(copyFilename);
|
||||
|
||||
if (FilenameExtension == NULL) {
|
||||
ssa_err("FilenameExtension NULL\n");
|
||||
free(copyFilename);
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
|
||||
ssa_printf(10, "ext: %s\n", FilenameExtension);
|
||||
|
||||
FilenameShort = basename(copyFilename);
|
||||
|
||||
/* cut extension */
|
||||
FilenameShort[strlen(FilenameShort) - strlen(FilenameExtension) - 1] =
|
||||
'\0';
|
||||
|
||||
ssa_printf(10, "basename: %s\n", FilenameShort);
|
||||
ssa_printf(10, "%s\n%s | %s | %s\n", copyFilename, FilenameFolder,
|
||||
FilenameShort, FilenameExtension);
|
||||
|
||||
if ((dir = opendir(FilenameFolder)) != NULL) {
|
||||
while ((dirzeiger = readdir(dir)) != NULL) {
|
||||
char subtitleFilename[PATH_MAX];
|
||||
char *subtitleExtension = NULL;
|
||||
|
||||
ssa_printf(20, "%s\n", (*dirzeiger).d_name);
|
||||
|
||||
strcpy(subtitleFilename, (*dirzeiger).d_name);
|
||||
|
||||
// Extension of Relativ Subtitle File Name
|
||||
subtitleExtension = getExtension(subtitleFilename);
|
||||
|
||||
if (subtitleExtension == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(subtitleExtension, "ssa") != 0
|
||||
&& strcmp(subtitleExtension, "ass") != 0)
|
||||
continue;
|
||||
|
||||
/* cut extension */
|
||||
subtitleFilename[strlen(subtitleFilename) -
|
||||
strlen(subtitleExtension) - 1] = '\0';
|
||||
|
||||
ssa_printf(10, "%s %s\n", FilenameShort, subtitleFilename);
|
||||
|
||||
if (strncmp
|
||||
(FilenameShort, subtitleFilename,
|
||||
strlen(FilenameShort)) == 0) {
|
||||
char absSubtitleFileName[PATH_MAX];
|
||||
/* found something of interest, so now make an absolut path name */
|
||||
|
||||
sprintf(absSubtitleFileName, "%s/%s.%s", FilenameFolder,
|
||||
subtitleFilename, subtitleExtension);
|
||||
|
||||
ssa_printf(10, "SSA: %s [%s]\n", subtitleExtension,
|
||||
subtitleFilename);
|
||||
ssa_printf(10, "\t->%s\n", absSubtitleFileName);
|
||||
|
||||
SsaTrack_t SsaSubtitle = {
|
||||
absSubtitleFileName,
|
||||
i,
|
||||
};
|
||||
|
||||
SsaManagerAdd(context, SsaSubtitle);
|
||||
|
||||
Track_t Subtitle;
|
||||
memset(&Subtitle, 0, sizeof(Subtitle));
|
||||
Subtitle.Name = subtitleExtension;
|
||||
Subtitle.Encoding = "S_TEXT/SSA";
|
||||
Subtitle.Id = i++;
|
||||
Subtitle.is_static = 1;
|
||||
context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle);
|
||||
}
|
||||
} /* while */
|
||||
closedir(dir);
|
||||
}
|
||||
/* if dir */
|
||||
free(copyFilename);
|
||||
|
||||
ssa_printf(10, "<\n");
|
||||
return cERR_SSA_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SsaOpenSubtitle(Context_t * context
|
||||
__attribute__ ((unused)), int pid)
|
||||
{
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
if (pid < TEXTSSAOFFSET) {
|
||||
ssa_err("trackid not for us\n");
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
|
||||
int trackid;
|
||||
for (trackid = 0; trackid < TrackCount; trackid++)
|
||||
if (Tracks[trackid].Id == pid)
|
||||
break;
|
||||
|
||||
if (trackid == TrackCount) {
|
||||
ssa_err("trackid not for us\n");
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
|
||||
ssa_printf(10, "%s\n", Tracks[trackid].File);
|
||||
|
||||
fssa = fopen(Tracks[trackid].File, "rb");
|
||||
|
||||
ssa_printf(10, "%s\n", fssa ? "fssa!=NULL" : "fssa==NULL");
|
||||
|
||||
if (!fssa) {
|
||||
ssa_err("cannot open file %s\n", Tracks[trackid].File);
|
||||
return cERR_SSA_ERROR;
|
||||
}
|
||||
return cERR_SSA_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SsaCloseSubtitle(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
if (fssa)
|
||||
fclose(fssa);
|
||||
|
||||
/* this closes the thread! */
|
||||
fssa = NULL;
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
return cERR_SSA_NO_ERROR;
|
||||
}
|
||||
|
||||
static int SsaSwitchSubtitle(Context_t * context, int *arg)
|
||||
{
|
||||
int ret = cERR_SSA_NO_ERROR;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
ret = SsaCloseSubtitle(context);
|
||||
|
||||
if (((ret |= SsaOpenSubtitle(context, *arg)) == cERR_SSA_NO_ERROR)
|
||||
&& (!hasThreadStarted)) {
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thread_sub, &attr, &SsaSubtitleThread, context);
|
||||
|
||||
hasThreadStarted = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int SsaDel(Context_t * context)
|
||||
{
|
||||
int ret = cERR_SSA_NO_ERROR;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
ret = SsaCloseSubtitle(context);
|
||||
|
||||
SsaManagerDel(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int Command(void *_context, ContainerCmd_t command, void *argument)
|
||||
{
|
||||
Context_t *context = (Context_t *) _context;
|
||||
int ret = cERR_SSA_NO_ERROR;
|
||||
|
||||
ssa_printf(10, "\n");
|
||||
|
||||
switch (command) {
|
||||
case CONTAINER_INIT:{
|
||||
char *filename = (char *) argument;
|
||||
SsaDel(context);
|
||||
ret = SsaGetSubtitle(context, filename);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_DEL:{
|
||||
ret = SsaDel(context);
|
||||
break;
|
||||
}
|
||||
case CONTAINER_SWITCH_SUBTITLE:{
|
||||
ret = SsaSwitchSubtitle(context, (int *) argument);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ssa_err("ConatinerCmd not supported! %d\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
ssa_printf(10, "ret = %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *SsaCapabilities[] = { "ssa", NULL };
|
||||
|
||||
Container_t SsaContainer = {
|
||||
"SSA",
|
||||
&Command,
|
||||
SsaCapabilities
|
||||
};
|
@@ -1,839 +0,0 @@
|
||||
/*
|
||||
* Subtitle output to one registered client.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* ***************************** */
|
||||
/* Includes */
|
||||
/* ***************************** */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <memory.h>
|
||||
#include <asm/types.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "output.h"
|
||||
#include "subtitle.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
/* ***************************** */
|
||||
/* Makros/Constants */
|
||||
/* ***************************** */
|
||||
|
||||
#define SUBTITLE_DEBUG
|
||||
|
||||
#ifdef SUBTITLE_DEBUG
|
||||
|
||||
static short debug_level = 0;
|
||||
|
||||
#define subtitle_printf(level, fmt, x...) do { \
|
||||
if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define subtitle_printf(level, fmt, x...)
|
||||
#endif
|
||||
|
||||
#ifndef SUBTITLE_SILENT
|
||||
#define subtitle_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define subtitle_err(fmt, x...)
|
||||
#endif
|
||||
|
||||
/* Error Constants */
|
||||
#define cERR_SUBTITLE_NO_ERROR 0
|
||||
#define cERR_SUBTITLE_ERROR -1
|
||||
|
||||
static const char FILENAME[] = "subtitle.c";
|
||||
|
||||
/*
|
||||
Number, Style, Name,, MarginL, MarginR, MarginV, Effect,, Text
|
||||
|
||||
1038,0,tdk,,0000,0000,0000,,That's not good.
|
||||
1037,0,tdk,,0000,0000,0000,,{\i1}Rack them up, rack them up,{\i0}\N{\i1}rack them up.{\i0} [90]
|
||||
1036,0,tdk,,0000,0000,0000,,Okay, rack them up.
|
||||
*/
|
||||
|
||||
#define PUFFERSIZE 20
|
||||
|
||||
/* ***************************** */
|
||||
/* Types */
|
||||
/* ***************************** */
|
||||
|
||||
struct sub_t {
|
||||
char *text;
|
||||
unsigned long long int pts;
|
||||
unsigned long int milliDuration;
|
||||
};
|
||||
|
||||
|
||||
/* ***************************** */
|
||||
/* Varaibles */
|
||||
/* ***************************** */
|
||||
|
||||
static pthread_mutex_t mutex;
|
||||
|
||||
static pthread_t thread_sub;
|
||||
|
||||
void *clientData = NULL;
|
||||
void (*clientFunction) (long int, size_t, char *, void *);
|
||||
|
||||
static struct sub_t subPuffer[PUFFERSIZE];
|
||||
static int readPointer = 0;
|
||||
static int writePointer = 0;
|
||||
static int hasThreadStarted = 0;
|
||||
static int isSubtitleOpened = 0;
|
||||
void (*dvbsubWrite)(AVSubtitle *, int64_t) = NULL;
|
||||
void (*dvbsubAssWrite)(AVCodecContext *, AVSubtitle *, int) = NULL;
|
||||
void (*dvbsubAssClear)(void) = NULL;
|
||||
|
||||
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* MISC Functions */
|
||||
/* ***************************** */
|
||||
static void getMutex(int line)
|
||||
{
|
||||
subtitle_printf(100, "%d requesting mutex\n", line);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
subtitle_printf(100, "%d received mutex\n", line);
|
||||
}
|
||||
|
||||
static void releaseMutex(int line)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
subtitle_printf(100, "%d released mutex\n", line);
|
||||
}
|
||||
|
||||
void replace_all(char **string, char *search, char *replace)
|
||||
{
|
||||
int len = 0;
|
||||
char *ptr = NULL;
|
||||
char tempString[512];
|
||||
char newString[512];
|
||||
|
||||
newString[0] = '\0';
|
||||
|
||||
if ((string == NULL) || (*string == NULL) || (search == NULL)
|
||||
|| (replace == NULL)) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(tempString, *string, 511);
|
||||
tempString[511] = '\0';
|
||||
|
||||
free(*string);
|
||||
|
||||
while ((ptr = strstr(tempString, search)) != NULL) {
|
||||
len = ptr - tempString;
|
||||
strncpy(newString, tempString, len);
|
||||
newString[len] = '\0';
|
||||
strcat(newString, replace);
|
||||
|
||||
len += strlen(search);
|
||||
strcat(newString, tempString + len);
|
||||
|
||||
strcpy(tempString, newString);
|
||||
}
|
||||
|
||||
subtitle_printf(20, "strdup in line %d\n", __LINE__);
|
||||
|
||||
if (newString[0] != '\0')
|
||||
*string = strdup(newString);
|
||||
else
|
||||
*string = strdup(tempString);
|
||||
|
||||
}
|
||||
|
||||
int subtitle_ParseASS(char **Line)
|
||||
{
|
||||
char *Text;
|
||||
int i;
|
||||
char *ptr1;
|
||||
|
||||
if ((Line == NULL) || (*Line == NULL)) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
Text = strdup(*Line);
|
||||
|
||||
subtitle_printf(10, "-> Text = %s\n", *Line);
|
||||
|
||||
ptr1 = Text;
|
||||
|
||||
for (i = 0; i < 9 && *ptr1 != '\0'; ptr1++) {
|
||||
|
||||
subtitle_printf(20, "%s", ptr1);
|
||||
|
||||
if (*ptr1 == ',')
|
||||
i++;
|
||||
}
|
||||
|
||||
free(*Line);
|
||||
|
||||
*Line = strdup(ptr1);
|
||||
free(Text);
|
||||
|
||||
replace_all(Line, "\\N", "\n");
|
||||
|
||||
replace_all(Line, "{\\i1}", "<i>");
|
||||
replace_all(Line, "{\\i0}", "</i>");
|
||||
|
||||
subtitle_printf(10, "<- Text=%s\n", *Line);
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
int subtitle_ParseSRT(char **Line)
|
||||
{
|
||||
|
||||
if ((Line == NULL) || (*Line == NULL)) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
subtitle_printf(20, "-> Text=%s\n", *Line);
|
||||
|
||||
replace_all(Line, "\x0d", "");
|
||||
replace_all(Line, "\n\n", "\\N");
|
||||
replace_all(Line, "\n", "");
|
||||
replace_all(Line, "\\N", "\n");
|
||||
replace_all(Line, "<EFBFBD>", "oe");
|
||||
replace_all(Line, "<EFBFBD>", "ae");
|
||||
replace_all(Line, "<EFBFBD>", "ue");
|
||||
replace_all(Line, "<EFBFBD>", "Oe");
|
||||
replace_all(Line, "<EFBFBD>", "Ae");
|
||||
replace_all(Line, "<EFBFBD>", "Ue");
|
||||
replace_all(Line, "<EFBFBD>", "ss");
|
||||
|
||||
subtitle_printf(10, "<- Text=%s\n", *Line);
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
int subtitle_ParseSSA(char **Line)
|
||||
{
|
||||
|
||||
if ((Line == NULL) || (*Line == NULL)) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
subtitle_printf(20, "-> Text=%s\n", *Line);
|
||||
|
||||
replace_all(Line, "\x0d", "");
|
||||
replace_all(Line, "\n\n", "\\N");
|
||||
replace_all(Line, "\n", "");
|
||||
replace_all(Line, "\\N", "\n");
|
||||
replace_all(Line, "<EFBFBD>", "oe");
|
||||
replace_all(Line, "<EFBFBD>", "ae");
|
||||
replace_all(Line, "<EFBFBD>", "ue");
|
||||
replace_all(Line, "<EFBFBD>", "Oe");
|
||||
replace_all(Line, "<EFBFBD>", "Ae");
|
||||
replace_all(Line, "<EFBFBD>", "Ue");
|
||||
replace_all(Line, "<EFBFBD>", "ss");
|
||||
|
||||
subtitle_printf(10, "<- Text=%s\n", *Line);
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
void addSub(Context_t * context, char *text, unsigned long long int pts,
|
||||
unsigned long int milliDuration)
|
||||
{
|
||||
int count = 20;
|
||||
|
||||
subtitle_printf(50, "index %d\n", writePointer);
|
||||
|
||||
if (context && context->playback && !context->playback->isPlaying) {
|
||||
subtitle_err("1. aborting ->no playback\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (text == NULL) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pts == 0) {
|
||||
subtitle_err("pts 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (milliDuration == 0) {
|
||||
subtitle_err("duration 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (subPuffer[writePointer].text != NULL) {
|
||||
//List is full, wait till we got some free space
|
||||
|
||||
if (context && context->playback && !context->playback->isPlaying) {
|
||||
subtitle_err("2. aborting ->no playback\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* konfetti: we dont want to block forever here. if no buffer
|
||||
* is available we start ring from the beginning and loose some stuff
|
||||
* which is acceptable!
|
||||
*/
|
||||
subtitle_printf(10, "waiting on free buffer %d - %d (%d) ...\n",
|
||||
writePointer, readPointer, count);
|
||||
usleep(10000);
|
||||
count--;
|
||||
|
||||
if (count == 0) {
|
||||
subtitle_err("abort waiting on buffer...\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
subtitle_printf(20, "from mkv: %s pts:%lld milliDuration:%lud\n", text,
|
||||
pts, milliDuration);
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
if (count == 0) {
|
||||
int i;
|
||||
subtitle_err("freeing not delivered data\n");
|
||||
|
||||
//Reset all
|
||||
readPointer = 0;
|
||||
writePointer = 0;
|
||||
|
||||
for (i = 0; i < PUFFERSIZE; i++) {
|
||||
if (subPuffer[i].text != NULL)
|
||||
free(subPuffer[i].text);
|
||||
subPuffer[i].text = NULL;
|
||||
subPuffer[i].pts = 0;
|
||||
subPuffer[i].milliDuration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
subPuffer[writePointer].text = strdup(text);
|
||||
subPuffer[writePointer].pts = pts;
|
||||
subPuffer[writePointer].milliDuration = milliDuration;
|
||||
|
||||
writePointer++;
|
||||
|
||||
if (writePointer == PUFFERSIZE)
|
||||
writePointer = 0;
|
||||
|
||||
if (writePointer == readPointer) {
|
||||
/* this should not happen, and means that there is nor reader or
|
||||
* the reader has performance probs ;)
|
||||
* the recovery is done at startup of this function - but next time
|
||||
*/
|
||||
subtitle_err("ups something went wrong. no more readers? \n");
|
||||
}
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
}
|
||||
|
||||
int getNextSub(char **text, unsigned long long int *pts,
|
||||
long int *milliDuration)
|
||||
{
|
||||
|
||||
subtitle_printf(50, "index %d\n", readPointer);
|
||||
|
||||
if (text == NULL) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
if (subPuffer[readPointer].text == NULL) {
|
||||
/* this is acutally not an error, because it may happen
|
||||
* that there is no subtitle for a while
|
||||
*/
|
||||
subtitle_printf(200, "null in subPuffer\n");
|
||||
releaseMutex(__LINE__);
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
*text = strdup(subPuffer[readPointer].text);
|
||||
free(subPuffer[readPointer].text);
|
||||
subPuffer[readPointer].text = NULL;
|
||||
|
||||
*pts = subPuffer[readPointer].pts;
|
||||
subPuffer[readPointer].pts = 0;
|
||||
|
||||
*milliDuration = subPuffer[readPointer].milliDuration;
|
||||
subPuffer[readPointer].milliDuration = 0;
|
||||
|
||||
readPointer++;
|
||||
|
||||
if (readPointer == PUFFERSIZE)
|
||||
readPointer = 0;
|
||||
|
||||
if (writePointer == readPointer) {
|
||||
/* this may happen, in normal case the reader is ones ahead the
|
||||
* writer. So this is the normal case that we eat the data
|
||||
* and have the reader reached.
|
||||
*/
|
||||
subtitle_printf(20,
|
||||
"ups something went wrong. no more writers? \n");
|
||||
}
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
subtitle_printf(20, "readPointer %d\n", readPointer);
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
/* **************************** */
|
||||
/* Worker Thread */
|
||||
/* **************************** */
|
||||
|
||||
static void *SubtitleThread(void *data)
|
||||
{
|
||||
Context_t *context = (Context_t *) data;
|
||||
char *subText = NULL;
|
||||
long int subMilliDuration = 0;
|
||||
unsigned long long int subPts = 0;
|
||||
unsigned long long int Pts = 0;
|
||||
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
while (context->playback->isCreationPhase) {
|
||||
subtitle_err("Thread waiting for end of init phase...\n");
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
subtitle_printf(10, "done\n");
|
||||
|
||||
while (context && context->playback && context->playback->isPlaying) {
|
||||
|
||||
int curtrackid = -1;
|
||||
|
||||
if (context && context->manager && context->manager->subtitle)
|
||||
context->manager->subtitle->Command(context, MANAGER_GET,
|
||||
&curtrackid);
|
||||
|
||||
subtitle_printf(50, "curtrackid %d\n", curtrackid);
|
||||
|
||||
if (curtrackid >= 0) {
|
||||
if (getNextSub(&subText, &subPts, &subMilliDuration) != 0) {
|
||||
usleep(500000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (context && context->playback)
|
||||
context->playback->Command(context, PLAYBACK_PTS, &Pts);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
if (Pts > subPts) {
|
||||
subtitle_printf(10, "subtitle is to late, ignoring\n");
|
||||
if (subText != NULL)
|
||||
free(subText);
|
||||
continue;
|
||||
}
|
||||
|
||||
subtitle_printf(20, "Pts:%llu < subPts%llu duration %ld\n",
|
||||
Pts, subPts, subMilliDuration);
|
||||
|
||||
while (context &&
|
||||
context->playback &&
|
||||
context->playback->isPlaying && Pts < subPts) {
|
||||
|
||||
unsigned long int diff = subPts - Pts;
|
||||
diff = (diff * 1000) / 90.0;
|
||||
|
||||
subtitle_printf(50, "DIFF: %lud\n", diff);
|
||||
|
||||
if (diff > 100)
|
||||
usleep(diff);
|
||||
|
||||
if (context && context->playback)
|
||||
context->playback->Command(context, PLAYBACK_PTS,
|
||||
&Pts);
|
||||
else {
|
||||
subtitle_err("no playback ? terminated?\n");
|
||||
break;
|
||||
}
|
||||
subtitle_printf(20, "cur: %llu wanted: %llu\n", Pts,
|
||||
subPts);
|
||||
}
|
||||
|
||||
if (context &&
|
||||
context->playback &&
|
||||
context->playback->isPlaying && subText != NULL) {
|
||||
|
||||
if (clientFunction != NULL)
|
||||
clientFunction(subMilliDuration, strlen(subText),
|
||||
subText, clientData);
|
||||
else
|
||||
subtitle_printf(10,
|
||||
"writing Sub failed (%ld) (%d) \"%s\"\n",
|
||||
subMilliDuration, strlen(subText),
|
||||
subText);
|
||||
|
||||
free(subText);
|
||||
}
|
||||
|
||||
} /* trackID >= 0 */
|
||||
else //Wait
|
||||
usleep(500000);
|
||||
|
||||
} /* outer while */
|
||||
|
||||
subtitle_printf(0, "has ended\n");
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ***************************** */
|
||||
/* Functions */
|
||||
/* ***************************** */
|
||||
|
||||
static int Write(void *_context, void *data)
|
||||
{
|
||||
Context_t *context = (Context_t *) _context;
|
||||
char *Encoding = NULL;
|
||||
char *Text;
|
||||
SubtitleOut_t *out;
|
||||
int DataLength;
|
||||
unsigned long long int Pts;
|
||||
float Duration;
|
||||
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
if (data == NULL) {
|
||||
subtitle_err("null pointer passed\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
out = (SubtitleOut_t *) data;
|
||||
|
||||
if (out->type == eSub_Txt) {
|
||||
Text = strdup((const char *) out->u.text.data);
|
||||
} else {
|
||||
/* fixme handle gfx subs from container_ass and send it to
|
||||
* the callback. this must be implemented also in e2/neutrino
|
||||
* then.
|
||||
*/
|
||||
subtitle_err("subtitle gfx currently not handled\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
DataLength = out->u.text.len;
|
||||
Pts = out->pts;
|
||||
Duration = out->duration;
|
||||
|
||||
context->manager->subtitle->Command(context, MANAGER_GETENCODING,
|
||||
&Encoding);
|
||||
|
||||
if (Encoding == NULL) {
|
||||
subtitle_err("encoding unknown\n");
|
||||
free(Text);
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
subtitle_printf(20, "Encoding:%s Text:%s Len:%d\n", Encoding, Text,
|
||||
DataLength);
|
||||
|
||||
if (!strncmp("S_TEXT/SSA", Encoding, 10) ||
|
||||
!strncmp("S_SSA", Encoding, 5))
|
||||
subtitle_ParseSSA(&Text);
|
||||
|
||||
else if (!strncmp("S_TEXT/ASS", Encoding, 10) ||
|
||||
!strncmp("S_AAS", Encoding, 5))
|
||||
subtitle_ParseASS(&Text);
|
||||
|
||||
else if (!strncmp("S_TEXT/SRT", Encoding, 10) ||
|
||||
!strncmp("S_SRT", Encoding, 5))
|
||||
subtitle_ParseSRT(&Text);
|
||||
else {
|
||||
subtitle_err("unknown encoding %s\n", Encoding);
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
subtitle_printf(10, "Text:%s Duration:%f\n", Text, Duration);
|
||||
|
||||
addSub(context, Text, Pts, Duration * 1000);
|
||||
|
||||
free(Text);
|
||||
free(Encoding);
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
static int subtitle_Open(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int i;
|
||||
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
if (isSubtitleOpened == 1) {
|
||||
subtitle_err("already opened! ignoring\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
//Reset all
|
||||
readPointer = 0;
|
||||
writePointer = 0;
|
||||
|
||||
for (i = 0; i < PUFFERSIZE; i++) {
|
||||
subPuffer[i].text = NULL;
|
||||
subPuffer[i].pts = 0;
|
||||
subPuffer[i].milliDuration = 0;
|
||||
}
|
||||
|
||||
isSubtitleOpened = 1;
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
static int subtitle_Close(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int i;
|
||||
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
//Reset all
|
||||
readPointer = 0;
|
||||
writePointer = 0;
|
||||
|
||||
for (i = 0; i < PUFFERSIZE; i++) {
|
||||
if (subPuffer[i].text != NULL)
|
||||
free(subPuffer[i].text);
|
||||
|
||||
subPuffer[i].text = NULL;
|
||||
subPuffer[i].pts = 0;
|
||||
subPuffer[i].milliDuration = 0;
|
||||
}
|
||||
|
||||
isSubtitleOpened = 0;
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
static int subtitle_Play(Context_t * context)
|
||||
{
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
if (hasThreadStarted == 0) {
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if (pthread_create
|
||||
(&thread_sub, &attr, &SubtitleThread, (void *) context) != 0) {
|
||||
subtitle_err("Error creating thread\n");
|
||||
hasThreadStarted = 0;
|
||||
} else {
|
||||
subtitle_printf(10, "Created thread\n");
|
||||
hasThreadStarted = 1;
|
||||
}
|
||||
} else {
|
||||
subtitle_err("thread already created.\n");
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
static int subtitle_Stop(Context_t * context __attribute__ ((unused)))
|
||||
{
|
||||
int wait_time = 20;
|
||||
int i;
|
||||
|
||||
subtitle_printf(10, "\n");
|
||||
|
||||
while ((hasThreadStarted != 0) && (--wait_time) > 0) {
|
||||
subtitle_printf(10,
|
||||
"Waiting for subtitle thread to terminate itself, will try another %d times\n",
|
||||
wait_time);
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
if (wait_time == 0) {
|
||||
subtitle_err("Timeout waiting for thread!\n");
|
||||
|
||||
return cERR_SUBTITLE_ERROR;
|
||||
}
|
||||
|
||||
hasThreadStarted = 0;
|
||||
|
||||
/* konfetti: thread has ended, so nobody will eat the date...
|
||||
* free the data...
|
||||
*/
|
||||
|
||||
getMutex(__LINE__);
|
||||
|
||||
//Reset all
|
||||
readPointer = 0;
|
||||
writePointer = 0;
|
||||
|
||||
for (i = 0; i < PUFFERSIZE; i++) {
|
||||
if (subPuffer[i].text != NULL)
|
||||
free(subPuffer[i].text);
|
||||
|
||||
subPuffer[i].text = NULL;
|
||||
subPuffer[i].pts = 0;
|
||||
subPuffer[i].milliDuration = 0;
|
||||
}
|
||||
|
||||
releaseMutex(__LINE__);
|
||||
|
||||
subtitle_printf(10, "<\n");
|
||||
|
||||
return cERR_SUBTITLE_NO_ERROR;
|
||||
}
|
||||
|
||||
void subtitle_SignalConnect(void (*fkt) (long int, size_t, char *, void *))
|
||||
{
|
||||
subtitle_printf(10, "%p\n", fkt);
|
||||
|
||||
clientFunction = fkt;
|
||||
}
|
||||
|
||||
void subtitle_SignalConnectBuffer(void *data)
|
||||
{
|
||||
subtitle_printf(10, "%p\n", data);
|
||||
|
||||
clientData = data;
|
||||
}
|
||||
|
||||
static int Command(void *_context, OutputCmd_t command, void *argument)
|
||||
{
|
||||
Context_t *context = (Context_t *) _context;
|
||||
int ret = cERR_SUBTITLE_NO_ERROR;
|
||||
|
||||
subtitle_printf(50, "%d\n", command);
|
||||
|
||||
switch (command) {
|
||||
case OUTPUT_OPEN:{
|
||||
ret = subtitle_Open(context);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_CLOSE:{
|
||||
ret = subtitle_Close(context);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_PLAY:{
|
||||
ret = subtitle_Play(context);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_STOP:{
|
||||
ret = subtitle_Stop(context);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_SWITCH:{
|
||||
subtitle_err("Subtitle Switch not implemented\n");
|
||||
ret = cERR_SUBTITLE_ERROR;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_GET_SUBTITLE_OUTPUT:{
|
||||
SubtitleOutputDef_t *out = (SubtitleOutputDef_t *) argument;
|
||||
out->dvbsubWrite = (void (*)(void *, int64_t))dvbsubWrite;
|
||||
out->dvbsubAssWrite = (void (*)(void *, void *, int))dvbsubAssWrite;
|
||||
out->dvbsubAssClear = (void (*)(void))dvbsubAssClear;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_SET_SUBTITLE_OUTPUT:{
|
||||
SubtitleOutputDef_t *out = (SubtitleOutputDef_t *) argument;
|
||||
dvbsubWrite = (void (*)(AVSubtitle *, int64_t))out->dvbsubWrite;
|
||||
dvbsubAssWrite = (void (*)(AVCodecContext *, AVSubtitle *, int))out->dvbsubAssWrite;
|
||||
dvbsubAssClear = (void (*)(void))out->dvbsubAssClear;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_SUBTITLE_REGISTER_FUNCTION:{
|
||||
subtitle_SignalConnect(argument);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_SUBTITLE_REGISTER_BUFFER:{
|
||||
subtitle_SignalConnectBuffer(argument);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_FLUSH:{
|
||||
subtitle_err("Subtitle Flush not implemented\n");
|
||||
ret = cERR_SUBTITLE_ERROR;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_PAUSE:{
|
||||
subtitle_err("Subtitle Pause not implemented\n");
|
||||
ret = cERR_SUBTITLE_ERROR;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_CONTINUE:{
|
||||
subtitle_err("Subtitle Continue not implemented\n");
|
||||
ret = cERR_SUBTITLE_ERROR;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
subtitle_err("OutputCmd %d not supported!\n", command);
|
||||
ret = cERR_SUBTITLE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
subtitle_printf(50, "exiting with value %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static char *SubtitleCapabilitis[] = { "subtitle", NULL };
|
||||
|
||||
struct Output_s SubtitleOutput = {
|
||||
"Subtitle",
|
||||
&Command,
|
||||
&Write,
|
||||
SubtitleCapabilitis
|
||||
};
|
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* framebuffer output/writer handling.
|
||||
*
|
||||
* This is a hacky implementation of a framebuffer output for the subtitling.
|
||||
* This is ment as a POV, later this should be implemented in enigma2 and
|
||||
* neutrino.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* ***************************** */
|
||||
/* Includes */
|
||||
/* ***************************** */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <memory.h>
|
||||
#include <asm/types.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "output.h"
|
||||
#include "debug.h"
|
||||
#include "misc.h"
|
||||
#include "writer.h"
|
||||
|
||||
/* ***************************** */
|
||||
/* Makros/Constants */
|
||||
/* ***************************** */
|
||||
|
||||
#define FB_DEBUG
|
||||
|
||||
#ifdef FB_DEBUG
|
||||
|
||||
static short debug_level = 0;
|
||||
|
||||
#define fb_printf(level, fmt, x...) do { \
|
||||
if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define fb_printf(level, fmt, x...)
|
||||
#endif
|
||||
|
||||
#ifndef FB_SILENT
|
||||
#define fb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0)
|
||||
#else
|
||||
#define fb_err(fmt, x...)
|
||||
#endif
|
||||
|
||||
#define _r(c) ((c)>>24)
|
||||
#define _g(c) (((c)>>16)&0xFF)
|
||||
#define _b(c) (((c)>>8)&0xFF)
|
||||
#define _a(c) ((c)&0xFF)
|
||||
|
||||
/* ***************************** */
|
||||
/* Types */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* Varaibles */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
|
||||
/* ***************************** */
|
||||
/* MISC Functions */
|
||||
/* ***************************** */
|
||||
|
||||
|
||||
/* ***************************** */
|
||||
/* Writer Functions */
|
||||
/* ***************************** */
|
||||
|
||||
static int reset()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writeData(void *_call)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
WriterFBCallData_t *call = (WriterFBCallData_t *) _call;
|
||||
|
||||
fb_printf(100, "\n");
|
||||
|
||||
if (!call) {
|
||||
fb_err("call data is NULL...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!call->destination) {
|
||||
fb_err("frame buffer == NULL. ignoring ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dst_stride = call->destStride / sizeof(uint32_t);
|
||||
int dst_delta = dst_stride - call->Width;
|
||||
uint32_t *dst = call->destination + call->y * dst_stride + call->x;
|
||||
|
||||
if (call->data) {
|
||||
int src_delta = call->Stride - call->Width;
|
||||
unsigned char *src = call->data;
|
||||
static uint32_t last_color = 0, colortable[256];
|
||||
|
||||
if (last_color != call->color) {
|
||||
// call->color is rgba, our spark frame buffer is argb
|
||||
uint32_t c = call->color >> 8, a = 255 - (call->color & 0xff);
|
||||
int i;
|
||||
for (i = 0; i < 256; i++) {
|
||||
uint32_t k = (a * i) >> 8;
|
||||
colortable[i] = k ? (c | (k << 24)) : 0;
|
||||
}
|
||||
last_color = call->color;
|
||||
}
|
||||
|
||||
fb_printf(100, "x %d\n", call->x);
|
||||
fb_printf(100, "y %d\n", call->y);
|
||||
fb_printf(100, "width %d\n", call->Width);
|
||||
fb_printf(100, "height %d\n", call->Height);
|
||||
fb_printf(100, "stride %d\n", call->Stride);
|
||||
fb_printf(100, "color 0x%.8x\n", call->color);
|
||||
fb_printf(100, "data %p\n", call->data);
|
||||
fb_printf(100, "dest %p\n", call->destination);
|
||||
fb_printf(100, "dest.stride %d\n", call->destStride);
|
||||
|
||||
unsigned char *src_final = src + call->Height * call->Width;
|
||||
for (; src < src_final; dst += dst_delta, src += src_delta) {
|
||||
u_char *src_end = src + call->Width;
|
||||
for (; src < src_end; dst++, src++) {
|
||||
uint32_t c = colortable[*src];
|
||||
if (c)
|
||||
*dst = c;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t *dst_final =
|
||||
dst + call->Width + call->Height * dst_stride;
|
||||
for (; dst < dst_final; dst += dst_delta) {
|
||||
uint32_t *dst_end = dst + call->Width;
|
||||
for (; dst < dst_end; dst++)
|
||||
*dst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fb_printf(100, "< %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ***************************** */
|
||||
/* Writer Definition */
|
||||
/* ***************************** */
|
||||
static WriterCaps_t caps = {
|
||||
"framebuffer",
|
||||
eGfx,
|
||||
"framebuffer",
|
||||
0
|
||||
};
|
||||
|
||||
struct Writer_s WriterFramebuffer = {
|
||||
&reset,
|
||||
&writeData,
|
||||
NULL,
|
||||
&caps
|
||||
};
|
Reference in New Issue
Block a user