yading@11: /* yading@11: * BluRay (libbluray) protocol yading@11: * yading@11: * Copyright (c) 2012 Petri Hintukainen users.sourceforge.net> yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavformat/avformat.h" yading@11: #include "libavformat/url.h" yading@11: #include "libavutil/opt.h" yading@11: yading@11: #define BLURAY_PROTO_PREFIX "bluray:" yading@11: #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ yading@11: yading@11: typedef struct { yading@11: const AVClass *class; yading@11: yading@11: BLURAY *bd; yading@11: yading@11: int playlist; yading@11: int angle; yading@11: int chapter; yading@11: /*int region;*/ yading@11: } BlurayContext; yading@11: yading@11: #define OFFSET(x) offsetof(BlurayContext, x) yading@11: static const AVOption options[] = { yading@11: {"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, yading@11: {"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM }, yading@11: {"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM }, yading@11: /*{"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 },*/ yading@11: {NULL} yading@11: }; yading@11: yading@11: static const AVClass bluray_context_class = { yading@11: .class_name = "bluray", yading@11: .item_name = av_default_item_name, yading@11: .option = options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: }; yading@11: yading@11: yading@11: static int check_disc_info(URLContext *h) yading@11: { yading@11: BlurayContext *bd = h->priv_data; yading@11: const BLURAY_DISC_INFO *disc_info; yading@11: yading@11: disc_info = bd_get_disc_info(bd->bd); yading@11: if (!disc_info) { yading@11: av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n"); yading@11: return -1; yading@11: } yading@11: yading@11: if (!disc_info->bluray_detected) { yading@11: av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n"); yading@11: return -1; yading@11: } yading@11: yading@11: /* AACS */ yading@11: if (disc_info->aacs_detected && !disc_info->aacs_handled) { yading@11: if (!disc_info->libaacs_detected) { yading@11: av_log(h, AV_LOG_ERROR, yading@11: "Media stream encrypted with AACS, install and configure libaacs\n"); yading@11: } else { yading@11: av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n"); yading@11: } yading@11: return -1; yading@11: } yading@11: yading@11: /* BD+ */ yading@11: if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { yading@11: /* yading@11: if (!disc_info->libbdplus_detected) { yading@11: av_log(h, AV_LOG_ERROR, yading@11: "Media stream encrypted with BD+, install and configure libbdplus"); yading@11: } else { yading@11: */ yading@11: av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n"); yading@11: /*}*/ yading@11: return -1; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int bluray_close(URLContext *h) yading@11: { yading@11: BlurayContext *bd = h->priv_data; yading@11: if (bd->bd) { yading@11: bd_close(bd->bd); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int bluray_open(URLContext *h, const char *path, int flags) yading@11: { yading@11: BlurayContext *bd = h->priv_data; yading@11: int num_title_idx; yading@11: const char *diskname = path; yading@11: yading@11: av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); yading@11: yading@11: bd->bd = bd_open(diskname, NULL); yading@11: if (!bd->bd) { yading@11: av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: /* check if disc can be played */ yading@11: if (check_disc_info(h) < 0) { yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: /* setup player registers */ yading@11: /* region code has no effect without menus yading@11: if (bd->region > 0 && bd->region < 5) { yading@11: av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1)); yading@11: bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region); yading@11: } yading@11: */ yading@11: yading@11: /* load title list */ yading@11: num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH); yading@11: av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx); yading@11: if (num_title_idx < 1) { yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: /* if playlist was not given, select longest playlist */ yading@11: if (bd->playlist < 0) { yading@11: uint64_t duration = 0; yading@11: int i; yading@11: for (i = 0; i < num_title_idx; i++) { yading@11: BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); yading@11: yading@11: av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", yading@11: info->playlist, yading@11: ((int)(info->duration / 90000) / 3600), yading@11: ((int)(info->duration / 90000) % 3600) / 60, yading@11: ((int)(info->duration / 90000) % 60)); yading@11: yading@11: if (info->duration > duration) { yading@11: bd->playlist = info->playlist; yading@11: duration = info->duration; yading@11: } yading@11: yading@11: bd_free_title_info(info); yading@11: } yading@11: av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); yading@11: } yading@11: yading@11: /* select playlist */ yading@11: if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { yading@11: av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: /* select angle */ yading@11: if (bd->angle >= 0) { yading@11: bd_select_angle(bd->bd, bd->angle); yading@11: } yading@11: yading@11: /* select chapter */ yading@11: if (bd->chapter > 1) { yading@11: bd_seek_chapter(bd->bd, bd->chapter - 1); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int bluray_read(URLContext *h, unsigned char *buf, int size) yading@11: { yading@11: BlurayContext *bd = h->priv_data; yading@11: int len; yading@11: yading@11: if (!bd || !bd->bd) { yading@11: return AVERROR(EFAULT); yading@11: } yading@11: yading@11: len = bd_read(bd->bd, buf, size); yading@11: yading@11: return len; yading@11: } yading@11: yading@11: static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) yading@11: { yading@11: BlurayContext *bd = h->priv_data; yading@11: yading@11: if (!bd || !bd->bd) { yading@11: return AVERROR(EFAULT); yading@11: } yading@11: yading@11: switch (whence) { yading@11: case SEEK_SET: yading@11: case SEEK_CUR: yading@11: case SEEK_END: yading@11: return bd_seek(bd->bd, pos); yading@11: yading@11: case AVSEEK_SIZE: yading@11: return bd_get_title_size(bd->bd); yading@11: } yading@11: yading@11: av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: yading@11: yading@11: URLProtocol ff_bluray_protocol = { yading@11: .name = "bluray", yading@11: .url_close = bluray_close, yading@11: .url_open = bluray_open, yading@11: .url_read = bluray_read, yading@11: .url_seek = bluray_seek, yading@11: .priv_data_size = sizeof(BlurayContext), yading@11: .priv_data_class = &bluray_context_class, yading@11: };