From 1005c3f53ec1959801ee8a6a0d53fd45f3003cf6 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 14 Feb 2012 23:01:24 +0100 Subject: [PATCH] add libeplayer3 from tdt git This imports libeplayer3 as of commit 9160371ccc6 (2012-02-02) git://gitorious.org/open-duckbox-project-sh4/tdt.git It would be better to use the original repo, but I need too many changes for now :-( --- libeplayer3/Makefile.am | 29 + libeplayer3/README | 72 + libeplayer3/container/container.c | 125 ++ libeplayer3/container/container_ass.c | 817 ++++++++++ libeplayer3/container/container_ffmpeg.c | 1734 ++++++++++++++++++++++ libeplayer3/container/text_srt.c | 490 ++++++ libeplayer3/container/text_ssa.c | 492 ++++++ libeplayer3/include/aac.h | 57 + libeplayer3/include/common.h | 17 + libeplayer3/include/container.h | 48 + libeplayer3/include/debug.h | 21 + libeplayer3/include/ffmpeg_metadata.h | 45 + libeplayer3/include/manager.h | 77 + libeplayer3/include/misc.h | 136 ++ libeplayer3/include/output.h | 79 + libeplayer3/include/pcm.h | 30 + libeplayer3/include/pes.h | 33 + libeplayer3/include/playback.h | 36 + libeplayer3/include/stm_ioctls.h | 325 ++++ libeplayer3/include/subtitle.h | 126 ++ libeplayer3/include/writer.h | 108 ++ libeplayer3/manager/audio.c | 249 ++++ libeplayer3/manager/manager.c | 93 ++ libeplayer3/manager/subtitle.c | 253 ++++ libeplayer3/manager/video.c | 242 +++ libeplayer3/output/linuxdvb.c | 1210 +++++++++++++++ libeplayer3/output/output.c | 353 +++++ libeplayer3/output/output_subtitle.c | 845 +++++++++++ libeplayer3/output/writer/aac.c | 290 ++++ libeplayer3/output/writer/ac3.c | 151 ++ libeplayer3/output/writer/divx.c | 215 +++ libeplayer3/output/writer/dts.c | 168 +++ libeplayer3/output/writer/flac.c | 151 ++ libeplayer3/output/writer/framebuffer.c | 196 +++ libeplayer3/output/writer/h263.c | 176 +++ libeplayer3/output/writer/h264.c | 439 ++++++ libeplayer3/output/writer/misc.c | 126 ++ libeplayer3/output/writer/mp3.c | 164 ++ libeplayer3/output/writer/mpeg2.c | 178 +++ libeplayer3/output/writer/pcm.c | 345 +++++ libeplayer3/output/writer/pes.c | 156 ++ libeplayer3/output/writer/vc1.c | 292 ++++ libeplayer3/output/writer/vorbis.c | 151 ++ libeplayer3/output/writer/wma.c | 183 +++ libeplayer3/output/writer/wmv.c | 280 ++++ libeplayer3/output/writer/writer.c | 141 ++ libeplayer3/playback/playback.c | 1061 +++++++++++++ libeplayer3/tools/eplayer2.c | 612 ++++++++ libeplayer3/tools/meta.c | 79 + 49 files changed, 13696 insertions(+) create mode 100644 libeplayer3/Makefile.am create mode 100644 libeplayer3/README create mode 100644 libeplayer3/container/container.c create mode 100644 libeplayer3/container/container_ass.c create mode 100644 libeplayer3/container/container_ffmpeg.c create mode 100644 libeplayer3/container/text_srt.c create mode 100644 libeplayer3/container/text_ssa.c create mode 100644 libeplayer3/include/aac.h create mode 100644 libeplayer3/include/common.h create mode 100644 libeplayer3/include/container.h create mode 100644 libeplayer3/include/debug.h create mode 100644 libeplayer3/include/ffmpeg_metadata.h create mode 100644 libeplayer3/include/manager.h create mode 100644 libeplayer3/include/misc.h create mode 100644 libeplayer3/include/output.h create mode 100644 libeplayer3/include/pcm.h create mode 100644 libeplayer3/include/pes.h create mode 100644 libeplayer3/include/playback.h create mode 100644 libeplayer3/include/stm_ioctls.h create mode 100644 libeplayer3/include/subtitle.h create mode 100644 libeplayer3/include/writer.h create mode 100644 libeplayer3/manager/audio.c create mode 100644 libeplayer3/manager/manager.c create mode 100644 libeplayer3/manager/subtitle.c create mode 100644 libeplayer3/manager/video.c create mode 100644 libeplayer3/output/linuxdvb.c create mode 100644 libeplayer3/output/output.c create mode 100644 libeplayer3/output/output_subtitle.c create mode 100644 libeplayer3/output/writer/aac.c create mode 100644 libeplayer3/output/writer/ac3.c create mode 100644 libeplayer3/output/writer/divx.c create mode 100644 libeplayer3/output/writer/dts.c create mode 100644 libeplayer3/output/writer/flac.c create mode 100644 libeplayer3/output/writer/framebuffer.c create mode 100644 libeplayer3/output/writer/h263.c create mode 100644 libeplayer3/output/writer/h264.c create mode 100644 libeplayer3/output/writer/misc.c create mode 100644 libeplayer3/output/writer/mp3.c create mode 100644 libeplayer3/output/writer/mpeg2.c create mode 100644 libeplayer3/output/writer/pcm.c create mode 100644 libeplayer3/output/writer/pes.c create mode 100644 libeplayer3/output/writer/vc1.c create mode 100644 libeplayer3/output/writer/vorbis.c create mode 100644 libeplayer3/output/writer/wma.c create mode 100644 libeplayer3/output/writer/wmv.c create mode 100644 libeplayer3/output/writer/writer.c create mode 100644 libeplayer3/playback/playback.c create mode 100644 libeplayer3/tools/eplayer2.c create mode 100644 libeplayer3/tools/meta.c diff --git a/libeplayer3/Makefile.am b/libeplayer3/Makefile.am new file mode 100644 index 0000000..24a6b98 --- /dev/null +++ b/libeplayer3/Makefile.am @@ -0,0 +1,29 @@ +lib_LTLIBRARIES = libeplayer3.la + +CXXFLAGS = -Wall + +INCLUDES = \ + -Iinclude + +libeplayer3_la_SOURCES = \ + container/container.c container/container_ffmpeg.c container/text_srt.c \ + container/text_ssa.c container/container_ass.c \ + manager/audio.c manager/manager.c manager/subtitle.c manager/video.c \ + output/output_subtitle.c output/linuxdvb.c output/output.c \ + playback/playback.c output/writer/writer.c output/writer/aac.c output/writer/wmv.c \ + output/writer/ac3.c output/writer/divx.c output/writer/wma.c output/writer/pes.c \ + output/writer/dts.c output/writer/mpeg2.c output/writer/mp3.c output/writer/misc.c \ + output/writer/h264.c output/writer/h263.c output/writer/vc1.c output/writer/framebuffer.c \ + output/writer/vorbis.c output/writer/flac.c output/writer/pcm.c + +AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ +-D_LARGEFILE64_SOURCE + +libeplayer3_la_LIBADD = -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng + +bin_PROGRAMS = eplayer3 meta +eplayer3_SOURCES = tools/eplayer2.c +eplayer3_LDADD = -leplayer3 -lpthread -lass -lm -lpng + +meta_SOURCES = tools/meta.c +meta_LDADD = -leplayer3 -lpthread -lass -lm -lpng diff --git a/libeplayer3/README b/libeplayer3/README new file mode 100644 index 0000000..fce93a3 --- /dev/null +++ b/libeplayer3/README @@ -0,0 +1,72 @@ +/* + * SCOPE: + * ------- + * + * libeplayer3 was developed to create a cleaner and more stable + * version of the libeplayer2. + * Currently the lib supports only one container, which handle all + * files by using the ffmpeg library. + * + * FEATURES: + * ----------------------- + * + * - more stable than libeplayer2. + * - more multimedia files are supported than libeplayer2. + * - mms stream support. + * - new videocodec support: + * - wmv and vc1 (sti7109 & sti7111 & sti7105 only). + * - flv. + * - improved http streaming support + * - subtitle rendering (ssa / ass) by using libass + * + * STYLE GUIDELINES: + * ------------------ + * + * If you decide to add some lines of code please ensure the following: + * - do not use a windows editor. + * - a tab must be emulated by 4 spaces (most editors support this). + * If you accidental break this rule use astyle to reorganize indentation, + * and dos2unix to remove windows style. + * + * Programming GUIDLINES: + * ----------------------- + * + * - the compiler is intentionally set to Wall, it would be nice if all + * programmer looks for warnings and solve them. + * - make sanity checks where ever you can. + * - freeing memory is an act of solidarity, but it also increases uptime + * of your receiver. ;) + * - if you detect stuff which may be generic, then make it generic. + * - commenting code is not a bad idea. + * + * KNOWN BUGS / PROBLEMS: + * ---------------------- + * + * - reverse playback needs improvement + * - some formats makes problems ? + * - getting stream info currently leads to a memory leak in e2. this is + * not a problem of this implementation its also exists in libeplayer2. + * e2 delivers a strdupped variable which is overwritten by what the container + * delivers. this is very hacky ;) -> (see comment in container_ffmpeg_get_info) + * + * License: + * -------- + * + * Copyright (C) 2010 crow, schischu, hellmaster1024 and konfetti. + * + * + * 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 + * + */ diff --git a/libeplayer3/container/container.c b/libeplayer3/container/container.c new file mode 100644 index 0000000..5e37932 --- /dev/null +++ b/libeplayer3/container/container.c @@ -0,0 +1,125 @@ +/* + * Main Container Handling. + * + * + * 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 + * + */ + +#include +#include + +#include "common.h" + +#define CONTAINER_DEBUG + +#ifdef CONTAINER_DEBUG + +static short debug_level = 10; + +#define container_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define container_printf(level, x...) +#endif + +#ifndef CONTAINER_SILENT +#define container_err(x...) do { printf(x); } while (0) +#else +#define container_err(x...) +#endif + + +static const char FILENAME[] = __FILE__; + +static void printContainerCapabilities() { + int i, j; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + container_printf(10, "Capabilities: "); + + for (i = 0; AvailableContainer[i] != NULL; i++) + for (j = 0; AvailableContainer[i]->Capabilities[j] != NULL; j++) + container_printf(10, "%s ", AvailableContainer[i]->Capabilities[j]); + container_printf(10, "\n"); +} + +static int selectContainer(Context_t *context, char * extension) { + int i, j; + int ret = -1; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + for (i = 0; AvailableContainer[i] != NULL; i++) + { + for (j = 0; AvailableContainer[i]->Capabilities[j] != NULL; j++) + if (!strcasecmp(AvailableContainer[i]->Capabilities[j], extension)) { + context->container->selectedContainer = AvailableContainer[i]; + + container_printf(10, "Selected Container: %s\n", context->container->selectedContainer->Name); + ret = 0; + break; + } + if (ret == 0) + break; + } + + if (ret != 0) { + container_err("No Container found :-(\n"); + } + + return ret; +} + + +static int Command(void *_context, ContainerCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; + int ret = 0; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case CONTAINER_ADD: { + ret = selectContainer(context, (char*) argument); + break; + } + case CONTAINER_CAPABILITIES: { + printContainerCapabilities(); + break; + } + case CONTAINER_DEL: { + context->container->selectedContainer = NULL; + break; + } + default: + container_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + break; + } + + return ret; +} + +extern Container_t SrtContainer; +extern Container_t SsaContainer; +extern Container_t ASSContainer; + +ContainerHandler_t ContainerHandler = { + "Output", + NULL, + &SrtContainer, + &SsaContainer, + &ASSContainer, + Command, +}; diff --git a/libeplayer3/container/container_ass.c b/libeplayer3/container/container_ass.c new file mode 100644 index 0000000..04efee4 --- /dev/null +++ b/libeplayer3/container/container_ass.c @@ -0,0 +1,817 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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); + + 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! + */ + 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); + } + } + + /* 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, + +}; diff --git a/libeplayer3/container/container_ffmpeg.c b/libeplayer3/container/container_ffmpeg.c new file mode 100644 index 0000000..4261290 --- /dev/null +++ b/libeplayer3/container/container_ffmpeg.c @@ -0,0 +1,1734 @@ +/* + * Container handling for all stream's handled by ffmpeg + * konfetti 2010; based on code from crow + * + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "misc.h" +#include "debug.h" +#include "aac.h" +#include "pcm.h" +#include "ffmpeg_metadata.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define FFMPEG_DEBUG + +#ifdef FFMPEG_DEBUG + +static short debug_level = 10; + +#define ffmpeg_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ffmpeg_printf(level, fmt, x...) +#endif + +#ifndef FFMPEG_SILENT +#define ffmpeg_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ffmpeg_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_CONTAINER_FFMPEG_NO_ERROR 0 +#define cERR_CONTAINER_FFMPEG_INIT -1 +#define cERR_CONTAINER_FFMPEG_NOT_SUPPORTED -2 +#define cERR_CONTAINER_FFMPEG_INVALID_FILE -3 +#define cERR_CONTAINER_FFMPEG_RUNNING -4 +#define cERR_CONTAINER_FFMPEG_NOMEM -5 +#define cERR_CONTAINER_FFMPEG_OPEN -6 +#define cERR_CONTAINER_FFMPEG_STREAM -7 +#define cERR_CONTAINER_FFMPEG_NULL -8 +#define cERR_CONTAINER_FFMPEG_ERR -9 +#define cERR_CONTAINER_FFMPEG_END_OF_FILE -10 + +static const char* FILENAME = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_mutex_t mutex; + +static pthread_t PlayThread; +static int hasPlayThreadStarted = 0; + +static AVFormatContext* avContext = NULL; + +static unsigned char isContainerRunning = 0; + +static long long int latestPts = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +static int container_ffmpeg_seek_bytes(off_t pos); +static int container_ffmpeg_seek(Context_t *context, float sec); +static int container_ffmpeg_seek_rel(Context_t *context, off_t pos, long long int pts, float sec); +static int container_ffmpeg_seek_bytes_rel(off_t start, off_t bytes); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void getMutex(const char *filename, const char *function, int line) { + ffmpeg_printf(100, "::%d requesting mutex\n", line); + + pthread_mutex_lock(&mutex); + + ffmpeg_printf(100, "::%d received mutex\n", line); +} + +void releaseMutex(const char *filename, const const char *function, int line) { + pthread_mutex_unlock(&mutex); + + ffmpeg_printf(100, "::%d released mutex\n", line); +} + +static char* Codec2Encoding(enum CodecID id, int* version) +{ + switch (id) + { + case CODEC_ID_MPEG1VIDEO: + return "V_MPEG1"; + case CODEC_ID_MPEG2VIDEO: + return "V_MPEG1"; + case CODEC_ID_H263: + case CODEC_ID_H263P: + case CODEC_ID_H263I: + return "V_H263"; + case CODEC_ID_FLV1: + return "V_FLV"; + case CODEC_ID_VP5: + case CODEC_ID_VP6: + case CODEC_ID_VP6F: + return "V_VP6"; + case CODEC_ID_RV10: + case CODEC_ID_RV20: + return "V_RMV"; + case CODEC_ID_MPEG4: +#if LIBAVCODEC_VERSION_MAJOR < 53 + case CODEC_ID_XVID: +#endif + case CODEC_ID_MSMPEG4V1: + case CODEC_ID_MSMPEG4V2: + case CODEC_ID_MSMPEG4V3: + return "V_MSCOMP"; + case CODEC_ID_WMV1: + *version = 1; + return "V_WMV"; + case CODEC_ID_WMV2: + *version = 2; + return "V_WMV"; + case CODEC_ID_WMV3: + *version = 3; + return "V_WMV"; + case CODEC_ID_VC1: + return "V_VC1"; + case CODEC_ID_H264: + case CODEC_ID_FFH264: + return "V_MPEG4/ISO/AVC"; + case CODEC_ID_AVS: + return "V_AVS"; + case CODEC_ID_MP2: + return "A_MPEG/L3"; + case CODEC_ID_MP3: + return "A_MP3"; + case CODEC_ID_AAC: + return "A_AAC"; + case CODEC_ID_AC3: + return "A_AC3"; + case CODEC_ID_DTS: + return "A_DTS"; + case CODEC_ID_WMAV1: + case CODEC_ID_WMAV2: + case 86056: //CODEC_ID_WMAPRO + return "A_WMA"; + case CODEC_ID_MLP: + return "A_MLP"; + case CODEC_ID_RA_144: + return "A_RMA"; + case CODEC_ID_RA_288: + return "A_RMA"; + case CODEC_ID_VORBIS: + return "A_IPCM"; //return "A_VORBIS"; + case CODEC_ID_FLAC: //86030 + return "A_IPCM"; //return "A_FLAC"; +/* subtitle */ + case CODEC_ID_SSA: + return "S_TEXT/ASS"; /* Hellmaster1024: seems to be ASS instead of SSA */ + case CODEC_ID_TEXT: /* Hellmaster1024: i dont have most of this, but lets hope it is normal text :-) */ + case CODEC_ID_DVD_SUBTITLE: + case CODEC_ID_DVB_SUBTITLE: + case CODEC_ID_XSUB: + case CODEC_ID_MOV_TEXT: + case CODEC_ID_HDMV_PGS_SUBTITLE: + case CODEC_ID_DVB_TELETEXT: + return "S_TEXT/SRT"; /* fixme */ + case CODEC_ID_SRT: + return "S_TEXT/SRT"; /* fixme */ + default: + ffmpeg_err("ERROR! CODEC NOT FOUND -> %d\n",id); + } + return NULL; +} + +long long int calcPts(AVStream* stream, AVPacket* packet) +{ + long long int pts; + + if ((stream == NULL) || (packet == NULL)) + { + ffmpeg_err("stream / packet null\n"); + return INVALID_PTS_VALUE; + } + + if(packet->pts == AV_NOPTS_VALUE) + pts = INVALID_PTS_VALUE; + else if (avContext->start_time == AV_NOPTS_VALUE) + pts = 90000.0 * (double)packet->pts * av_q2d(stream->time_base); + else + pts = 90000.0 * (((double)(packet->pts) * av_q2d(stream->time_base)) - (avContext->start_time / AV_TIME_BASE)); + + if (pts & 0x8000000000000000ull) + pts = INVALID_PTS_VALUE; + + return pts; +} + +/*Hellmaster1024: get the Duration of the subtitle from the SSA line*/ +float getDurationFromSSALine(unsigned char* line){ + int i,h,m,s,ms; + char* Text = strdup((char*) line); + char* ptr1; + char* ptr[10]; + long int msec; + + ptr1 = Text; + ptr[0]=Text; + for (i=0; i < 3 && *ptr1 != '\0'; ptr1++) { + if (*ptr1 == ',') { + ptr[++i]=ptr1+1; + *ptr1 = '\0'; + } + } + + sscanf(ptr[2],"%d:%d:%d.%d",&h,&m,&s,&ms); + msec = (ms*10) + (s*1000) + (m*60*1000) + (h*24*60*1000); + sscanf(ptr[1],"%d:%d:%d.%d",&h,&m,&s,&ms); + msec -= (ms*10) + (s*1000) + (m*60*1000) + (h*24*60*1000); + + ffmpeg_printf(10, "%s %s %f\n", ptr[2], ptr[1], (float) msec / 1000.0); + + free(Text); + return (float)msec/1000.0; +} + +/* search for metatdata in context and stream + * and map it to our metadata. + */ +static char* searchMeta(AVMetadata *metadata, char* ourTag) +{ + AVMetadataTag *tag = NULL; + int i = 0; + + while (metadata_map[i] != NULL) + { + if (strcmp(ourTag, metadata_map[i]) == 0) + { + while ((tag = av_metadata_get(metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + { + if (strcmp(tag->key, metadata_map[ i + 1 ]) == 0) + { + return tag->value; + } + } + } + i++; + } + + return NULL; +} + +/* **************************** */ +/* Worker Thread */ +/* **************************** */ + +static void FFMPEGThread(Context_t *context) { + AVPacket packet; + off_t currentReadPosition = 0; /* last read position */ + off_t lastReverseSeek = 0; /* max address to read before seek again in reverse play */ + off_t lastSeek = -1; + long long int lastPts = -1, currentVideoPts = -1, currentAudioPts = -1, showtime = 0, bofcount = 0; + int err = 0, gotlastPts = 0, audioMute = 0; + AudioVideoOut_t avOut; + + /* Softdecoding buffer*/ + unsigned char *samples = NULL; + + + ffmpeg_printf(10, "\n"); + + while ( context->playback->isCreationPhase ) + { + ffmpeg_err("Thread waiting for end of init phase...\n"); + usleep(1000); + } + ffmpeg_printf(10, "Running!\n"); + + while ( context && context->playback && context->playback->isPlaying ) { + + //IF MOVIE IS PAUSED, WAIT + if (context->playback->isPaused) { + ffmpeg_printf(20, "paused\n"); + + usleep(100000); + continue; + } + + if (context->playback->isSeeking) { + ffmpeg_printf(10, "seeking\n"); + + usleep(100000); + continue; + } + +#define reverse_playback_3 +#ifdef reverse_playback_3 +if (context->playback->BackWard && av_gettime() >= showtime) +{ + audioMute = 1; + context->output->Command(context, OUTPUT_CLEAR, "v"); + + if(bofcount == 1) + { + showtime = av_gettime(); + usleep(100000); + continue; + } + + if(lastPts == -1) + { + if(currentVideoPts != -1) + lastPts = currentVideoPts; + else + lastPts = currentAudioPts; + } + + + if((err = container_ffmpeg_seek_rel(context, lastSeek, lastPts, (float) context->playback->Speed)) < 0) + { + ffmpeg_err( "Error seeking\n"); + + if (err == cERR_CONTAINER_FFMPEG_END_OF_FILE) + { + bofcount = 1; + } + } + + lastPts = lastPts + (context->playback->Speed * 90000); + showtime = av_gettime() + 300000; //jump back all 300ms +} + +if(!context->playback->BackWard && audioMute) +{ + lastPts = -1; + bofcount = 0; + showtime = 0; + audioMute = 0; + context->output->Command(context, OUTPUT_AUDIOMUTE, "0"); +} +#endif + +#ifdef reverse_playback_2 + /* should we seek back again ? + * reverse play and currentReadPosition >= end of seek reverse play area ? */ + if ((context->playback->BackWard) && (currentReadPosition >= lastReverseSeek)) + { + /* fixme: surplus detection */ + int surplus = 1; + + ffmpeg_printf(20, "new seek ->c %lld, l %lld, ls %lld, lp %lld\n", currentReadPosition, lastReverseSeek, lastSeek, lastPts); + + context->output->Command(context, OUTPUT_DISCONTINUITY_REVERSE, &surplus); + + /* save the maximum read position, if we reach this, we must + * seek back again. + */ + if(lastReverseSeek == 0) + lastReverseSeek = currentReadPosition; + else + lastReverseSeek = lastSeek; + +#define use_sec_to_seek +#if defined(use_sec_to_seek) + if ((err = container_ffmpeg_seek_rel(context, lastSeek, lastPts, -5)) < 0) +#else + if ((err = container_ffmpeg_seek_bytes_rel(lastSeek, /* context->playback->BackWard */ -188 * 200)) < 0) +#endif + { + ffmpeg_err( "Error seeking\n"); + + if (err == cERR_CONTAINER_FFMPEG_END_OF_FILE) + { + break; + } + } + else + { + lastSeek = currentReadPosition = url_ftell(avContext->pb); + gotlastPts = 1; + +#ifndef use_sec_to_seek + if (err != lastSeek) + ffmpeg_err("upssssssssssssssss seek not doing what I want\n"); +#endif + +/* + if (currentVideoPts != -1) + lastPts = currentVideoPts; + else + lastPts = currentAudioPts; +*/ + } + } else + if (!context->playback->BackWard) + { + lastReverseSeek = 0; + lastSeek = -1; + lastPts = -1; + gotlastPts = 0; + } + +#endif + getMutex(FILENAME, __FUNCTION__,__LINE__); + +#define use_read_frame +#ifdef use_read_frame + if (av_read_frame(avContext, &packet) == 0 ) +#else + if (av_read_packet(avContext, &packet) == 0 ) +#endif + { + long long int pts; + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * subtitleTrack = NULL; + + int index = packet.stream_index; + + currentReadPosition = url_ftell(avContext->pb); + + if (context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack) < 0) + ffmpeg_err("error getting video track\n"); + + if (context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack) < 0) + ffmpeg_err("error getting audio track\n"); + + if (context->manager->subtitle->Command(context, MANAGER_GET_TRACK, &subtitleTrack) < 0) + ffmpeg_err("error getting subtitle track\n"); + + ffmpeg_printf(200, "packet.size %d - index %d\n", packet.size, index); + + if (videoTrack != NULL) { + if (videoTrack->Id == index) { + currentVideoPts = videoTrack->pts = pts = calcPts(videoTrack->stream, &packet); + + if ((currentVideoPts > latestPts) && (currentVideoPts != INVALID_PTS_VALUE)) + latestPts = currentVideoPts; + +#ifdef reverse_playback_2 + if (currentVideoPts != INVALID_PTS_VALUE && gotlastPts == 1) + { + lastPts = currentVideoPts; + gotlastPts = 0; + } +#endif + + ffmpeg_printf(200, "VideoTrack index = %d %lld\n",index, currentVideoPts); + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = videoTrack->extraData; + avOut.extralen = videoTrack->extraSize; + avOut.frameRate = videoTrack->frame_rate; + avOut.timeScale = videoTrack->TimeScale; + avOut.width = videoTrack->width; + avOut.height = videoTrack->height; + avOut.type = "video"; + + if (context->output->video->Write(context, &avOut) < 0) { + ffmpeg_err("writing data to video device failed\n"); + } + } + } + + if (audioTrack != NULL) { + if (audioTrack->Id == index) { + currentAudioPts = audioTrack->pts = pts = calcPts(audioTrack->stream, &packet); + + if ((currentAudioPts > latestPts) && (!videoTrack)) + latestPts = currentAudioPts; + +#ifdef reverse_playback_2 + if (currentAudioPts != INVALID_PTS_VALUE && gotlastPts == 1 && (!videoTrack)) + { + lastPts = currentAudioPts; + gotlastPts = 0; + } +#endif + + ffmpeg_printf(200, "AudioTrack index = %d\n",index); + + if (audioTrack->inject_as_pcm == 1) + { + int bytesDone = 0; + unsigned int samples_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + AVPacket avpkt; + avpkt = packet; + + // This way the buffer is only allocated if we really need it + if(samples == NULL) + samples = (unsigned char *)malloc(samples_size); + + while(avpkt.size > 0) + { + int decoded_data_size = samples_size; + + bytesDone = avcodec_decode_audio3(( (AVStream*) audioTrack->stream)->codec, + (short *)(samples), &decoded_data_size, &avpkt); + + + if(bytesDone < 0) // Error Happend + break; + + avpkt.data += bytesDone; + avpkt.size -= bytesDone; + + if(decoded_data_size <= 0) + continue; + + pcmPrivateData_t extradata; + extradata.uNoOfChannels = ((AVStream*) audioTrack->stream)->codec->channels; + extradata.uSampleRate = ((AVStream*) audioTrack->stream)->codec->sample_rate; + extradata.uBitsPerSample = 16; + extradata.bLittleEndian = 1; + + avOut.data = samples; + avOut.len = decoded_data_size; + + avOut.pts = pts; + avOut.extradata = &extradata; + avOut.extralen = sizeof(extradata); + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + ffmpeg_err("writing data to audio device failed\n"); + } + } + else if (audioTrack->have_aacheader == 1) + { + ffmpeg_printf(200,"write audio aac\n"); + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = audioTrack->aacbuf; + avOut.extralen = audioTrack->aacbuflen; + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + { + ffmpeg_err("(aac) writing data to audio device failed\n"); + } + } + else + { + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = NULL; + avOut.extralen = 0; + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + { + ffmpeg_err("writing data to audio device failed\n"); + } + } + } + } + + if (subtitleTrack != NULL) { + if (subtitleTrack->Id == index) { + float duration=3.0; + ffmpeg_printf(100, "subtitleTrack->stream %p \n", subtitleTrack->stream); + + pts = calcPts(subtitleTrack->stream, &packet); + + if ((pts > latestPts) && (!videoTrack) && (!audioTrack)) + latestPts = pts; + + /*Hellmaster1024: in mkv the duration for ID_TEXT is stored in convergence_duration */ + ffmpeg_printf(20, "Packet duration %d\n", packet.duration); + ffmpeg_printf(20, "Packet convergence_duration %lld\n", packet.convergence_duration); + + if(packet.duration != 0 && packet.duration != AV_NOPTS_VALUE ) + duration=((float)packet.duration)/1000.0; + else if(packet.convergence_duration != 0 && packet.convergence_duration != AV_NOPTS_VALUE ) + duration=((float)packet.convergence_duration)/1000.0; + else if(((AVStream*)subtitleTrack->stream)->codec->codec_id == CODEC_ID_SSA) + { + /*Hellmaster1024 if the duration is not stored in packet.duration or + packet.convergence_duration we need to calculate it any other way, for SSA it is stored in + the Text line*/ + duration = getDurationFromSSALine(packet.data); + } else { + /* no clue yet */ + } + + + /* konfetti: I've found cases where the duration from getDurationFromSSALine + * is zero (start end and are really the same in text). I think it make's + * no sense to pass those. + */ + if (duration > 0.0) + { + /* is there a decoder ? */ + if (avcodec_find_decoder(((AVStream*) subtitleTrack->stream)->codec->codec_id) != NULL) + { + AVSubtitle sub; + int got_sub_ptr; + + if (avcodec_decode_subtitle2(((AVStream*) subtitleTrack->stream)->codec, &sub, &got_sub_ptr, &packet) < 0) + { + ffmpeg_err("error decoding subtitle\n"); + } else + { + int i; + + ffmpeg_printf(0, "format %d\n", sub.format); + ffmpeg_printf(0, "start_display_time %d\n", sub.start_display_time); + ffmpeg_printf(0, "end_display_time %d\n", sub.end_display_time); + ffmpeg_printf(0, "num_rects %d\n", sub.num_rects); + ffmpeg_printf(0, "pts %lld\n", sub.pts); + + for (i = 0; i < sub.num_rects; i++) + { + + ffmpeg_printf(0, "x %d\n", sub.rects[i]->x); + ffmpeg_printf(0, "y %d\n", sub.rects[i]->y); + ffmpeg_printf(0, "w %d\n", sub.rects[i]->w); + ffmpeg_printf(0, "h %d\n", sub.rects[i]->h); + ffmpeg_printf(0, "nb_colors %d\n", sub.rects[i]->nb_colors); + ffmpeg_printf(0, "type %d\n", sub.rects[i]->type); + ffmpeg_printf(0, "text %s\n", sub.rects[i]->text); + ffmpeg_printf(0, "ass %s\n", sub.rects[i]->ass); + // pict ->AVPicture + + } + } + + } + else + if(((AVStream*)subtitleTrack->stream)->codec->codec_id == CODEC_ID_SSA) + { + SubtitleData_t data; + + ffmpeg_printf(10, "videoPts %lld\n", currentVideoPts); + + data.data = packet.data; + data.len = packet.size; + data.extradata = subtitleTrack->extraData; + data.extralen = subtitleTrack->extraSize; + data.pts = pts; + data.duration = duration; + + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + } + else + { + /* hopefully native text ;) */ + + unsigned char* line = text_to_ass((char *)packet.data,pts/90,duration); + ffmpeg_printf(50,"text line is %s\n",(char *)packet.data); + ffmpeg_printf(50,"Sub line is %s\n",line); + ffmpeg_printf(20, "videoPts %lld %f\n", currentVideoPts,currentVideoPts/90000.0); + SubtitleData_t data; + data.data = line; + data.len = strlen((char*)line); + data.extradata = DEFAULT_ASS_HEAD; + data.extralen = strlen(DEFAULT_ASS_HEAD); + data.pts = pts; + data.duration = duration; + + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + free(line); + } + } /* duration */ + } + } + + if (packet.data) + av_free_packet(&packet); + } + else { + ffmpeg_err("no data ->end of file reached ? \n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + break; + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + } /* while */ + + // Freeing the allocated buffer for softdecoding + if (samples != NULL) { + free(samples); + samples = NULL; + } + + hasPlayThreadStarted = 0; + + ffmpeg_printf(10, "terminating\n"); +} + +/* **************************** */ +/* Container part for ffmpeg */ +/* **************************** */ + +int container_ffmpeg_init(Context_t *context, char * filename) +{ + int n, err; + + ffmpeg_printf(10, ">\n"); + + if (filename == NULL) { + ffmpeg_err("filename NULL\n"); + + return cERR_CONTAINER_FFMPEG_NULL; + } + + if (context == NULL) { + ffmpeg_err("context NULL\n"); + + return cERR_CONTAINER_FFMPEG_NULL; + } + + ffmpeg_printf(10, "filename %s\n", filename); + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + if (isContainerRunning) { + ffmpeg_err("ups already running?\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_RUNNING; + } + + /* initialize ffmpeg */ + avcodec_register_all(); + av_register_all(); + + if ((err = av_open_input_file(&avContext, filename, NULL, 0, NULL)) != 0) { + char error[512]; + + ffmpeg_err("av_open_input_file failed %d (%s)\n", err, filename); + av_strerror(err, error, 512); + ffmpeg_err("Cause: %s\n", error); + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_OPEN; + } + + avContext->flags = AVFMT_FLAG_GENPTS; + + ffmpeg_printf(20, "find_streaminfo\n"); + + if (av_find_stream_info(avContext) < 0) { + ffmpeg_err("Error av_find_stream_info\n"); +#ifdef this_is_ok + /* crow reports that sometimes this returns an error + * but the file is played back well. so remove this + * until other works are done and we can prove this. + */ + av_close_input_file(avContext); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_STREAM; +#endif + } + + ffmpeg_printf(20, "dump format\n"); + + dump_format(avContext, 0, filename, 0); + + ffmpeg_printf(1, "number streams %d\n", avContext->nb_streams); + + for ( n = 0; n < avContext->nb_streams; n++) { + Track_t track; + AVStream *stream = avContext->streams[n]; + int version = 0; + + char* encoding = Codec2Encoding(stream->codec->codec_id, &version); + + if (encoding != NULL) + ffmpeg_printf(1, "%d. encoding = %s - version %d\n", n, encoding, version); + + /* some values in track are unset and therefor copyTrack segfaults. + * so set it by default to NULL! + */ + memset(&track, 0, sizeof(track)); + + switch (stream->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ffmpeg_printf(10, "CODEC_TYPE_VIDEO %d\n",stream->codec->codec_type); + + if (encoding != NULL) { + track.type = eTypeES; + track.version = version; + + track.width = stream->codec->width; + track.height = stream->codec->height; + + track.extraData = stream->codec->extradata; + track.extraSize = stream->codec->extradata_size; + + track.frame_rate = stream->r_frame_rate.num; + + track.aacbuf = 0; + track.have_aacheader = -1; + + double frame_rate = av_q2d(stream->r_frame_rate); /* rational to double */ + + ffmpeg_printf(10, "frame_rate = %f\n", frame_rate); + + track.frame_rate = frame_rate * 1000.0; + + /* fixme: revise this */ + + if (track.frame_rate < 23970) + track.TimeScale = 1001; + else + track.TimeScale = 1000; + + ffmpeg_printf(10, "bit_rate = %d\n",stream->codec->bit_rate); + ffmpeg_printf(10, "flags = %d\n",stream->codec->flags); + ffmpeg_printf(10, "frame_bits = %d\n",stream->codec->frame_bits); + ffmpeg_printf(10, "time_base.den %d\n",stream->time_base.den); + ffmpeg_printf(10, "time_base.num %d\n",stream->time_base.num); + ffmpeg_printf(10, "frame_rate %d\n",stream->r_frame_rate.num); + ffmpeg_printf(10, "TimeScale %d\n",stream->r_frame_rate.den); + + ffmpeg_printf(10, "frame_rate %d\n", track.frame_rate); + ffmpeg_printf(10, "TimeScale %d\n", track.TimeScale); + + track.Name = "und"; + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if (context->manager->video) + if (context->manager->video->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add track %d\n", n); + } + + } + else { + ffmpeg_err("codec type video but codec unknown %d\n", stream->codec->codec_id); + } + break; + case AVMEDIA_TYPE_AUDIO: + ffmpeg_printf(10, "CODEC_TYPE_AUDIO %d\n",stream->codec->codec_type); + + if (encoding != NULL) { + AVDictionaryEntry *lang; + track.type = eTypeES; + + lang = av_dict_get(stream->metadata, "language", NULL, 0); + + if (lang) + track.Name = strdup(lang->value); + else + track.Name = strdup("und"); + + ffmpeg_printf(10, "Language %s\n", track.Name); + + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + track.duration = (double)stream->duration * av_q2d(stream->time_base) * 1000.0; + + track.aacbuf = 0; + track.have_aacheader = -1; + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if(!strncmp(encoding, "A_IPCM", 6)) + { + track.inject_as_pcm = 1; + ffmpeg_printf(10, " Handle inject_as_pcm = %d\n", track.inject_as_pcm); + + AVCodec *codec = avcodec_find_decoder(stream->codec->codec_id); + +//( (AVStream*) audioTrack->stream)->codec->flags |= CODEC_FLAG_TRUNCATED; + if(codec != NULL && !avcodec_open(stream->codec, codec)) + printf("AVCODEC__INIT__SUCCESS\n"); + else + printf("AVCODEC__INIT__FAILED\n"); + + + } + else if(stream->codec->codec_id == CODEC_ID_AAC) { + ffmpeg_printf(10,"Create AAC ExtraData\n"); + ffmpeg_printf(10,"stream->codec->extradata_size %d\n", stream->codec->extradata_size); + Hexdump(stream->codec->extradata, stream->codec->extradata_size); + + /* extradata +13 10 56 e5 9d 48 00 (anderen cops) + object_type: 00010 2 = LC + sample_rate: 011 0 6 = 24000 + chan_config: 0010 2 = Stereo + 000 0 + 1010110 111 = 0x2b7 + 00101 = SBR + 1 + 0011 = 48000 + 101 01001000 = 0x548 + ps = 0 + 0000000 +*/ + + unsigned int object_type = 2; // LC + unsigned int sample_index = aac_get_sample_rate_index(stream->codec->sample_rate); + unsigned int chan_config = stream->codec->channels; + if(stream->codec->extradata_size >= 2) { + object_type = stream->codec->extradata[0] >> 3; + sample_index = ((stream->codec->extradata[0] & 0x7) << 1) + + (stream->codec->extradata[1] >> 7); + chan_config = (stream->codec->extradata[1] >> 3) && 0xf; + } + + ffmpeg_printf(10,"aac object_type %d\n", object_type); + ffmpeg_printf(10,"aac sample_index %d\n", sample_index); + ffmpeg_printf(10,"aac chan_config %d\n", chan_config); + + object_type -= 1; // Cause of ADTS + + track.aacbuflen = AAC_HEADER_LENGTH; + track.aacbuf = malloc(8); + track.aacbuf[0] = 0xFF; + track.aacbuf[1] = 0xF1; + track.aacbuf[2] = ((object_type & 0x03) << 6) | (sample_index << 2) | ((chan_config >> 2) & 0x01); + track.aacbuf[3] = (chan_config & 0x03) << 6; + track.aacbuf[4] = 0x00; + track.aacbuf[5] = 0x1F; + track.aacbuf[6] = 0xFC; + + printf("AAC_HEADER -> "); + Hexdump(track.aacbuf,7); + track.have_aacheader = 1; + + } else if(stream->codec->codec_id == CODEC_ID_WMAV1 + || stream->codec->codec_id == CODEC_ID_WMAV2 + || 86056 ) //CODEC_ID_WMAPRO) //if (stream->codec->extradata_size > 0) + { + ffmpeg_printf(10,"Create WMA ExtraData\n"); + track.aacbuflen = 104 + stream->codec->extradata_size; + track.aacbuf = malloc(track.aacbuflen); + memset (track.aacbuf, 0, track.aacbuflen); + unsigned char ASF_Stream_Properties_Object[16] = + {0x91,0x07,0xDC,0xB7,0xB7,0xA9,0xCF,0x11,0x8E,0xE6,0x00,0xC0,0x0C,0x20,0x53,0x65}; + memcpy(track.aacbuf + 0, ASF_Stream_Properties_Object, 16); // ASF_Stream_Properties_Object + memcpy(track.aacbuf + 16, &track.aacbuflen, 4); //FrameDateLength + + unsigned int sizehi = 0; + memcpy(track.aacbuf + 20, &sizehi, 4); // sizehi (not used) + + unsigned char ASF_Audio_Media[16] = + {0x40,0x9E,0x69,0xF8,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B}; + memcpy(track.aacbuf + 24, ASF_Audio_Media, 16); //ASF_Audio_Media + + unsigned char ASF_Audio_Spread[16] = + {0x50,0xCD,0xC3,0xBF,0x8F,0x61,0xCF,0x11,0x8B,0xB2,0x00,0xAA,0x00,0xB4,0xE2,0x20}; + memcpy(track.aacbuf + 40, ASF_Audio_Spread, 16); //ASF_Audio_Spread + + memset(track.aacbuf + 56, 0, 4); // time_offset (not used) + memset(track.aacbuf + 60, 0, 4); // time_offset_hi (not used) + + unsigned int type_specific_data_length = 18 + stream->codec->extradata_size; + memcpy(track.aacbuf + 64, &type_specific_data_length, 4); //type_specific_data_length + + unsigned int error_correction_data_length = 8; + memcpy(track.aacbuf + 68, &error_correction_data_length, 4); //error_correction_data_length + + unsigned short flags = 1; // stream_number + memcpy(track.aacbuf + 72, &flags, 2); //flags + + unsigned int reserved = 0; + memcpy(track.aacbuf + 74, &reserved, 4); // reserved + + // type_specific_data +#define WMA_VERSION_1 0x160 +#define WMA_VERSION_2_9 0x161 +#define WMA_VERSION_9_PRO 0x162 +#define WMA_LOSSLESS 0x163 + unsigned short codec_id = 0; + switch(stream->codec->codec_id) { + //TODO: What code for lossless ? + case 86056/*CODEC_ID_WMAPRO*/: + codec_id = WMA_VERSION_9_PRO; + break; + case CODEC_ID_WMAV2: + codec_id = WMA_VERSION_2_9 ; + break; + case CODEC_ID_WMAV1: + default: + codec_id = WMA_VERSION_1; + break; + } + memcpy(track.aacbuf + 78, &codec_id, 2); //codec_id + + unsigned short number_of_channels = stream->codec->channels; + memcpy(track.aacbuf + 80, &number_of_channels, 2); //number_of_channels + + unsigned int samples_per_second = stream->codec->sample_rate; + ffmpeg_printf(1, "samples_per_second = %d\n", samples_per_second); + memcpy(track.aacbuf + 82, &samples_per_second, 4); //samples_per_second + + unsigned int average_number_of_bytes_per_second = stream->codec->bit_rate / 8; + ffmpeg_printf(1, "average_number_of_bytes_per_second = %d\n", average_number_of_bytes_per_second); + memcpy(track.aacbuf + 86, &average_number_of_bytes_per_second, 4); //average_number_of_bytes_per_second + + unsigned short block_alignment = stream->codec->block_align; + ffmpeg_printf(1, "block_alignment = %d\n", block_alignment); + memcpy(track.aacbuf + 90, &block_alignment, 2); //block_alignment + + unsigned short bits_per_sample = + stream->codec->sample_fmt>=0?(stream->codec->sample_fmt+1)*8:8; + ffmpeg_printf(1, "bits_per_sample = %d (%d)\n", bits_per_sample, stream->codec->sample_fmt); + memcpy(track.aacbuf + 92, &bits_per_sample, 2); //bits_per_sample + + memcpy(track.aacbuf + 94, &stream->codec->extradata_size, 2); //bits_per_sample + + memcpy(track.aacbuf + 96, stream->codec->extradata, stream->codec->extradata_size); + + ffmpeg_printf(1, "aacbuf:\n"); + Hexdump(track.aacbuf, track.aacbuflen); + + //ffmpeg_printf(1, "priv_data:\n"); + //Hexdump(stream->codec->priv_data, track.aacbuflen); + + track.have_aacheader = 1; + } + + if (context->manager->audio) + { + if (context->manager->audio->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add track %d\n", n); + } + } + + } + else { + ffmpeg_err("codec type audio but codec unknown %d\n", stream->codec->codec_id); + } + break; + case AVMEDIA_TYPE_SUBTITLE: + { + AVDictionaryEntry *lang; + + ffmpeg_printf(10, "CODEC_TYPE_SUBTITLE %d\n",stream->codec->codec_type); + + lang = av_dict_get(stream->metadata, "language", NULL, 0); + + if (lang) + track.Name = strdup(lang->value); + else + track.Name = strdup("und"); + + ffmpeg_printf(10, "Language %s\n", track.Name); + + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + track.duration = (double)stream->duration * av_q2d(stream->time_base) * 1000.0; + + track.aacbuf = 0; + track.have_aacheader = -1; + + track.width = -1; /* will be filled online from videotrack */ + track.height = -1; /* will be filled online from videotrack */ + + track.extraData = stream->codec->extradata; + track.extraSize = stream->codec->extradata_size; + + ffmpeg_printf(1, "subtitle codec %d\n", stream->codec->codec_id); + ffmpeg_printf(1, "subtitle width %d\n", stream->codec->width); + ffmpeg_printf(1, "subtitle height %d\n", stream->codec->height); + ffmpeg_printf(1, "subtitle stream %p\n", stream); + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if (track.Name) + ffmpeg_printf(10, "FOUND SUBTITLE %s\n", track.Name); + + if (context->manager->subtitle) + if (context->manager->subtitle->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add subtitle track %d\n", n); + } + + break; + } + case AVMEDIA_TYPE_UNKNOWN: + case AVMEDIA_TYPE_DATA: + case AVMEDIA_TYPE_ATTACHMENT: + case AVMEDIA_TYPE_NB: + default: + ffmpeg_err("not handled or unknown codec_type %d\n", stream->codec->codec_type); + break; + } + + } /* for */ + + /* init */ + latestPts = 0; + isContainerRunning = 1; + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_play(Context_t *context) +{ + int error; + int ret = 0; + pthread_attr_t attr; + + ffmpeg_printf(10, "\n"); + + if ( context && context->playback && context->playback->isPlaying ) { + ffmpeg_printf(10, "is Playing\n"); + } + else { + ffmpeg_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 *)&FFMPEGThread, context)) != 0) { + ffmpeg_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error)); + + hasPlayThreadStarted = 0; + ret = cERR_CONTAINER_FFMPEG_ERR; + } + else { + ffmpeg_printf(10, "Created thread\n"); + + hasPlayThreadStarted = 1; + } + } + else { + ffmpeg_printf(10, "A thread already exists!\n"); + + ret = cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int container_ffmpeg_stop(Context_t *context) { + int ret = cERR_CONTAINER_FFMPEG_NO_ERROR; + int wait_time = 20; + + ffmpeg_printf(10, "\n"); + + if (!isContainerRunning) + { + ffmpeg_err("Container not running\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + while ( (hasPlayThreadStarted != 0) && (--wait_time) > 0 ) { + ffmpeg_printf(10, "Waiting for ffmpeg thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + ffmpeg_err( "Timeout waiting for thread!\n"); + + ret = cERR_CONTAINER_FFMPEG_ERR; + } + + hasPlayThreadStarted = 0; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + if (avContext != NULL) { + av_close_input_file(avContext); + avContext = NULL; + } + + isContainerRunning = 0; + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "ret %d\n", ret); + return ret; +} + +static int container_ffmpeg_seek_bytes(off_t pos) { + int flag = AVSEEK_FLAG_BYTE; + off_t current_pos = url_ftell(avContext->pb); + + ffmpeg_printf(20, "seeking to position %lld (bytes)\n", pos); + + if (current_pos > pos) + flag |= AVSEEK_FLAG_BACKWARD; + + if (avformat_seek_file(avContext, -1, INT64_MIN, pos, INT64_MAX, flag) < 0) + { + ffmpeg_err( "Error seeking\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(30, "current_pos after seek %lld\n", url_ftell(avContext->pb)); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* seeking relative to a given byteposition N bytes ->for reverse playback needed */ +static int container_ffmpeg_seek_bytes_rel(off_t start, off_t bytes) { + int flag = AVSEEK_FLAG_BYTE; + off_t newpos; + off_t current_pos = url_ftell(avContext->pb); + + if (start == -1) + start = current_pos; + + ffmpeg_printf(250, "start:%lld bytes:%lld\n", start, bytes); + + newpos = start + bytes; + + if (current_pos > newpos) + flag |= AVSEEK_FLAG_BACKWARD; + + if (newpos < 0) + { + ffmpeg_err("end of file reached\n"); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(20, "seeking to position %lld (bytes)\n", newpos); + +/* fixme: should we adapt INT64_MIN/MAX to some better value? + * take a loog in ffmpeg to be sure what this paramter are doing + */ + if (avformat_seek_file(avContext, -1, INT64_MIN, newpos, INT64_MAX, flag) < 0) + { + ffmpeg_err( "Error seeking\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(30, "current_pos after seek %lld\n", url_ftell(avContext->pb)); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* seeking relative to a given byteposition N seconds ->for reverse playback needed */ +static int container_ffmpeg_seek_rel(Context_t *context, off_t pos, long long int pts, float sec) { + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * current = NULL; + int flag = 0; + + ffmpeg_printf(10, "seeking %f sec relativ to %lld\n", sec, pos); + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + + if (current == NULL) { + ffmpeg_err( "no track avaibale to seek\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (pos == -1) + { + pos = url_ftell(avContext->pb); + } + + if (pts == -1) + pts = current->pts; + + if (sec < 0) + flag |= AVSEEK_FLAG_BACKWARD; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "iformat->flags %d\n", avContext->iformat->flags); + + if (avContext->iformat->flags & AVFMT_TS_DISCONT) + { + if (avContext->bit_rate) + { + sec *= avContext->bit_rate / 8.0; + + ffmpeg_printf(10, "bit_rate %d\n", avContext->bit_rate); + } + else + { + sec *= 180000.0; + } + + pos += sec; + + if (pos < 0) + { + ffmpeg_err("end of file reached\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(10, "1. seeking to position %lld bytes ->sec %f\n", pos, sec); + + if (container_ffmpeg_seek_bytes(pos) < 0) + { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return pos; + } + else + { + sec += ((float) pts / 90000.0f); + + if (sec < 0) + sec = 0; + + ffmpeg_printf(10, "2. seeking to position %f sec ->time base %f %d\n", sec, av_q2d(((AVStream*) current->stream)->time_base), AV_TIME_BASE); + + if (av_seek_frame(avContext, -1 , sec * AV_TIME_BASE, flag) < 0) { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (sec <= 0) + { + ffmpeg_err("end of file reached\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_seek(Context_t *context, float sec) { + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * current = NULL; + int flag = 0; + +#if !defined(VDR1722) + ffmpeg_printf(10, "seeking %f sec\n", sec); + + if (sec == 0.0) + { + ffmpeg_err("sec = 0.0 ignoring\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } +#else + ffmpeg_printf(10, "goto %f sec\n", sec); + + if (sec < 0.0) + { + ffmpeg_err("sec < 0.0 ignoring\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } +#endif + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + + if (current == NULL) { + ffmpeg_err( "no track avaibale to seek\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (sec < 0) + flag |= AVSEEK_FLAG_BACKWARD; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "iformat->flags %d\n", avContext->iformat->flags); + + if (avContext->iformat->flags & AVFMT_TS_DISCONT) + { +/* konfetti: for ts streams seeking frame per seconds does not work (why?). + * I take this algo partly from ffplay.c. + * + * seeking per HTTP does still not work very good. forward seeks everytime + * about 10 seconds, backward does not work. + */ + + off_t pos = url_ftell(avContext->pb); + + ffmpeg_printf(10, "pos %lld %d\n", pos, avContext->bit_rate); + + if (avContext->bit_rate) + { + sec *= avContext->bit_rate / 8.0; + + ffmpeg_printf(10, "bit_rate %d\n", avContext->bit_rate); + } + else + { + sec *= 180000.0; + } +#if !defined(VDR1722) + pos += sec; +#else + pos = sec; +#endif + if (pos < 0) + { + ffmpeg_err("end of file reached\n"); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(10, "1. seeking to position %lld bytes ->sec %f\n", pos, sec); + + if (container_ffmpeg_seek_bytes(pos) < 0) + { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + } else + { +#if !defined(VDR1722) + sec += ((float) current->pts / 90000.0f); +#endif + ffmpeg_printf(10, "2. seeking to position %f sec ->time base %f %d\n", sec, av_q2d(((AVStream*) current->stream)->time_base), AV_TIME_BASE); + + if (av_seek_frame(avContext, -1 /* or streamindex */, sec * AV_TIME_BASE, flag) < 0) { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_get_length(Context_t *context, double * length) { + ffmpeg_printf(50, "\n"); + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * subtitleTrack = NULL; + Track_t * current = NULL; + + if (length == NULL) { + ffmpeg_err( "null pointer passed\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + context->manager->subtitle->Command(context, MANAGER_GET_TRACK, &subtitleTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + else if (subtitleTrack != NULL) + current = subtitleTrack; + + *length = 0.0; + + if (current != NULL) { + if (current->duration == 0) + return cERR_CONTAINER_FFMPEG_ERR; + else + *length = (current->duration / 1000.0); + } + else { + if (avContext != NULL) + { + *length = (avContext->duration / 1000.0); + } else + { + ffmpeg_err( "no Track not context ->no problem :D\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + } + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_swich_audio(Context_t* context, int* arg) +{ + ffmpeg_printf(10, "track %d\n", *arg); + /* Hellmaster1024: nothing to do here!*/ + float sec=-5.0; + context->playback->Command(context, PLAYBACK_SEEK, (void*)&sec); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_swich_subtitle(Context_t* context, int* arg) +{ + /* Hellmaster1024: nothing to do here!*/ + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* konfetti comment: I dont like the mechanism of overwriting + * the pointer in infostring. This lead in most cases to + * user errors, like it is in the current version (libeplayer2 <-->e2->servicemp3.cpp). + * From e2 there is passed a tag=strdup here and we overwrite this + * strdupped tag. This lead to dangling pointers which are never freed! + * I do not free the string here because this is the wrong way. The mechanism + * should be changed, or e2 should pass it in a different way... + */ +static int container_ffmpeg_get_info(Context_t* context, char ** infoString) +{ + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + char* meta = NULL; + + ffmpeg_printf(20, ">\n"); + + if (avContext != NULL) + { + if ((infoString == NULL) || (*infoString == NULL)) + { + ffmpeg_err("infostring NULL\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(20, "%s\n", *infoString); + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if ((meta = searchMeta(avContext->metadata, *infoString)) == NULL) + { + if (audioTrack != NULL) + { + AVStream* stream = audioTrack->stream; + + meta = searchMeta(stream->metadata, *infoString); + } + + if ((meta == NULL) && (videoTrack != NULL)) + { + AVStream* stream = videoTrack->stream; + + meta = searchMeta(stream->metadata, *infoString); + } + } + + if (meta != NULL) + { + *infoString = strdup(meta); + } + else + { + ffmpeg_printf(1, "no metadata found for \"%s\"\n", *infoString); + *infoString = strdup("not found"); + } + } else + { + ffmpeg_err("avContext NULL\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + return cERR_CONTAINER_FFMPEG_NO_ERROR; + +} + +static int Command(void *_context, ContainerCmd_t command, void * argument) +{ + Context_t *context = (Context_t*) _context; + int ret = cERR_CONTAINER_FFMPEG_NO_ERROR; + + ffmpeg_printf(50, "Command %d\n", command); + + switch(command) + { + case CONTAINER_INIT: { + char * FILENAME = (char *)argument; + ret = container_ffmpeg_init(context, FILENAME); + break; + } + case CONTAINER_PLAY: { + ret = container_ffmpeg_play(context); + break; + } + case CONTAINER_STOP: { + ret = container_ffmpeg_stop(context); + break; + } + case CONTAINER_SEEK: { + ret = container_ffmpeg_seek(context, (float)*((float*)argument)); + break; + } + case CONTAINER_LENGTH: { + double length = 0; + ret = container_ffmpeg_get_length(context, &length); + + *((double*)argument) = (double)length; + break; + } + case CONTAINER_SWITCH_AUDIO: { + ret = container_ffmpeg_swich_audio(context, (int*) argument); + break; + } + case CONTAINER_SWITCH_SUBTITLE: { + ret = container_ffmpeg_swich_subtitle(context, (int*) argument); + break; + } + case CONTAINER_INFO: { + ret = container_ffmpeg_get_info(context, (char **)argument); + break; + } + case CONTAINER_STATUS: { + *((int*)argument) = hasPlayThreadStarted; + break; + } + case CONTAINER_LAST_PTS: { + *((long long int*)argument) = latestPts; + break; + } + default: + ffmpeg_err("ContainerCmd %d not supported!\n", command); + ret = cERR_CONTAINER_FFMPEG_ERR; + break; + } + + ffmpeg_printf(50, "exiting with value %d\n", ret); + + return ret; +} + +static char *FFMPEG_Capabilities[] = {"avi", "mkv", "mp4", "ts", "mov", "flv", "flac", "mp3", "mpg", "m2ts", "vob", "wmv","wma", "asf", "mp2", "m4v", "m4a", "divx", "dat", "mpeg", "trp", "mts", "vdr", "ogg", NULL }; + +Container_t FFMPEGContainer = { + "FFMPEG", + &Command, + FFMPEG_Capabilities, + +}; diff --git a/libeplayer3/container/text_srt.c b/libeplayer3/container/text_srt.c new file mode 100644 index 0000000..3ef0598 --- /dev/null +++ b/libeplayer3/container/text_srt.c @@ -0,0 +1,490 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" +#include "misc.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define SRT_DEBUG + +#ifdef SRT_DEBUG + +static short debug_level = 10; + +#define srt_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __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, __FILE__, __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[] = __FILE__; + +/* ***************************** */ +/* 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 = 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 = strdup(Text); + + free(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, 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++; + } +} + +static char ** SrtManagerList(Context_t *context) { + 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; +} + +static void SrtManagerDel(Context_t * context) { + 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); + + FilenameFolder = dirname(copyFilename); + + if (FilenameFolder == NULL) + { + srt_err("FilenameFolder NULL\n"); + return cERR_SRT_ERROR; + } + + srt_printf(10, "folder: %s\n", FilenameFolder); + + getExtension(copyFilename, &FilenameExtension); + + if (FilenameExtension == NULL) + { + srt_err("FilenameExtension NULL\n"); + free(FilenameFolder); + 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 + getExtension(subtitleFilename, &subtitleExtension); + + if (subtitleExtension == NULL) + continue; + + if (strcmp(subtitleExtension, "srt") != 0) + { + free(subtitleExtension); + 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 = { + subtitleExtension, + "S_TEXT/SRT", + i++, + }; + context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle); + } + + free(subtitleExtension); + } /* while */ + closedir(dir); + } /* if dir */ + + free(FilenameExtension); + free(copyFilename); + + srt_printf(10, "<\n"); + return cERR_SRT_NO_ERROR; +} + +static int SrtOpenSubtitle(Context_t *context, int trackid) { + srt_printf(10, "\n"); + + if(trackid < TEXTSRTOFFSET || (trackid % TEXTSRTOFFSET) >= TrackCount) { + srt_err("trackid not for us\n"); + return cERR_SRT_ERROR; + } + + trackid %= TEXTSRTOFFSET; + + 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) { + 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; + 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, +}; diff --git a/libeplayer3/container/text_ssa.c b/libeplayer3/container/text_ssa.c new file mode 100644 index 0000000..fe15b07 --- /dev/null +++ b/libeplayer3/container/text_ssa.c @@ -0,0 +1,492 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, __FILE__, __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, __FILE__, __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 + +//Buffer size used in getLine function. Do not set to value less than 1 !!! +#define SSA_BUFFER_SIZE 14 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* 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], 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, 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++; + } +} + +static char ** SsaManagerList(Context_t *context) { + 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; +} + +static void SsaManagerDel(Context_t * context) { + 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); + + FilenameFolder = dirname(copyFilename); + + if (FilenameFolder == NULL) + { + ssa_err("FilenameFolder NULL\n"); + return cERR_SSA_ERROR; + } + + ssa_printf(10, "folder: %s\n", FilenameFolder); + + getExtension(copyFilename, &FilenameExtension); + + if (FilenameExtension == NULL) + { + ssa_err("FilenameExtension NULL\n"); + free(FilenameFolder); + 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 + getExtension(subtitleFilename, &subtitleExtension); + + if (subtitleExtension == NULL) + continue; + + if ( strcmp(subtitleExtension, "ssa") != 0 && strcmp(subtitleExtension, "ass") != 0 ) + { + free(subtitleExtension); + 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 = { + subtitleExtension, + "S_TEXT/SSA", + i++, + }; + context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle); + } + + free(subtitleExtension); + } /* while */ + closedir(dir); + } /* if dir */ + + free(FilenameExtension); + free(copyFilename); + + ssa_printf(10, "<\n"); + + return cERR_SSA_NO_ERROR; +} +static int SsaOpenSubtitle(Context_t *context, int trackid) { + ssa_printf(10, "\n"); + + if(trackid < TEXTSSAOFFSET || (trackid % TEXTSSAOFFSET) >= TrackCount ) { + ssa_err("trackid not for us\n"); + return cERR_SSA_ERROR; + } + + trackid %= TEXTSSAOFFSET; + + 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) { + 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; + 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, +}; diff --git a/libeplayer3/include/aac.h b/libeplayer3/include/aac.h new file mode 100644 index 0000000..b9a6283 --- /dev/null +++ b/libeplayer3/include/aac.h @@ -0,0 +1,57 @@ +/* + * aac helper + * + * 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 + * + */ + +#ifndef acc_123 +#define acc_123 + +#define AAC_HEADER_LENGTH 7 + +static inline int aac_get_sample_rate_index (uint32_t sample_rate) +{ + if (96000 <= sample_rate) + return 0; + else if (88200 <= sample_rate) + return 1; + else if (64000 <= sample_rate) + return 2; + else if (48000 <= sample_rate) + return 3; + else if (44100 <= sample_rate) + return 4; + else if (32000 <= sample_rate) + return 5; + else if (24000 <= sample_rate) + return 6; + else if (22050 <= sample_rate) + return 7; + else if (16000 <= sample_rate) + return 8; + else if (12000 <= sample_rate) + return 9; + else if (11025 <= sample_rate) + return 10; + else if (8000 <= sample_rate) + return 11; + else if (7350 <= sample_rate) + return 12; + else + return 13; +} + +#endif diff --git a/libeplayer3/include/common.h b/libeplayer3/include/common.h new file mode 100644 index 0000000..c1ebb38 --- /dev/null +++ b/libeplayer3/include/common.h @@ -0,0 +1,17 @@ +#ifndef COMMON_H_ +#define COMMON_H_ + +#include "container.h" +#include "output.h" +#include "manager.h" +#include "playback.h" +#include + +typedef struct Context_s { + PlaybackHandler_t * playback; + ContainerHandler_t * container; + OutputHandler_t * output; + ManagerHandler_t * manager; +} Context_t; + +#endif diff --git a/libeplayer3/include/container.h b/libeplayer3/include/container.h new file mode 100644 index 0000000..c01b290 --- /dev/null +++ b/libeplayer3/include/container.h @@ -0,0 +1,48 @@ +#ifndef CONTAINER_H_ +#define CONTAINER_H_ + +#include + +typedef enum { +CONTAINER_INIT, +CONTAINER_ADD, +CONTAINER_CAPABILITIES, +CONTAINER_PLAY, +CONTAINER_STOP, +CONTAINER_SEEK, +CONTAINER_LENGTH, +CONTAINER_DEL, +CONTAINER_SWITCH_AUDIO, +CONTAINER_SWITCH_SUBTITLE, +CONTAINER_INFO, +CONTAINER_STATUS, +CONTAINER_LAST_PTS, +CONTAINER_DATA +} ContainerCmd_t; + +typedef struct Container_s { + char * Name; + int (* Command) (/*Context_t*/void *, ContainerCmd_t, void *); + char ** Capabilities; + +} Container_t; + + +extern Container_t FFMPEGContainer; + +static Container_t * AvailableContainer[] = { + &FFMPEGContainer, + NULL +}; + +typedef struct ContainerHandler_s { + char * Name; + Container_t * selectedContainer; + Container_t * textSrtContainer; + Container_t * textSsaContainer; + Container_t * assContainer; + + int (* Command) (/*Context_t*/void *, ContainerCmd_t, void *); +} ContainerHandler_t; + +#endif diff --git a/libeplayer3/include/debug.h b/libeplayer3/include/debug.h new file mode 100644 index 0000000..502f7a7 --- /dev/null +++ b/libeplayer3/include/debug.h @@ -0,0 +1,21 @@ +#ifndef debug_123 +#define debug_123 + +#include +#include + +static inline void Hexdump(unsigned char *Data, int length) +{ + + int k; + for (k = 0; k < length; k++) + { + printf("%02x ", Data[k]); + if (((k+1)&31)==0) + printf("\n"); + } + printf("\n"); + +} + +#endif diff --git a/libeplayer3/include/ffmpeg_metadata.h b/libeplayer3/include/ffmpeg_metadata.h new file mode 100644 index 0000000..b8a7472 --- /dev/null +++ b/libeplayer3/include/ffmpeg_metadata.h @@ -0,0 +1,45 @@ +#ifndef _ffmpeg_metadata_123 +#define _ffmpeg_metadata_123 + +/* these file contains a list of metadata tags which can be used by applications + * to stream specific information. it maps the tags to ffmpeg specific tags. + * + * fixme: if we add other container for some resons later (maybe some other libs + * support better demuxing or something like this), then we should think on a + * more generic mechanism! + */ + +/* metatdata map list: + */ +char* metadata_map[] = +{ + /* our tags ffmpeg tag / id3v2 */ + "Title", "TIT2", + "Title", "TT2", + "Artist", "TPE1", + "Artist", "TP1", + "AlbumArtist", "TPE2", + "AlbumArtist", "TP2", + "Album", "TALB", + "Album", "TAL", + "Year", "TDRL", /* fixme */ + "Year", "TDRC", /* fixme */ + "Comment", "unknown", + "Track", "TRCK", + "Track", "TRK", + "Copyright", "TCOP", + "Composer", "TCOM", + "Genre", "TCON", + "Genre", "TCO", + "EncodedBy", "TENC", + "EncodedBy", "TEN", + "Language", "TLAN", + "Performer", "TPE3", + "Performer", "TP3", + "Publisher", "TPUB", + "Encoder", "TSSE", + "Disc", "TPOS", + NULL +}; + +#endif diff --git a/libeplayer3/include/manager.h b/libeplayer3/include/manager.h new file mode 100644 index 0000000..79bc684 --- /dev/null +++ b/libeplayer3/include/manager.h @@ -0,0 +1,77 @@ +#ifndef MANAGER_H_ +#define MANAGER_H_ + +#include +#include + +typedef enum { + MANAGER_ADD, + MANAGER_LIST, + MANAGER_GET, + MANAGER_GETNAME, + MANAGER_SET, + MANAGER_GETENCODING, + MANAGER_DEL, + MANAGER_GET_TRACK, +} ManagerCmd_t; + +typedef enum { + eTypeES, + eTypePES +} eTrackTypeEplayer; + +typedef struct Track_s { + char * Name; + char * Encoding; + int Id; + + /* new field for ffmpeg - add at the end so no problem + * can occur with not changed srt saa container + */ + char* language; + + /* length of track */ + long long int duration; + unsigned int frame_rate; + unsigned int TimeScale; + int version; + long long int pts; + + /* for later use: */ + eTrackTypeEplayer type; + int width; + int height; + + /* stream from ffmpeg */ + void * stream; + /* codec extra data (header or some other stuff) */ + void * extraData; + int extraSize; + + uint8_t* aacbuf; + unsigned int aacbuflen; + int have_aacheader; + + /* If player2 or the elf do not support decoding of audio codec set this. + * AVCodec is than used for softdecoding and stream will be injected as PCM */ + int inject_as_pcm; +} Track_t; + +typedef struct Manager_s { + char * Name; + int (* Command) (/*Context_t*/void *, ManagerCmd_t, void *); + char ** Capabilities; + +} Manager_t; + +typedef struct ManagerHandler_s { + char * Name; + Manager_t * audio; + Manager_t * video; + Manager_t * subtitle; +} ManagerHandler_t; + +void freeTrack(Track_t* track); +void copyTrack(Track_t* to, Track_t* from); + +#endif diff --git a/libeplayer3/include/misc.h b/libeplayer3/include/misc.h new file mode 100644 index 0000000..16569fc --- /dev/null +++ b/libeplayer3/include/misc.h @@ -0,0 +1,136 @@ +#ifndef misc_123 +#define misc_123 + +#include + +/* some useful things needed by many files ... */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct BitPacker_s +{ + unsigned char* Ptr; /* write pointer */ + unsigned int BitBuffer; /* bitreader shifter */ + int Remaining; /* number of remaining in the shifter */ +} BitPacker_t; + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define INVALID_PTS_VALUE 0x200000000ull + +/*#define BIG_READS*/ +#if defined (BIG_READS) +#define BLOCK_COUNT 8 +#else +#define BLOCK_COUNT 1 +#endif +#define TP_PACKET_SIZE 188 +#define BD_TP_PACKET_SIZE 192 +#define NUMBER_PACKETS (199*BLOCK_COUNT) +#define BUFFER_SIZE (TP_PACKET_SIZE*NUMBER_PACKETS) +#define PADDING_LENGTH (1024*BLOCK_COUNT) + +/* subtitle hacks ->for file subtitles */ +#define TEXTSRTOFFSET 100 +#define TEXTSSAOFFSET 200 + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length); +void FlushBits(BitPacker_t * ld); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static inline void getExtension(char * FILENAMEname, char ** extension) { + + int i = 0; + int stringlength; + + if (extension == NULL) + return; + + *extension = NULL; + + if (FILENAMEname == NULL) + return; + + stringlength = (int) strlen(FILENAMEname); + + for (i = 0; stringlength - i > 0; i++) { + if (FILENAMEname[stringlength - i - 1] == '.') { + *extension = strdup(FILENAMEname+(stringlength - i)); + break; + } + } +} + +static inline void getUPNPExtension(char * FILENAMEname, char ** extension) { + char* str; + + if (extension == NULL) + return; + + *extension = NULL; + + if (FILENAMEname == NULL) + return; + + str = strstr(FILENAMEname, "ext="); + + if (str != NULL) + { + *extension = strdup(str + strlen("ext=") + 1); + return; + } + *extension = NULL; +} + +/* the function returns the base name */ +static inline char * basename(char * name) +{ + int i = 0; + int pos = 0; + + while(name[i] != 0) + { + if(name[i] == '/') + pos = i; + i++; + } + + if(name[pos] == '/') + pos++; + + return name + pos; +} + +/* the function returns the directry name */ +static inline char * dirname(char * name) +{ + static char path[100]; + int i = 0; + int pos = 0; + + while((name[i] != 0) && (i < sizeof(path))) + { + if(name[i] == '/') + pos = i; + path[i] = name[i]; + i++; + } + + path[i] = 0; + path[pos] = 0; + + return path; +} + +#endif diff --git a/libeplayer3/include/output.h b/libeplayer3/include/output.h new file mode 100644 index 0000000..a0290d4 --- /dev/null +++ b/libeplayer3/include/output.h @@ -0,0 +1,79 @@ +#ifndef OUTPUT_H_ +#define OUTPUT_H_ + +#include + +typedef enum { + OUTPUT_INIT, + OUTPUT_ADD, + OUTPUT_DEL, + OUTPUT_CAPABILITIES, + OUTPUT_PLAY, + OUTPUT_STOP, + OUTPUT_PAUSE, + OUTPUT_OPEN, + OUTPUT_CLOSE, + OUTPUT_FLUSH, + OUTPUT_CONTINUE, + OUTPUT_FASTFORWARD, + OUTPUT_AVSYNC, + OUTPUT_CLEAR, + OUTPUT_PTS, + OUTPUT_SWITCH, + OUTPUT_SLOWMOTION, + OUTPUT_AUDIOMUTE, + OUTPUT_REVERSE, + OUTPUT_DISCONTINUITY_REVERSE, + OUTPUT_GET_FRAME_COUNT, +/* fixme: e2 */ + OUTPUT_SUBTITLE_REGISTER_FUNCTION = 222, + OUTPUT_SUBTITLE_REGISTER_BUFFER = 223, + OUTPUT_GET_SUBTITLE_OUTPUT, + OUTPUT_SET_SUBTITLE_OUTPUT, +} OutputCmd_t; + +typedef struct +{ + unsigned char* data; + unsigned int len; + + unsigned char* extradata; + unsigned int extralen; + + unsigned long long int pts; + + float frameRate; + unsigned int timeScale; + + unsigned int width; + unsigned int height; + + char* type; +} AudioVideoOut_t; + +typedef struct Output_s { + char * Name; + int (* Command) (/*Context_t*/void *, OutputCmd_t, void *); + int (* Write) (/*Context_t*/void *, void* privateData); + char ** Capabilities; + +} Output_t; + +extern Output_t LinuxDvbOutput; +extern Output_t SubtitleOutput; + +static Output_t * AvailableOutput[] = { + &LinuxDvbOutput, + &SubtitleOutput, + NULL +}; + +typedef struct OutputHandler_s { + char * Name; + Output_t * audio; + Output_t * video; + Output_t * subtitle; + int (* Command) (/*Context_t*/void *, OutputCmd_t, void *); +} OutputHandler_t; + +#endif diff --git a/libeplayer3/include/pcm.h b/libeplayer3/include/pcm.h new file mode 100644 index 0000000..36e2d76 --- /dev/null +++ b/libeplayer3/include/pcm.h @@ -0,0 +1,30 @@ +/* + * pcm helper + * + * 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 + * + */ + +#ifndef pcm_h_ +#define pcm_h_ + +typedef struct pcmPrivateData_s +{ + int uNoOfChannels; + int uSampleRate; + int uBitsPerSample; + int bLittleEndian; +} pcmPrivateData_t; +#endif diff --git a/libeplayer3/include/pes.h b/libeplayer3/include/pes.h new file mode 100644 index 0000000..a8eb3be --- /dev/null +++ b/libeplayer3/include/pes.h @@ -0,0 +1,33 @@ +#ifndef pes_123 +#define pes_123 + +#define PES_MAX_HEADER_SIZE 64 +#define PES_PRIVATE_DATA_FLAG 0x80 +#define PES_PRIVATE_DATA_LENGTH 8 +#define PES_LENGTH_BYTE_0 5 +#define PES_LENGTH_BYTE_1 4 +#define PES_FLAGS_BYTE 7 +#define PES_EXTENSION_DATA_PRESENT 0x01 +#define PES_HEADER_DATA_LENGTH_BYTE 8 +#define PES_MIN_HEADER_SIZE 9 +#define PES_START_CODE_RESERVED_4 0xfd +#define PES_VERSION_FAKE_START_CODE 0x31 + + +#define MAX_PES_PACKET_SIZE 65400 + + +/* start codes */ +#define PCM_PES_START_CODE 0xbd +#define PRIVATE_STREAM_1_PES_START_CODE 0xbd +#define H263_VIDEO_PES_START_CODE 0xfe +#define H264_VIDEO_PES_START_CODE 0xe2 +#define MPEG_VIDEO_PES_START_CODE 0xe0 +#define MPEG_AUDIO_PES_START_CODE 0xc0 +#define VC1_VIDEO_PES_START_CODE 0xfd +#define AAC_AUDIO_PES_START_CODE 0xcf + +int InsertPesHeader (unsigned char *data, int size, unsigned char stream_id, unsigned long long int pts, int pic_start_code); +int InsertVideoPrivateDataHeader(unsigned char *data, int payload_size); + +#endif diff --git a/libeplayer3/include/playback.h b/libeplayer3/include/playback.h new file mode 100644 index 0000000..342ecc9 --- /dev/null +++ b/libeplayer3/include/playback.h @@ -0,0 +1,36 @@ +#ifndef PLAYBACK_H_ +#define PLAYBACK_H_ +#include + +typedef enum {PLAYBACK_OPEN, PLAYBACK_CLOSE, PLAYBACK_PLAY, PLAYBACK_STOP, PLAYBACK_PAUSE, PLAYBACK_CONTINUE, PLAYBACK_FLUSH, PLAYBACK_TERM, PLAYBACK_FASTFORWARD, PLAYBACK_SEEK, PLAYBACK_PTS, PLAYBACK_LENGTH, PLAYBACK_SWITCH_AUDIO, PLAYBACK_SWITCH_SUBTITLE, PLAYBACK_INFO, PLAYBACK_SLOWMOTION, PLAYBACK_FASTBACKWARD, PLAYBACK_GET_FRAME_COUNT} PlaybackCmd_t; + +typedef struct PlaybackHandler_s { + char * Name; + + int fd; + + unsigned char isFile; + unsigned char isHttp; + unsigned char isUPNP; + + unsigned char isPlaying; + unsigned char isPaused; + unsigned char isForwarding; + unsigned char isSeeking; + unsigned char isCreationPhase; + + float BackWard; + int SlowMotion; + int Speed; + int AVSync; + + unsigned char isVideo; + unsigned char isAudio; + unsigned char isSubtitle; + + int (* Command) (/*Context_t*/void *, PlaybackCmd_t, void *); + char * uri; + off_t size; +} PlaybackHandler_t; + +#endif diff --git a/libeplayer3/include/stm_ioctls.h b/libeplayer3/include/stm_ioctls.h new file mode 100644 index 0000000..b058777 --- /dev/null +++ b/libeplayer3/include/stm_ioctls.h @@ -0,0 +1,325 @@ +/* + * stm_ioctls.h + * + * Copyright (C) STMicroelectronics Limited 2005. All rights reserved. + * + * Extensions to the LinuxDVB API (v3) implemented by the Havana implemenation. + */ + +#ifndef H_STM_IOCTLS +#define H_STM_IOCTLS + +/* + * Whenever a sequence of values is extended (define or enum) always add the new values + * So that old values are unchange to maintain binary compatibility. + */ + +#define DVB_SPEED_NORMAL_PLAY 1000 +#define DVB_SPEED_STOPPED 0 +#define DVB_SPEED_REVERSE_STOPPED 0x80000000 +#define DVB_FRAME_RATE_MULTIPLIER 1000 + +#define VIDEO_FULL_SCREEN (VIDEO_CENTER_CUT_OUT+1) + +#define DMX_FILTER_BY_PRIORITY_LOW 0x00010000 /* These flags tell the transport pes filter whether to filter */ +#define DMX_FILTER_BY_PRIORITY_HIGH 0x00020000 /* using the ts priority bit and, if so, whether to filter on */ +#define DMX_FILTER_BY_PRIORITY_MASK 0x00030000 /* bit set or bit clear */ + +/* + * Extra events + */ + +#define VIDEO_EVENT_FIRST_FRAME_ON_DISPLAY 5 /*(VIDEO_EVENT_VSYNC+1)*/ +#define VIDEO_EVENT_FRAME_DECODED_LATE (VIDEO_EVENT_FIRST_FRAME_ON_DISPLAY+1) +#define VIDEO_EVENT_DATA_DELIVERED_LATE (VIDEO_EVENT_FRAME_DECODED_LATE+1) +#define VIDEO_EVENT_STREAM_UNPLAYABLE (VIDEO_EVENT_DATA_DELIVERED_LATE+1) +#define VIDEO_EVENT_TRICK_MODE_CHANGE (VIDEO_EVENT_STREAM_UNPLAYABLE+1) +#define VIDEO_EVENT_VSYNC_OFFSET_MEASURED (VIDEO_EVENT_TRICK_MODE_CHANGE+1) +#define VIDEO_EVENT_FATAL_ERROR (VIDEO_EVENT_VSYNC_OFFSET_MEASURED+1) +#define VIDEO_EVENT_OUTPUT_SIZE_CHANGED (VIDEO_EVENT_FATAL_ERROR+1) +#define VIDEO_EVENT_FATAL_HARDWARE_FAILURE (VIDEO_EVENT_OUTPUT_SIZE_CHANGED+1) + +/* + * List of possible container types - used to select demux.. If stream_source is VIDEO_SOURCE_DEMUX + * then default is TRANSPORT, if stream_source is VIDEO_SOURCE_MEMORY then default is PES + */ +typedef enum { + STREAM_TYPE_NONE, /* Deprecated */ + STREAM_TYPE_TRANSPORT,/* Use latest PTI driver so it can be Deprecated */ + STREAM_TYPE_PES, + STREAM_TYPE_ES, /* Deprecated */ + STREAM_TYPE_PROGRAM, /* Deprecated */ + STREAM_TYPE_SYSTEM, /* Deprecated */ + STREAM_TYPE_SPU, /* Deprecated */ + STREAM_TYPE_NAVI, /* Deprecated */ + STREAM_TYPE_CSS, /* Deprecated */ + STREAM_TYPE_AVI, /* Deprecated */ + STREAM_TYPE_MP3, /* Deprecated */ + STREAM_TYPE_H264, /* Deprecated */ + STREAM_TYPE_ASF, /* Needs work so it can be deprecated */ + STREAM_TYPE_MP4, /* Deprecated */ + STREAM_TYPE_RAW, /* Deprecated */ +} stream_type_t; + +/* + * List of possible video encodings - used to select frame parser and codec. + */ +typedef enum { + VIDEO_ENCODING_AUTO, + VIDEO_ENCODING_MPEG1, + VIDEO_ENCODING_MPEG2, + VIDEO_ENCODING_MJPEG, + VIDEO_ENCODING_DIVX3, + VIDEO_ENCODING_DIVX4, + VIDEO_ENCODING_DIVX5, + VIDEO_ENCODING_MPEG4P2, + VIDEO_ENCODING_H264, + VIDEO_ENCODING_WMV, + VIDEO_ENCODING_VC1, + VIDEO_ENCODING_RAW, + VIDEO_ENCODING_H263, + VIDEO_ENCODING_FLV1, + VIDEO_ENCODING_VP6, + VIDEO_ENCODING_RMV, + VIDEO_ENCODING_DIVXHD, + VIDEO_ENCODING_AVS, + VIDEO_ENCODING_VP3, + VIDEO_ENCODING_THEORA, + VIDEO_ENCODING_COMPOCAP, + VIDEO_ENCODING_NONE, + VIDEO_ENCODING_PRIVATE +} video_encoding_t; + + +/* + * List of possible audio encodings - used to select frame parser and codec. + */ +typedef enum { + AUDIO_ENCODING_AUTO, + AUDIO_ENCODING_PCM, + AUDIO_ENCODING_LPCM, + AUDIO_ENCODING_MPEG1, + AUDIO_ENCODING_MPEG2, + AUDIO_ENCODING_MP3, + AUDIO_ENCODING_AC3, + AUDIO_ENCODING_DTS, + AUDIO_ENCODING_AAC, + AUDIO_ENCODING_WMA, + AUDIO_ENCODING_RAW, + AUDIO_ENCODING_LPCMA, + AUDIO_ENCODING_LPCMH, + AUDIO_ENCODING_LPCMB, + AUDIO_ENCODING_SPDIF, /* + +typedef enum { eNone, eAudio, eVideo, eGfx} eWriterType_t; + +typedef struct { + int fd; + unsigned char* data; + unsigned int len; + unsigned long long int Pts; + unsigned char* private_data; + unsigned int private_size; + unsigned int FrameRate; + unsigned int FrameScale; + unsigned int Width; + unsigned int Height; + unsigned char Version; +} WriterAVCallData_t; + +typedef struct { + unsigned char* data; + unsigned int Width; + unsigned int Height; + unsigned int Stride; + unsigned int color; + + unsigned int x; /* dst x ->given by ass */ + unsigned int y; /* dst y ->given by ass */ + + /* destination values if we use a shared framebuffer */ + int fd; + unsigned int Screen_Width; + unsigned int Screen_Height; + unsigned char* destination; + unsigned int destStride; +} WriterFBCallData_t; + +typedef struct WriterCaps_s { + char* name; + eWriterType_t type; + char* textEncoding; + /* fixme: revise if this is an enum! */ + int dvbEncoding; +} WriterCaps_t; + +typedef struct Writer_s { + int (* reset) (); + int (* writeData) (void*); + int (* writeReverseData) (void*); + WriterCaps_t *caps; +} Writer_t; + +extern Writer_t WriterAudioIPCM; +extern Writer_t WriterAudioMP3; +extern Writer_t WriterAudioMPEGL3; +extern Writer_t WriterAudioAC3; +extern Writer_t WriterAudioAAC; +extern Writer_t WriterAudioDTS; +extern Writer_t WriterAudioWMA; +extern Writer_t WriterAudioFLAC; +extern Writer_t WriterAudioVORBIS; + +extern Writer_t WriterVideoMPEG2; +extern Writer_t WriterVideoMPEGH264; +extern Writer_t WriterVideoH264; +extern Writer_t WriterVideoWMV; +extern Writer_t WriterVideoDIVX; +extern Writer_t WriterVideoFOURCC; +extern Writer_t WriterVideoMSCOMP; +extern Writer_t WriterVideoH263; +extern Writer_t WriterVideoFLV; +extern Writer_t WriterVideoVC1; +extern Writer_t WriterFramebuffer; + +static Writer_t * AvailableWriter[] = { + &WriterAudioIPCM, + &WriterAudioMP3, + &WriterAudioMPEGL3, + &WriterAudioAC3, + &WriterAudioAAC, + &WriterAudioDTS, + &WriterAudioWMA, + &WriterAudioFLAC, + &WriterAudioVORBIS, + + &WriterVideoMPEG2, + &WriterVideoMPEGH264, + &WriterVideoH264, + &WriterVideoDIVX, + &WriterVideoFOURCC, + &WriterVideoMSCOMP, + &WriterVideoWMV, + &WriterVideoH263, + &WriterVideoFLV, + &WriterVideoVC1, + &WriterFramebuffer, + NULL +}; + +Writer_t* getWriter(char* encoding); + +Writer_t* getDefaultVideoWriter(); +Writer_t* getDefaultAudioWriter(); +Writer_t* getDefaultFramebufferWriter(); + +#endif diff --git a/libeplayer3/manager/audio.c b/libeplayer3/manager/audio.c new file mode 100644 index 0000000..742270a --- /dev/null +++ b/libeplayer3/manager/audio.c @@ -0,0 +1,249 @@ +/* + * audio manager handling. + * + * 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 +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 20 + +#define AUDIO_MGR_DEBUG + +#ifdef AUDIO_MGR_DEBUG + +static short debug_level = 0; + +#define audio_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define audio_mgr_printf(level, x...) +#endif + +#ifndef AUDIO_MGR_SILENT +#define audio_mgr_err(x...) do { printf(x); } while (0) +#else +#define audio_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_AUDIO_MGR_NO_ERROR 0 +#define cERR_AUDIO_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = 0; //TRACK[0] as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + + audio_mgr_printf(10, "%s::%s name=\"%s\" encoding=\"%s\" id=%d\n", FILENAME, __FUNCTION__, track.Name, track.Encoding, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + audio_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_AUDIO_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + TrackCount++; + } else { + audio_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_AUDIO_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isAudio = 1; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_AUDIO_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + int i = 0, j = 0; + char ** tracklist = NULL; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + audio_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + tracklist[j] = NULL; + } + + audio_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + + int i = 0; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + free(Tracks); + Tracks = NULL; + } else + { + audio_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_AUDIO_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = 0; + context->playback->isAudio = 0; + + audio_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_AUDIO_MGR_NO_ERROR; +} + + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_AUDIO_MGR_NO_ERROR; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + audio_mgr_printf(20, "%s::%s MANAGER_GET\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + audio_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + else + *((Track_t**)argument) = NULL; + break; + } + case MANAGER_GETENCODING: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = *((int*)argument); + + audio_mgr_printf(20, "%s::%s MANAGER_SET id=%d\n", FILENAME, __FUNCTION__, id); + + if (id < TrackCount) + CurrentTrack = id; + else + { + audio_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_AUDIO_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + audio_mgr_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_AUDIO_MGR_ERROR; + break; + } + + audio_mgr_printf(10, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s AudioManager = { + "Audio", + &Command, + NULL, + +}; diff --git a/libeplayer3/manager/manager.c b/libeplayer3/manager/manager.c new file mode 100644 index 0000000..2146bf0 --- /dev/null +++ b/libeplayer3/manager/manager.c @@ -0,0 +1,93 @@ +/* + * manager handling. + * + * 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 +#include +#include "manager.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +extern Manager_t AudioManager; +extern Manager_t VideoManager; +extern Manager_t SubtitleManager; + +ManagerHandler_t ManagerHandler = { + "ManagerHandler", + &AudioManager, + &VideoManager, + &SubtitleManager, +}; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ +void copyTrack(Track_t* to, Track_t* from) +{ + *to = *from; + + if (from->Name != NULL) + to->Name = strdup(from->Name); + else + to->Name = strdup("Unknown"); + + if (from->Encoding != NULL) + to->Encoding = strdup(from->Encoding); + else + to->Encoding = strdup("Unknown"); + + if (from->language != NULL) + to->language = strdup(from->language); + else + to->language = strdup("Unknown"); +} + +void freeTrack(Track_t* track) +{ + if (track->Name != NULL) + free(track->Name); + + if (track->Encoding != NULL) + free(track->Encoding); + + if (track->language != NULL) + free(track->language); + + if (track->aacbuf != NULL) + free(track->aacbuf); + +} + diff --git a/libeplayer3/manager/subtitle.c b/libeplayer3/manager/subtitle.c new file mode 100644 index 0000000..e22ad20 --- /dev/null +++ b/libeplayer3/manager/subtitle.c @@ -0,0 +1,253 @@ +/* + * subtitle manager handling. + * + * 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 +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 20 + +#define SUBTITLE_MGR_DEBUG + +#ifdef SUBTITLE_MGR_DEBUG + +static short debug_level = 10; + +#define subtitle_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define subtitle_mgr_printf(level, x...) +#endif + +#ifndef SUBTITLE_MGR_SILENT +#define subtitle_mgr_err(x...) do { printf(x); } while (0) +#else +#define subtitle_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_SUBTITLE_MGR_NO_ERROR 0 +#define cERR_SUBTITLE_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = -1; //no as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + + subtitle_mgr_printf(10, "%s::%s %s %s %d\n", FILENAME, __FUNCTION__, track.Name, track.Encoding, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + subtitle_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_SUBTITLE_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + TrackCount++; + } else { + + subtitle_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_SUBTITLE_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isSubtitle = 1; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_SUBTITLE_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + char ** tracklist = NULL; + int i = 0, j = 0; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + subtitle_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + + tracklist[j] = NULL; + } + + subtitle_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + + int i = 0; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + + free(Tracks); + Tracks = NULL; + } else + { + subtitle_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_SUBTITLE_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = -1; + context->playback->isSubtitle = 0; + + subtitle_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_SUBTITLE_MGR_NO_ERROR; +} + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_SUBTITLE_MGR_NO_ERROR; + + subtitle_mgr_printf(50, "%s::%s %d\n", FILENAME, __FUNCTION__, command); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + subtitle_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + { + subtitle_mgr_printf(120, "return %d, %p\n", CurrentTrack, &Tracks[CurrentTrack]); + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + } + else + { + subtitle_mgr_printf(20, "return NULL\n"); + *((Track_t**)argument) = NULL; + } + break; + } + case MANAGER_GETENCODING: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = *((int*)argument); + + subtitle_mgr_printf(20, "%s::%s MANAGER_SET id=%d\n", FILENAME, __FUNCTION__, id); + + if (id < TrackCount) + CurrentTrack = id; + else + { + subtitle_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_SUBTITLE_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + subtitle_mgr_err("%s:%s: ConatinerCmd not supported!", FILENAME, __FUNCTION__); + ret = cERR_SUBTITLE_MGR_ERROR; + break; + } + + subtitle_mgr_printf(50, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s SubtitleManager = { + "Subtitle", + &Command, + NULL, + +}; diff --git a/libeplayer3/manager/video.c b/libeplayer3/manager/video.c new file mode 100644 index 0000000..b50e22b --- /dev/null +++ b/libeplayer3/manager/video.c @@ -0,0 +1,242 @@ +/* + * video manager handling. + * + * 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 +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 4 + +#define VIDEO_MGR_DEBUG + +#ifdef VIDEO_MGR_DEBUG + +static short debug_level = 0; + +#define video_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define video_mgr_printf(level, x...) +#endif + +#ifndef VIDEO_MGR_SILENT +#define video_mgr_err(x...) do { printf(x); } while (0) +#else +#define video_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_VIDEO_MGR_NO_ERROR 0 +#define cERR_VIDEO_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = 0; //TRACK[0] as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + video_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_VIDEO_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + + TrackCount++; + } else { + video_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_VIDEO_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isVideo = 1; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_VIDEO_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + int i = 0, j = 0; + char ** tracklist = NULL; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + video_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + tracklist[j] = NULL; + } + + video_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + int i = 0; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + free(Tracks); + Tracks = NULL; + } else + { + video_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_VIDEO_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = 0; + context->playback->isVideo = 0; + + video_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_VIDEO_MGR_NO_ERROR; +} + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_VIDEO_MGR_NO_ERROR; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + video_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + else + *((Track_t**)argument) = NULL; + break; + } + case MANAGER_GETENCODING: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = (int) argument; + + if (id < TrackCount) + CurrentTrack = id; + else + { + video_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_VIDEO_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + video_mgr_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_VIDEO_MGR_ERROR; + break; + } + + video_mgr_printf(10, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s VideoManager = { + "Video", + &Command, + NULL, + +}; diff --git a/libeplayer3/output/linuxdvb.c b/libeplayer3/output/linuxdvb.c new file mode 100644 index 0000000..d1c5bb0 --- /dev/null +++ b/libeplayer3/output/linuxdvb.c @@ -0,0 +1,1210 @@ +/* + * LinuxDVB Output handling. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "stm_ioctls.h" +#include "writer.h" +#include "misc.h" +#include "pes.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define LINUXDVB_DEBUG + +static short debug_level = 10; + +static const char FILENAME[] = __FILE__; + +#ifdef LINUXDVB_DEBUG +#define linuxdvb_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x ); } while (0) +#else +#define linuxdvb_printf(x...) +#endif + +#ifndef LINUXDVB_SILENT +#define linuxdvb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define linuxdvb_err(x...) +#endif + +#define cERR_LINUXDVB_NO_ERROR 0 +#define cERR_LINUXDVB_ERROR -1 + +static const char VIDEODEV[] = "/dev/dvb/adapter0/video0"; +static const char AUDIODEV[] = "/dev/dvb/adapter0/audio0"; + +static int videofd = -1; +static int audiofd = -1; + +unsigned long long int sCURRENT_PTS = 0; + +pthread_mutex_t LinuxDVBmutex; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +int LinuxDvbStop(Context_t *context, char * type); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void getLinuxDVBMutex(const char *filename, const char *function, int line) { + + linuxdvb_printf(250, "requesting mutex\n"); + + pthread_mutex_lock(&LinuxDVBmutex); + + linuxdvb_printf(250, "received mutex\n"); +} + +void releaseLinuxDVBMutex(const char *filename, const char *function, int line) { + pthread_mutex_unlock(&LinuxDVBmutex); + + linuxdvb_printf(250, "released mutex\n"); + +} + +int LinuxDvbOpen(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd == -1) { + videofd = open(VIDEODEV, O_RDWR); + + if (videofd <= 0) + { + linuxdvb_err("failed to open %s - errno %d\n", VIDEODEV, errno); + linuxdvb_err("%s\n", strerror(errno)); + return cERR_LINUXDVB_ERROR; + } + + if (ioctl( videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + if (ioctl( videofd, VIDEO_SELECT_SOURCE, (void*)VIDEO_SOURCE_MEMORY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SELECT_SOURCE: %s\n", strerror(errno)); + } + + if (ioctl( videofd, VIDEO_SET_STREAMTYPE, (void*)STREAM_TYPE_PROGRAM) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_STREAMTYPE: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + } + + } + if (audio && audiofd == -1) { + audiofd = open(AUDIODEV, O_RDWR); + + if (audiofd <= 0) + { + linuxdvb_err("failed to open %s - errno %d\n", AUDIODEV, errno); + linuxdvb_err("%s\n", strerror(errno)); + + if (videofd != -1) + close(videofd); + return cERR_LINUXDVB_ERROR; + } + + if (ioctl( audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + if (ioctl( audiofd, AUDIO_SELECT_SOURCE, (void*)AUDIO_SOURCE_MEMORY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SELECT_SOURCE: %s\n", strerror(errno)); + } + + if (ioctl( audiofd, AUDIO_SET_STREAMTYPE, (void*)STREAM_TYPE_PROGRAM) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_STREAMTYPE: %s\n", strerror(errno)); + } + } + + return cERR_LINUXDVB_NO_ERROR; +} + +int LinuxDvbClose(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + /* closing stand alone is not allowed, so prevent + * user from closing and dont call stop. stop will + * set default values for us (speed and so on). + */ + LinuxDvbStop(context, type); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + close(videofd); + videofd = -1; + } + if (audio && audiofd != -1) { + close(audiofd); + audiofd = -1; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_LINUXDVB_NO_ERROR; +} + +int LinuxDvbPlay(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + Writer_t* writer; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + char * Encoding = NULL; + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(10, "V %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) VIDEO_ENCODING_AUTO) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } else + { + linuxdvb_printf(20, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + if (ioctl(videofd, VIDEO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + free(Encoding); + } + if (audio && audiofd != -1) { + char * Encoding = NULL; + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "0 A %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*)AUDIO_ENCODING_MP3) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } else + { + linuxdvb_printf(20, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + ret = -1; + } + } + + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + free(Encoding); + } + + return ret; +} + +int LinuxDvbStop(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + /* set back to normal speed (end trickmodes) */ + if (ioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + /* set back to normal speed (end trickmodes) */ + if (ioctl(audiofd, AUDIO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + } + + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbPause(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_FREEZE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FREEZE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_PAUSE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PAUSE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbContinue(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CONTINUE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CONTINUE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CONTINUE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CONTINUE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + linuxdvb_printf(10, "exiting\n"); + + + return ret; +} + +int LinuxDvbReverseDiscontinuity(Context_t *context, int* surplus) { + int ret = cERR_LINUXDVB_NO_ERROR; + int dis_type = VIDEO_DISCONTINUITY_CONTINUOUS_REVERSE | *surplus; + + linuxdvb_printf(50, "\n"); + + if (ioctl( videofd, VIDEO_DISCONTINUITY, (void*) dis_type) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_DISCONTINUITY: %s\n", strerror(errno)); + } + + linuxdvb_printf(50, "exiting\n"); + + return ret; +} + +int LinuxDvbAudioMute(Context_t *context, char *flag) { + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(10, "\n"); + + if (audiofd != -1) { + if(*flag == '1') + { + //AUDIO_SET_MUTE has no effect with new player + //if (ioctl(audiofd, AUDIO_SET_MUTE, 1) == -1) + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + //linuxdvb_err("AUDIO_SET_MUTE: %s\n", strerror(errno)); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else + { + //AUDIO_SET_MUTE has no effect with new player + //if (ioctl(audiofd, AUDIO_SET_MUTE, 0) == -1) + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + //linuxdvb_err("AUDIO_SET_MUTE: %s\n", strerror(errno)); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + } + + linuxdvb_printf(10, "exiting\n"); + + return ret; +} + + +int LinuxDvbFlush(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_FLUSH ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FLUSH: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + } + } + + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_FLUSH ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_FLUSH: %s\n", strerror(errno)); + } + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + } + + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting\n"); + + return cERR_LINUXDVB_NO_ERROR; +} + +#ifndef use_set_speed_instead_ff +int LinuxDvbFastForward(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d speed %d\n", video, audio, context->playback->Speed); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + /* konfetti comment: speed is a value given in skipped frames */ + + if (ioctl(videofd, VIDEO_FAST_FORWARD, context->playback->Speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FAST_FORWARD: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#else + +static unsigned int SpeedList[] = +{ + 1000, 1100, 1200, 1300, 1500, + 2000, 3000, 4000, 5000, 8000, + 12000, 16000, + 125, 250, 500, 700, 800, 900 +}; + +int LinuxDvbFastForward(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + int speedIndex; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + speedIndex = context->playback->Speed % (sizeof (SpeedList) / sizeof (int)); + + linuxdvb_printf(1, "speedIndex %d\n", speedIndex); + + if (ioctl(videofd, VIDEO_SET_SPEED, SpeedList[speedIndex]) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + if (audio && audiofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + speedIndex = context->playback->Speed % (sizeof (SpeedList) / sizeof (int)); + + linuxdvb_printf(1, "speedIndex %d\n", speedIndex); + + if (ioctl(audiofd, AUDIO_SET_SPEED, SpeedList[speedIndex]) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#endif + + +int LinuxDvbReverse(Context_t *context, char * type) { +#ifdef reverse_playback_2 + int ret = cERR_LINUXDVB_NO_ERROR; + int speed; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (context->playback->Speed >= 0) + { + linuxdvb_err("error speed is greater 0, but should be a neg value in skipped frames (or zero)\n"); + return cERR_LINUXDVB_ERROR; + } + + /* speed == 0 indicates end of trick mode, otherwise negative value of skipped frames + * multiplicated with DVB_SPEED_NORMAL_PLAY (currently 1000) + */ + speed = (context->playback->Speed == 0) ? DVB_SPEED_REVERSE_STOPPED : + context->playback->Speed * DVB_SPEED_NORMAL_PLAY; + + linuxdvb_printf(10, "speed %d - %d\n", speed, context->playback->Speed); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(videofd, VIDEO_SET_SPEED, speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + if (audio && audiofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(audiofd, AUDIO_SET_SPEED, speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +#endif +} + +int LinuxDvbSlowMotion(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_SLOWMOTION, context->playback->SlowMotion) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SLOWMOTION: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +int LinuxDvbAVSync(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + /* konfetti: this one is dedicated to audiofd so we + * are ignoring what is given by type! I think we should + * remove this param. Therefor we should add a variable + * setOn or something like that instead, this would remove + * using a variable inside the structure. + */ + if (audiofd != -1) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(audiofd, AUDIO_SET_AV_SYNC, context->playback->AVSync) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_AV_SYNC: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + return ret; +} + +int LinuxDvbClear(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting\n"); + + return ret; +} + +int LinuxDvbPts(Context_t *context, unsigned long long int* pts) { + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(50, "\n"); + + // pts is a non writting requests and can be done in parallel to other requests + //getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (videofd != -1) + { + if (ioctl(videofd, VIDEO_GET_PTS, (void*)&sCURRENT_PTS) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_GET_PTS: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else if (audiofd != -1) + { + if (ioctl(audiofd, AUDIO_GET_PTS, (void*)&sCURRENT_PTS) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_GET_PTS: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else { + sCURRENT_PTS = 0; + ret = cERR_LINUXDVB_ERROR; + } + + *((unsigned long long int *)pts)=(unsigned long long int)sCURRENT_PTS; + + //releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbGetFrameCount(Context_t *context, unsigned long long int* frameCount) { + int ret = cERR_LINUXDVB_NO_ERROR; + dvb_play_info_t playInfo; + + linuxdvb_printf(50, "\n"); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (videofd != -1) + { + if (ioctl(videofd, VIDEO_GET_PLAY_INFO, (void*)&playInfo) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_GET_PLAY_INFO: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + else linuxdvb_err("V: %ull\n", playInfo.frame_count); + } + else if (audiofd != -1) + { + if (ioctl(audiofd, AUDIO_GET_PLAY_INFO, (void*)&playInfo) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_GET_PLAY_INFO: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + else linuxdvb_err("A: %ull\n", playInfo.frame_count); + } + else { + ret = cERR_LINUXDVB_ERROR; + } + + if(ret == cERR_LINUXDVB_NO_ERROR) + *((unsigned long long int *)frameCount) = playInfo.frame_count; + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbSwitch(Context_t *context, char * type) { + unsigned char audio = !strcmp("audio", type); + unsigned char video = !strcmp("video", type); + Writer_t* writer; + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (audio && audiofd != -1) { + char * Encoding = NULL; + if (context && context->manager && context->manager->audio) { + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(10, "A %s\n", Encoding); + + writer = getWriter(Encoding); + + if (ioctl(audiofd, AUDIO_STOP ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + + } + + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + + } + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) AUDIO_ENCODING_MP3) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + } + } else + { + linuxdvb_printf(10, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + } + } + + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + } + free(Encoding); + } + else + linuxdvb_printf(20, "no context for Audio\n"); + } + + if (video && videofd != -1) { + char * Encoding = NULL; + if (context && context->manager && context->manager->video) { + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + if (ioctl(videofd, VIDEO_STOP ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_CLEAR_BUFFER ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + linuxdvb_printf(10, "V %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) VIDEO_ENCODING_AUTO) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + } + } else + { + linuxdvb_printf(10, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + } + } + + if (ioctl(videofd, VIDEO_PLAY, NULL) == -1) + { + /* konfetti: fixme: think on this, I think we should + * return an error here and stop the playback mode + */ + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_PLAY: %s\n", strerror(errno)); + } + free(Encoding); + } + else + linuxdvb_printf(20, "no context for Video\n"); + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + } + + linuxdvb_printf(10, "exiting\n"); + + return cERR_LINUXDVB_NO_ERROR; +} + +static int Write(void *_context, void* _out) +{ + Context_t *context = (Context_t *) _context; + AudioVideoOut_t *out = (AudioVideoOut_t*) _out; + int ret = cERR_LINUXDVB_NO_ERROR; + int res = 0; + unsigned char video = 0; + unsigned char audio = 0; + Writer_t* writer; + WriterAVCallData_t call; + + if (out == NULL) + { + linuxdvb_err("null pointer passed\n"); + return cERR_LINUXDVB_ERROR; + } + + video = !strcmp("video", out->type); + audio = !strcmp("audio", out->type); + + linuxdvb_printf(20, "DataLength=%u PrivateLength=%u Pts=%llu FrameRate=%f\n", + out->len, out->extralen, out->pts, out->frameRate); + linuxdvb_printf(20, "v%d a%d\n", video, audio); + + if (video) { + char * Encoding = NULL; + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "Encoding = %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_printf(20, "searching default writer ... %s\n", Encoding); + writer = getDefaultVideoWriter(); + } + + if (writer == NULL) + { + linuxdvb_err("unknown video codec and no default writer %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + call.fd = videofd; + call.data = out->data; + call.len = out->len; + call.Pts = out->pts; + call.private_data = out->extradata; + call.private_size = out->extralen; + call.FrameRate = out->frameRate; + call.FrameScale = out->timeScale; + call.Width = out->width; + call.Height = out->height; + call.Version = 0; // is unsingned char + + if (writer->writeData) + res = writer->writeData(&call); + + if (res <= 0) + { + linuxdvb_err("failed to write data %d - %d\n", res, errno); + linuxdvb_err("%s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + free(Encoding); + } else if (audio) { + char * Encoding = NULL; + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "%s::%s Encoding = %s\n", FILENAME, __FUNCTION__, Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_printf(20, "searching default writer ... %s\n", Encoding); + writer = getDefaultAudioWriter(); + } + + if (writer == NULL) + { + linuxdvb_err("unknown audio codec %s and no default writer\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + call.fd = audiofd; + call.data = out->data; + call.len = out->len; + call.Pts = out->pts; + call.private_data = out->extradata; + call.private_size = out->extralen; + call.FrameRate = out->frameRate; + call.FrameScale = out->timeScale; + call.Version = 0; /* -1; unsigned char cannot be negative */ + + if (writer->writeData) + res = writer->writeData(&call); + + if (res <= 0) + { + linuxdvb_err("failed to write data %d - %d\n", res, errno); + linuxdvb_err("%s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + free(Encoding); + } + + return ret; +} + +static int reset(Context_t *context) +{ + int ret = cERR_LINUXDVB_NO_ERROR; + Writer_t* writer; + char * Encoding = NULL; + + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("unknown video codec %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + writer->reset(); + } + + free(Encoding); + + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("unknown video codec %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + writer->reset(); + } + + free(Encoding); + + return ret; +} + +static int Command(void *_context, OutputCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(50, "Command %d\n", command); + + switch(command) { + case OUTPUT_OPEN: { + ret = LinuxDvbOpen(context, (char*)argument); + break; + } + case OUTPUT_CLOSE: { + ret = LinuxDvbClose(context, (char*)argument); + reset(context); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_PLAY: { // 4 + sCURRENT_PTS = 0; + ret = LinuxDvbPlay(context, (char*)argument); + break; + } + case OUTPUT_STOP: { + reset(context); + ret = LinuxDvbStop(context, (char*)argument); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_FLUSH: { + ret = LinuxDvbFlush(context, (char*)argument); + reset(context); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_PAUSE: { + ret = LinuxDvbPause(context, (char*)argument); + break; + } + case OUTPUT_CONTINUE: { + ret = LinuxDvbContinue(context, (char*)argument); + break; + } + case OUTPUT_FASTFORWARD: { + return LinuxDvbFastForward(context, (char*)argument); + break; + } + case OUTPUT_REVERSE: { + return LinuxDvbReverse(context, (char*)argument); + break; + } + case OUTPUT_AVSYNC: { + ret = LinuxDvbAVSync(context, (char*)argument); + break; + } + case OUTPUT_CLEAR: { + ret = LinuxDvbClear(context, (char*)argument); + break; + } + case OUTPUT_PTS: { + unsigned long long int pts = 0; + ret = LinuxDvbPts(context, &pts); + *((unsigned long long int*)argument) = (unsigned long long int)pts; + break; + } + case OUTPUT_SWITCH: { + ret = LinuxDvbSwitch(context, (char*)argument); + break; + } + case OUTPUT_SLOWMOTION: { + return LinuxDvbSlowMotion(context, (char*)argument); + break; + } + case OUTPUT_AUDIOMUTE: { + return LinuxDvbAudioMute(context, (char*)argument); + break; + } + case OUTPUT_DISCONTINUITY_REVERSE: { + return LinuxDvbReverseDiscontinuity(context, (int*)argument); + break; + } + case OUTPUT_GET_FRAME_COUNT: { + unsigned long long int frameCount = 0; + ret = LinuxDvbGetFrameCount(context, &frameCount); + *((unsigned long long int*)argument) = (unsigned long long int)frameCount; + break; + } + default: + linuxdvb_err("ContainerCmd %d not supported!\n", command); + ret = cERR_LINUXDVB_ERROR; + break; + } + + linuxdvb_printf(50, "exiting with value %d\n", ret); + + return ret; +} + +static char *LinuxDvbCapabilities[] = { "audio", "video", NULL }; + +struct Output_s LinuxDvbOutput = { + "LinuxDvb", + &Command, + &Write, + LinuxDvbCapabilities, + +}; + diff --git a/libeplayer3/output/output.c b/libeplayer3/output/output.c new file mode 100644 index 0000000..5689897 --- /dev/null +++ b/libeplayer3/output/output.c @@ -0,0 +1,353 @@ +/* + * Output handling. + * + * + * 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 +#include +#include "common.h" +#include "output.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define OUTPUT_DEBUG + +#ifdef OUTPUT_DEBUG + +static short debug_level = 0; + +#define output_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define output_printf(level, x...) +#endif + +#ifndef OUTPUT_SILENT +#define output_err(x...) do { printf(x); } while (0) +#else +#define output_err(x...) +#endif + +/* Error Constants */ +#define cERR_OUTPUT_NO_ERROR 0 +#define cERR_OUTPUT_INTERNAL_ERROR -1 + +static const char* FILENAME = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static void printOutputCapabilities() { + int i, j; + + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + output_printf(10, "Capabilities:\n"); + + for (i = 0; AvailableOutput[i] != NULL; i++) { + output_printf(10, "\t%s : ", AvailableOutput[i]->Name); + + for (j = 0; AvailableOutput[i]->Capabilities[j] != NULL; j++) + output_printf(10, "%s ", AvailableOutput[i]->Capabilities[j]); + output_printf(10, "\n"); + } +} + +/* ***************************** */ +/* Output Functions */ +/* ***************************** */ + +static void OutputAdd(Context_t *context, char * port) { + int i, j; + + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + for (i = 0; AvailableOutput[i] != NULL; i++) + for (j = 0; AvailableOutput[i]->Capabilities[j] != NULL; j++) + if (!strcmp(AvailableOutput[i]->Capabilities[j], port)) { + if (!strcmp("audio", port)) + context->output->audio = AvailableOutput[i]; + else if (!strcmp("video", port)) + context->output->video = AvailableOutput[i]; + else if (!strcmp("subtitle", port)) + context->output->subtitle = AvailableOutput[i]; + break; + } +} + +static void OutputDel(Context_t *context, char * port) { + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (!strcmp("audio", port)) + context->output->audio = NULL; + else if (!strcmp("video", port)) + context->output->video = NULL; + else if (!strcmp("subtitle", port)) + context->output->subtitle = NULL; + +} + +static int Command(void *_context, OutputCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_OUTPUT_NO_ERROR; + + output_printf(10, "%s::%s Command %d\n", FILENAME, __FUNCTION__, command); + + switch(command) { + case OUTPUT_OPEN: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_OPEN, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_OPEN, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_OPEN, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CLOSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_CLOSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_CLOSE, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_CLOSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_ADD: { + OutputAdd(context, (char*) argument); + break; + } + case OUTPUT_DEL: { + OutputDel(context, (char*) argument); + break; + } + case OUTPUT_CAPABILITIES: { + printOutputCapabilities(); + break; + } + case OUTPUT_PLAY: { // 4 + if (context && context->playback ) { + if (context->playback->isVideo) + ret = context->output->video->Command(context, OUTPUT_PLAY, "video"); + + if (!ret) { // success or not executed, dunn care + if (context->playback->isAudio) + ret = context->output->audio->Command(context, OUTPUT_PLAY, "audio"); + + if (!ret) { // success or not executed, dunn care + if (context->playback->isSubtitle) + ret = context->output->subtitle->Command(context, OUTPUT_PLAY, "subtitle"); + } + } + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_STOP: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_STOP, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_STOP, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_STOP, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_FLUSH: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_FLUSH, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_FLUSH, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_FLUSH, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_PAUSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_PAUSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_PAUSE, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_FASTFORWARD: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_FASTFORWARD, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_FASTFORWARD, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_REVERSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_REVERSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_REVERSE, "audio"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CONTINUE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_CONTINUE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_CONTINUE, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_CONTINUE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_AVSYNC: { + if (context && context->playback ) { + if (context->playback->isVideo && context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_AVSYNC, "audio"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CLEAR: { + if (context && context->playback ) { + if (context->playback->isVideo && (argument == NULL || *(char *) argument == 'v')) + ret |= context->output->video->Command(context, OUTPUT_CLEAR, "video"); + if (context->playback->isAudio && (argument == NULL || *(char *) argument == 'a')) + ret |= context->output->audio->Command(context, OUTPUT_CLEAR, "audio"); + //if (context->playback->isSubtitle && (argument == NULL || *(char *) argument == 's')) + // ret |= context->output->subtitle->Command(context, OUTPUT_CLEAR, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_PTS: { + if (context && context->playback ) { + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_PTS, argument); + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_PTS, argument); + //if (context->playback->isSubtitle) + // return context->output->subtitle->Command(context, OUTPUT_PTS, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_SWITCH: { + if (context && context->playback ) { + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_SWITCH, "audio"); + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_SWITCH, "video"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_SLOWMOTION: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_SLOWMOTION, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_SLOWMOTION, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_AUDIOMUTE: { + if (context && context->playback ) { + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_AUDIOMUTE, (char*) argument); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_DISCONTINUITY_REVERSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_DISCONTINUITY_REVERSE, (void*) argument); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_GET_FRAME_COUNT: { + if (context && context->playback ) { + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_GET_FRAME_COUNT, argument); + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_GET_FRAME_COUNT, argument); + //if (context->playback->isSubtitle) + // return context->output->subtitle->Command(context, OUTPUT_GET_FRAME_COUNT, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + default: + output_err("%s::%s OutputCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + + output_printf(10, "%s::%s exiting with value %d\n", FILENAME, __FUNCTION__, ret); + + return ret; +} + +OutputHandler_t OutputHandler = { + "Output", + NULL, + NULL, + NULL, + &Command, +}; diff --git a/libeplayer3/output/output_subtitle.c b/libeplayer3/output/output_subtitle.c new file mode 100644 index 0000000..c5a1a8f --- /dev/null +++ b/libeplayer3/output/output_subtitle.c @@ -0,0 +1,845 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define SUBTITLE_DEBUG + +#ifdef SUBTITLE_DEBUG + +static short debug_level = 10; + +#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; + +static int screen_width = 0; +static int screen_height = 0; +static int destStride = 0; +static int shareFramebuffer = 0; +static int framebufferFD = -1; +static unsigned char* destination = 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}", ""); + replace_all(Line, "{\\i0}", ""); + + 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, "ö", "oe"); + replace_all(Line, "ä", "ae"); + replace_all(Line, "ü", "ue"); + replace_all(Line, "Ö", "Oe"); + replace_all(Line, "Ä", "Ae"); + replace_all(Line, "Ü", "Ue"); + replace_all(Line, "ß", "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, "ö", "oe"); + replace_all(Line, "ä", "ae"); + replace_all(Line, "ü", "ue"); + replace_all(Line, "Ö", "Oe"); + replace_all(Line, "Ä", "Ae"); + replace_all(Line, "Ü", "Ue"); + replace_all(Line, "ß", "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) { + 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) { + 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) { + 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->screen_width = screen_width; + out->screen_height = screen_height; + out->shareFramebuffer = shareFramebuffer; + out->framebufferFD = framebufferFD; + out->destination = destination; + out->destStride = destStride; + break; + } + case OUTPUT_SET_SUBTITLE_OUTPUT: { + SubtitleOutputDef_t* out = (SubtitleOutputDef_t*)argument; + screen_width = out->screen_width; + screen_height = out->screen_height; + shareFramebuffer = out->shareFramebuffer; + framebufferFD = out->framebufferFD; + destination = out->destination; + destStride = out->destStride; + 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, + +}; + diff --git a/libeplayer3/output/writer/aac.c b/libeplayer3/output/writer/aac.c new file mode 100644 index 0000000..73c5f10 --- /dev/null +++ b/libeplayer3/output/writer/aac.c @@ -0,0 +1,290 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define AAC_HEADER_LENGTH 7 + +#define AAC_DEBUG + +#ifdef AAC_DEBUG + +static short debug_level = 0; + +#define aac_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define aac_printf(level, fmt, x...) +#endif + +#ifndef AAC_SILENT +#define aac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define aac_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/// ** AAC ADTS format ** +/// +/// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM +/// MMMMMMMM MMMNNNNN NNNNNNOO ........ +/// +/// Sign Length Position Description +/// +/// A 12 (31-20) Sync code +/// B 1 (19) ID +/// C 2 (18-17) layer +/// D 1 (16) protect absent +/// E 2 (15-14) profile +/// F 4 (13-10) sample freq index +/// G 1 (9) private +/// H 3 (8-6) channel config +/// I 1 (5) original/copy +/// J 1 (4) home +/// K 1 (3) copyright id +/// L 1 (2) copyright start +/// M 13 (1-0,31-21) frame length +/// N 11 (20-10) adts buffer fullness +/// O 2 (9-8) num of raw data blocks in frame + +/* +LC: Audio: aac, 44100 Hz, stereo, s16, 192 kb/ ->ff f1 50 80 00 1f fc +HE: Audio: aac, 48000 Hz, stereo, s16, 77 kb/s ->ff f1 4c 80 00 1f fc +*/ + +/* +ADIF = basic format called Audio Data Interchange Format (ADIF) + consisting of a single header followed by the raw AAC audio data blocks +ADTS = streaming format called Audio Data Transport Stream (ADTS) + consisting of a series of frames, each frame having a header followed by the AAC audio data +LOAS = Low Overhead Audio Stream (LOAS), a self-synchronizing streaming format +*/ + +/* +AvailableBytes = Writen Bytes +Sync = Bits.Get(11); +if (Sync == AAC_AUDIO_LOAS_ASS_SYNC_WORD{0x2b7}) + Type = AAC_AUDIO_LOAS_FORMAT; + FrameSize = Bits.Get(13) + AAC_LOAS_ASS_SYNC_LENGTH_HEADER_SIZE{3}; + if (FrameSize > AAC_LOAS_ASS_MAX_FRAME_SIZE{8192}) + // ERROR + AvailableBytes = AvailableBytes - AAC_LOAS_ASS_MAX_FRAME_SIZE{8192}; + + ImplicitSbrExtension = true; + ExplicitSbrExtension = false; + + if (AvailableBytes > 0) + useSameStreamMux = Bits->Get(1); + else + useSameStreamMux = true; + + if ( !useSameStreamMux ) + audioMuxVersion = Bits->Get(1); // Has to be 0 + if (!audioMuxVersion) + // only get program 0 and layer 0 information ... + Bits->FlushUnseen(1 + 6 + 4 + 3); // allStreamSameTimeFraming, numSubFrames, numProgram, numLayer + audioObjectType = Bits->Get(5); + if ((audioObjectType != AAC_AUDIO_PROFILE_LC{2}) && (audioObjectType != AAC_AUDIO_PROFILE_SBR{5})) + // Error + + samplingFrequencyIndex = Bits->Get(4); + channelConfiguration = Bits->Get(4); + if (audioObjectType == AAC_AUDIO_PROFILE_SBR{5}) + ImplicitSbrExtension = false; + ExplicitSbrExtension = true; + samplingFrequencyIndex = Bits->Get(4); + audioObjectType = Bits->Get(5); + if (audioObjectType != AAC_AUDIO_PROFILE_LC{2}) + // Error + *SampleCount = 1024 * ((ImplicitSbrExtension || ExplicitSbrExtension)?2:1); + *SamplingFrequency *= (ImplicitSbrExtension?2:1); +else + Sync |= Bits.Get(1) << 11; + if (Sync == AAC_AUDIO_ADTS_SYNC_WORD{0xfff}) + Type = AAC_AUDIO_ADTS_FORMAT; // Supports only LC + ID = Bits.Get(1); + Layer = Bits.Get(2); // Has to be 0 + protection_absent = Bits.Get(1); + profile_ObjectType = Bits.Get(2); + if ((profile_ObjectType+1) != AAC_AUDIO_PROFILE_LC) + return + sampling_frequency_index = Bits.Get(4); + SamplingFrequency = aac_sample_rates[sampling_frequency_index] * 2; + Bits.FlushUnseen(1); //private_bit + channel_configuration = Bits.Get(3); + Bits.FlushUnseen(1 + 1 + 1 + 1); //original/copy, home, copyright_identification_bit, copyright_identification_start + FrameSize = Bits.Get(13); // aac_frame_length + if (FrameSize < AAC_ADTS_MIN_FRAME_SIZE{7}) + // Error + Bits.FlushUnseen(11); //adts_buffer_fullness + no_raw_data_blocks_in_frame = Bits.Get(2); + // multiple the sample count by two in case a sbr object is present + SampleCount = (no_raw_data_blocks_in_frame + 1) * 1024 * 2 ; + else + Sync |= Bits.Get(4) << 12; + if (Sync == AAC_AUDIO_LOAS_EPASS_SYNC_WORD{0x4de1}) + Type = AAC_AUDIO_LOAS_FORMAT; + ... + else + Sync |= Bits.Get(16) << 16; + if (Sync == AAC_AUDIO_ADIF_SYNC_WORD{0x41444946}) + Type = AAC_AUDIO_ADIF_FORMAT; + //not supported + + +*/ + +static unsigned char DefaultAACHeader[] = { + 0xff, + 0xf1, + /*0x00, 0x00*/0x50, //((Profile & 0x03) << 6) | (SampleIndex << 2) | ((Channels >> 2) & 0x01);s + 0x80, //(Channels & 0x03) << 6; + 0x00, + 0x1f, + 0xfc +}; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char ExtraData[AAC_HEADER_LENGTH]; + unsigned int PacketLength; + + aac_printf(10, "\n"); + + if (call == NULL) + { + aac_err("call data is NULL...\n"); + return 0; + } + + aac_printf(10, "AudioPts %lld\n", call->Pts); + + PacketLength = call->len + AAC_HEADER_LENGTH; + + if ((call->data == NULL) || (call->len <= 0)) + { + aac_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + aac_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (call->private_data == NULL) + { + aac_printf(10, "private_data = NULL\n"); + + call->private_data = DefaultAACHeader; + call->private_size = AAC_HEADER_LENGTH; + } + + memcpy (ExtraData, call->private_data, AAC_HEADER_LENGTH); + ExtraData[3] |= (PacketLength >> 12) & 0x3; + ExtraData[4] = (PacketLength >> 3) & 0xff; + ExtraData[5] |= (PacketLength << 5) & 0xe0; + + unsigned int HeaderLength = InsertPesHeader (PesHeader, PacketLength, AAC_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(HeaderLength + sizeof(ExtraData) + call->len); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, ExtraData, sizeof(ExtraData)); + memcpy (PacketStart + HeaderLength + sizeof(ExtraData), call->data, call->len); + + aac_printf(100, "H %d d %d ExtraData %d\n", HeaderLength, call->len, sizeof(ExtraData)); + + int len = write(call->fd, PacketStart, HeaderLength + call->len + sizeof(ExtraData)); + + free(PacketStart); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "aac", + eAudio, + "A_AAC", + AUDIO_ENCODING_AAC +}; + +struct Writer_s WriterAudioAAC = { + &reset, + &writeData, + NULL, + &caps +}; diff --git a/libeplayer3/output/writer/ac3.c b/libeplayer3/output/writer/ac3.c new file mode 100644 index 0000000..c39c971 --- /dev/null +++ b/libeplayer3/output/writer/ac3.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define AC3_HEADER_LENGTH 7 + +#define AC3_DEBUG + +#ifdef AC3_DEBUG + +static short debug_level = 0; + +#define ac3_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ac3_printf(level, fmt, x...) +#endif + +#ifndef AC3_SILENT +#define ac3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ac3_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + ac3_printf(10, "\n"); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + if (call == NULL) + { + ac3_err("call data is NULL...\n"); + return 0; + } + + ac3_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + ac3_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + ac3_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len, PRIVATE_STREAM_1_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_ac3 = { + "ac3", + eAudio, + "A_AC3", + AUDIO_ENCODING_AC3, +}; + +struct Writer_s WriterAudioAC3 = { + &reset, + &writeData, + NULL, + &caps_ac3, +}; + diff --git a/libeplayer3/output/writer/divx.c b/libeplayer3/output/writer/divx.c new file mode 100644 index 0000000..c65d572 --- /dev/null +++ b/libeplayer3/output/writer/divx.c @@ -0,0 +1,215 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define DIVX_DEBUG + +#ifdef DIVX_DEBUG + +static short debug_level = 0; + +#define divx_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define divx_printf(level, fmt, x...) +#endif + +#ifndef DIVX_SILENT +#define divx_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define divx_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char FakeHeaders[64]; // 64bytes should be enough to make the fake headers + unsigned int FakeHeaderLength; + unsigned int ExtraLength = 0; + unsigned char Version = 5; + unsigned int FakeStartCode = (Version << 8) | PES_VERSION_FAKE_START_CODE; + unsigned int HeaderLength = 0; + unsigned int usecPerFrame = 41708; /* Hellmaster1024: default value */ + BitPacker_t ld = {FakeHeaders, 0, 32}; + + divx_printf(10, "\n"); + + if (call == NULL) + { + divx_err("call data is NULL...\n"); + return 0; + } + + divx_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + divx_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + divx_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + usecPerFrame = 1000000000 / call->FrameRate; + divx_printf(10, "Microsecends per frame = %d\n", usecPerFrame); + + memset(FakeHeaders, 0, sizeof(FakeHeaders)); + + /* Create info record for frame parser */ + /* divx4 & 5 + VOS + PutBits(&ld, 0x0, 8); + PutBits(&ld, 0x0, 8); + */ + PutBits(&ld, 0x1b0, 32); // startcode + PutBits(&ld, 0, 8); // profile = reserved + PutBits(&ld, 0x1b2, 32); // startcode (user data) + PutBits(&ld, 0x53545443, 32); // STTC - an embedded ST timecode from an avi file + PutBits(&ld, usecPerFrame , 32); + // microseconds per frame + FlushBits(&ld); + + FakeHeaderLength = (ld.Ptr - (FakeHeaders)); + + if (initialHeader) ExtraLength = call->private_size; + + HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_VIDEO_PES_START_CODE, call->Pts, FakeStartCode); + unsigned char* PacketStart = malloc(call->len + HeaderLength + FakeHeaderLength + ExtraLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, FakeHeaders, FakeHeaderLength); + if (initialHeader) { + memcpy (PacketStart + HeaderLength + FakeHeaderLength, call->private_data, call->private_size); + initialHeader = 0; + } + memcpy (PacketStart + HeaderLength + FakeHeaderLength + ExtraLength, call->data, call->len); + + int len = write(call->fd, PacketStart ,call->len + HeaderLength + FakeHeaderLength + ExtraLength); + + free(PacketStart); + + divx_printf(10, "xvid_Write < len=%d\n", len); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t mpeg4p2_caps = { + "mscomp", + eVideo, + "V_MSCOMP", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoMSCOMP = { + &reset, + &writeData, + NULL, + &mpeg4p2_caps, +}; + +static WriterCaps_t fourcc_caps = { + "fourcc", + eVideo, + "V_MS/VFW/FOURCC", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoFOURCC = { + &reset, + &writeData, + NULL, + &fourcc_caps, +}; + +static WriterCaps_t divx_caps = { + "divx", + eVideo, + "V_MKV/XVID", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoDIVX = { + &reset, + &writeData, + NULL, + &divx_caps, +}; diff --git a/libeplayer3/output/writer/dts.c b/libeplayer3/output/writer/dts.c new file mode 100644 index 0000000..a123b87 --- /dev/null +++ b/libeplayer3/output/writer/dts.c @@ -0,0 +1,168 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define PES_AUDIO_PRIVATE_HEADER_SIZE 16 // consider maximum private header size. +#define PES_AUDIO_HEADER_SIZE (32 + PES_AUDIO_PRIVATE_HEADER_SIZE) +#define PES_AUDIO_PACKET_SIZE 2028 +#define SPDIF_AUDIO_PACKET_SIZE (1024 * sizeof(unsigned int) * 2) // stereo 32bit samples. + +#define DTS_DEBUG + +#ifdef DTS_DEBUG + +static short debug_level = 0; + +#define dts_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define dts_printf(level, fmt, x...) +#endif + +#ifndef DTS_SILENT +#define dts_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define dts_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int i = 0; + unsigned char PesHeader[PES_AUDIO_HEADER_SIZE]; + unsigned char * Data = 0; + + dts_printf(10, "\n"); + + if (call == NULL) + { + dts_err("call data is NULL...\n"); + return 0; + } + + dts_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + dts_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + dts_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + memset (PesHeader, '0', PES_AUDIO_HEADER_SIZE); + + Data = (unsigned char *) malloc(call->len); + memcpy(Data, call->data, call->len); + + /* 16-bit byte swap all data before injecting it */ + for (i=0; i< call->len; i+=2) + { + unsigned char Tmp = Data[i]; + Data[i] = Data[i+1]; + Data[i+1] = Tmp; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_AUDIO_PES_START_CODE/*PRIVATE_STREAM_1_PES_START_CODE*/, call->Pts, 0); + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd,PacketStart,call->len + HeaderLength); + + free(PacketStart); + free(Data); + + dts_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "dts", + eAudio, + "A_DTS", + AUDIO_ENCODING_DTS, +}; + +struct Writer_s WriterAudioDTS = { + &reset, + &writeData, + NULL, + &caps, +}; diff --git a/libeplayer3/output/writer/flac.c b/libeplayer3/output/writer/flac.c new file mode 100644 index 0000000..e1c01a1 --- /dev/null +++ b/libeplayer3/output/writer/flac.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define FLAC_DEBUG + +#ifdef FLAC_DEBUG + +static short debug_level = 1; + +#define flac_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define flac_printf(level, fmt, x...) +#endif + +#ifndef FLAC_SILENT +#define flac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define flac_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + flac_printf(10, "\n"); + + if (call == NULL) + { + flac_err("call data is NULL...\n"); + return 0; + } + + flac_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + flac_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + flac_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + flac_printf(10, "flac_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_flac = { + "flac", + eAudio, + "A_FLAC", + AUDIO_ENCODING_LPCM, //AUDIO_ENCODING_FLAC, +}; + +struct Writer_s WriterAudioFLAC = { + &reset, + &writeData, + NULL, + &caps_flac, +}; + diff --git a/libeplayer3/output/writer/framebuffer.c b/libeplayer3/output/writer/framebuffer.c new file mode 100644 index 0000000..e55faad --- /dev/null +++ b/libeplayer3/output/writer/framebuffer.c @@ -0,0 +1,196 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = 10; + +#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) +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + int x,y; + int res = 0; + unsigned char* dst; + + WriterFBCallData_t* call = (WriterFBCallData_t*) _call; + + fb_printf(100, "\n"); + + if (call == NULL) + { + fb_err("call data is NULL...\n"); + return 0; + } + + if (call->destination == NULL) + { + fb_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (call->data != NULL) + { + unsigned int opacity = 255 - ((unsigned int)_a(call->color)); + unsigned int r = (unsigned int)_r(call->color); + unsigned int g = (unsigned int)_g(call->color); + unsigned int b = (unsigned int) _b(call->color); + int src_stride = call->Stride; + int dst_stride = call->destStride; + int dst_delta = dst_stride - call->Width*4; + int x,y; + const unsigned char *src = call->data; + unsigned char *dst = call->destination + (call->y * dst_stride + call->x * 4); + unsigned int k,ck,t; + + 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 %d\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); + + fb_printf(100, "r 0x%hhx, g 0x%hhx, b 0x%hhx, a 0x%hhx, opacity %d\n", r, g, b, a, opacity); + + for (y=0;yHeight;y++) + { + for (x = 0; x < call->Width; x++) + { + k = ((unsigned)src[x]) * opacity / 255; + ck = 255 - k; + t = *dst; + *dst++ = (k*b + ck*t) / 255; + t = *dst; + *dst++ = (k*g + ck*t) / 255; + t = *dst; + *dst++ = (k*r + ck*t) / 255; + *dst++ = 0; + } + + dst += dst_delta; + src += src_stride; + } + } else + { + for (y = 0; y < call->Height; y++) + memset(call->destination + ((call->y + y) * call->destStride) + call->x * 4, 0, call->Width * 4); + } + + 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, +}; diff --git a/libeplayer3/output/writer/h263.c b/libeplayer3/output/writer/h263.c new file mode 100644 index 0000000..63c8ca4 --- /dev/null +++ b/libeplayer3/output/writer/h263.c @@ -0,0 +1,176 @@ +/* + * linuxdvb output/writer handling. + * + * crow 2010 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define H263_DEBUG + +#ifdef H263_DEBUG + +static short debug_level = 0; + +#define h263_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h263_printf(level, fmt, x...) +#endif + +#ifndef H263_SILENT +#define h263_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h263_err(fmt, x...) +#endif +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char DataCopy[PES_MAX_HEADER_SIZE]; + int len = 0; + + h263_printf(10, "\n"); + + if (call == NULL) + { + h263_err("call data is NULL...\n"); + return 0; + } + + h263_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + h263_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h263_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader(PesHeader, call->len, H263_VIDEO_PES_START_CODE, call->Pts,0); + + int PrivateHeaderLength = InsertVideoPrivateDataHeader (&PesHeader[HeaderLength], call->len); + + int PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength; + + PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff; + PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff; + PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength; + PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT; + + HeaderLength += PrivateHeaderLength; + + unsigned char *PacketData = call->data - HeaderLength; + + memcpy(DataCopy, PacketData, HeaderLength); + memcpy(PacketData, PesHeader, HeaderLength); + + len = write(call->fd, PacketData, call->len + HeaderLength); + + memcpy(PacketData, DataCopy, HeaderLength); + + h263_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_h263 = { + "h263", + eVideo, + "V_H263", + VIDEO_ENCODING_H263, +}; + +struct Writer_s WriterVideoH263 = { + &reset, + &writeData, + NULL, + &caps_h263, +}; + +static WriterCaps_t caps_flv = { + "FLV", + eVideo, + "V_FLV", + VIDEO_ENCODING_FLV1, +}; + +struct Writer_s WriterVideoFLV = { + &reset, + &writeData, + NULL, + &caps_flv, +}; diff --git a/libeplayer3/output/writer/h264.c b/libeplayer3/output/writer/h264.c new file mode 100644 index 0000000..3fdd4c8 --- /dev/null +++ b/libeplayer3/output/writer/h264.c @@ -0,0 +1,439 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define H264_DEBUG + +#ifdef H264_DEBUG + +static short debug_level = 0; + +#define h264_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h264_printf(level, fmt, x...) +#endif + +#ifndef H264_SILENT +#define h264_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h264_err(fmt, x...) +#endif + +#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24 +#define CONTAINER_PARAMETERS_VERSION 0x00 + +/* ***************************** */ +/* Types */ +/* ***************************** */ +typedef struct avcC_s +{ + unsigned char Version; /* configurationVersion */ + unsigned char Profile; /* AVCProfileIndication */ + unsigned char Compatibility; /* profile_compatibility */ + unsigned char Level; /* AVCLevelIndication */ + unsigned char NalLengthMinusOne; /* held in bottom two bits */ + unsigned char NumParamSets; /* held in bottom 5 bits */ + unsigned char Params[1]; /* {length,params}{length,params}...sequence then picture*/ +} avcC_t; + + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +const unsigned char Head[] = {0, 0, 0, 1}; +static int initialHeader = 1; +static unsigned int NalLengthBytes = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char* PacketStart = NULL; + unsigned int PacketStartSIZE = 0; + unsigned int HeaderLength; + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned long long int VideoPts; + unsigned int TimeDelta; + unsigned int TimeScale; + int len = 0; + static int NoOtherBeginningFound = 1; + h264_printf(10, "\n"); + + if (call == NULL) + { + h264_err("call data is NULL...\n"); + return 0; + } + + TimeDelta = call->FrameRate; + TimeScale = call->FrameScale; + VideoPts = call->Pts; + + h264_printf(10, "VideoPts %lld - %d %d\n", call->Pts, TimeDelta, TimeScale); + + if ((call->data == NULL) || (call->len <= 0)) + { + h264_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h264_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if((call->data[0] == 0x00 && call->data[1] == 0x00 && call->data[2] == 0x00 && call->data[3] == 0x01) || + (call->data[0] == 0x00 && call->data[1] == 0x00 && call->data[2] == 0x01 && NoOtherBeginningFound) || + (call->data[0] == 0xff && call->data[1] == 0xff && call->data[2] == 0xff && call->data[3] == 0xff)) + { + unsigned int FakeStartCode = (call->Version << 8) | PES_VERSION_FAKE_START_CODE; + unsigned int PrivateLength=0; + if(initialHeader)PrivateLength = call->private_size; + HeaderLength = InsertPesHeader(PesHeader, call->len, + MPEG_VIDEO_PES_START_CODE, call->Pts, FakeStartCode); + /*Hellmaster1024: some packets will only be accepted by the player if we send one byte more than + data is available. The content of this byte does not matter. It will be ignored + by the player */ + unsigned char *PacketData = malloc(HeaderLength + call->len + PrivateLength + 1); + + memcpy(PacketData, PesHeader, HeaderLength); + if(initialHeader){ + memcpy (PacketData + HeaderLength, call->private_data, PrivateLength); + initialHeader=0; + } + memcpy (PacketData + HeaderLength + PrivateLength, call->data, call->len); + + len = write(call->fd, PacketData, call->len + HeaderLength + PrivateLength + 1); + + free(PacketData); + + return len; + } + NoOtherBeginningFound = 0; + + if (initialHeader) + { + unsigned char* HeaderData = malloc(BUFFER_SIZE+PADDING_LENGTH); + avcC_t* avcCHeader = (avcC_t*)call->private_data; + int i; + unsigned int ParamSets; + unsigned int ParamOffset; + unsigned int InitialHeaderLength = 0; + unsigned int ParametersLength; + + if (avcCHeader == NULL) + { + h264_err("private_data NULL\n"); + free(HeaderData); + return -1; + } + + if (avcCHeader->Version != 1) + h264_err("Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version); + + ParametersLength = 0; + + HeaderData[ParametersLength++] = 0x00; // Start code + HeaderData[ParametersLength++] = 0x00; + HeaderData[ParametersLength++] = 0x01; + HeaderData[ParametersLength++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS; + // Container message version - changes when/if we vary the format of the message + HeaderData[ParametersLength++] = CONTAINER_PARAMETERS_VERSION; + HeaderData[ParametersLength++] = 0xff; // Field separator + + if( TimeDelta == 0xffffffff ) + TimeDelta = (TimeScale > 1000) ? 1001 : 1; + + HeaderData[ParametersLength++] = (TimeScale >> 24) & 0xff; // Output the timescale + HeaderData[ParametersLength++] = (TimeScale >> 16) & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = (TimeScale >> 8) & 0xff; + HeaderData[ParametersLength++] = TimeScale & 0xff; + HeaderData[ParametersLength++] = 0xff; + + HeaderData[ParametersLength++] = (TimeDelta >> 24) & 0xff; // Output frame period + HeaderData[ParametersLength++] = (TimeDelta >> 16) & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = (TimeDelta >> 8) & 0xff; + HeaderData[ParametersLength++] = TimeDelta & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = 0x80; // Rsbp trailing bits + + HeaderLength = InsertPesHeader (PesHeader, ParametersLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + PacketStart = malloc(HeaderLength + ParametersLength); + PacketStartSIZE = HeaderLength + ParametersLength; + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, HeaderData, ParametersLength); + len += write (call->fd, PacketStart, HeaderLength + ParametersLength); + + NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1; + ParamSets = avcCHeader->NumParamSets & 0x1f; + + h264_printf(20, "avcC contents:\n"); + h264_printf(20, " version: %d\n", avcCHeader->Version); + h264_printf(20, " profile: %d\n", avcCHeader->Profile); + h264_printf(20, " profile compatibility: %d\n", avcCHeader->Compatibility); + h264_printf(20, " level: %d\n", avcCHeader->Level); + h264_printf(20, " nal length bytes: %d\n", NalLengthBytes); + h264_printf(20, " number of sequence param sets: %d\n", ParamSets); + + ParamOffset = 0; + for (i = 0; i < ParamSets; i++) { + unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset+1]; + + h264_printf(20, " sps %d has length %d\n", i, PsLength); + + if (HeaderLength + InitialHeaderLength + sizeof(Head) > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + sizeof(Head)); + PacketStartSIZE = HeaderLength + InitialHeaderLength + sizeof(Head); + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, Head, sizeof(Head)); + InitialHeaderLength += sizeof(Head); + + if (HeaderLength + InitialHeaderLength + PsLength > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + PsLength); + PacketStartSIZE = HeaderLength + InitialHeaderLength + PsLength; + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, &avcCHeader->Params[ParamOffset+2], PsLength); + + InitialHeaderLength += PsLength; + ParamOffset += PsLength+2; + } + + ParamSets = avcCHeader->Params[ParamOffset]; + + h264_printf(20, " number of picture param sets: %d\n", ParamSets); + + ParamOffset++; + for (i = 0; i < ParamSets; i++) { + unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset+1]; + + h264_printf (20, " pps %d has length %d\n", i, PsLength); + + if (HeaderLength + InitialHeaderLength + sizeof(Head) > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + sizeof(Head)); + PacketStartSIZE = HeaderLength + InitialHeaderLength + sizeof(Head); + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, Head, sizeof(Head)); + InitialHeaderLength += sizeof(Head); + + if (HeaderLength + InitialHeaderLength + PsLength > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + PsLength); + PacketStartSIZE = HeaderLength + InitialHeaderLength + PsLength; + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, &avcCHeader->Params[ParamOffset+2], PsLength); + InitialHeaderLength += PsLength; + ParamOffset += PsLength+2; + } + + HeaderLength = InsertPesHeader (PesHeader, InitialHeaderLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + memcpy (PacketStart, PesHeader, HeaderLength); + + len += write (call->fd, PacketStart, HeaderLength + InitialHeaderLength); + + initialHeader = 0; + + free(PacketStart); + free(HeaderData); + } + + unsigned int SampleSize = call->len; + unsigned int NalStart = 0; + unsigned int VideoPosition = 0; + + do { + unsigned int NalLength; + unsigned char NalData[4]; + int NalPresent = 1; + + memcpy (NalData, call->data + VideoPosition, NalLengthBytes); + VideoPosition += NalLengthBytes; + NalLength = (NalLengthBytes == 1) ? NalData[0] : + (NalLengthBytes == 2) ? (NalData[0] << 8) | NalData[1] : + (NalLengthBytes == 3) ? (NalData[0] << 16) | (NalData[1] << 8) | NalData[2] : + (NalData[0] << 24) | (NalData[1] << 16) | (NalData[2] << 8) | NalData[3]; + + h264_printf(20, "NalStart = %u + NalLength = %u > SampleSize = %u\n", NalStart, NalLength, SampleSize); + + if (NalStart + NalLength > SampleSize) { + + h264_printf(20, "nal length past end of buffer - size %u frame offset %u left %u\n", + NalLength, NalStart , SampleSize - NalStart ); + + NalStart = SampleSize; + } else { + NalStart += NalLength + NalLengthBytes; + while (NalLength > 0) { + unsigned int PacketLength = (NalLength < BUFFER_SIZE) ? NalLength : BUFFER_SIZE; + int ExtraLength = 0; + unsigned char* PacketStart; + + NalLength -= PacketLength; + + if (NalPresent) { + PacketStart = malloc(sizeof(Head) + PacketLength); + memcpy (PacketStart + sizeof(Head), call->data + VideoPosition, PacketLength); + VideoPosition += PacketLength; + + memcpy (PacketStart, Head, sizeof(Head)); + ExtraLength = sizeof(Head); + } else { + PacketStart = malloc(PacketLength); + memcpy (PacketStart, call->data + VideoPosition, PacketLength); + VideoPosition += PacketLength; + } + + PacketLength += ExtraLength; + + h264_printf (20, " pts=%llu\n", VideoPts); + + HeaderLength = InsertPesHeader (PesHeader, PacketLength, MPEG_VIDEO_PES_START_CODE, VideoPts, 0); + + unsigned char* WritePacketStart = malloc(HeaderLength + PacketLength); + memcpy (WritePacketStart, PesHeader, HeaderLength); + memcpy (WritePacketStart+HeaderLength, PacketStart, PacketLength); + free(PacketStart); + + PacketLength += HeaderLength; + len += write (call->fd, WritePacketStart, PacketLength); + free(WritePacketStart); + + NalPresent = 0; + VideoPts = INVALID_PTS_VALUE; + } + } + } while (NalStart < SampleSize); + + if (len < 0) + { + h264_err("error writing data errno = %d\n", errno); + h264_err("%s\n", strerror(errno)); + } + + h264_printf (10, "< len %d\n", len); + return len; +} + +static int writeReverseData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + +#ifndef old_reverse_playback + + h264_printf(10, "\n"); + + if (call == NULL) + { + h264_err("call data is NULL...\n"); + return 0; + } + + h264_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + h264_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h264_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + return 0; + +#else + + return 0; + +#endif + +} +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "h264", + eVideo, + "V_MPEG4/ISO/AVC", + VIDEO_ENCODING_H264, +}; + +struct Writer_s WriterVideoH264 = { + &reset, + &writeData, + &writeReverseData, + &caps, +}; + diff --git a/libeplayer3/output/writer/misc.c b/libeplayer3/output/writer/misc.c new file mode 100644 index 0000000..9252851 --- /dev/null +++ b/libeplayer3/output/writer/misc.c @@ -0,0 +1,126 @@ +/* + * LinuxDVB Output handling. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length) +{ + unsigned int bit_buf; + int bit_left; + + bit_buf = ld->BitBuffer; + bit_left = ld->Remaining; + +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("code = %d, length = %d, bit_buf = 0x%x, bit_left = %d\n", code, length, bit_buf, bit_left); +#endif /* DEBUG_PUTBITS */ + + if (length < bit_left) + { + /* fits into current buffer */ + bit_buf = (bit_buf << length) | code; + bit_left -= length; + } + else + { + /* doesn't fit */ + bit_buf <<= bit_left; + bit_buf |= code >> (length - bit_left); + ld->Ptr[0] = (char)(bit_buf >> 24); + ld->Ptr[1] = (char)(bit_buf >> 16); + ld->Ptr[2] = (char)(bit_buf >> 8); + ld->Ptr[3] = (char)bit_buf; + ld->Ptr += 4; + length -= bit_left; + bit_buf = code & ((1 << length) - 1); + bit_left = 32 - length; + bit_buf = code; + } + +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("bit_left = %d, bit_buf = 0x%x\n", bit_left, bit_buf); +#endif /* DEBUG_PUTBITS */ + + /* writeback */ + ld->BitBuffer = bit_buf; + ld->Remaining = bit_left; +} + +void FlushBits(BitPacker_t * ld) +{ + ld->BitBuffer <<= ld->Remaining; + while (ld->Remaining < 32) + { +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("flushing 0x%2.2x\n", ld->BitBuffer >> 24); +#endif /* DEBUG_PUTBITS */ + *ld->Ptr++ = ld->BitBuffer >> 24; + ld->BitBuffer <<= 8; + ld->Remaining += 8; + } + ld->Remaining = 32; + ld->BitBuffer = 0; +} + diff --git a/libeplayer3/output/writer/mp3.c b/libeplayer3/output/writer/mp3.c new file mode 100644 index 0000000..4ba936d --- /dev/null +++ b/libeplayer3/output/writer/mp3.c @@ -0,0 +1,164 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define MP3_DEBUG + +#ifdef MP3_DEBUG + +static short debug_level = 0; + +#define mp3_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mp3_printf(level, fmt, x...) +#endif + +#ifndef MP3_SILENT +#define mp3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mp3_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + mp3_printf(10, "\n"); + + if (call == NULL) + { + mp3_err("call data is NULL...\n"); + return 0; + } + + mp3_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + mp3_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + mp3_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + mp3_printf(10, "mp3_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_mp3 = { + "mp3", + eAudio, + "A_MP3", + AUDIO_ENCODING_MP3, +}; + +struct Writer_s WriterAudioMP3 = { + &reset, + &writeData, + NULL, + &caps_mp3, +}; + +static WriterCaps_t caps_mpegl3 = { + "mpeg/l3", + eAudio, + "A_MPEG/L3", + AUDIO_ENCODING_MPEG2, +}; + +struct Writer_s WriterAudioMPEGL3 = { + &reset, + &writeData, + NULL, + &caps_mpegl3, +}; diff --git a/libeplayer3/output/writer/mpeg2.c b/libeplayer3/output/writer/mpeg2.c new file mode 100644 index 0000000..3869369 --- /dev/null +++ b/libeplayer3/output/writer/mpeg2.c @@ -0,0 +1,178 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define MPEG2_DEBUG + +#ifdef MPEG2_DEBUG + +static short debug_level = 0; + +#define mpeg2_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mpeg2_printf(level, fmt, x...) +#endif + +#ifndef MPEG2_SILENT +#define mpeg2_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mpeg2_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + int len = 0; + int Position = 0; + + mpeg2_printf(10, "\n"); + + if (call == NULL) + { + mpeg2_err("call data is NULL...\n"); + return 0; + } + + mpeg2_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + mpeg2_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + mpeg2_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + while(1) { + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + mpeg2_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, 0xe0, call->Pts, 0); + unsigned char* PacketStart = malloc(PacketLength + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + + mpeg2_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ +static WriterCaps_t caps = { + "mpeg2", + eVideo, + "V_MPEG2", + VIDEO_ENCODING_AUTO, +}; + +struct Writer_s WriterVideoMPEG2 = { + &reset, + &writeData, + NULL, + &caps, +}; + +static WriterCaps_t h264_caps = { + "mpges_h264", + eVideo, + "V_MPEG2/H264", + VIDEO_ENCODING_H264, +}; + +struct Writer_s WriterVideoMPEGH264 = { + &reset, + &writeData, + NULL, + &h264_caps, +}; diff --git a/libeplayer3/output/writer/pcm.c b/libeplayer3/output/writer/pcm.c new file mode 100644 index 0000000..9c22733 --- /dev/null +++ b/libeplayer3/output/writer/pcm.c @@ -0,0 +1,345 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" +#include "pcm.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define PCM_DEBUG + +#ifdef PCM_DEBUG + +static short debug_level = 1; + +#define pcm_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define pcm_printf(level, fmt, x...) +#endif + +#ifndef PCM_SILENT +#define pcm_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define pcm_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static int initialHeader = 1; + +static unsigned int SubFrameLen = 0; +static unsigned int SubFramesPerPES = 0; + +static const unsigned char clpcm_pes[18] = { 0x00, 0x00, 0x01, 0xBD, //start code + 0x07, 0xF1, //pes length + 0x81, 0x81, 0x09, //fixed + 0x21, 0x00, 0x01, 0x00, 0x01, //PTS marker bits + 0x1E, 0x60, 0x0A, //first pes only, 0xFF after + 0xFF + }; +static const unsigned char clpcm_prv[14] = { 0xA0, //sub_stream_id + 0, 0, //resvd and UPC_EAN_ISRC stuff, unused + 0x0A, //private header length + 0, 9, //first_access_unit_pointer + 0x00, //emph,rsvd,stereo,downmix + 0x0F, //quantisation word length 1,2 + 0x0F, //audio sampling freqency 1,2 + 0, //resvd, multi channel type + 0, //bit shift on channel GR2, assignment + 0x80, //dynamic range control + 0, 0 //resvd for copyright management + }; + +static unsigned char lpcm_pes[18]; +static unsigned char lpcm_prv[14]; + +static unsigned char breakBuffer[8192]; +static unsigned int breakBufferFillSize = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int prepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian) +{ + printf("rate: %d ch: %d bits: %d (%d bps)\n", + uSampleRate/*Format->dwSamplesPerSec*/, + uNoOfChannels/*Format->wChannels*/, + uBitsPerSample/*Format->wBitsPerSample*/, + (uBitsPerSample/*Format->wBitsPerSample*/ / 8) + ); + + SubFrameLen = 0; + SubFramesPerPES = 0; + breakBufferFillSize = 0; + + memcpy(lpcm_pes, clpcm_pes, sizeof(lpcm_pes)); + memcpy(lpcm_prv, clpcm_prv, sizeof(lpcm_prv)); + + //figure out size of subframe + //and set up sample rate + switch(uSampleRate) { + case 48000: SubFrameLen = 40; + break; + case 96000: lpcm_prv[8] |= 0x10; + SubFrameLen = 80; + break; + case 192000: lpcm_prv[8] |= 0x20; + SubFrameLen = 160; + break; + case 44100: lpcm_prv[8] |= 0x80; + SubFrameLen = 40; + break; + case 88200: lpcm_prv[8] |= 0x90; + SubFrameLen = 80; + break; + case 176400: lpcm_prv[8] |= 0xA0; + SubFrameLen = 160; + break; + default: break; + } + + SubFrameLen *= uNoOfChannels; + SubFrameLen *= (uBitsPerSample / 8); + + //rewrite PES size to have as many complete subframes per PES as we can + SubFramesPerPES = ((2048-sizeof(lpcm_pes))-sizeof(lpcm_prv))/SubFrameLen; + SubFrameLen *= SubFramesPerPES; + + lpcm_pes[4] = ((SubFrameLen+(sizeof(lpcm_pes)-6)+sizeof(lpcm_prv))>>8) & 0xFF; + lpcm_pes[5] = (SubFrameLen+(sizeof(lpcm_pes)-6)+sizeof(lpcm_prv)) & 0xFF; + + //set number of channels + lpcm_prv[10] = uNoOfChannels - 1; + + switch(uBitsPerSample) { + case 16: break; + case 24: lpcm_prv[7] |= 0x20; + break; + default: printf("inappropriate bits per sample (%d) - must be 16 or 24\n",uBitsPerSample); + return 1; + } + + return 0; +} + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + pcm_printf(10, "\n"); + + if (call == NULL) + { + pcm_err("call data is NULL...\n"); + return 0; + } + + pcm_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + pcm_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + pcm_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + pcmPrivateData_t* pcmPrivateData = (pcmPrivateData_t*)call->private_data; + + if (initialHeader) + { + initialHeader = 0; + prepareClipPlay(pcmPrivateData->uNoOfChannels, pcmPrivateData->uSampleRate, + pcmPrivateData->uBitsPerSample, pcmPrivateData->bLittleEndian); + } + + unsigned char * buffer = call->data; + int size = call->len; + //printf("PCM %d size SubFrameLen=%d\n", size, SubFrameLen); + + unsigned int qty; + unsigned int n; + unsigned int injectBufferSize = sizeof(lpcm_pes) + sizeof(lpcm_prv) + SubFrameLen; + unsigned char * injectBuffer = (unsigned char *)malloc(sizeof(unsigned char)*injectBufferSize); + unsigned char * injectBufferDataPointer = &injectBuffer[sizeof(lpcm_pes)+sizeof(lpcm_prv)]; + int pos; + + for(pos = 0; pos < size; ) + { + //printf("PCM %s - Position=%d\n", __FUNCTION__, pos); + if((size - pos) < SubFrameLen) + { + breakBufferFillSize = size - pos; + memcpy(breakBuffer, &buffer[pos], sizeof(unsigned char) * breakBufferFillSize); + //printf("PCM %s - Unplayed=%d\n", __FUNCTION__, breakBufferFillSize); + break; + } + + //get first PES's worth + if(breakBufferFillSize > 0) + { + memcpy(injectBufferDataPointer, breakBuffer, sizeof(unsigned char)*breakBufferFillSize); + memcpy(&injectBufferDataPointer[breakBufferFillSize], &buffer[pos], sizeof(unsigned char)*(SubFrameLen - breakBufferFillSize)); + pos += (SubFrameLen - breakBufferFillSize); + breakBufferFillSize = 0; + } else + { + memcpy(injectBufferDataPointer, &buffer[pos], sizeof(unsigned char)*SubFrameLen); + pos += SubFrameLen; + } + + //write the PES header + memcpy(injectBuffer, lpcm_pes, sizeof(lpcm_pes)); + + //write the private data area + memcpy(&injectBuffer[sizeof(lpcm_pes)], lpcm_prv, sizeof(lpcm_prv)); + + //write the PCM data + if(pcmPrivateData->uBitsPerSample == 16) { + for(n=0; nfd, injectBuffer, injectBufferSize); + //printf("PCM %d bytes injected\n", injectBufferSize); + //Hexdump(injectBuffer, 126); + } + free(injectBuffer); + + return size; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_pcm = { + "pcm", + eAudio, + "A_PCM", + AUDIO_ENCODING_LPCMA, +}; + +struct Writer_s WriterAudioPCM = { + &reset, + &writeData, + NULL, + &caps_pcm, +}; + +static WriterCaps_t caps_ipcm = { + "ipcm", + eAudio, + "A_IPCM", + AUDIO_ENCODING_LPCMA, +}; + +struct Writer_s WriterAudioIPCM = { + &reset, + &writeData, + NULL, + &caps_ipcm, +}; + diff --git a/libeplayer3/output/writer/pes.c b/libeplayer3/output/writer/pes.c new file mode 100644 index 0000000..5501269 --- /dev/null +++ b/libeplayer3/output/writer/pes.c @@ -0,0 +1,156 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +int InsertVideoPrivateDataHeader(unsigned char *data, int payload_size) +{ + BitPacker_t ld2 = {data, 0, 32}; + int i; + + PutBits (&ld2, PES_PRIVATE_DATA_FLAG, 8); + PutBits (&ld2, payload_size & 0xff, 8); + PutBits (&ld2, (payload_size >> 8) & 0xff, 8); + PutBits (&ld2, (payload_size >> 16) & 0xff, 8); + + for (i = 4; i < (PES_PRIVATE_DATA_LENGTH+1); i++) + PutBits (&ld2, 0, 8); + + FlushBits (&ld2); + + return PES_PRIVATE_DATA_LENGTH + 1; + +} + +int InsertPesHeader (unsigned char *data, int size, unsigned char stream_id, unsigned long long int pts, int pic_start_code) +{ + BitPacker_t ld2 = {data, 0, 32}; + + if (size > MAX_PES_PACKET_SIZE) + printf("%s: Packet bigger than 63.9K eeeekkkkk\n",__FUNCTION__); + + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x1 ,8); // Start Code + PutBits(&ld2,stream_id ,8); // Stream_id = Audio Stream + //4 + PutBits(&ld2,size + 3 + (pts != INVALID_PTS_VALUE ? 5:0) + (pic_start_code ? (5) : 0),16); // PES_packet_length + //6 = 4+2 + PutBits(&ld2,0x2 ,2); // 10 + PutBits(&ld2,0x0 ,2); // PES_Scrambling_control + PutBits(&ld2,0x0 ,1); // PES_Priority + PutBits(&ld2,0x0 ,1); // data_alignment_indicator + PutBits(&ld2,0x0 ,1); // Copyright + PutBits(&ld2,0x0 ,1); // Original or Copy + //7 = 6+1 + + if (pts!=INVALID_PTS_VALUE) + PutBits(&ld2,0x2 ,2); + else + PutBits(&ld2,0x0 ,2); // PTS_DTS flag + + PutBits(&ld2,0x0 ,1); // ESCR_flag + PutBits(&ld2,0x0 ,1); // ES_rate_flag + PutBits(&ld2,0x0 ,1); // DSM_trick_mode_flag + PutBits(&ld2,0x0 ,1); // additional_copy_ingo_flag + PutBits(&ld2,0x0 ,1); // PES_CRC_flag + PutBits(&ld2,0x0 ,1); // PES_extension_flag + //8 = 7+1 + + if (pts!=INVALID_PTS_VALUE) + PutBits(&ld2,0x5,8); + else + PutBits(&ld2,0x0 ,8); // PES_header_data_length + //9 = 8+1 + + if (pts!=INVALID_PTS_VALUE) + { + PutBits(&ld2,0x2,4); + PutBits(&ld2,(pts>>30) & 0x7,3); + PutBits(&ld2,0x1,1); + PutBits(&ld2,(pts>>15) & 0x7fff,15); + PutBits(&ld2,0x1,1); + PutBits(&ld2,pts & 0x7fff,15); + PutBits(&ld2,0x1,1); + } + //14 = 9+5 + + if (pic_start_code) + { + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x1 ,8); // Start Code + PutBits(&ld2,pic_start_code & 0xff ,8); // 00, for picture start + PutBits(&ld2,(pic_start_code >> 8 )&0xff,8); // For any extra information (like in mpeg4p2, the pic_start_code) + //14 + 4 = 18 + } + + FlushBits(&ld2); + + return (ld2.Ptr - data); + +} diff --git a/libeplayer3/output/writer/vc1.c b/libeplayer3/output/writer/vc1.c new file mode 100644 index 0000000..4f423ea --- /dev/null +++ b/libeplayer3/output/writer/vc1.c @@ -0,0 +1,292 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMV3_PRIVATE_DATA_LENGTH 4 + +#define METADATA_STRUCT_A_START 12 +#define METADATA_STRUCT_B_START 24 +#define METADATA_STRUCT_B_FRAMERATE_START 32 +#define METADATA_STRUCT_C_START 8 + + +#define VC1_SEQUENCE_LAYER_METADATA_START_CODE 0x80 +#define VC1_FRAME_START_CODE 0x0d + +#define VC1_DEBUG + +#ifdef VC1_DEBUG + +static short debug_level = 10; + +#define vc1_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vc1_printf(level, fmt, x...) +#endif + +#ifndef VC1_SILENT +#define vc1_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vc1_err(fmt, x...) +#endif + + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +static const unsigned char SequenceLayerStartCode[] = {0x00, 0x00, 0x01, VC1_SEQUENCE_LAYER_METADATA_START_CODE}; + + +static const unsigned char Metadata[] = +{ + 0x00, 0x00, 0x00, 0xc5, + 0x04, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile*/ + 0x00, 0x00, 0x00, 0x00, /* Struct A */ + 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, /* Struct B */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; +static unsigned char FrameHeaderSeen = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + FrameHeaderSeen = 0; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int len = 0; + + vc1_printf(10, "\n"); + + if (call == NULL) { + vc1_err("call data is NULL...\n"); + return 0; + } + + vc1_printf(10, "VideoPts %lld\n", call->Pts); + + vc1_printf(10, "Got Private Size %d\n", call->private_size); + + + if ((call->data == NULL) || (call->len <= 0)) { + vc1_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) { + vc1_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + + unsigned char PesPacket[PES_MIN_HEADER_SIZE+128]; + unsigned char* PesPtr; + unsigned int MetadataLength; + unsigned int crazyFramerate = 0; + + vc1_printf(10, "Framerate: %u\n", call->FrameRate); + vc1_printf(10, "biWidth: %d\n", call->Width); + vc1_printf(10, "biHeight: %d\n", call->Height); + + crazyFramerate = ((10000000.0 / call->FrameRate) * 1000.0); + vc1_printf(10, "crazyFramerate: %u\n", crazyFramerate); + + { + PesPtr = &PesPacket[PES_MIN_HEADER_SIZE]; + + memcpy (PesPtr, SequenceLayerStartCode, sizeof(SequenceLayerStartCode)); + PesPtr += sizeof(SequenceLayerStartCode); + + memcpy (PesPtr, Metadata, sizeof(Metadata)); + PesPtr += METADATA_STRUCT_C_START; + + // + PesPtr += WMV3_PRIVATE_DATA_LENGTH; + + /* Metadata Header Struct A */ + *PesPtr++ = (call->Height >> 0) & 0xff; + *PesPtr++ = (call->Height >> 8) & 0xff; + *PesPtr++ = (call->Height >> 16) & 0xff; + *PesPtr++ = call->Height >> 24; + *PesPtr++ = (call->Width >> 0) & 0xff; + *PesPtr++ = (call->Width >> 8) & 0xff; + *PesPtr++ = (call->Width >> 16) & 0xff; + *PesPtr++ = call->Width >> 24; + + PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */ + + *PesPtr++ = (crazyFramerate >> 0) & 0xff; + *PesPtr++ = (crazyFramerate >> 8) & 0xff; + *PesPtr++ = (crazyFramerate >> 16) & 0xff; + *PesPtr++ = crazyFramerate >> 24; + + MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + len = write(call->fd, PesPacket, HeaderLength + MetadataLength); + } + + { + int i; + + /* For VC1 the codec private data is a standard vc1 sequence header so we just copy it to the output */ + memcpy (&PesPacket[PES_MIN_HEADER_SIZE], call->private_data, call->private_size); + + vc1_printf(10, "Private Data:\n"); + for (i = 0; i < call->private_size; i++) + vc1_printf(10, "%02x ", PesPacket[PES_MIN_HEADER_SIZE+i]); + vc1_printf(10, "\n"); + + int HeaderLength = InsertPesHeader (PesPacket, call->private_size, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + len = write(call->fd, PesPacket, call->private_size + HeaderLength); + } + initialHeader = 0; + } + + if(call->len > 0 && call->data) { + int Position = 0; + unsigned char insertSampleHeader = 1; + + while(1) { + + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + vc1_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + memset (PesHeader, '0', PES_MAX_HEADER_SIZE); + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, call->Pts, 0); + unsigned char* PacketStart; + + if(insertSampleHeader) { + const unsigned char Vc1FrameStartCode[] = {0, 0, 1, VC1_FRAME_START_CODE}; + +/* + vc1_printf(10, "Data Start: {00 00 01 0d} - "); + int i; + for (i = 0; i < 4; i++) vc1_printf(10, "%02x ", call->data[i]); + vc1_printf(10, "\n"); +*/ + + if (!FrameHeaderSeen && (call->len > 3) && (memcmp (call->data, Vc1FrameStartCode, 4) == 0)) + FrameHeaderSeen = 1; + if (!FrameHeaderSeen) + { + memcpy (&PesHeader[HeaderLength], Vc1FrameStartCode, sizeof(Vc1FrameStartCode)); + HeaderLength += sizeof(Vc1FrameStartCode); + } + insertSampleHeader = 0; + } + + PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + } + + vc1_printf(10, "< %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "vc1", + eVideo, + "V_VC1", + VIDEO_ENCODING_VC1 +}; + +struct Writer_s WriterVideoVC1 = { + &reset, + &writeData, + NULL, + &caps +}; + diff --git a/libeplayer3/output/writer/vorbis.c b/libeplayer3/output/writer/vorbis.c new file mode 100644 index 0000000..4415e65 --- /dev/null +++ b/libeplayer3/output/writer/vorbis.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define VORBIS_DEBUG + +#ifdef VORBIS_DEBUG + +static short debug_level = 1; + +#define vorbis_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vorbis_printf(level, fmt, x...) +#endif + +#ifndef VORBIS_SILENT +#define vorbis_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vorbis_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + vorbis_printf(10, "\n"); + + if (call == NULL) + { + vorbis_err("call data is NULL...\n"); + return 0; + } + + vorbis_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + vorbis_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + vorbis_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + vorbis_printf(10, "vorbis_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_vorbis = { + "vorbis", + eAudio, + "A_VORBIS", + AUDIO_ENCODING_VORBIS, +}; + +struct Writer_s WriterAudioVORBIS = { + &reset, + &writeData, + NULL, + &caps_vorbis, +}; + diff --git a/libeplayer3/output/writer/wma.c b/libeplayer3/output/writer/wma.c new file mode 100644 index 0000000..2fe0acc --- /dev/null +++ b/libeplayer3/output/writer/wma.c @@ -0,0 +1,183 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMA_DEBUG + +#ifdef WMA_DEBUG + +static short debug_level = 10; + +#define wma_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wma_printf(level, fmt, x...) +#endif + +#ifndef WMA_SILENT +#define wma_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wma_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int len = 0; + + wma_printf(10, "\n"); + + if (call == NULL) + { + wma_err("call data is NULL...\n"); + return 0; + } + + wma_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + wma_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + wma_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + int HeaderLength; + + if ((call->private_size <= 0) || (call->private_data == NULL)) + { + wma_err("private NULL.\n"); + return -1; + } + + HeaderLength = InsertPesHeader (PesHeader, call->private_size, MPEG_AUDIO_PES_START_CODE, 0, 0); + + unsigned char* PacketStart = malloc(call->private_size + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->private_data, call->private_size); + + len = write(call->fd, PacketStart, call->private_size + HeaderLength); + + free(PacketStart); + + initialHeader = 0; + } + + if (call->len > 0 && call->data) + { + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + } + + wma_printf(10, "wma < %d\n", len); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "wma", + eAudio, + "A_WMA", + AUDIO_ENCODING_WMA +}; + +struct Writer_s WriterAudioWMA = { + &reset, + &writeData, + NULL, + &caps +}; diff --git a/libeplayer3/output/writer/wmv.c b/libeplayer3/output/writer/wmv.c new file mode 100644 index 0000000..04ce6f5 --- /dev/null +++ b/libeplayer3/output/writer/wmv.c @@ -0,0 +1,280 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMV3_PRIVATE_DATA_LENGTH 4 + +#define METADATA_STRUCT_A_START 12 +#define METADATA_STRUCT_B_START 24 +#define METADATA_STRUCT_B_FRAMERATE_START 32 +#define METADATA_STRUCT_C_START 8 + +#define WMV_DEBUG + +#ifdef WMV_DEBUG + +static short debug_level = 10; + +#define wmv_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wmv_printf(level, fmt, x...) +#endif + +#ifndef WMV_SILENT +#define wmv_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wmv_err(fmt, x...) +#endif + + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct +{ + unsigned char privateData[WMV3_PRIVATE_DATA_LENGTH]; + unsigned int width; + unsigned int height; + unsigned int framerate; +} awmv_t; + +static const unsigned char Metadata[] = +{ + 0x00, 0x00, 0x00, 0xc5, + 0x04, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile*/ + 0x00, 0x00, 0x00, 0x00, /* Struct A */ + 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, /* Struct B */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + awmv_t *private_data = (awmv_t *)malloc(sizeof(awmv_t)); + int len = 0; + + wmv_printf(10, "\n"); + + if (call == NULL) { + wmv_err("call data is NULL...\n"); + return 0; + } + + wmv_printf(10, "VideoPts %lld\n", call->Pts); + + wmv_printf(10, "Got Private Size %d\n", call->private_size); + + memcpy(private_data->privateData, call->private_data, + call->private_size>WMV3_PRIVATE_DATA_LENGTH?WMV3_PRIVATE_DATA_LENGTH:call->private_size); + + private_data->width = call->Width; + private_data->height = call->Height; + private_data->framerate = call->FrameRate; + + if ((call->data == NULL) || (call->len <= 0)) { + wmv_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) { + wmv_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + unsigned char PesPacket[PES_MIN_HEADER_SIZE+128]; + unsigned char* PesPtr; + unsigned int MetadataLength; + unsigned int crazyFramerate = 0; + + if (private_data == NULL) { + wmv_err("private_data NULL\n"); + return -1; + } + + wmv_printf(10, "Framerate: %u\n", private_data->framerate); + wmv_printf(10, "biWidth: %d\n", private_data->width); + wmv_printf(10, "biHeight: %d\n", private_data->height); + + crazyFramerate = ((10000000.0 / private_data->framerate) * 1000.0); + wmv_printf(10, "crazyFramerate: %u\n", crazyFramerate); + + PesPtr = &PesPacket[PES_MIN_HEADER_SIZE]; + + memcpy (PesPtr, Metadata, sizeof(Metadata)); + PesPtr += METADATA_STRUCT_C_START; + + memcpy (PesPtr, private_data->privateData, WMV3_PRIVATE_DATA_LENGTH); + PesPtr += WMV3_PRIVATE_DATA_LENGTH; + + /* Metadata Header Struct A */ + *PesPtr++ = (private_data->height >> 0) & 0xff; + *PesPtr++ = (private_data->height >> 8) & 0xff; + *PesPtr++ = (private_data->height >> 16) & 0xff; + *PesPtr++ = private_data->height >> 24; + *PesPtr++ = (private_data->width >> 0) & 0xff; + *PesPtr++ = (private_data->width >> 8) & 0xff; + *PesPtr++ = (private_data->width >> 16) & 0xff; + *PesPtr++ = private_data->width >> 24; + + PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */ + + *PesPtr++ = (crazyFramerate >> 0) & 0xff; + *PesPtr++ = (crazyFramerate >> 8) & 0xff; + *PesPtr++ = (crazyFramerate >> 16) & 0xff; + *PesPtr++ = crazyFramerate >> 24; + + MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + len = write(call->fd,PesPacket, HeaderLength + MetadataLength); + + initialHeader = 0; + } + + if(call->len > 0 && call->data) { + int Position = 0; + unsigned char insertSampleHeader = 1; + while(1) { + + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + wmv_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + memset (PesHeader, '0', PES_MAX_HEADER_SIZE); + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, call->Pts, 0); + unsigned char* PacketStart; + + if(insertSampleHeader) { + unsigned int PesLength; + unsigned int PrivateHeaderLength; + + PrivateHeaderLength = InsertVideoPrivateDataHeader (&PesHeader[HeaderLength], + call->len); + /* Update PesLength */ + PesLength = PesHeader[PES_LENGTH_BYTE_0] + + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength; + PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff; + PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff; + PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength; + PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT; + + HeaderLength += PrivateHeaderLength; + insertSampleHeader = 0; + } + + PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + } + + wmv_printf(10, "< %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "wmv", + eVideo, + "V_WMV", + VIDEO_ENCODING_WMV +}; + +struct Writer_s WriterVideoWMV = { + &reset, + &writeData, + NULL, + &caps +}; + diff --git a/libeplayer3/output/writer/writer.c b/libeplayer3/output/writer/writer.c new file mode 100644 index 0000000..1b33ca7 --- /dev/null +++ b/libeplayer3/output/writer/writer.c @@ -0,0 +1,141 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 + * + * 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 +#include + +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WRITER_DEBUG + +#ifdef WRITER_DEBUG + +static short debug_level = 0; + +#define writer_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define writer_printf(level, x...) +#endif + +#ifndef WRITER_SILENT +#define writer_err(x...) do { printf(x); } while (0) +#else +#define writer_err(x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +Writer_t* getWriter(char* encoding) +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, encoding) == 0) + { + writer_printf(50, "%s: found writer \"%s\" for \"%s\"\n", __func__, AvailableWriter[i]->caps->name, encoding); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found for \"%s\"\n", __func__, encoding); + + return NULL; +} + +Writer_t* getDefaultVideoWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, "V_MPEG2") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + +Writer_t* getDefaultAudioWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, "A_MP3") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + +Writer_t* getDefaultFramebufferWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + writer_printf(10, "%s\n", AvailableWriter[i]->caps->textEncoding); + if (strcmp(AvailableWriter[i]->caps->textEncoding, "framebuffer") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + diff --git a/libeplayer3/playback/playback.c b/libeplayer3/playback/playback.c new file mode 100644 index 0000000..e1fe779 --- /dev/null +++ b/libeplayer3/playback/playback.c @@ -0,0 +1,1061 @@ +/* + * GPL + * duckbox 2010 + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "playback.h" +#include "common.h" +#include "misc.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define PLAYBACK_DEBUG + +static short debug_level = 10; + +#ifdef PLAYBACK_DEBUG +#define playback_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define playback_printf(level, fmt, x...) +#endif + +#ifndef PLAYBACK_SILENT +#define playback_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define playback_err(fmt, x...) +#endif + +#define cERR_PLAYBACK_NO_ERROR 0 +#define cERR_PLAYBACK_ERROR -1 + +#define cMaxSpeed_ff 128 /* fixme: revise */ +#define cMaxSpeed_fr -320 /* fixme: revise */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_t supervisorThread; +static int hasThreadStarted = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +static int PlaybackTerminate(Context_t *context); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +/* **************************** */ +/* Supervisor Thread */ +/* **************************** */ + +static void SupervisorThread(Context_t *context) { + int status = 0, lastStatus = 0; + long long int playPts = -1; + long long int lastPts = -1; + int dieNow = 0; + int count = 0; + + playback_printf(10, ">\n"); + + while ( context && context->playback && context->playback->isPlaying ) + { + if (context->container->selectedContainer != NULL) + context->container->selectedContainer->Command(context, CONTAINER_STATUS, &status); + + if (context->container->selectedContainer != NULL) + context->container->selectedContainer->Command(context, CONTAINER_LAST_PTS, &lastPts); + +#ifdef FRAMECOUNT_WORKS +// This is a good place to implement buffer managment +long long int frameCount = -1; +int ret = context->playback->Command(context, PLAYBACK_GET_FRAME_COUNT, &frameCount); +playback_printf(1, "Framecount = %ull\n", frameCount); +status = 1; +#endif + + if ((status == 0) && (status != lastStatus)) + { + playback_printf(1, "container has ended, syncing to playback pts ...\n"); + +#define FLUSH_AFTER_CONTAINER_ENDED +#ifdef FLUSH_AFTER_CONTAINER_ENDED + // These means that we have injected everything we got, so flush it. + // As this is a thread, the call should block the thread as long as frames are beeing played. + // The main thread should not be blocked by this. + // This also helps for files which dont have any pts at all + if (context->output->Command(context, OUTPUT_FLUSH, NULL) < 0) + { + playback_err("failed to flush output.\n"); + } +#endif + + while (!dieNow) + { + if (context && context->playback && context->playback->isPlaying) + { + int ret = context->playback->Command(context, PLAYBACK_PTS, &playPts); + + playback_err("playbackPts %lld ->lastPts %lld ret %d\n", playPts, lastPts, ret); + + if (ret != cERR_PLAYBACK_NO_ERROR || playPts + (2 * 90000) >= lastPts) + dieNow = 1; + + } else + { + playback_err("playback already died ?\n"); + dieNow = 1; + } + + count++; + + if (count == 200) + { + playback_err("something went wrong, expect end but never reached?\n"); + dieNow = 1; + } + usleep(10000); + } + } + + lastStatus = status; + + if (dieNow) + break; + + usleep(10000); + + } /* while */ + + playback_printf(10, "<\n"); + + hasThreadStarted = 0; + PlaybackTerminate(context); + + playback_printf(0, "terminating\n"); +} + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int PlaybackOpen(Context_t *context, char * uri) { + playback_printf(10, "URI=%s\n", uri); + + context->playback->uri = strdup(uri); + + if (!context->playback->isPlaying) { + if (!strncmp("file://", uri, 7)) { + char * extension = NULL; + context->playback->isFile = 1; + context->playback->isHttp = 0; + context->playback->isUPNP = 0; + + getExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR; + + if(context->container->Command(context, CONTAINER_ADD, extension) < 0) + return cERR_PLAYBACK_ERROR; + if (context->container->selectedContainer != NULL) { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, uri) < 0) + return cERR_PLAYBACK_ERROR; + } else { + return cERR_PLAYBACK_ERROR; + } + + free(extension); + + //CHECK FOR SUBTITLES + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_INIT, uri+7); + + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_INIT, uri+7); + + if (context->container && context->container->assContainer) + context->container->assContainer->Command(context, CONTAINER_INIT, NULL); + + } else if (!strncmp("http://", uri, 7)) { +/* char * extension = NULL;*/ + context->playback->isFile = 0; + context->playback->isHttp = 1; + context->playback->isUPNP = 0; + + /* Hellmaster1024: http streams often do not have a propper ending like .mp3 so we let ffmpeg handle + all kind of http streams + if(!extension) + getExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR;*/ + + if(context->container->Command(context, CONTAINER_ADD, "mp3") < 0) + return cERR_PLAYBACK_ERROR; + + if (context->container->selectedContainer != NULL) + { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, context->playback->uri) < 0) + return cERR_PLAYBACK_ERROR; + } else + { + return cERR_PLAYBACK_ERROR; + } + + //free(extension); + } /* http */ + else if (!strncmp("mms://", uri, 6) || !strncmp("rtsp://", uri, 7) || !strncmp("rtmp://", uri, 7)) { +/* char * extension = NULL; */ + context->playback->isFile = 0; + context->playback->isHttp = 1; + context->playback->isUPNP = 0; + /* Hellmaster1024: http streams often do not have a propper ending like .mp3 so we let ffmpeg handle + all kind of http streams + if (!extension) + getExtension(uri+6, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR;*/ + + if (!strncmp("mms://", uri, 6)) { + // mms is in reality called rtsp, and ffmpeg expects this + char * tUri = (char*)malloc(strlen(uri) + 2); + strncpy(tUri+1, uri, strlen(uri)+1); + strncpy(tUri, "rtsp", 4); + free(context->playback->uri); + context->playback->uri = strdup(tUri); + free(tUri); + } + + if(context->container->Command(context, CONTAINER_ADD, "mp3") < 0) + return cERR_PLAYBACK_ERROR; + + if (context->container->selectedContainer != NULL) + { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, context->playback->uri) < 0) + return cERR_PLAYBACK_ERROR; + } else + { + return cERR_PLAYBACK_ERROR; + } + + //free(extension); + } /* upnp */ + else if (!strncmp("upnp://", uri, 7)) { + char * extension = NULL; + context->playback->isFile = 0; + context->playback->isHttp = 0; + context->playback->isUPNP = 1; + + context->playback->uri += 7; /* jump over upnp:// */ + + getUPNPExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR; + + if(context->container->Command(context, CONTAINER_ADD, extension) < 0) + { + playback_err("container CONTAINER_ADD failed\n"); + return cERR_PLAYBACK_ERROR; + } + if (context->container->selectedContainer != NULL) { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, uri+7) < 0) + { + playback_err("container CONTAINER_INIT failed\n"); + return cERR_PLAYBACK_ERROR; + } + } else { + playback_err("selected container is null\n"); + return cERR_PLAYBACK_ERROR; + } + + free(extension); + + } /* upnp */ + else { + playback_err("Unknown stream!\n"); + return cERR_PLAYBACK_ERROR; + } + } + else + { + playback_err("playback alread running\n"); + return cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value 0\n"); + + return cERR_PLAYBACK_NO_ERROR; +} + +static int PlaybackClose(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->container->Command(context, CONTAINER_DEL, NULL) < 0) + { + playback_err("container delete failed\n"); + } + +//FIXME KILLED BY signal 7 or 11 + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_DEL, NULL); + + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_DEL, NULL); + + context->manager->audio->Command(context, MANAGER_DEL, NULL); + context->manager->video->Command(context, MANAGER_DEL, NULL); + context->manager->subtitle->Command(context, MANAGER_DEL, NULL); + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPlay(Context_t *context) { + pthread_attr_t attr; + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (!context->playback->isPlaying) { + context->playback->AVSync = 1; + context->output->Command(context, OUTPUT_AVSYNC, NULL); + + context->playback->isCreationPhase = 1; // allows the created thread to go into wait mode + ret = context->output->Command(context, OUTPUT_PLAY, NULL); + + if (ret != 0) { + playback_err("OUTPUT_PLAY failed!\n"); + playback_err("clearing isCreationPhase!\n"); + + context->playback->isCreationPhase = 0; // allow thread to go into next state + } else { + context->playback->isPlaying = 1; + context->playback->isPaused = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + + if (hasThreadStarted == 0) { + int error; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&supervisorThread, &attr, (void *)&SupervisorThread, context)) != 0) + { + playback_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error)); + + hasThreadStarted = 0; + ret = cERR_PLAYBACK_ERROR; + } + else + { + playback_printf(10, "Created thread\n"); + + hasThreadStarted = 1; + } + } + + playback_printf(10, "clearing isCreationPhase!\n"); + + context->playback->isCreationPhase = 0; // allow thread to go into next state + + ret = context->container->selectedContainer->Command(context, CONTAINER_PLAY, NULL); + if (ret != 0) { + playback_err("CONTAINER_PLAY failed!\n"); + } + + } + + } else + { + playback_err("playback already running\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPause(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying && !context->playback->isPaused) { + + if(context->playback->SlowMotion) + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->output->Command(context, OUTPUT_PAUSE, NULL); + + context->playback->isPaused = 1; + //context->playback->isPlaying = 1; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + } else + { + playback_err("playback not playing or already in pause mode\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackContinue(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying && + (context->playback->isPaused || context->playback->isForwarding || context->playback->BackWard || context->playback->SlowMotion)) { + + if(context->playback->SlowMotion) + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->output->Command(context, OUTPUT_CONTINUE, NULL); + + context->playback->isPaused = 0; + //context->playback->isPlaying = 1; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + } else + { + playback_err("continue not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackStop(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + int wait_time = 20; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying) { + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + context->output->Command(context, OUTPUT_STOP, NULL); + context->container->selectedContainer->Command(context, CONTAINER_STOP, NULL); + + } else + { + playback_err("stop not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + while ( (hasThreadStarted != 0) && (--wait_time) > 0 ) { + playback_printf(10, "Waiting for supervisor thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + playback_err( "Timeout waiting for thread!\n"); + + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackTerminate(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + int wait_time = 20; + + playback_printf(20, "\n"); + + if ( context && context->playback && context->playback->isPlaying ) { + //First Flush and than delete container, else e2 cant read length of file anymore + + if (context->output->Command(context, OUTPUT_FLUSH, NULL) < 0) + { + playback_err("failed to flush output.\n"); + } + + ret = context->container->selectedContainer->Command(context, CONTAINER_STOP, NULL); + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + } else + { + playback_err("%p %p %d\n", context, context->playback, context->playback->isPlaying); + + /* fixme: konfetti: we should return an error here but this seems to be a condition which + * can happen and is not a real error, which leads to a dead neutrino. should investigate + * here later. + */ + } + + while ( (hasThreadStarted != 0) && (--wait_time) > 0 ) { + playback_printf(10, "Waiting for supervisor thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + playback_err( "Timeout waiting for thread!\n"); + + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackFastForward(Context_t *context, int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "speed %d\n", *speed); + + /* Audio only forwarding not supported */ + if (context->playback->isVideo && !context->playback->isHttp && !context->playback->BackWard && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed <= 0) || (*speed > cMaxSpeed_ff)) + { + playback_err("speed %d out of range (1 - %d) \n", *speed, cMaxSpeed_ff); + return cERR_PLAYBACK_ERROR; + } + + context->playback->isForwarding = 1; + context->playback->Speed = *speed; + + playback_printf(20, "Speed: %d x {%d}\n", *speed, context->playback->Speed); + + context->output->Command(context, OUTPUT_FASTFORWARD, NULL); + } else + { + playback_err("fast forward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +#ifdef reverse_playback_1 +static pthread_t FBThread; +/* konfetti: see below */ +static unsigned char isFBThreadStarted = 0; + +static void FastBackwardThread(Context_t *context) +{ + playback_printf(10, "\n"); + + context->output->Command(context, OUTPUT_AUDIOMUTE, "1"); + while(context->playback && context->playback->isPlaying && context->playback->BackWard) + { + context->playback->isSeeking = 1; + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->output->Command(context, OUTPUT_PAUSE, NULL); + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->container->selectedContainer->Command(context, CONTAINER_SEEK, &context->playback->BackWard); + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->playback->isSeeking = 0; + context->output->Command(context, OUTPUT_CONTINUE, NULL); + + //context->container->selectedContainer->Command(context, CONTAINER_SEEK, &context->playback->BackWard); + //context->output->Command(context, OUTPUT_CLEAR, "video"); + usleep(500000); + } + //context->output->Command(context, OUTPUT_CLEAR, NULL); + context->output->Command(context, OUTPUT_AUDIOMUTE, "0"); + isFBThreadStarted = 0; + + playback_printf(10, "exit\n"); +} + +static int PlaybackFastBackward(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + int error; + pthread_attr_t attr; + + playback_printf(10, "speed %d\n", *speed); + + /* Audio only backwarding not supported */ + if (context->playback->isVideo && !context->playback->isHttp && !context->playback->isForwarding && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed > 0) || (*speed < cMaxSpeed_fr)) + { + playback_err("speed %d out of range (0 - %d) \n", *speed, cMaxSpeed_fr); + return cERR_PLAYBACK_ERROR; + } + + context->playback->BackWard = -(*speed); + + playback_printf(20, "Speed: %d x {%f}\n", *speed, context->playback->BackWard); + + if(!isFBThreadStarted) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&FBThread, &attr, (void *)&FastBackwardThread, context)) != 0) + { + playback_err("Error creating thread error:%d:%s\n",error,strerror(error)); + isFBThreadStarted = 0; + ret = cERR_PLAYBACK_ERROR; + } else + isFBThreadStarted = 1; + } + } else + { + playback_err("fast backward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#else +static int PlaybackFastBackward(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "speed = %d\n", *speed); + + /* Audio only reverse play not supported */ + if (context->playback->isVideo && !context->playback->isForwarding && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed > 0) || (*speed < cMaxSpeed_fr)) + { + playback_err("speed %d out of range (0 - %d) \n", *speed, cMaxSpeed_fr); + return cERR_PLAYBACK_ERROR; + } + + if (*speed == 0) + { + context->playback->BackWard = 0; + context->playback->Speed = 0; /* reverse end */ + } else + { + context->playback->isSeeking = 1; + context->playback->Speed = *speed; + context->playback->BackWard = 2^(*speed); + + playback_printf(1, "S %d B %f\n", context->playback->Speed, context->playback->BackWard); + } + + context->output->Command(context, OUTPUT_AUDIOMUTE, "1"); + context->output->Command(context, OUTPUT_CLEAR, NULL); + if (context->output->Command(context, OUTPUT_REVERSE, NULL) < 0) + { + playback_err("OUTPUT_REVERSE failed\n"); + context->playback->BackWard = 0; + context->playback->Speed = 1; + context->playback->isSeeking = 0; + ret = cERR_PLAYBACK_ERROR; + } + } else + { + playback_err("fast backward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + context->playback->isSeeking = 0; + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#endif + + +static int PlaybackSlowMotion(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + //Audio only forwarding not supported + if (context->playback->isVideo && !context->playback->isHttp && context->playback->isPlaying) { + if(context->playback->isPaused) + PlaybackContinue(context); + + switch(*speed) { + case 2: + context->playback->SlowMotion = 2; + break; + case 4: + context->playback->SlowMotion = 4; + break; + case 8: + context->playback->SlowMotion = 8; + break; + } + + playback_printf(20, "SlowMotion: %d x {%d}\n", *speed, context->playback->SlowMotion); + + context->output->Command(context, OUTPUT_SLOWMOTION, NULL); + } else + { + playback_err("slowmotion not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSeek(Context_t *context, float * pos) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "pos: %f\n", *pos); + + if (!context->playback->isHttp && context->playback->isPlaying && !context->playback->isForwarding && !context->playback->BackWard && !context->playback->SlowMotion && !context->playback->isPaused) { + context->playback->isSeeking = 1; + + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->container->selectedContainer->Command(context, CONTAINER_SEEK, pos); + + context->playback->isSeeking = 0; + + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPts(Context_t *context, unsigned long long int* pts) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *pts = 0; + + if (context->playback->isPlaying) { + ret = context->output->Command(context, OUTPUT_PTS, pts); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackGetFrameCount(Context_t *context, unsigned long long int* frameCount) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *frameCount = 0; + + if (context->playback->isPlaying) { + ret = context->output->Command(context, OUTPUT_GET_FRAME_COUNT, frameCount); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackLength(Context_t *context, double* length) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *length = 0; + + if (context->playback->isPlaying) { + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_LENGTH, length); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSwitchAudio(Context_t *context, int* track) { + int ret = cERR_PLAYBACK_NO_ERROR; + int curtrackid = 0; + int nextrackid = 0; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying) { + if (context->manager && context->manager->audio) { + context->manager->audio->Command(context, MANAGER_GET, &curtrackid); + context->manager->audio->Command(context, MANAGER_SET, track); + context->manager->audio->Command(context, MANAGER_GET, &nextrackid); + } + + if(nextrackid != curtrackid) { + + //PlaybackPause(context); + + if (context->output && context->output->audio) + context->output->audio->Command(context, OUTPUT_SWITCH, (void*)"audio"); + + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_SWITCH_AUDIO, &nextrackid); + + //PlaybackContinue(context); + } + } else + { + playback_err("switch audio not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSwitchSubtitle(Context_t *context, int* track) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "Track: %d\n", *track); + + if (context && context->playback && context->playback->isPlaying ) { + if (context->manager && context->manager->subtitle) { + int trackid; + + if (context->manager->subtitle->Command(context, MANAGER_SET, track) < 0) + { + playback_err("manager set track failed\n"); + } + + context->manager->subtitle->Command(context, MANAGER_GET, &trackid); + +/* konfetti: I make this hack a little bit nicer, + * but its still a hack in my opinion ;) + */ + if (context->container && context->container->assContainer) + context->container->assContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + + if (trackid >= TEXTSRTOFFSET) + { + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + } + if (trackid >= TEXTSSAOFFSET) + { + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + } + + + + } else + { + ret = cERR_PLAYBACK_ERROR; + playback_err("no subtitle\n"); + } + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackInfo(Context_t *context, char** infoString) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + +/* konfetti comment: + * removed if clause here (playback running) because its + * not necessary for all container. e.g. in case of ffmpeg + * container playback must not play to get the info. + */ + + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_INFO, infoString); + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int Command(void* _context, PlaybackCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; /* to satisfy compiler */ + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "Command %d\n", command); + + + switch(command) { + case PLAYBACK_OPEN: { + ret = PlaybackOpen(context, (char*)argument); + break; + } + case PLAYBACK_CLOSE: { + ret = PlaybackClose(context); + break; + } + case PLAYBACK_PLAY: { + ret = PlaybackPlay(context); + break; + } + case PLAYBACK_STOP: { + ret = PlaybackStop(context); + break; + } + case PLAYBACK_PAUSE: { // 4 + ret = PlaybackPause(context); + break; + } + case PLAYBACK_CONTINUE: { + ret = PlaybackContinue(context); + break; + } + case PLAYBACK_TERM: { + ret = PlaybackTerminate(context); + break; + } + case PLAYBACK_FASTFORWARD: { + ret = PlaybackFastForward(context,(int*)argument); + break; + } + case PLAYBACK_SEEK: { + ret = PlaybackSeek(context, (float*)argument); + break; + } + case PLAYBACK_PTS: { // 10 + ret = PlaybackPts(context, (unsigned long long int*)argument); + break; + } + case PLAYBACK_LENGTH: { // 11 + ret = PlaybackLength(context, (double*)argument); + break; + } + case PLAYBACK_SWITCH_AUDIO: { + ret = PlaybackSwitchAudio(context, (int*)argument); + break; + } + case PLAYBACK_SWITCH_SUBTITLE: { + ret = PlaybackSwitchSubtitle(context, (int*)argument); + break; + } + case PLAYBACK_INFO: { + ret = PlaybackInfo(context, (char**)argument); + break; + } + case PLAYBACK_SLOWMOTION: { + ret = PlaybackSlowMotion(context,(int*)argument); + break; + } + case PLAYBACK_FASTBACKWARD: { + ret = PlaybackFastBackward(context,(int*)argument); + break; + } + case PLAYBACK_GET_FRAME_COUNT: { // 10 + ret = PlaybackGetFrameCount(context, (unsigned long long int*)argument); + break; + } + default: + playback_err("PlaybackCmd %d not supported!\n", command); + ret = cERR_PLAYBACK_ERROR; + break; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + + +PlaybackHandler_t PlaybackHandler = { + "Playback", + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &Command, + "", + 0, +}; diff --git a/libeplayer3/tools/eplayer2.c b/libeplayer3/tools/eplayer2.c new file mode 100644 index 0000000..2b8bbc2 --- /dev/null +++ b/libeplayer3/tools/eplayer2.c @@ -0,0 +1,612 @@ +/* + * eplayer3: command line playback using libeplayer3 + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "subtitle.h" + +extern OutputHandler_t OutputHandler; +extern PlaybackHandler_t PlaybackHandler; +extern ContainerHandler_t ContainerHandler; +extern ManagerHandler_t ManagerHandler; + +Context_t * player = NULL; + +/* ******************************************** */ +/* Framebuffer for subtitle */ +/* ******************************************** */ +static int fd = -1; +static unsigned char* lfb = NULL; +struct fb_fix_screeninfo fix; +struct fb_var_screeninfo screeninfo, oldscreen; + +static int stride = 0; +static int xRes = 0; +static int yRes = 0; +static int bpp = 0; + +int kbhit(void) { + struct timeval tv; + fd_set read_fd; + + tv.tv_sec=1; + tv.tv_usec=0; + + FD_ZERO(&read_fd); + FD_SET(0,&read_fd); + + if(select(1, &read_fd, NULL, NULL, &tv) == -1) + return 0; + + if(FD_ISSET(0,&read_fd)) + return 1; + + return 0; +} + +void framebuffer_init() +{ + int available = 0; + + fd = open("/dev/fb0", O_RDWR); + + if (fd < 0) + { + perror("/dev/fb0"); + return; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo) < 0) + { + perror("FBIOGET_VSCREENINFO"); + return; + } + + memcpy(&oldscreen, &screeninfo, sizeof(screeninfo)); + + ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo); + + printf("mode %d, %d, %d\n", screeninfo.xres, screeninfo.yres, screeninfo.bits_per_pixel); + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + printf("fb failed\n"); + } + + stride = fix.line_length; + xRes = screeninfo.xres; + yRes = screeninfo.yres; + bpp = screeninfo.bits_per_pixel; + + printf("stride = %d, width %d\n", stride, xRes); + + available = fix.smem_len; + + printf("%dk video mem\n", available/1024); + + lfb = (unsigned char*) mmap(0, available, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + + if (lfb == NULL) + { + perror("mmap"); + return; + } + + memset(lfb, 0, available); +} + + +int main(int argc,char* argv[]) { + SubtitleOutputDef_t out; + int showInfos = 0, noinput = 0; + char file[255] = {""}; + int speed = 0, speedmap = 0; + printf("%s >\n", __FILE__); + + if (argc < 2) + { + printf("give me a filename please\n"); + exit(1); + } + + if (strstr(argv[1], "://") == NULL) + { + strcpy(file, "file://"); + } + + strcat(file, argv[1]); + + /* debug helper */ + if(argc == 3 && !strcmp(argv[2], "-d")) + { + showInfos = 1; + } + + if(argc == 3 && !strcmp(argv[2], "-n")) + noinput = 1; + + player = malloc(sizeof(Context_t)); + + player->playback = &PlaybackHandler; + player->output = &OutputHandler; + player->container = &ContainerHandler; + player->manager = &ManagerHandler; + + printf("%s\n", player->output->Name); + + //Registrating output devices + player->output->Command(player,OUTPUT_ADD, "audio"); + player->output->Command(player,OUTPUT_ADD, "video"); + player->output->Command(player,OUTPUT_ADD, "subtitle"); + + framebuffer_init(); + + /* for testing ass subtitles */ + out.screen_width = xRes; + out.screen_height = yRes; + out.framebufferFD = fd; + out.destination = lfb; + out.destStride = stride; + out.shareFramebuffer = 1; + + player->output->subtitle->Command(player, (OutputCmd_t)OUTPUT_SET_SUBTITLE_OUTPUT, (void*) &out); + + if(player->playback->Command(player, PLAYBACK_OPEN, file) < 0) + return 10; + + { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + + player->manager->video->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("VideoTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + + player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("SubtitleTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + } + { + int AudioTrackId = -1; + char * AudioTrackEncoding = NULL; + char * AudioTrackName = NULL; + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Audio Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + player->manager->video->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->video->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->video->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Video Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + player->manager->subtitle->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->subtitle->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->subtitle->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Subtitle Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + /* player->manager->audio->Command(player, MANAGER_SET, 2); + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + free(AudioTrackName); + AudioTrackName = NULL;*/ + + } + { + player->output->Command(player, OUTPUT_OPEN, NULL); + + if (showInfos == 1) + { + char *tags[] = + { + "Title", + "Artist", + "Album", + "Year", + "Genre", + "Comment", + "Track", + "Copyright", + "TestLibEplayer", + NULL + }; + int i = 0; + while (tags[i] != NULL) + { + char* tag = tags[i]; + player->playback->Command(player, PLAYBACK_INFO, &tag); +#if !defined(VDR1722) + if (tag != NULL) + printf("\t%s:\t%s\n",tags[i], tag); + else + printf("\t%s:\tNULL\n",tags[i]); +#endif + i++; + } + + player->output->Command(player, OUTPUT_CLOSE, NULL); + + exit(1); + } else + player->playback->Command(player, PLAYBACK_PLAY, NULL); + + /*{ + int pid = 0; + player->playback->Command(player, PLAYBACK_SWITCH_SUBTITLE, (void*)&pid); + }*/ + + while(player->playback->isPlaying) { + int Key = 0; + + if(kbhit()) + if(noinput == 0) + Key = getchar(); + + if(!player->playback->isPlaying) { + break; + } + + if(Key == 0) + continue; + + switch (Key) { + case 'a': { + int Key2 = getchar(); + switch (Key2) { + case 'l': { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + break; + } + case 'c': { + int AudioTrackId = -1; + char * AudioTrackEncoding = NULL; + char * AudioTrackName = NULL; + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Audio Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + break; + } + default: { + Key2 -= 0x30; + if(Key2 >= 0 && Key2 <= 9) { + player->playback->Command(player, PLAYBACK_SWITCH_AUDIO, (void*)&Key2); + } + + } + } + break; + } + + case 's': { + int Key2 = getchar(); + switch (Key2) { + case 'l': { + char ** TrackList = NULL; + player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("SubtitleTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + break; + } + case 'c': { + int SubtitleTrackId = -1; + char * SubtitleTrackEncoding = NULL; + char * SubtitleTrackName = NULL; + player->manager->subtitle->Command(player, MANAGER_GET, &SubtitleTrackId); + player->manager->subtitle->Command(player, MANAGER_GETENCODING, &SubtitleTrackEncoding); + player->manager->subtitle->Command(player, MANAGER_GETNAME, &SubtitleTrackName); + printf("Current Subtitle Track : %d %s %s\n", SubtitleTrackId, SubtitleTrackEncoding, SubtitleTrackName); + free(SubtitleTrackEncoding); + free(SubtitleTrackName); + SubtitleTrackEncoding = NULL; + SubtitleTrackName = NULL; + + break; + } + default: { + Key2 -= 0x30; + if(Key2 >= 0 && Key2 <= 9) { + player->playback->Command(player, PLAYBACK_SWITCH_SUBTITLE, (void*)&Key2); + } + + } + } + break; + } + + + case 'q': + player->playback->Command(player, PLAYBACK_STOP, NULL); + break; + + case 'c': + player->playback->Command(player, PLAYBACK_CONTINUE, NULL); + break; + + case 'p': + player->playback->Command(player, PLAYBACK_PAUSE, NULL); + break; + + case 'f': { + + if (speed < 0) + speed = 0; + + speed++; + + if (speed > 7) + speed = 1; + + switch(speed) + { + case 1: speedmap = 1; break; + case 2: speedmap = 3; break; + case 3: speedmap = 7; break; + case 4: speedmap = 15; break; + case 5: speedmap = 31; break; + case 6: speedmap = 63; break; + case 7: speedmap = 127; break; + } + + player->playback->Command(player, PLAYBACK_FASTFORWARD, &speedmap); + break; + } + + case 'b': { + if (speed > 0) + speed = 0; + + speed--; + + if (speed < -7) + speed = -1; + + switch(speed) + { + case -1: speedmap = -5; break; + case -2: speedmap = -10; break; + case -3: speedmap = -20; break; + case -4: speedmap = -40; break; + case -5: speedmap = -80; break; + case -6: speedmap = -160; break; + case -7: speedmap = -320; break; + } + + player->playback->Command(player, PLAYBACK_FASTBACKWARD, &speedmap); + break; + } +#if defined(VDR1722) + case 'g': { + char gotoString [256]; + gets (gotoString); + int gotoPos = atoi(gotoString); + + double length = 0; + float sec; + + printf("gotoPos %i\n", gotoPos); + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + + if(gotoPos <= 0){ + printf("kleiner als erlaubt\n"); + sec = 0.0; + }else if(gotoPos >= ((int)length - 10)){ + printf("laenger als erlaubt\n"); + sec = (int)length - 10; + }else{ + printf("normal action\n"); + sec = gotoPos; + } + + player->playback->Command(player, PLAYBACK_SEEK, (void*)&sec); + printf("goto postion (%i sec)\n", sec); + break; + } +#endif + case 'k': { +#if !defined(VDR1722) + int Key2 = getchar() - 48; + float sec=0.0; + printf("seconds %d \n", Key2); + switch (Key2) { + case 1: sec=-15.0;break; + case 4: sec=-60.0;break; + case 7: sec=-300.0;break; + case 3: sec= 15.0;break; + case 6: sec= 60.0;break; + case 9: sec= 300.0;break; + } +#else + char seek [256]; + gets (seek); + unsigned int seekTo = atoi(seek); + double length = 0; + float sec; + + unsigned long long int CurrentPTS = 0; + player->playback->Command(player, PLAYBACK_PTS, &CurrentPTS); + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + + int CurrentSec = CurrentPTS / 90000; + printf("CurrentSec = %i, seekTo = %i, abs(seekTo) = %i seekTo + CurrentSec %i\n", CurrentSec, seekTo, abs(seekTo), (seekTo + CurrentSec)); + int ergSec = CurrentSec + seekTo; + if(ergSec < 0){ + printf("kleiner als erlaubt\n"); + sec = 0.0; + }else if((CurrentSec + seekTo) >= ((int)length - 10)){ + printf("laenger als erlaubt\n"); + sec = (int)length - 10; + }else{ + printf("normal action\n"); + sec = seekTo + CurrentSec; + } + + printf("springe %i \n", (int)sec); +#endif + player->playback->Command(player, PLAYBACK_SEEK, (void*)&sec); + break; + } + + case 'l': { + double length = 0; + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + printf("Length = %02d:%02d:%02d (%.4f sec)\n", (int)((length/60)/60)%60, (int)(length/60)%60, (int)length%60, length); + break; + } + case 'j': { + unsigned long long int pts = 0; + player->playback->Command(player, PLAYBACK_PTS, &pts); + unsigned long long int sec = pts / 90000; + printf("Pts = %02d:%02d:%02d (%llu.0000 sec)\n", (int)((sec/60)/60)%60, (int)(sec/60)%60, (int)sec%60, sec); + break; + } + + case 'i': + { + char *tags[] = + { + "Title", + "Artist", + "Album", + "Year", + "Genre", + "Comment", + "Track", + "Copyright", + "TestLibEplayer", + NULL + }; + int i = 0; + while (tags[i] != NULL) + { + char* tag = tags[i]; + player->playback->Command(player, PLAYBACK_INFO, &tag); + + if (tag != NULL) + printf("\t%s:\t%s\n",tags[i], tag); + else + printf("\t%s:\tNULL\n",tags[i]); + i++; + } + break; + } + default: { + printf("Control:\n"); + printf("al: List audio tracks\n"); + printf("ac: List current audio track\n"); + printf("a[id] Select audio track\n"); + printf("sl: List subtitles\n"); + printf("sc: List current subtitle\n"); + printf("s[id] Select subtitles\n"); + printf("q: Stop\n"); + printf("c: Continue\n"); + printf("p: Pause\n"); + printf("f: Increase speed (Fast forward) (stepwise)\n"); + printf("b: Decrease speed (Fast reverse) (stepwise)\n"); + printf("l: Print duration\n"); + printf("j: Print current PTS\n"); + printf("k[1,4,7]: Jump back [15,60,300] seconds\n"); + printf("k[3,6,9]: Jump forward [15,60,300] seconds\n"); + printf("i: Print Info\n"); + break; + } + } + } + + player->output->Command(player, OUTPUT_CLOSE, NULL); + } + + //printOutputCapabilities(); + + exit(0); +} diff --git a/libeplayer3/tools/meta.c b/libeplayer3/tools/meta.c new file mode 100644 index 0000000..842d58b --- /dev/null +++ b/libeplayer3/tools/meta.c @@ -0,0 +1,79 @@ +/* konfetti + * gpl + * 2010 + * + * example utitility to show metatags with ffmpeg. + */ + +#include +#include +#include + +#include +#include + +static AVFormatContext* avContext = NULL; + +void dump_metadata() +{ + AVMetadataTag *tag = NULL; + while ((tag = av_metadata_get(avContext->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + printf("%s: %s\n", tag->key, tag->value); +} + + +int main(int argc,char* argv[]) +{ + char file[255] = {""}; + int err, i; + + if (argc < 2) + { + printf("give me a filename please\n"); + return -1; + } + + if (strstr(argv[1], "://") == NULL) + { + strcpy(file, "file://"); + } + + strcat(file, argv[1]); + + av_register_all(); + + if ((err = av_open_input_file(&avContext, file, NULL, 0, NULL)) != 0) { + char error[512]; + + printf("av_open_input_file failed %d (%s)\n", err, file); + av_strerror(err, error, 512); + printf("Cause: %s\n", error); + + return -1; + } + + if (av_find_stream_info(avContext) < 0) + { + printf("Error av_find_stream_info\n"); + } + + printf("\n***\n"); + dump_metadata(); + + printf("\nstream specific metadata:\n"); + for (i = 0; i < avContext->nb_streams; i++) + { + AVStream* stream = avContext->streams[i]; + + if (stream) + { + AVMetadataTag *tag = NULL; + + if (stream->metadata != NULL) + while ((tag = av_metadata_get(stream->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + printf("%s: %s\n", tag->key, tag->value); + } + } + + return 0; +}