mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
844 lines
24 KiB
C
844 lines
24 KiB
C
/*
|
||
* 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 <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 = 10;
|
||
|
||
#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 "/usr/share/fonts/FreeSans.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.7;
|
||
static float ass_line_spacing = 0.7;
|
||
|
||
static unsigned int screen_width = 0;
|
||
static unsigned int screen_height = 0;
|
||
static int shareFramebuffer = 0;
|
||
static int framebufferFD = -1;
|
||
static unsigned char* destination = NULL;
|
||
static int destStride = 0;
|
||
static int threeDMode =0;
|
||
|
||
static ASS_Track* ass_track = NULL;
|
||
|
||
static region_t* firstRegion = NULL;
|
||
|
||
/* ***************************** */
|
||
/* Prototypes */
|
||
/* ***************************** */
|
||
|
||
/* ***************************** */
|
||
/* MISC Functions */
|
||
/* ***************************** */
|
||
|
||
void ass_msg_callback(int level, const char *format, va_list va, void *ctx)
|
||
{
|
||
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 != NULL)
|
||
{
|
||
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.fd = framebufferFD;
|
||
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);
|
||
if(threeDMode == 1){
|
||
out.x = screen_width/2 + next->x;
|
||
writer->writeData(&out);
|
||
}else if(threeDMode == 2){
|
||
out.y = screen_height/2 + next->y;
|
||
writer->writeData(&out);
|
||
}
|
||
}
|
||
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 == NULL)
|
||
{
|
||
ass_err("no framebuffer writer found!\n");
|
||
}
|
||
|
||
prev = next = firstRegion;
|
||
while (next != NULL)
|
||
{
|
||
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.fd = framebufferFD;
|
||
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);
|
||
if(threeDMode == 1){
|
||
out.x = screen_width/2 + next->x;
|
||
writer->writeData(&out);
|
||
}else if(threeDMode == 2){
|
||
out.y = screen_height/2 + next->y;
|
||
writer->writeData(&out);
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
ass_printf(100, "%d %d %d %d %ld\n", x, y, w, h, undisplay);
|
||
|
||
if (firstRegion == NULL)
|
||
{
|
||
firstRegion = malloc(sizeof(region_t));
|
||
new = firstRegion;
|
||
} else
|
||
{
|
||
new = firstRegion;
|
||
while (new->next != NULL)
|
||
new = new->next;
|
||
|
||
new->next = malloc(sizeof(region_t));
|
||
new = new->next;
|
||
}
|
||
|
||
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) {
|
||
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;
|
||
}
|
||
|
||
getMutex(__LINE__);
|
||
|
||
//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(1000);
|
||
|
||
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 != NULL && 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();
|
||
|
||
while (context && context->playback && context->playback->isPlaying &&
|
||
(img) && (change != 0))
|
||
{
|
||
WriterFBCallData_t out;
|
||
time_t now = time(NULL);
|
||
time_t undisplay = now + 10;
|
||
|
||
if (ass_track && ass_track->events)
|
||
{
|
||
undisplay = now + ass_track->events->Duration / 1000 + 0.5;
|
||
}
|
||
|
||
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.fd = framebufferFD;
|
||
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;
|
||
|
||
storeRegion(img->dst_x, img->dst_y,
|
||
img->w, img->h, undisplay);
|
||
|
||
if (shareFramebuffer)
|
||
{
|
||
if(context && context->playback && context->playback->isPlaying && writer){
|
||
writer->writeData(&out);
|
||
if(threeDMode == 1){
|
||
out.x = screen_width/2 + img->dst_x;
|
||
writer->writeData(&out);
|
||
}else if(threeDMode == 2){
|
||
out.y = screen_height/2 + img->dst_y;
|
||
writer->writeData(&out);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* application does not want to share framebuffer,
|
||
* so there is hopefully installed an output callback
|
||
* in the subtitle output!
|
||
*/
|
||
#ifdef MARTII
|
||
SubtitleOut_t sub_out;
|
||
|
||
sub_out.type = eSub_Gfx;
|
||
|
||
if (ass_track->events)
|
||
{
|
||
/* fixme: check values */
|
||
sub_out.pts = ass_track->events->Start * 90.0;
|
||
sub_out.duration = ass_track->events->Duration / 1000.0;
|
||
} else
|
||
{
|
||
sub_out.pts = playPts;
|
||
sub_out.duration = 10.0;
|
||
}
|
||
|
||
sub_out.u.gfx.data = img->bitmap;
|
||
sub_out.u.gfx.Width = img->w;
|
||
sub_out.u.gfx.Height = img->h;
|
||
sub_out.u.gfx.x = img->dst_x;
|
||
sub_out.u.gfx.y = img->dst_y;
|
||
if(context && context->playback && context->playback->isPlaying &&
|
||
context->output && context->output->subtitle)
|
||
context->output->subtitle->Write(context, &sub_out);
|
||
#else
|
||
SubtitleOut_t out;
|
||
|
||
out.type = eSub_Gfx;
|
||
|
||
if (ass_track->events)
|
||
{
|
||
/* fixme: check values */
|
||
out.pts = ass_track->events->Start * 90.0;
|
||
out.duration = ass_track->events->Duration / 1000.0;
|
||
} else
|
||
{
|
||
out.pts = playPts;
|
||
out.duration = 10.0;
|
||
}
|
||
|
||
out.u.gfx.data = img->bitmap;
|
||
out.u.gfx.Width = img->w;
|
||
out.u.gfx.Height = img->h;
|
||
out.u.gfx.x = img->dst_x;
|
||
out.u.gfx.y = img->dst_y;
|
||
if(context && context->playback && context->playback->isPlaying &&
|
||
context->output && context->output->subtitle)
|
||
context->output->subtitle->Write(context, &out);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/* Next image */
|
||
img = img->next;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* noop */
|
||
}
|
||
|
||
releaseMutex(__LINE__);
|
||
} else
|
||
{
|
||
usleep(1000);
|
||
}
|
||
|
||
/* cleanup no longer used but not overwritten regions */
|
||
checkRegions();
|
||
} /* while */
|
||
|
||
hasPlayThreadStarted = 0;
|
||
|
||
ass_printf(10, "terminating\n");
|
||
}
|
||
|
||
/* **************************** */
|
||
/* Container part for ass */
|
||
/* **************************** */
|
||
|
||
int container_ass_init(Context_t *context)
|
||
{
|
||
int modefd;
|
||
char buf[16];
|
||
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);
|
||
|
||
modefd=open("/proc/stb/video/3d_mode", O_RDWR);
|
||
if(modefd > 0){
|
||
read(modefd, buf, 15);
|
||
buf[15]='\0';
|
||
close(modefd);
|
||
}else threeDMode = 0;
|
||
|
||
if(strncmp(buf,"sbs",3)==0)threeDMode = 1;
|
||
else if(strncmp(buf,"tab",3)==0)threeDMode = 2;
|
||
else threeDMode = 0;
|
||
|
||
screen_width = output.screen_width;
|
||
screen_height = output.screen_height;
|
||
shareFramebuffer = output.shareFramebuffer;
|
||
framebufferFD = output.framebufferFD;
|
||
destination = output.destination;
|
||
destStride = output.destStride;
|
||
|
||
ass_printf(10, "width %d, height %d, share %d, fd %d, 3D %d\n",
|
||
screen_width, screen_height, shareFramebuffer, framebufferFD, threeDMode);
|
||
|
||
if(threeDMode == 0){
|
||
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 ) );
|
||
}else if(threeDMode == 1){
|
||
ass_set_frame_size(ass_renderer, screen_width/2, screen_height);
|
||
ass_set_margins(ass_renderer, (int)(0.03 * screen_height), (int)(0.03 * screen_height) ,
|
||
(int)(0.03 * screen_width/2 ), (int)(0.03 * screen_width/2 ) );
|
||
}else if(threeDMode == 2){
|
||
ass_set_frame_size(ass_renderer, screen_width, screen_height/2);
|
||
ass_set_margins(ass_renderer, (int)(0.03 * screen_height/2), (int)(0.03 * screen_height/2) ,
|
||
(int)(0.03 * screen_width ), (int)(0.03 * screen_width ) );
|
||
}
|
||
|
||
ass_set_use_margins(ass_renderer, 0 );
|
||
ass_set_font_scale(ass_renderer, ass_font_scale);
|
||
|
||
ass_set_hinting(ass_renderer, ASS_HINTING_LIGHT);
|
||
ass_set_line_spacing(ass_renderer, ass_line_spacing);
|
||
ass_set_fonts(ass_renderer, ASS_FONT, "Arial", 0, NULL, 1);
|
||
|
||
if(threeDMode == 0){
|
||
ass_set_aspect_ratio( ass_renderer, 1.0, 1.0);
|
||
}else if(threeDMode == 1){
|
||
ass_set_aspect_ratio( ass_renderer, 0.5, 1.0);
|
||
}else if(threeDMode == 2){
|
||
ass_set_aspect_ratio( ass_renderer, 1.0, 0.5);
|
||
}
|
||
|
||
|
||
isContainerRunning = 1;
|
||
|
||
return cERR_CONTAINER_ASS_NO_ERROR;
|
||
}
|
||
|
||
int container_ass_process_data(Context_t *context, 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;
|
||
}
|
||
}
|
||
|
||
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) {
|
||
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 != NULL)
|
||
{
|
||
writer->reset();
|
||
}
|
||
|
||
releaseMutex(__LINE__);
|
||
|
||
ass_printf(10, "ret %d\n", ret);
|
||
return ret;
|
||
}
|
||
|
||
static int container_ass_switch_subtitle(Context_t* context, int* arg)
|
||
{
|
||
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
|
||
};
|