001/******************************************************************************* 002 * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved. 003 * 004 * This file is part of AVPKit. 005 * 006 * AVPKit is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU Lesser General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * AVPKit is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with AVPKit. If not, see <http://www.gnu.org/licenses/>. 018 *******************************************************************************/ 019 020package com.avpkit.core.io; 021 022import java.io.File; 023import java.io.IOException; 024import java.io.RandomAccessFile; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import com.avpkit.core.io.IURLProtocolHandler; 029 030/** 031 * Implementation of URLProtocolHandler that can read and write files. 032 * 033 * This just duplicates all the functionality in the default "file:" protocol 034 * that FFMPEG implemements, but demonstrates how you can have FFMPEG 035 * call back into Java. 036 * 037 * @author aclarke 038 * 039 */ 040public class FileProtocolHandler implements IURLProtocolHandler 041{ 042 File file = null; 043 RandomAccessFile stream = null; 044 045 private final Logger log = LoggerFactory.getLogger(this.getClass()); 046 047 public FileProtocolHandler() 048 { 049 log.debug("Initializing file protocol handler without file"); 050 this.file = null; 051 } 052 053 public FileProtocolHandler(File file) 054 { 055 log.debug("Initializing file protocol handler: {}", file); 056 this.file = file; 057 } 058 059 public FileProtocolHandler(String filename) 060 { 061 log.debug("Initializing file protocol handler: {}", filename); 062 filename = getFilename(filename); 063 if (filename != null) 064 { 065 this.file = new File(filename); 066 } 067 else 068 { 069 this.file = null; 070 } 071 } 072 073 public int close() 074 { 075 log.debug("Closing file: {}", file); 076 try 077 { 078 stream.close(); 079 } 080 catch (IOException e) 081 { 082 log.error("Error closing file: {}", file); 083 e.printStackTrace(); 084 return -1; 085 } 086 087 log.debug("Succesfully closed file: {}", file); 088 return 0; 089 } 090 091 public int open(String url, int flags) 092 { 093 int retval = -1; 094 log.debug("attempting to open {} with flags {}", url == null ? file 095 : url, flags); 096 if (stream != null) 097 this.close(); 098 099 if (this.file == null) 100 { 101 url = getFilename(url); 102 if (url != null) 103 { 104 file = new File(url); 105 } 106 } 107 log.debug("Opening file: {}", file); 108 109 final String mode; 110 switch (flags) 111 { 112 case URL_RDWR: 113 mode = "rw"; 114 break; 115 case URL_WRONLY_MODE: 116 // RandomAccessFile doesn't support write-only 117 mode = "rw"; 118 break; 119 case URL_RDONLY_MODE: 120 mode = "r"; 121 break; 122 default: 123 log.error("Invalid flag passed to open: {}", file); 124 return retval; 125 } 126 127 log.debug("read mode \"{}\" for file: {}", mode, file); 128 129 try 130 { 131 stream = new RandomAccessFile(file, mode); 132 retval = 0; 133 } 134 catch (Exception e) 135 { 136 log.error("Could not find file: {}; ex: {}", file, e); 137 return retval; 138 } 139 log.debug("Opened file: {}", file); 140 return retval; 141 } 142 143 public int read(byte[] buf, int size) 144 { 145 //log.debug("Attempting to read {} bytes from: {}", size, file); 146 try 147 { 148 int ret = -1; 149 ret = stream.read(buf, 0, size); 150 //log.debug("Got result for read: {}", ret); 151 return ret; 152 } 153 catch (IOException e) 154 { 155 log.error("Got IO exception reading from file: {}", file); 156 e.printStackTrace(); 157 return -1; 158 } 159 } 160 161 public long seek(long offset, int whence) 162 { 163 try 164 { 165 final long seek; 166 if (whence == SEEK_SET) 167 seek = offset; 168 else if (whence == SEEK_CUR) 169 seek = stream.getFilePointer() + offset; 170 else if (whence == SEEK_END) 171 seek = stream.length() + offset; 172 else if (whence == SEEK_SIZE) 173 // odd feature of the protocol handler; this request 174 // just returns the file size without actually seeking 175 return (int) stream.length(); 176 else 177 { 178 log.error("invalid seek value \"{}\" for file: {}", whence, file); 179 return -1; 180 } 181 182 stream.seek(seek); 183 log.debug("seeking to \"{}\" in: {}", seek, file); 184 return seek; 185 } 186 catch (IOException e) 187 { 188 log.error("got io exception \"{}\" while seeking in: {}", e 189 .getMessage(), file); 190 e.printStackTrace(); 191 return -1; 192 } 193 } 194 195 public int write(byte[] buf, int size) 196 { 197 //log.debug("writing {} bytes to: {}", size, file); 198 try 199 { 200 stream.write(buf, 0, size); 201 return size; 202 } 203 catch (IOException e) 204 { 205 log.error("Got error writing to file: {}", file); 206 e.printStackTrace(); 207 return -1; 208 } 209 } 210 211 private String getFilename(String url) 212 { 213 String retval = url; 214 if (url != null && url.length() > 0) 215 { 216 int colonIndex = url.indexOf(":"); 217 if (colonIndex > 0) 218 { 219 // remove the URL prefix 220 retval = url.substring(colonIndex + 1); 221 } 222 } 223 log.debug("url->filename: {}->{}", url, retval); 224 return retval; 225 } 226 227 public boolean isStreamed(String url, int flags) 228 { 229 return false; 230 } 231}