1/* 2 * BluRay (libbluray) protocol 3 * 4 * Copyright (c) 2012 Petri Hintukainen <phintuka <at> users.sourceforge.net> 5 * 6 * This file is part of FFmpeg. 7 * 8 * FFmpeg is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * FFmpeg is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with FFmpeg; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23#include <libbluray/bluray.h> 24 25#include "libavutil/avstring.h" 26#include "libavformat/avformat.h" 27#include "libavformat/url.h" 28#include "libavutil/opt.h" 29 30#define BLURAY_PROTO_PREFIX "bluray:" 31#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ 32 33typedef struct { 34 const AVClass *class; 35 36 BLURAY *bd; 37 38 int playlist; 39 int angle; 40 int chapter; 41 /*int region;*/ 42} BlurayContext; 43 44#define OFFSET(x) offsetof(BlurayContext, x) 45static const AVOption options[] = { 46{"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, 47{"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM }, 48{"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM }, 49/*{"region", "bluray player region code (1 = region A, 2 = region B, 4 = region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, AV_OPT_FLAG_DECODING_PARAM },*/ 50{NULL} 51}; 52 53static const AVClass bluray_context_class = { 54 .class_name = "bluray", 55 .item_name = av_default_item_name, 56 .option = options, 57 .version = LIBAVUTIL_VERSION_INT, 58}; 59 60 61static int check_disc_info(URLContext *h) 62{ 63 BlurayContext *bd = h->priv_data; 64 const BLURAY_DISC_INFO *disc_info; 65 66 disc_info = bd_get_disc_info(bd->bd); 67 if (!disc_info) { 68 av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n"); 69 return -1; 70 } 71 72 if (!disc_info->bluray_detected) { 73 av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n"); 74 return -1; 75 } 76 77 /* AACS */ 78 if (disc_info->aacs_detected && !disc_info->aacs_handled) { 79 if (!disc_info->libaacs_detected) { 80 av_log(h, AV_LOG_ERROR, 81 "Media stream encrypted with AACS, install and configure libaacs\n"); 82 } else { 83 av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n"); 84 } 85 return -1; 86 } 87 88 /* BD+ */ 89 if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { 90 /* 91 if (!disc_info->libbdplus_detected) { 92 av_log(h, AV_LOG_ERROR, 93 "Media stream encrypted with BD+, install and configure libbdplus"); 94 } else { 95 */ 96 av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n"); 97 /*}*/ 98 return -1; 99 } 100 101 return 0; 102} 103 104static int bluray_close(URLContext *h) 105{ 106 BlurayContext *bd = h->priv_data; 107 if (bd->bd) { 108 bd_close(bd->bd); 109 } 110 111 return 0; 112} 113 114static int bluray_open(URLContext *h, const char *path, int flags) 115{ 116 BlurayContext *bd = h->priv_data; 117 int num_title_idx; 118 const char *diskname = path; 119 120 av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); 121 122 bd->bd = bd_open(diskname, NULL); 123 if (!bd->bd) { 124 av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); 125 return AVERROR(EIO); 126 } 127 128 /* check if disc can be played */ 129 if (check_disc_info(h) < 0) { 130 return AVERROR(EIO); 131 } 132 133 /* setup player registers */ 134 /* region code has no effect without menus 135 if (bd->region > 0 && bd->region < 5) { 136 av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1)); 137 bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region); 138 } 139 */ 140 141 /* load title list */ 142 num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH); 143 av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx); 144 if (num_title_idx < 1) { 145 return AVERROR(EIO); 146 } 147 148 /* if playlist was not given, select longest playlist */ 149 if (bd->playlist < 0) { 150 uint64_t duration = 0; 151 int i; 152 for (i = 0; i < num_title_idx; i++) { 153 BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); 154 155 av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", 156 info->playlist, 157 ((int)(info->duration / 90000) / 3600), 158 ((int)(info->duration / 90000) % 3600) / 60, 159 ((int)(info->duration / 90000) % 60)); 160 161 if (info->duration > duration) { 162 bd->playlist = info->playlist; 163 duration = info->duration; 164 } 165 166 bd_free_title_info(info); 167 } 168 av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); 169 } 170 171 /* select playlist */ 172 if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { 173 av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); 174 return AVERROR(EIO); 175 } 176 177 /* select angle */ 178 if (bd->angle >= 0) { 179 bd_select_angle(bd->bd, bd->angle); 180 } 181 182 /* select chapter */ 183 if (bd->chapter > 1) { 184 bd_seek_chapter(bd->bd, bd->chapter - 1); 185 } 186 187 return 0; 188} 189 190static int bluray_read(URLContext *h, unsigned char *buf, int size) 191{ 192 BlurayContext *bd = h->priv_data; 193 int len; 194 195 if (!bd || !bd->bd) { 196 return AVERROR(EFAULT); 197 } 198 199 len = bd_read(bd->bd, buf, size); 200 201 return len; 202} 203 204static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) 205{ 206 BlurayContext *bd = h->priv_data; 207 208 if (!bd || !bd->bd) { 209 return AVERROR(EFAULT); 210 } 211 212 switch (whence) { 213 case SEEK_SET: 214 case SEEK_CUR: 215 case SEEK_END: 216 return bd_seek(bd->bd, pos); 217 218 case AVSEEK_SIZE: 219 return bd_get_title_size(bd->bd); 220 } 221 222 av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); 223 return AVERROR(EINVAL); 224} 225 226 227URLProtocol ff_bluray_protocol = { 228 .name = "bluray", 229 .url_close = bluray_close, 230 .url_open = bluray_open, 231 .url_read = bluray_read, 232 .url_seek = bluray_seek, 233 .priv_data_size = sizeof(BlurayContext), 234 .priv_data_class = &bluray_context_class, 235}; 236