use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ManifestKind {
Hls,
Dash,
}
impl fmt::Display for ManifestKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Hls => f.write_str("HLS"),
Self::Dash => f.write_str("DASH"),
}
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum HttpInputError {
#[error("invalid HTTP input URL: {reason}")]
InvalidUrl {
reason: &'static str,
},
#[error(
"HTTP input URLs must not contain userinfo; pass credentials with the Authorization header"
)]
UserinfoForbidden,
#[error(
"Rust HTTP input supports one HTTP resource per input; {kind} manifests \
open nested resources and are not supported. Use Input::from(url) with an \
FFmpeg build that includes HTTPS/TLS, or resolve the manifest outside \
ez-ffmpeg and provide a single media stream"
)]
ManifestUnsupported {
kind: ManifestKind,
},
#[error(
"Rust HTTP input rejected a nested resource requested by the demuxer. This \
input format is not supported by the single-resource HttpInput API"
)]
NestedResourceUnsupported,
#[error("HTTP server returned a non-identity Content-Encoding; media input requires identity")]
UnsupportedContentEncoding,
#[error("HTTP request failed with status {code}")]
Status {
code: u16,
},
#[error("HTTP authentication required")]
AuthenticationRequired,
#[error("HTTP access denied")]
AccessDenied,
#[error("HTTP proxy authentication required")]
ProxyAuthenticationRequired,
#[error("HTTP resource not found")]
NotFound,
#[error("HTTP request timed out")]
Timeout,
#[error("HTTP body idle timeout")]
ReadIdleTimeout,
#[error("TLS certificate or hostname verification failed")]
TlsVerification,
#[error("HTTP range is not satisfiable")]
RangeNotSatisfiable,
#[error("The HTTP server does not support byte-range requests required to seek this input")]
RangeIgnored,
#[error("HTTP resource changed between requests")]
ResourceChanged,
#[error("too many HTTP redirects")]
TooManyRedirects,
#[error("refusing HTTPS to HTTP redirect")]
HttpsDowngrade,
#[error("HTTP header '{name}' is reserved by HttpInput")]
HeaderReserved {
name: String,
},
#[error("invalid HTTP header '{name}'")]
HeaderInvalid {
name: String,
},
#[error("no usable TLS trust anchors; add a custom CA or install system roots")]
NoTrustAnchors,
#[error("invalid TLS certificate PEM")]
InvalidCertificate,
#[error("invalid TLS client identity PEM (need cert chain and unencrypted private key)")]
IdentityInvalid,
#[error("invalid HTTP proxy configuration")]
InvalidProxy,
#[error("HTTP timeouts must be greater than zero")]
InvalidTimeout,
#[error("HTTP transport error: {message}")]
Transport {
message: String,
},
#[error("HTTP input interrupted")]
Interrupted,
#[error("HTTP response body was truncated before the declared length")]
TruncatedBody,
}
impl HttpInputError {
pub(crate) fn to_errno(&self) -> i32 {
use ffmpeg_sys_next::{
AVERROR, AVERROR_EXIT, EACCES, ECONNRESET, EHOSTUNREACH, EINVAL, EIO, ELOOP, ENOENT,
ENOSYS, EPERM, ETIMEDOUT,
};
match self {
Self::Interrupted => AVERROR_EXIT,
Self::NotFound => AVERROR(ENOENT),
Self::Timeout | Self::ReadIdleTimeout => AVERROR(ETIMEDOUT),
Self::AuthenticationRequired
| Self::AccessDenied
| Self::ProxyAuthenticationRequired
| Self::TlsVerification => AVERROR(EACCES),
Self::NestedResourceUnsupported => AVERROR(EPERM),
Self::TooManyRedirects => AVERROR(ELOOP),
Self::InvalidUrl { .. }
| Self::UserinfoForbidden
| Self::HeaderReserved { .. }
| Self::HeaderInvalid { .. }
| Self::RangeNotSatisfiable
| Self::Status { code: 400 } => AVERROR(EINVAL),
Self::RangeIgnored => AVERROR(ENOSYS),
Self::Transport { message } if message.contains("reset") => AVERROR(ECONNRESET),
Self::Transport { message }
if message.contains("dns") || message.contains("resolve") =>
{
AVERROR(EHOSTUNREACH)
}
_ => AVERROR(EIO),
}
}
}