bdsplice.c 6.34 KB
Newer Older
1 2 3 4
/*
 * This file is part of libbluray
 * Copyright (C) 2009-2010  John Stebbins
 *
gates's avatar
gates committed
5 6 7 8
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
9
 *
gates's avatar
gates committed
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
gates's avatar
gates committed
12 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
14
 *
gates's avatar
gates committed
15 16 17
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
18 19
 */

20 21 22 23
#if HAVE_CONFIG_H
#include "config.h"
#endif

24
#include <stdio.h>
25 26 27
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
cRTrn13's avatar
cRTrn13 committed
28
#include <inttypes.h>
29

30 31
#include "libbluray/bluray.h"

32
#define PKT_SIZE 192
33
#define BUF_SIZE (PKT_SIZE * 1024)
34 35 36 37 38
#define MIN(a,b) (((a) < (b)) ? a : b)

static void
_usage(char *cmd)
{
39
    fprintf(stderr,
40 41
"Usage: %s -t title    [-c first[-last]] [-k keyfile] [-a angle]  <bd path> [dest]\n"
"       %s -p playlist [-c first[-last]] [-k keyfile] [-a angle]  <bd path> [dest]\n"
42
"Summary:\n"
43
"    Given a title or playlist number and Blu-Ray directory tree,\n"
44 45 46
"    find the clips that compose the movie and splice\n"
"    them together in the destination file\n"
"Options:\n"
47
"    t N         - Index of title to splice. First title is 1.\n"
48
"    p N         - Playlist to splice.\n"
49 50 51 52 53
"    a N         - Angle. First angle is 1.\n"
"    c N or N-M  - Chapter or chapter range. First chapter is 1.\n"
"    k keyfile   - AACS keyfile path.\n"
"    <bd path>   - Path to root of Blu-Ray directory tree.\n"
"    [dest]      - Destination of spliced clips. stdout if not specified.\n"
54
, cmd, cmd);
55 56 57 58

    exit(EXIT_FAILURE);
}

59
#define OPTS "c:vt:p:k:a:"
60 61 62 63

int
main(int argc, char *argv[])
{
john's avatar
john committed
64
    int title_no = -1;
65
    int playlist = -1;
66
    int angle = 0;
67
    char *bdpath = NULL, *dest = NULL;
68
    FILE *out;
john's avatar
john committed
69
    int opt;
70
    int verbose = 0;
71 72 73 74 75 76 77 78 79 80
    int64_t total = 0;
    int64_t pos, end_pos = -1;
    size_t size, wrote;
    int bytes;
    int title_count;
    BLURAY *bd;
    int chapter_start = 0;
    int chapter_end = -1;
    uint8_t buf[BUF_SIZE];
    char *keyfile = NULL;
81
    BLURAY_TITLE_INFO *ti;
82 83 84 85

    do {
        opt = getopt(argc, argv, OPTS);
        switch (opt) {
86
            case -1:
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
                if (optind < argc && bdpath == NULL) {
                    bdpath = argv[optind];
                    optind++;
                    opt = 1;
                }
                else if (optind < argc && dest == NULL) {
                    dest = argv[optind];
                    optind++;
                    opt = 1;
                }
                break;

            case 'c': {
                int match;
                match = sscanf(optarg, "%d-%d", &chapter_start, &chapter_end);
                if (match == 1) {
                    chapter_end = chapter_start + 1;
                }
                chapter_start--;
                chapter_end--;
            } break;

            case 'k':
                keyfile = optarg;
                break;

            case 'a':
                angle = atoi(optarg);
                angle--;
116 117 118
                break;

            case 't':
119 120 121
                if (playlist >= 0) {
                    _usage(argv[0]);
                }
john's avatar
john committed
122
                title_no = atoi(optarg);
123
                title_no--;
124 125
                break;

126 127 128 129 130 131 132
            case 'p':
                if (title_no >= 0) {
                    _usage(argv[0]);
                }
                playlist = atoi(optarg);
                break;

133 134 135 136 137 138 139 140 141 142
            case 'v':
                verbose = 1;
                break;

            default:
                _usage(argv[0]);
                break;
        }
    } while (opt != -1);

143
    if (title_no < 0 && playlist < 0) {
144 145
        _usage(argv[0]);
    }
146
    if (optind < argc) {
147 148
        _usage(argv[0]);
    }
149 150 151 152 153

    bd = bd_open(bdpath, keyfile);
    if (bd == NULL) {
        fprintf(stderr, "Failed to open disc: %s\n", bdpath);
        return 1;
154
    }
john's avatar
john committed
155

156
    title_count = bd_get_titles(bd, TITLES_RELEVANT, 0);
157 158 159 160 161
    if (title_count <= 0) {
        fprintf(stderr, "No titles found: %s\n", bdpath);
        return 1;
    }

162 163 164 165 166 167 168 169 170 171 172 173 174
    if (title_no >= 0) {
        if (!bd_select_title(bd, title_no)) {
            fprintf(stderr, "Failed to open title: %d\n", title_no);
            return 1;
        }
        ti = bd_get_title_info(bd, title_no, angle);

    } else {
        if (!bd_select_playlist(bd, playlist)) {
            fprintf(stderr, "Failed to open playlist: %d\n", playlist);
            return 1;
        }
        ti = bd_get_playlist_info(bd, playlist, angle);
175
    }
john's avatar
john committed
176

177 178 179 180 181 182 183 184 185
    if (dest) {
        out = fopen(dest, "wb");
        if (out == NULL) {
            fprintf(stderr, "Failed to open destination: %s\n", dest);
            return 1;
        }
    } else {
        out = stdout;
    }
john's avatar
john committed
186

187 188 189 190
    if (angle >= (int)ti->angle_count) {
        fprintf(stderr, "Invalid angle %d > angle count %d. Using angle 1.\n", 
                angle+1, ti->angle_count);
        angle = 0;
191
    }
192
    bd_select_angle(bd, angle);
193

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    if (chapter_start >= (int)ti->chapter_count) {
        fprintf(stderr, "First chapter %d > chapter count %d\n", 
                chapter_start+1, ti->chapter_count);
        return 1;
    }
    if (chapter_end >= (int)ti->chapter_count) {
        chapter_end = -1;
    }
    if (chapter_end >= 0) {
        end_pos = bd_chapter_pos(bd, chapter_end);
    }
    bd_free_title_info(ti);

    bd_seek_chapter(bd, chapter_start);
    pos = bd_tell(bd);
    while (end_pos < 0 || pos < end_pos) {
        size = BUF_SIZE;
        if (size > (size_t)(end_pos - pos)) {
            size = end_pos - pos;
213
        }
214 215 216
        bytes = bd_read(bd, buf, size);
        if (bytes <= 0) {
            break;
217
        }
218 219 220
        pos = bd_tell(bd);
        wrote = fwrite(buf, 1, bytes, out);
        if (wrote != (size_t)bytes) {
Petri Hintukainen's avatar
Petri Hintukainen committed
221
            fprintf(stderr, "read/write sizes do not match: %d/%zu\n", bytes, wrote);
222
        }
223 224 225 226 227 228 229
        if (wrote == 0) {
            if (ferror(out)) {
                perror("Write error");
            }
            break;
        }
        total += PKT_SIZE * wrote;
230
    }
john's avatar
john committed
231
    if (verbose) {
cRTrn13's avatar
cRTrn13 committed
232
        fprintf(stderr, "Wrote %"PRId64" bytes\n", total);
john's avatar
john committed
233
    }
234
    bd_close(bd);
235 236 237 238
    fclose(out);
    return 0;
}