#define BCTEXTLEN 1024
#define BLURAY_TS_PKTSZ 192L
+static const int bd_sig = 2;
extern "C" {
#include "libavfilter/buffersrc.h"
BLURAY_AUDIO_FORMAT_MONO = 1,
BLURAY_AUDIO_FORMAT_STEREO = 3,
BLURAY_AUDIO_FORMAT_MULTI_CHAN = 6,
- BLURAY_AUDIO_FORMAT_COMBO = 12, // Stereo ac3/dts,
+ BLURAY_AUDIO_FORMAT_COMBO = 12, // Stereo ac3/dts,
BLURAY_AUDIO_RATE_48 = 1,
BLURAY_AUDIO_RATE_96 = 4,
int64_t fpos, len;
public:
bs_length() { fpos = len = 0; }
+ int64_t bs_posb(bs_file &bs) { return bs.posb() - fpos; }
void bs_len(bs_file &bs, int n) {
bs.write(len, n); fpos = bs.posb();
}
void bs_end(bs_file &bs) {
- len = bs.posb() - fpos;
+ len = bs_posb(bs);
}
void bs_ofs(bs_file &bs, int n) {
bs.write(fpos-n/8, n);
}
+ void bs_zofs(bs_file &bs, int n) {
+ bs.write(!len ? 0 : fpos-n/8, n);
+ }
};
class _bd_stream_info {
~clpi_cpi() { remove_all_objects(); }
};
+class clpi_cmrk : public bs_length {
+public:
+ int write();
+
+ clpi_cmrk() {}
+ ~clpi_cmrk() {}
+};
+
+
class bd_uo_mask {
public:
unsigned int menu_call : 1;
int write_pip_metadata_extension();
int write();
- mpls_pl() { sig = 1; }
+ mpls_pl() { sig = bd_sig; }
~mpls_pl() {
play_item.remove_all_objects();
sub_path.remove_all_objects();
clpi_extents extents;
clpi_programs programs_ss;
clpi_cpi cpi_ss;
+ clpi_cmrk cmrk;
int write_header();
int write();
int write_clpi_extension(int id1, int id2, void *handle);
int write_mpls_extension(int id1, int id2, void *handle);
- clpi_cl() { sig = 1; }
+ clpi_cl() { sig = bd_sig; }
~clpi_cl() {}
};
int sig;
ArrayList<movie_obj *> movies;
- movie_file() { sig = 1; }
+ movie_file() { sig = bd_sig; }
~movie_file() {
movies.remove_all_objects();
}
int write();
index_file() {
- sig = 1;
+ sig = bd_sig;
memset(user_data, 0, sizeof(user_data));
}
~index_file() {
int sig;
int write();
- bdid() { sig = 1; }
+ bdid() { sig = bd_sig; }
~bdid() {}
};
int av_idx;
AVMediaType type;
AVCodecID codec_id;
+ AVCodecContext *ctx;
int64_t start_pts;
int64_t end_pts;
int64_t last_pts;
int64_t duration;
stream(AVMediaType ty, int i) {
- type = ty; av_idx = i;
+ type = ty; av_idx = i; ctx = 0;
start_pts = INT64_MAX; end_pts = INT64_MIN;
last_pts = -1;
}
- ~stream() {}
+ ~stream() { if( ctx ) avcodec_free_context(&ctx); }
};
class mark {
ArrayList<clpi_cl *> cl;
ArrayList<mpls_pl *> pl;
+ void add_movie(uint32_t *ops, int n);
int compose();
int write(char *fn);
bdj_name = cstrdup(nm);
}
-void
+void
pb_obj::write_hdmv_obj(int id_ref)
{
bs.write(pb_typ, 2);
bs.writeb("INDX", 4);
bs.writeb(sig == 1 ? "0100" : "0200", 4);
bs_ofs(bs, 32);
- exten.bs_ofs(bs, 32);
+ exten.bs_zofs(bs, 32);
int appinfo_start = 0x28;
bs.posb(appinfo_start);
appinf.bs_len(bs, 32);
return 1;
};
+ bs.padb(0x15 - bs_posb(bs));
bs_end(bs);
return 0;
}
return 0;
}
+int
+clpi_cmrk::write()
+{
+ bs_len(bs, 32);
+ bs_end(bs);
+ return 0;
+}
+
int
clpi_extents::write()
{
if( programs.write() ) return 1;
cpi_start_addr = bs.posb();
if( cpi.write() ) return 1;
+ clip_mark_start_addr = bs.posb();
+ if( cmrk.write() ) return 1;
//if( has_ext_data ) {
// ext_data_start_addr = bs.pos();
// bdmv_write_extension_data(write_clpi_extension, this);
fprintf(stderr, "unrecognized stream type %02x\n", stream_type);
break;
};
+ bs.padb(9 - strm.bs_posb(bs));
strm.bs_end(bs);
code.bs_len(bs, 8);
fprintf(stderr, "mpls_stream: unrecognized coding type %02x\n", coding_type);
break;
};
+ bs.padb(5 - code.bs_posb(bs));
code.bs_end(bs);
return 0;
}
char bdmv_path[BCTEXTLEN];
sprintf(bdmv_path, "%s/BDMV", path);
if( mk_bdmv_dir(bdmv_path) ) return 1;
+ char cert_path[BCTEXTLEN];
+ sprintf(cert_path, "%s/CERTIFICATE", path);
+ if( mk_bdmv_dir(cert_path) ) return 1;
+ char cert_backup[BCTEXTLEN];
+ sprintf(cert_backup, "%s/BACKUP", cert_path);
+ if( mk_bdmv_dir(cert_backup) ) return 1;
char stream_path[BCTEXTLEN];
- sprintf(stream_path, "%s/BDMV/STREAM", path);
+ sprintf(stream_path, "%s/STREAM", bdmv_path);
if( mk_dir(stream_path) ) return 1;
char auxdata_path[BCTEXTLEN];
- sprintf(auxdata_path, "%s/BDMV/AUXDATA", path);
+ sprintf(auxdata_path, "%s/AUXDATA", bdmv_path);
if( mk_dir(auxdata_path) ) return 1;
char meta_path[BCTEXTLEN];
- sprintf(meta_path, "%s/BDMV/META", path);
+ sprintf(meta_path, "%s/META", bdmv_path);
if( mk_dir(meta_path) ) return 1;
char backup_path[BCTEXTLEN];
sprintf(backup_path, "%s/BACKUP", bdmv_path);
uint32_t pkt = mp->pos / BLURAY_TS_PKTSZ;
if( last_pkt >= pkt ) continue;
last_pkt = pkt;
- int64_t coarse_pts = (pts >> 18) & ~0x01;
+ int64_t coarse_pts = (pts >> 18); // & ~0x01;
int64_t fine_pts = (pts & 0x7ffff) >> 8;
uint32_t mpkt = pkt & ~0x1ffff;
- if( !cp || cp->pts_ep != coarse_pts || cp->spn_ep != mpkt ) {
+ if( !cp || cp->pts_ep != coarse_pts || mpkt > cp->spn_ep ) {
cp = new clpi_ep_coarse();
map->coarse.append(cp);
cp->ref_ep_fine_id = map->fine.size();
cp->pts_ep = coarse_pts;
- cp->spn_ep = mpkt;
+ cp->spn_ep = pkt;
}
clpi_ep_fine *fp = new clpi_ep_fine();
map->fine.append(fp);
int stream_type = 0;
switch (codec_id) {
case AV_CODEC_ID_MPEG1VIDEO:
+ stream_type = BLURAY_STREAM_TYPE_VIDEO_MPEG1;
+ break;
case AV_CODEC_ID_MPEG2VIDEO:
stream_type = BLURAY_STREAM_TYPE_VIDEO_MPEG2;
break;
stream_type = BLURAY_STREAM_TYPE_VIDEO_H264;
break;
case AV_CODEC_ID_MP2:
- case AV_CODEC_ID_MP3:
stream_type = BLURAY_STREAM_TYPE_AUDIO_MPEG1;
break;
+ case AV_CODEC_ID_MP3:
+ stream_type = BLURAY_STREAM_TYPE_AUDIO_MPEG2;
+ break;
case AV_CODEC_ID_AC3:
stream_type = BLURAY_STREAM_TYPE_AUDIO_AC3;
break;
+ case AV_CODEC_ID_EAC3:
+ stream_type = BLURAY_STREAM_TYPE_AUDIO_AC3PLUS;
+ break;
case AV_CODEC_ID_DTS:
stream_type = BLURAY_STREAM_TYPE_AUDIO_DTS;
break;
case AV_CODEC_ID_TRUEHD:
stream_type = BLURAY_STREAM_TYPE_AUDIO_TRUHD;
break;
+ case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
+ stream_type = BLURAY_STREAM_TYPE_SUB_PG;
+ break;
default:
fprintf(stderr, "unknown bluray stream type %s\n", avcodec_get_name(codec_id));
exit(1);
static int bd_video_format(int w, int h, int ilace)
{
- if( w == 720 && h == 480 && ilace ) return BLURAY_VIDEO_FORMAT_480I;
- if( w == 720 && h == 576 && ilace ) return BLURAY_VIDEO_FORMAT_576I;
- if( w == 720 && h == 480 && !ilace ) return BLURAY_VIDEO_FORMAT_480P;
- if( w == 1920 && h == 1080 && ilace ) return BLURAY_VIDEO_FORMAT_1080I;
- if( w == 1280 && h == 720 && !ilace ) return BLURAY_VIDEO_FORMAT_720P;
- if( w == 1920 && h == 1080 && !ilace ) return BLURAY_VIDEO_FORMAT_1080P;
- if( w == 720 && h == 576 && !ilace ) return BLURAY_VIDEO_FORMAT_576P;
+ if( w == 720 && h == 480 && ilace ) return BLURAY_VIDEO_FORMAT_480I;
+ if( w == 720 && h == 576 && ilace ) return BLURAY_VIDEO_FORMAT_576I;
+ if( w == 720 && h == 480 && !ilace ) return BLURAY_VIDEO_FORMAT_480P;
+ if( w == 720 && h == 576 && !ilace ) return BLURAY_VIDEO_FORMAT_576P;
+// this seems to be overly restrictive
+ if( w == 1280 && h == 720 /* && !ilace*/ ) return BLURAY_VIDEO_FORMAT_720P;
+ if( w == 1440 && h == 1080 /* && ilace*/ ) return BLURAY_VIDEO_FORMAT_1080I;
+ if( w == 1920 && h == 1080 /* && !ilace*/ ) return BLURAY_VIDEO_FORMAT_1080P;
fprintf(stderr, "unknown bluray video format %dx%d %silace\n",
w, h, !ilace ? "not " : "");
exit(1);
double aspect = (w * ratio) / h;
if( fabs(aspect-1.333) < 0.01 ) return BLURAY_ASPECT_RATIO_4_3;
if( fabs(aspect-1.777) < 0.01 ) return BLURAY_ASPECT_RATIO_16_9;
- return w == 720 ? BLURAY_ASPECT_RATIO_4_3 : BLURAY_ASPECT_RATIO_16_9;
+ return w == 720 ? BLURAY_ASPECT_RATIO_4_3 : BLURAY_ASPECT_RATIO_16_9;
fprintf(stderr, "unknown bluray aspect ratio %5.3f\n",aspect);
exit(1);
}
+static int field_probe(AVFormatContext *fmt_ctx, AVStream *st)
+{
+ AVDictionary *copts = 0;
+ //av_dict_copy(&copts, opts, 0);
+ AVCodecID codec_id = st->codecpar->codec_id;
+ AVCodec *decoder = avcodec_find_decoder(codec_id);
+ AVCodecContext *ctx = avcodec_alloc_context3(decoder);
+ if( !ctx ) {
+ fprintf(stderr,"codec alloc failed\n");
+ return -1;
+ }
+ avcodec_parameters_to_context(ctx, st->codecpar);
+ if( avcodec_open2(ctx, decoder, &copts) < 0 ) {
+ fprintf(stderr,"codec open failed\n");
+ return -1;
+ }
+ av_dict_free(&copts);
+
+ AVFrame *ipic = av_frame_alloc();
+ AVPacket ipkt;
+ av_init_packet(&ipkt);
+ int ilaced = -1;
+ for( int retrys=100; --retrys>=0 && ilaced<0; ) {
+ av_packet_unref(&ipkt);
+ int ret = av_read_frame(fmt_ctx, &ipkt);
+ if( ret == AVERROR_EOF ) break;
+ if( ret != 0 ) continue;
+ if( ipkt.stream_index != st->index ) continue;
+ if( !ipkt.data || !ipkt.size ) continue;
+ ret = avcodec_send_packet(ctx, &ipkt);
+ if( ret < 0 ) {
+ fprintf(stderr, "avcodec_send_packet failed\n");
+ break;
+ }
+ ret = avcodec_receive_frame(ctx, ipic);
+ if( ret >= 0 ) {
+ ilaced = ipic->interlaced_frame ? 1 : 0;
+ break;
+ }
+ if( ret != AVERROR(EAGAIN) )
+ fprintf(stderr, "avcodec_receive_frame failed %d\n", ret);
+ }
+ av_packet_unref(&ipkt);
+ av_frame_free(&ipic);
+ avcodec_free_context(&ctx);
+ return ilaced;
+}
+
int media_info::scan()
{
struct stat st;
int ep_pid = -1;
for( int i=0; ret>=0 && i<(int)fmt_ctx->nb_streams; ++i ) {
AVStream *st = fmt_ctx->streams[i];
- AVMediaType type = st->codec->codec_type;
+ AVMediaType type = st->codecpar->codec_type;
switch( type ) {
case AVMEDIA_TYPE_VIDEO: break;
case AVMEDIA_TYPE_AUDIO: break;
+ case AVMEDIA_TYPE_SUBTITLE: break;
default: continue;
}
stream *s = new stream(type, i);
s->pid = st->id;
- AVCodecID codec_id = st->codec->codec_id;
+ AVCodecID codec_id = st->codecpar->codec_id;
+ AVCodec *decoder = avcodec_find_decoder(codec_id);
+ s->ctx = avcodec_alloc_context3(decoder);
+ if( !s->ctx ) {
+ fprintf(stderr, "avcodec_alloc_context failed\n");
+ continue;
+ }
switch( type ) {
case AVMEDIA_TYPE_VIDEO: {
if( ep_pid < 0 ) ep_pid = st->id;
s->coding_type = bd_stream_type(codec_id);
- s->format = bd_video_format(st->codec->width, st->codec->height,
- st->codec->flags & CODEC_FLAG_INTERLACED_ME);
- s->rate = bd_video_rate(!st->codec->framerate.den ? 0 :
- (double)st->codec->framerate.num / st->codec->framerate.den);
- s->aspect = bd_aspect_ratio(st->codec->width, st->codec->height,
+ int ilace = field_probe(fmt_ctx, st);
+ if( ilace < 0 ) {
+ fprintf(stderr, "interlace probe failed\n");
+ exit(1);
+ }
+ s->format = bd_video_format(st->codecpar->width, st->codecpar->height, ilace);
+ AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
+ s->rate = bd_video_rate(!framerate.den ? 0 : (double)framerate.num / framerate.den);
+ s->aspect = bd_aspect_ratio(st->codecpar->width, st->codecpar->height,
!st->sample_aspect_ratio.num || !st->sample_aspect_ratio.den ? 1. :
(double)st->sample_aspect_ratio.num / st->sample_aspect_ratio.den);
break; }
case AVMEDIA_TYPE_AUDIO: {
s->coding_type = bd_stream_type(codec_id);
- s->format = bd_audio_format(st->codec->channels);
- s->rate = bd_audio_rate(st->codec->sample_rate);
- strcpy((char*)s->lang, "und");
+ s->format = bd_audio_format(st->codecpar->channels);
+ s->rate = bd_audio_rate(st->codecpar->sample_rate);
+ strcpy((char*)s->lang, "eng");
+ break; }
+ case AVMEDIA_TYPE_SUBTITLE: {
+ s->coding_type = bd_stream_type(codec_id);
+ AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", 0, 0);
+ strncpy((char*)s->lang, lang ? lang->value : "und", sizeof(s->lang));
break; }
default:
break;
s->duration = av_rescale_q(st->duration, st->time_base, clk45k);
streams.append(s);
- AVCodec *decoder = avcodec_find_decoder(codec_id);
AVDictionary *copts = 0;
- ret = avcodec_open2(st->codec, decoder, &copts);
+ ret = avcodec_open2(s->ctx, decoder, &copts);
}
if( ep_pid < 0 )
ep_pid = fmt_ctx->nb_streams > 0 ? fmt_ctx->streams[0]->id : 0;
program *pgm = new program(-1, 1);
pgm->ep_pid = ep_pid;
pgm->pmt_pid = 0x1000;
- pgm->pcr_pid = 0x100;
+ pgm->pcr_pid = 0x1001;
pgm->duration = 0;
for( int jj=0; jj<streams.size(); ++jj ) {
AVStream *st = fmt_ctx->streams[jj];
- AVMediaType type = st->codec->codec_type;
+ AVMediaType type = st->codecpar->codec_type;
switch( type ) {
case AVMEDIA_TYPE_VIDEO:
case AVMEDIA_TYPE_AUDIO:
for( int jj=0; jj<(int)pgrm->nb_stream_indexes; ++jj ) {
int av_idx = pgrm->stream_index[jj];
AVStream *st = fmt_ctx->streams[av_idx];
- AVMediaType type = st->codec->codec_type;
+ AVMediaType type = st->codecpar->codec_type;
switch( type ) {
case AVMEDIA_TYPE_VIDEO:
if( ep_pid < 0 ) ep_pid = st->id;
break;
case AVMEDIA_TYPE_AUDIO:
+ case AVMEDIA_TYPE_SUBTITLE:
break;
default:
continue;
if( ret >= 0 )
ret = scan(fmt_ctx);
- for( int i=0; i<(int)fmt_ctx->nb_streams; ++i )
- avcodec_close(fmt_ctx->streams[i]->codec);
+ for( int i=0; i<(int)streams.size(); ++i )
+ avcodec_close(streams[i]->ctx);
avformat_close_input(&fmt_ctx);
return ret;
int ret = 0;
AVPacket ipkt;
av_init_packet(&ipkt);
-
+#if 1
+// zero pts at pos zero
+ for( int i=0; i<programs.size(); ++i ) {
+ program *p = programs[i];
+ for( int j=0; j<p->strm_idx.size(); ++j ) {
+ stream *sp = streams[p->strm_idx[j]];
+ sp->last_pts = 0;
+ AVStream *st = fmt_ctx->streams[sp->av_idx];
+ p->add_label(0, 0, 0, st->id);
+ }
+ }
+#endif
for( int64_t count=0; ret>=0; ++count ) {
av_packet_unref(&ipkt);
ipkt.data = 0; ipkt.size = 0;
return ret != AVERROR_EOF ? -1 : 0;
}
+void
+Media::add_movie(uint32_t *ops, int n)
+{
+ movie_obj *mp = new movie_obj();
+ mp->resume_intention_flag = 1;
+ uint32_t *eop = ops + n/sizeof(*ops);
+ while( ops < eop ) {
+ command_obj *cmd = new command_obj();
+ cmd->cmd = htobe32(*ops++);
+ cmd->dst = *ops++;
+ cmd->src = *ops++;
+ mp->cmds.append(cmd);
+ }
+ mov.movies.append(mp);
+}
+
int
Media::compose()
{
-// index
- bs.init();
- idx.sig = 1;
- idx.first_play.set_hdmv(0, pb_typ_movie);
- idx.top_menu.set_hdmv(0xffff, pb_typ_iactv);
-
// movie
bs.init();
- mov.sig = 1;
+
+// top menu
+ int top_menu_obj = mov.movies.size();
movie_obj *mp = new movie_obj();
mp->resume_intention_flag = 1;
command_obj *cmd = new command_obj();
cmd->cmd = htobe32(0x21810000); cmd->dst = 1; cmd->src = 0;
mp->cmds.append(cmd); // JUMP_TITLE 1
- cmd = new command_obj();
- cmd->cmd = htobe32(0x00020000); cmd->dst = 0; cmd->src = 0;
- mp->cmds.append(cmd);
- mov.movies.append(mp); // BREAK
+ mov.movies.append(mp);
-
+// titles
for( int ii=0; ii<size(); ++ii ) {
mp = new movie_obj();
mp->resume_intention_flag = 1;
mov.movies.append(mp); // BREAK
}
+// first play
+ int first_play_obj = mov.movies.size();
mp = new movie_obj();
mp->resume_intention_flag = 1;
cmd = new command_obj();
- cmd->cmd = htobe32(0x21810000); cmd->dst = 1; cmd->src = 0;
- mp->cmds.append(cmd); // JUMP_TITLE 1
- cmd = new command_obj();
- cmd->cmd = htobe32(0x00020000); cmd->dst = 0; cmd->src = 0;
- mp->cmds.append(cmd);
- mov.movies.append(mp); // BREAK
+ cmd->cmd = htobe32(0x21810000); cmd->dst = 0; cmd->src = 0;
+ mp->cmds.append(cmd); // JUMP_TITLE 0 ; top menu
+ mov.movies.append(mp);
+
+// index
+ bs.init();
+ idx.first_play.set_hdmv(first_play_obj, pb_typ_iactv);
+ idx.top_menu.set_hdmv(top_menu_obj, pb_typ_iactv);
title_obj *tp = 0;
// clips
cp->clip.clip_stream_type = 1;
cp->clip.application_type = BLURAY_APP_TYPE_MAIN_MOVIE;
cp->clip.ts_recording_rate = ip->bit_rate;
- uint32_t ts_pkt_count = ip->file_size / BLURAY_TS_PKTSZ + 1;
+ uint32_t ts_pkt_count = ip->file_size / BLURAY_TS_PKTSZ;
cp->clip.num_source_packets = ts_pkt_count;
cp->clip.ts_type_info.validity = 0x80;
strcpy(cp->clip.ts_type_info.format_id, "HDMV");
s->aspect = sp->aspect;
break;
case AVMEDIA_TYPE_AUDIO:
+ case AVMEDIA_TYPE_SUBTITLE:
memcpy(s->lang,sp->lang,sizeof(s->lang));
break;
default:
p->streams.append(s);
}
clpi_ep_map_entry *map = new clpi_ep_map_entry(pgm->ep_pid);
+ map->ep_stream_type = 1;
pgm->build_toc(map);
cp->cpi.append(map);
cp->programs.append(p);
pgm = ip->prog();
mpls_pi *pi = new mpls_pi();
pi->connection_condition = 1; // seamless
+// pi->uo_mask.xxx = 1;
pi->in_time = pgm->start_time;
pi->out_time = pgm->end_time;
if( ip->still )
switch( sp->type ) {
case AVMEDIA_TYPE_VIDEO: break;
case AVMEDIA_TYPE_AUDIO: break;
+ case AVMEDIA_TYPE_SUBTITLE: break;
default: continue;
}
mpls_stream *ps = new mpls_stream();
memcpy(ps->lang, sp->lang, sizeof(ps->lang));
pi->stn.audio.append(ps);
break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ memcpy(ps->lang, sp->lang, sizeof(ps->lang));
+ pi->stn.pg.append(ps);
+ break;
default:
break;
}
pp->play_item.append(pi);
}
// chapter marks every ch_duration ticks
- int64_t ch_duration = 45000 * 60*10;
+ int64_t ch_duration = 45000 * 60*5;
int64_t mrktm = ch_duration;
int64_t plytm = 0;
int pmark = 0, pitem = 0;
int Media::
bd_copy(const char *ifn, const char *fmt, ...)
{
- int n, ret = 1;
- char bfr[0x40000];
+ int bfrsz = 0x40000, ret = 1;
+ char bfr[bfrsz];
FILE *ifp = fopen(ifn,"r");
if( ifp ) {
va_list ap;
if( ofp ) {
setvbuf(ifp, 0, _IOFBF, 0x80000);
setvbuf(ofp, 0, _IOFBF, 0x80000);
- while( (n=fread(bfr,1,sizeof(bfr),ifp)) > 0 ) fwrite(bfr,1,n,ofp);
- fclose(ofp);
ret = 0;
+ int n = bfrsz;
+ while( !ret && n >= bfrsz ) {
+ n = fread(bfr,1,bfrsz,ifp);
+ if( n > 0 && (int)fwrite(bfr,1,n,ofp) != n ) {
+ fprintf(stderr, "cant write: %s\n",filename);
+ ret = 1;
+ }
+ }
+ if( ferror(ifp) ) {
+ fprintf(stderr, "read error: %s = %m\n",ifn);
+ ret = 1;
+ }
+ if( ferror(ofp) ) {
+ fprintf(stderr, "write error: %s = %m\n",filename);
+ ret = 1;
+ }
+ if( fclose(ofp) ) {
+ fprintf(stderr, "close error: %s = %m\n",filename);
+ ret = 1;
+ }
}
fclose(ifp);
}