Add missing DVR files
This commit is contained in:
parent
d373a6f691
commit
4c4201ee3e
3 changed files with 847 additions and 0 deletions
99
dvr/dvr.h
Normal file
99
dvr/dvr.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Digital Video Recorder
|
||||
* Copyright (C) 2008 Andreas Öman
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DVR_H
|
||||
#define DVR_H
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
#include "epg.h"
|
||||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
|
||||
extern char *dvr_storage;
|
||||
extern char *dvr_format;
|
||||
extern char *dvr_file_postfix;
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
DVR_SCHEDULED, /* Scheduled for recording (in the future) */
|
||||
DVR_RECORDING,
|
||||
DVR_COMPLETED /* If recording failed, de->de_error is set to
|
||||
a string */
|
||||
|
||||
} dvr_entry_sched_state_t;
|
||||
|
||||
|
||||
typedef struct dvr_entry {
|
||||
LIST_ENTRY(dvr_entry) de_global_link;
|
||||
int de_id;
|
||||
|
||||
channel_t *de_channel;
|
||||
LIST_ENTRY(dvr_entry) de_channel_link;
|
||||
|
||||
time_t de_start;
|
||||
time_t de_stop;
|
||||
|
||||
char *de_creator;
|
||||
char *de_filename; /* Initially null if no filename has been
|
||||
generated yet */
|
||||
char *de_title; /* Title in UTF-8 (from EPG) */
|
||||
char *de_desc; /* Description in UTF-8 (from EPG) */
|
||||
|
||||
char *de_error;
|
||||
|
||||
dvr_entry_sched_state_t de_sched_state;
|
||||
|
||||
gtimer_t de_timer;
|
||||
|
||||
/**
|
||||
* Fields for recording
|
||||
*/
|
||||
|
||||
th_subscription_t *de_s;
|
||||
|
||||
} dvr_entry_t;
|
||||
|
||||
/**
|
||||
* Prototypes
|
||||
*/
|
||||
void dvr_entry_create_by_event(event_t *e, const char *creator);
|
||||
|
||||
void dvr_init(void);
|
||||
|
||||
void dvr_rec_start(dvr_entry_t *de);
|
||||
|
||||
dvr_entry_t *dvr_entry_find_by_id(int id);
|
||||
|
||||
void dvr_entry_cancel(dvr_entry_t *de);
|
||||
|
||||
|
||||
/**
|
||||
* Query interface
|
||||
*/
|
||||
typedef struct dvr_query_result {
|
||||
dvr_entry_t **dqr_array;
|
||||
int dqr_entries;
|
||||
int dqr_alloced;
|
||||
} dvr_query_result_t;
|
||||
|
||||
void dvr_query(dvr_query_result_t *dqr);
|
||||
void dvr_query_free(dvr_query_result_t *dqr);
|
||||
void dvr_query_sort(dvr_query_result_t *dqr);
|
||||
|
||||
#endif /* DVR_H */
|
440
dvr/dvr_db.c
Normal file
440
dvr/dvr_db.c
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Digital Video Recorder
|
||||
* Copyright (C) 2008 Andreas Öman
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dvr.h"
|
||||
#include "notify.h"
|
||||
|
||||
char *dvr_storage;
|
||||
char *dvr_format;
|
||||
char *dvr_file_postfix;
|
||||
|
||||
|
||||
static int de_tally;
|
||||
|
||||
static int dvr_retention_time = 86400 * 31;
|
||||
|
||||
struct dvr_entry_list dvrentries;
|
||||
|
||||
static void dvr_save(dvr_entry_t *de);
|
||||
|
||||
static void dvr_timer_expire(void *aux);
|
||||
static void dvr_timer_start_recording(void *aux);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvrdb_changed(void)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_u32(m, "reload", 1);
|
||||
notify_by_msg("dvrdb", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_entry_link(dvr_entry_t *de)
|
||||
{
|
||||
time_t now, preamble;
|
||||
|
||||
LIST_INSERT_HEAD(&dvrentries, de, de_global_link);
|
||||
|
||||
time(&now);
|
||||
|
||||
preamble = de->de_start - 30;
|
||||
|
||||
if(now >= de->de_stop) {
|
||||
de->de_sched_state = DVR_COMPLETED;
|
||||
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
|
||||
de->de_stop + dvr_retention_time);
|
||||
|
||||
} else {
|
||||
de->de_sched_state = DVR_SCHEDULED;
|
||||
|
||||
gtimer_arm_abs(&de->de_timer, dvr_timer_start_recording, de, preamble);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_entry_create_by_event(event_t *e, const char *creator)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
char tbuf[30];
|
||||
struct tm tm;
|
||||
|
||||
if(e->e_channel == NULL || e->e_title == NULL)
|
||||
return;
|
||||
|
||||
LIST_FOREACH(de, &e->e_channel->ch_dvrs, de_channel_link)
|
||||
if(de->de_start == e->e_start)
|
||||
return;
|
||||
|
||||
de = calloc(1, sizeof(dvr_entry_t));
|
||||
de->de_id = ++de_tally;
|
||||
|
||||
de->de_channel = e->e_channel;
|
||||
LIST_INSERT_HEAD(&de->de_channel->ch_dvrs, de, de_channel_link);
|
||||
|
||||
de->de_start = e->e_start;
|
||||
de->de_stop = e->e_start + e->e_duration;
|
||||
|
||||
de->de_creator = strdup(creator);
|
||||
de->de_title = strdup(e->e_title);
|
||||
de->de_desc = e->e_desc ? strdup(e->e_desc) : NULL;
|
||||
|
||||
dvr_entry_link(de);
|
||||
|
||||
localtime_r(&de->de_start, &tm);
|
||||
strftime(tbuf, sizeof(tbuf), "%c", &tm);
|
||||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, "
|
||||
"scheduled for recording by \"%s\"",
|
||||
de->de_title, de->de_channel->ch_name, tbuf, creator);
|
||||
|
||||
dvrdb_changed();
|
||||
dvr_save(de);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_entry_destroy(dvr_entry_t *de)
|
||||
{
|
||||
assert(de->de_s == NULL); /* Can't have any subscriptions running */
|
||||
|
||||
hts_settings_remove("dvrdb/%d", de->de_id);
|
||||
|
||||
LIST_REMOVE(de, de_channel_link);
|
||||
|
||||
gtimer_disarm(&de->de_timer);
|
||||
|
||||
LIST_REMOVE(de, de_global_link);
|
||||
|
||||
free(de->de_creator);
|
||||
free(de->de_title);
|
||||
free(de->de_desc);
|
||||
|
||||
free(de);
|
||||
|
||||
dvrdb_changed();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_db_load_one(htsmsg_t *c, int id)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
const char *s, *title, *creator;
|
||||
channel_t *ch;
|
||||
uint32_t start, stop;
|
||||
|
||||
if(htsmsg_get_u32(c, "start", &start))
|
||||
return;
|
||||
if(htsmsg_get_u32(c, "stop", &stop))
|
||||
return;
|
||||
|
||||
if((s = htsmsg_get_str(c, "channel")) == NULL)
|
||||
return;
|
||||
if((ch = channel_find_by_name(s, 0)) == NULL)
|
||||
return;
|
||||
|
||||
if((title = htsmsg_get_str(c, "title")) == NULL)
|
||||
return;
|
||||
|
||||
if((creator = htsmsg_get_str(c, "creator")) == NULL)
|
||||
return;
|
||||
|
||||
de = calloc(1, sizeof(dvr_entry_t));
|
||||
de->de_id = id;
|
||||
|
||||
de_tally = MAX(id, de_tally);
|
||||
|
||||
de->de_channel = ch;
|
||||
LIST_INSERT_HEAD(&de->de_channel->ch_dvrs, de, de_channel_link);
|
||||
|
||||
de->de_start = start;
|
||||
de->de_stop = stop;
|
||||
de->de_creator = strdup(creator);
|
||||
de->de_title = strdup(title);
|
||||
|
||||
tvh_str_set(&de->de_desc, htsmsg_get_str(c, "description"));
|
||||
tvh_str_set(&de->de_filename, htsmsg_get_str(c, "filename"));
|
||||
tvh_str_set(&de->de_error, htsmsg_get_str(c, "error"));
|
||||
dvr_entry_link(de);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_db_load(void)
|
||||
{
|
||||
htsmsg_t *l, *c;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if((l = hts_settings_load("dvrdb")) != NULL) {
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
dvr_db_load_one(c, atoi(f->hmf_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_save(dvr_entry_t *de)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
htsmsg_add_str(m, "channel", de->de_channel->ch_name);
|
||||
htsmsg_add_u32(m, "start", de->de_start);
|
||||
htsmsg_add_u32(m, "stop", de->de_stop);
|
||||
|
||||
htsmsg_add_str(m, "creator", de->de_creator);
|
||||
|
||||
if(de->de_filename != NULL)
|
||||
htsmsg_add_str(m, "filename", de->de_filename);
|
||||
|
||||
htsmsg_add_str(m, "title", de->de_title);
|
||||
|
||||
if(de->de_desc != NULL)
|
||||
htsmsg_add_str(m, "description", de->de_desc);
|
||||
|
||||
|
||||
if(de->de_error != NULL)
|
||||
htsmsg_add_str(m, "error", de->de_error);
|
||||
|
||||
hts_settings_save(m, "dvrdb/%d", de->de_id);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_timer_expire(void *aux)
|
||||
{
|
||||
dvr_entry_t *de = aux;
|
||||
dvr_entry_destroy(de);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_stop_recording(dvr_entry_t *de, const char *errmsg)
|
||||
{
|
||||
// dvr_rec_stop(de);
|
||||
|
||||
de->de_sched_state = DVR_COMPLETED;
|
||||
tvh_str_set(&de->de_error, errmsg);
|
||||
errmsg = errmsg ?: "Recording completed OK";
|
||||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": %s",
|
||||
de->de_title, de->de_channel->ch_name, errmsg);
|
||||
|
||||
dvrdb_changed();
|
||||
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
|
||||
de->de_stop + dvr_retention_time);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_timer_stop_recording(void *aux)
|
||||
{
|
||||
dvr_entry_t *de = aux;
|
||||
dvr_stop_recording(de, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_timer_start_recording(void *aux)
|
||||
{
|
||||
dvr_entry_t *de = aux;
|
||||
|
||||
de->de_sched_state = DVR_RECORDING;
|
||||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting",
|
||||
de->de_title, de->de_channel->ch_name);
|
||||
|
||||
dvrdb_changed();
|
||||
|
||||
// dvr_rec_start(de);
|
||||
|
||||
gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de, de->de_stop);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dvr_entry_t *
|
||||
dvr_entry_find_by_id(int id)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
if(de->de_id == id)
|
||||
break;
|
||||
return de;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_entry_cancel(dvr_entry_t *de)
|
||||
{
|
||||
switch(de->de_sched_state) {
|
||||
case DVR_SCHEDULED:
|
||||
dvr_entry_destroy(de);
|
||||
break;
|
||||
|
||||
case DVR_RECORDING:
|
||||
dvr_stop_recording(de, "Aborted by user");
|
||||
break;
|
||||
|
||||
case DVR_COMPLETED:
|
||||
dvr_entry_destroy(de);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_init(void)
|
||||
{
|
||||
dvr_storage = strdup("/tmp");
|
||||
dvr_format = strdup("matroska");
|
||||
dvr_file_postfix = strdup("mkv");
|
||||
|
||||
dvr_db_load();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_query_add_entry(dvr_query_result_t *dqr, dvr_entry_t *de)
|
||||
{
|
||||
if(dqr->dqr_entries == dqr->dqr_alloced) {
|
||||
/* Need to alloc more space */
|
||||
|
||||
dqr->dqr_alloced = MAX(100, dqr->dqr_alloced * 2);
|
||||
dqr->dqr_array = realloc(dqr->dqr_array,
|
||||
dqr->dqr_alloced * sizeof(dvr_entry_t *));
|
||||
}
|
||||
dqr->dqr_array[dqr->dqr_entries++] = de;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_query(dvr_query_result_t *dqr)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
memset(dqr, 0, sizeof(dvr_query_result_t));
|
||||
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
dvr_query_add_entry(dqr, de);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_query_free(dvr_query_result_t *dqr)
|
||||
{
|
||||
free(dqr->dqr_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting functions
|
||||
*/
|
||||
static int
|
||||
dvr_sort_start_descending(const void *A, const void *B)
|
||||
{
|
||||
dvr_entry_t *a = *(dvr_entry_t **)A;
|
||||
dvr_entry_t *b = *(dvr_entry_t **)B;
|
||||
return b->de_start - a->de_start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_query_sort(dvr_query_result_t *dqr)
|
||||
{
|
||||
int (*sf)(const void *a, const void *b);
|
||||
|
||||
if(dqr->dqr_array == NULL)
|
||||
return;
|
||||
|
||||
sf = dvr_sort_start_descending;
|
||||
qsort(dqr->dqr_array, dqr->dqr_entries, sizeof(dvr_entry_t *), sf);
|
||||
}
|
||||
|
308
dvr/dvr_rec.c
Normal file
308
dvr/dvr_rec.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Digital Video Recorder
|
||||
* Copyright (C) 2008 Andreas Öman
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libavutil/avstring.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dvr.h"
|
||||
|
||||
static void dvr_transport_available(dvr_entry_t *de);
|
||||
static void dvr_transport_unavailable(dvr_entry_t *de, const char *errmsg);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_subscription_callback(struct th_subscription *s,
|
||||
subscription_event_t event, void *opaque)
|
||||
{
|
||||
dvr_entry_t *de = opaque;
|
||||
const char *notifymsg = NULL;
|
||||
|
||||
switch(event) {
|
||||
case SUBSCRIPTION_EVENT_INVALID:
|
||||
abort();
|
||||
|
||||
case SUBSCRIPTION_TRANSPORT_RUN:
|
||||
dvr_transport_available(de);
|
||||
return;
|
||||
|
||||
case SUBSCRIPTION_NO_INPUT:
|
||||
notifymsg = "No input detected";
|
||||
break;
|
||||
|
||||
case SUBSCRIPTION_NO_DESCRAMBLER:
|
||||
notifymsg = "No descrambler available";
|
||||
break;
|
||||
|
||||
case SUBSCRIPTION_NO_ACCESS:
|
||||
notifymsg = "Access denied";
|
||||
break;
|
||||
|
||||
case SUBSCRIPTION_RAW_INPUT:
|
||||
notifymsg = "Unable to reassemble packets from input";
|
||||
break;
|
||||
|
||||
case SUBSCRIPTION_VALID_PACKETS:
|
||||
return;
|
||||
|
||||
case SUBSCRIPTION_TRANSPORT_NOT_AVAILABLE:
|
||||
notifymsg = "No transport available at the moment, automatic retry";
|
||||
break;
|
||||
|
||||
case SUBSCRIPTION_TRANSPORT_LOST:
|
||||
dvr_transport_unavailable(de, "Lost transport");
|
||||
return;
|
||||
|
||||
case SUBSCRIPTION_DESTROYED:
|
||||
dvr_transport_unavailable(de, NULL); /* Recording completed */
|
||||
return;
|
||||
}
|
||||
if(notifymsg != NULL)
|
||||
tvhlog(LOG_WARNING, "dvr", "\"%s\" on \"%s\": %s",
|
||||
de->de_title, de->de_channel->ch_name, notifymsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_rec_start(dvr_entry_t *de)
|
||||
{
|
||||
if(de->de_s != NULL)
|
||||
return;
|
||||
|
||||
de->de_s = subscription_create_from_channel(de->de_channel, 1000, "pvr",
|
||||
dvr_subscription_callback, de);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Replace any slash chars in a string with dash
|
||||
*/
|
||||
static void
|
||||
deslashify(char *s)
|
||||
{
|
||||
int i, len = strlen(s);
|
||||
for(i = 0; i < len; i++) if(s[i] == '/')
|
||||
s[i] = '-';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filename generator
|
||||
*
|
||||
* - convert from utf8
|
||||
* - avoid duplicate filenames
|
||||
*
|
||||
*/
|
||||
static void
|
||||
pvr_generate_filename(dvr_entry_t *de)
|
||||
{
|
||||
char fullname[1000];
|
||||
int tally = 0;
|
||||
struct stat st;
|
||||
char *chname;
|
||||
char *filename;
|
||||
|
||||
if(de->de_filename != NULL) {
|
||||
free(de->de_filename);
|
||||
de->de_filename = NULL;
|
||||
}
|
||||
|
||||
filename = strdup(de->de_title);
|
||||
deslashify(filename);
|
||||
|
||||
chname = strdup(de->de_channel->ch_name);
|
||||
deslashify(chname);
|
||||
|
||||
snprintf(fullname, sizeof(fullname), "%s/%s-%s.%s",
|
||||
dvr_storage, chname, filename, dvr_file_postfix);
|
||||
|
||||
while(1) {
|
||||
if(stat(fullname, &st) == -1) {
|
||||
tvhlog(LOG_DEBUG, "pvr", "File \"%s\" -- %s -- Using for recording",
|
||||
fullname, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
tvhlog(LOG_DEBUG, "pvr", "Overwrite protection, file \"%s\" exists",
|
||||
fullname);
|
||||
|
||||
tally++;
|
||||
|
||||
snprintf(fullname, sizeof(fullname), "%s/%s-%s-%d.%s",
|
||||
dvr_storage, chname, filename, tally,dvr_file_postfix);
|
||||
}
|
||||
|
||||
de->de_filename = strdup(fullname);
|
||||
|
||||
free(filename);
|
||||
free(chname);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
|
||||
{
|
||||
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_transport_available(dvr_entry_t *de)
|
||||
{
|
||||
AVOutputFormat *fmt;
|
||||
AVFormatContext *fctx;
|
||||
th_stream_t *st;
|
||||
th_muxstream_t *tms;
|
||||
AVCodecContext *ctx;
|
||||
AVCodec *codec;
|
||||
enum CodecID codec_id;
|
||||
enum CodecType codec_type;
|
||||
const char *codec_name;
|
||||
char urlname[512];
|
||||
int err;
|
||||
|
||||
th_transport_t *t = de->de_s->ths_transport;
|
||||
|
||||
pvr_generate_filename(de);
|
||||
|
||||
/* Find lavf format */
|
||||
|
||||
fmt = guess_format(dvr_format, NULL, NULL);
|
||||
if(fmt == NULL) {
|
||||
dvr_rec_fatal_error(de, "Unable to open file format \"%s\" for output",
|
||||
dvr_format);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Init format context */
|
||||
|
||||
fctx = av_alloc_format_context();
|
||||
|
||||
av_strlcpy(fctx->title, de->de_title ?: "",
|
||||
sizeof(fctx->title));
|
||||
|
||||
av_strlcpy(fctx->comment, de->de_desc ?: "",
|
||||
sizeof(fctx->comment));
|
||||
|
||||
av_strlcpy(fctx->copyright, de->de_channel->ch_name,
|
||||
sizeof(fctx->copyright));
|
||||
|
||||
fctx->oformat = fmt;
|
||||
|
||||
/* Open output file */
|
||||
|
||||
snprintf(urlname, sizeof(urlname), "file:%s", de->de_filename);
|
||||
|
||||
if((err = url_fopen(&fctx->pb, urlname, URL_WRONLY)) < 0) {
|
||||
av_free(fctx);
|
||||
dvr_rec_fatal_error(de, "Unable to create output file \"%s\"",
|
||||
de->de_filename);
|
||||
return;
|
||||
}
|
||||
|
||||
av_set_parameters(fctx, NULL);
|
||||
|
||||
|
||||
/**
|
||||
* Setup each stream
|
||||
*/
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
switch(st->st_type) {
|
||||
default:
|
||||
continue;
|
||||
case HTSTV_MPEG2VIDEO:
|
||||
codec_id = CODEC_ID_MPEG2VIDEO;
|
||||
codec_type = CODEC_TYPE_VIDEO;
|
||||
codec_name = "mpeg2 video";
|
||||
break;
|
||||
|
||||
case HTSTV_MPEG2AUDIO:
|
||||
codec_id = CODEC_ID_MP2;
|
||||
codec_type = CODEC_TYPE_AUDIO;
|
||||
codec_name = "mpeg2 audio";
|
||||
break;
|
||||
|
||||
case HTSTV_AC3:
|
||||
codec_id = CODEC_ID_AC3;
|
||||
codec_type = CODEC_TYPE_AUDIO;
|
||||
codec_name = "AC3 audio";
|
||||
break;
|
||||
|
||||
case HTSTV_H264:
|
||||
codec_id = CODEC_ID_H264;
|
||||
codec_type = CODEC_TYPE_VIDEO;
|
||||
codec_name = "h.264 video";
|
||||
break;
|
||||
}
|
||||
|
||||
codec = avcodec_find_decoder(codec_id);
|
||||
if(codec == NULL) {
|
||||
tvhlog(LOG_ERR, "dvr",
|
||||
"%s - Cannot find codec for %s, ignoring stream",
|
||||
de->de_title, codec_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
tms = calloc(1, sizeof(th_muxstream_t));
|
||||
tms->tms_stream = st;
|
||||
tms->tms_index = fctx->nb_streams;
|
||||
|
||||
tms->tms_avstream = av_new_stream(fctx, fctx->nb_streams);
|
||||
|
||||
ctx = tms->tms_avstream->codec;
|
||||
ctx->codec_id = codec_id;
|
||||
ctx->codec_type = codec_type;
|
||||
|
||||
if(avcodec_open(ctx, codec) < 0) {
|
||||
tvhlog(LOG_ERR, "dvr",
|
||||
"%s - Cannot open codec for %s, ignoring stream",
|
||||
de->de_title, codec_name);
|
||||
free(ctx);
|
||||
continue;
|
||||
}
|
||||
|
||||
// LIST_INSERT_HEAD(&tm->tm_streams, tms, tms_muxer_link0);
|
||||
memcpy(tms->tms_avstream->language, tms->tms_stream->st_lang, 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_transport_unavailable(dvr_entry_t *de, const char *errmsg)
|
||||
{
|
||||
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue