Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license = "MIT"
hdr10plus = { path = "./hdr10plus", features = ["hevc", "json"] }
bitvec_helpers = { version = "4.0.2", default-features = false, features = ["bitstream-io"] }
hevc_parser = { version = "0.6.11", features = ["hevc_io"] }
av1_parser = { git = "https://github.com/sven-pke/av1_parser", tag = "v0.1.0", features = ["av1_io"] }

clap = { version = "4.6.0", features = ["derive", "wrap_help", "deprecated"] }
serde_json = { version = "1.0.149", features = ["preserve_order"] }
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# hdr10plus_tool [![CI](https://github.com/quietvoid/hdr10plus_tool/workflows/CI/badge.svg)](https://github.com/quietvoid/hdr10plus_tool/actions/workflows/ci.yml) [![Artifacts](https://github.com/quietvoid/hdr10plus_tool/workflows/Artifacts/badge.svg)](https://github.com/quietvoid/hdr10plus_tool/actions/workflows/release.yml)
CLI utility to work with HDR10+ in HEVC files.
CLI utility to work with HDR10+ in HEVC and AV1 files.
Previously named `hdr10plus_parser`, now it's more than just a parser.

 
Expand All @@ -21,14 +21,15 @@ Options that apply to the commands:

## Commands
* ### **extract**
Extracts the HDR10+ metadata from a HEVC file to a JSON file.
Extracts the HDR10+ metadata from a HEVC or AV1 file to a JSON file.
Also calculates the scene information for compatibility with Samsung tools.

If no output is specified, the file is only parsed partially to verify presence of metadata.

Input file:
- HEVC bitstream
- Matroska: MKV file containing a HEVC video track.
- AV1 bitstream: low overhead OBU stream or IVF file, recognised by its content rather than its extension.
- Matroska: MKV file containing a HEVC or AV1 video track.


**Flags**:
Expand All @@ -42,6 +43,9 @@ Options that apply to the commands:

# Directly using MKV file
hdr10plus_tool extract video.mkv -o metadata.json

# AV1
hdr10plus_tool extract video.av1 -o metadata.json
```
```console
ffmpeg -i input.mkv -map 0:v:0 -c copy -bsf:v hevc_mp4toannexb -f hevc - | hdr10plus_tool extract -o metadata.json -
Expand All @@ -55,21 +59,27 @@ Options that apply to the commands:
 
* ### **inject**
Interleaves HDR10+ metadata NAL units before slices in an HEVC encoded bitstream.
For AV1, one metadata OBU goes before the first frame of each temporal unit, in an OBU stream or IVF file; the output keeps the container of the input.
`--verify` has no effect with this command.

**Example**:
```console
hdr10plus_tool inject -i video.hevc -j metadata.json -o injected_output.hevc

hdr10plus_tool inject -i video.ivf -j metadata.json -o injected_output.ivf
```

 
* ### **remove**
Removes HDR10+ metadata NAL units (or SEI messages) in an HEVC encoded bitstream.
Removes HDR10+ metadata NAL units (or SEI messages) in an HEVC encoded bitstream, or the metadata OBUs in an AV1 one.
AV1 input may also be a Matroska file, written out as an OBU stream.
`--verify` has no effect with this command.

**Example**:
```console
hdr10plus_tool remove video.hevc -o hdr10plus_removed_output.hevc

hdr10plus_tool remove video.mkv -o hdr10plus_removed_output.av1
```
```console
ffmpeg -i input.mkv -map 0:v:0 -c copy -bsf:v hevc_mp4toannexb -f hevc - | hdr10plus_tool remove -
Expand Down
Binary file added assets/av1_tests/regular.av1
Binary file not shown.
Binary file added assets/av1_tests/regular.ivf
Binary file not shown.
Binary file added assets/av1_tests/regular.mkv
Binary file not shown.
Binary file added assets/av1_tests/regular_bl.av1
Binary file not shown.
Binary file added assets/av1_tests/regular_bl.ivf
Binary file not shown.
24 changes: 20 additions & 4 deletions src/commands/extract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::Result;

use super::{CliOptions, ExtractArgs, input_from_either};
use crate::core::initialize_progress_bar;
use crate::core::parser::{Parser, ParserOptions};
use crate::core::{av1, initialize_progress_bar};

pub struct Extractor {}

Expand All @@ -17,12 +17,28 @@ impl Extractor {
} = args;
let input = input_from_either("extract", input, input_pos)?;

let format = hevc_parser::io::format_from_path(&input)?;

if !options.verify && output.is_none() {
options.verify = true
};

let stdin = match av1::Input::open(&input)? {
av1::Input::Av1(mut av1) => {
let pb = av1.progress_bar()?;
let mut parser = Parser::new(
input,
output,
options,
pb,
skip_reorder,
ParserOptions { limit },
);

return parser.process_av1_input(&mut av1);
}
av1::Input::Other(stdin) => stdin,
};

let format = hevc_parser::io::format_from_path(&input)?;
let pb = initialize_progress_bar(&format, &input)?;
let mut parser = Parser::new(
input,
Expand All @@ -33,6 +49,6 @@ impl Extractor {
ParserOptions { limit },
);

parser.process_input(&format)
parser.process_input(&format, stdin)
}
}
20 changes: 19 additions & 1 deletion src/commands/inject.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fs::File;
use std::io::{BufReader, BufWriter, Write, stdout};
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use anyhow::{Result, bail};
use hevc_parser::utils::{
Expand All @@ -15,6 +15,7 @@ use processor::{HevcProcessor, HevcProcessorOpts};
use hdr10plus::metadata_json::{Hdr10PlusJsonMetadata, MetadataJsonRoot};

use crate::commands::InjectArgs;
use crate::core::av1;
use crate::core::{initialize_progress_bar, st2094_40_sei_msg};

use super::{CliOptions, input_from_either};
Expand Down Expand Up @@ -94,6 +95,23 @@ impl Injector {

pub fn inject_json(args: InjectArgs, cli_options: CliOptions) -> Result<()> {
let input = input_from_either("inject", args.input.clone(), args.input_pos.clone())?;

// Injection rewrites a raw stream, for AV1 as for HEVC: an AV1 file on
// disk, raw or IVF. Nothing from Matroska or a pipe.
if let av1::Input::Av1(mut av1) = av1::Input::open(&input)? {
if av1.format() == av1_parser::io::IoFormat::Matroska || input == Path::new("-") {
bail!(
"Injector: Must be a raw HEVC or AV1 bitstream file; \
Matroska and piped input are not supported for injection"
);
}

let output = args
.output
.unwrap_or_else(|| av1.default_output("injected_output"));
return av1.inject(&args.json, &output, cli_options.validate);
}

let format = hevc_parser::io::format_from_path(&input)?;

if let IoFormat::Raw = format {
Expand Down
26 changes: 15 additions & 11 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ pub enum ArgPeakBrightnessSource {

#[derive(Parser, Debug)]
pub enum Command {
#[command(about = "Extracts the HDR10+ metadata from HEVC SEI messages to a JSON file")]
#[command(
about = "Extracts the HDR10+ metadata from HEVC SEI messages or AV1 metadata OBUs to a JSON file"
)]
Extract(ExtractArgs),

#[command(
about = "Interleaves HDR10+ metadata NAL units before slices in an HEVC encoded bitstream"
about = "Interleaves HDR10+ metadata NAL units before slices in an HEVC encoded bitstream, or metadata OBUs in an AV1 one"
)]
Inject(InjectArgs),

#[command(about = "Removes HDR10+ metadata SEI messages in an HEVC encoded bitstream")]
#[command(
about = "Removes HDR10+ metadata SEI messages in an HEVC encoded bitstream, or metadata OBUs in an AV1 one"
)]
Remove(RemoveArgs),

#[command(about = "Plot the HDR10+ dynamic brightness metadata")]
Expand All @@ -49,7 +53,7 @@ pub enum Command {
pub struct ExtractArgs {
#[arg(
id = "input",
help = "Sets the input HEVC file to use, or piped with -",
help = "Sets the input HEVC or AV1 file to use, or piped with -",
long,
short = 'i',
conflicts_with = "input_pos",
Expand All @@ -60,7 +64,7 @@ pub struct ExtractArgs {

#[arg(
id = "input_pos",
help = "Sets the input HEVC file to use, or piped with - (positional)",
help = "Sets the input HEVC or AV1 file to use, or piped with - (positional)",
conflicts_with = "input",
required_unless_present = "input",
value_hint = ValueHint::FilePath
Expand Down Expand Up @@ -94,7 +98,7 @@ pub struct ExtractArgs {
pub struct InjectArgs {
#[arg(
id = "input",
help = "Sets the input HEVC file to use",
help = "Sets the input HEVC or AV1 file to use",
long,
short = 'i',
conflicts_with = "input_pos",
Expand All @@ -105,7 +109,7 @@ pub struct InjectArgs {

#[arg(
id = "input_pos",
help = "Sets the input HEVC file to use (positional)",
help = "Sets the input HEVC or AV1 file to use (positional)",
conflicts_with = "input",
required_unless_present = "input",
value_hint = ValueHint::FilePath
Expand All @@ -123,7 +127,7 @@ pub struct InjectArgs {
#[arg(
long,
short = 'o',
help = "Output HEVC file location",
help = "Output file location",
value_hint = ValueHint::FilePath
)]
pub output: Option<PathBuf>,
Expand All @@ -133,7 +137,7 @@ pub struct InjectArgs {
pub struct RemoveArgs {
#[arg(
id = "input",
help = "Sets the input HEVC file to use, or piped with -",
help = "Sets the input HEVC or AV1 file to use, or piped with -",
long,
short = 'i',
conflicts_with = "input_pos",
Expand All @@ -144,7 +148,7 @@ pub struct RemoveArgs {

#[arg(
id = "input_pos",
help = "Sets the input HEVC file to use, or piped with - (positional)",
help = "Sets the input HEVC or AV1 file to use, or piped with - (positional)",
conflicts_with = "input",
required_unless_present = "input",
value_hint = ValueHint::FilePath
Expand All @@ -154,7 +158,7 @@ pub struct RemoveArgs {
#[arg(
long,
short = 'o',
help = "Sets the output HEVC file to use",
help = "Sets the output file to use",
value_hint = ValueHint::FilePath
)]
pub output: Option<PathBuf>,
Expand Down
27 changes: 22 additions & 5 deletions src/commands/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use indicatif::ProgressBar;
use hevc_parser::io::{IoFormat, IoProcessor};

use super::{CliOptions, RemoveArgs, input_from_either};
use crate::core::{initialize_progress_bar, prefix_sei_removed_hdr10plus_nalu};
use crate::core::{av1, initialize_progress_bar, prefix_sei_removed_hdr10plus_nalu};

pub struct Remover {
input: PathBuf,
Expand All @@ -28,6 +28,15 @@ impl Remover {
} = args;
let input = input_from_either("remove", input, input_pos)?;

let stdin = match av1::Input::open(&input)? {
av1::Input::Av1(mut av1) => {
let output =
output.unwrap_or_else(|| av1.default_output("hdr10plus_removed_output"));
return av1.remove(&output);
}
av1::Input::Other(stdin) => stdin,
};

let format = hevc_parser::io::format_from_path(&input)?;

if format == IoFormat::Matroska {
Expand All @@ -50,10 +59,16 @@ impl Remover {
),
};

remover.process_input(&format)
remover.process_input(&format, stdin)
}

pub fn process_input(&mut self, format: &IoFormat) -> Result<()> {
/// `stdin`: a reader over stdin that already holds the bytes read to
/// tell AV1 from HEVC. Those cannot be read from stdin a second time.
pub fn process_input(
&mut self,
format: &IoFormat,
stdin: Option<Box<dyn BufRead>>,
) -> Result<()> {
let chunk_size = 100_000;

let processor_opts = HevcProcessorOpts {
Expand All @@ -62,8 +77,10 @@ impl Remover {
};
let mut processor = HevcProcessor::new(format.clone(), processor_opts, chunk_size);

let stdin = std::io::stdin();
let mut reader = Box::new(stdin.lock()) as Box<dyn BufRead>;
let mut reader = match stdin {
Some(reader) => reader,
None => Box::new(std::io::stdin().lock()) as Box<dyn BufRead>,
};

if let IoFormat::Raw = format {
let file = File::open(&self.input)?;
Expand Down
Loading