Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

希望改进 AVFormatContextOutput::create 函数, 可以轻松实现 output format 的 custom io #45

Closed
imxood opened this issue Jun 30, 2021 · 15 comments

Comments

@imxood
Copy link
Contributor

imxood commented Jun 30, 2021

现在接口还不是很完善, 由于我需要 output format 的 custom io, 需要实现: 不输出到文件(即文件名是null), 指定format 比如为mp4, 我需要用 custom io, 在输出format时, 拿到输出的所有数据做别的处理.
现在我的做法是修改了rsmpeg源码, 可以满足我的需求, 但是希望源码可以直接可以满足需求:
image

我的用法:
image
image

非常感谢这个库, 整体还是很好用~

@ldm0
Copy link
Member

ldm0 commented Jun 30, 2021

泪目,这破库居然还有人用,快发PR。

@ldm0
Copy link
Member

ldm0 commented Jun 30, 2021

如果没有时间发的话说一声我自己改下

@imxood
Copy link
Contributor Author

imxood commented Jul 1, 2021

泪目,这破库居然还有人用,快发PR。

我考虑下output format, 周末发个PR~

@imxood
Copy link
Contributor Author

imxood commented Jul 4, 2021

@ldm0 代码编译都正常, 可是, custom io 的 output, 我把输出写到文件中, 文件无法播放..折腾了几天!!

moov atom not found
foo.mp4: Invalid data found when processing input

@ldm0
Copy link
Member

ldm0 commented Jul 4, 2021

@ldm0 代码编译都正常, 可是, custom io 的 output, 我把输出写到文件中, 文件无法播放..折腾了几天!!

moov atom not found
foo.mp4: Invalid data found when processing input

代码瞧瞧呢?

@imxood
Copy link
Contributor Author

imxood commented Jul 5, 2021

代码在这里 https://github.com/imxood/rs-ffmpeg-player/blob/main/server/src/video.rs
在我做了下面这步后, 就没有 moov atom not found 了,

let ist = input_format_context.streams().get(i).unwrap();
      {
        if let Some(ist_metadata) = ist.metadata() {
          if let Some(mut metadata) = out_stream.metadata_mut() {
            unsafe {
              ffi::av_dict_copy(
                &mut metadata.as_mut_ptr(),
                ist_metadata.as_ptr(),
                AV_DICT_DONT_OVERWRITE as i32,
              )
            };
          }
        }
      }

现在错误是:

Output #0, mp4, to './foo1.mp4':
Output file #0 does not contain any stream

AVFormatContextOutput::create 这个函数还是用我改了以后的.

不知道是不是某些参数没有设置, 比如说 什么rate pts, 我没有基础的视频的相关知识....

@imxood
Copy link
Contributor Author

imxood commented Jul 5, 2021

看起来还是同样的问题哎

ffmpeg.exe -i ./foo1.mp4

[mov,mp4,m4a,3gp,3g2,mj2 @ 0000025f2f2a6300] moov atom not found
./foo1.mp4: Invalid data found when processing input

ffmpeg.exe ./foo1.mp4

Output #0, mp4, to './foo1.mp4':
Output file #0 does not contain any stream

@ldm0
Copy link
Member

ldm0 commented Jul 5, 2021

看起来还是同样的问题哎

ffmpeg.exe -i ./foo1.mp4

[mov,mp4,m4a,3gp,3g2,mj2 @ 0000025f2f2a6300] moov atom not found
./foo1.mp4: Invalid data found when processing input

ffmpeg.exe ./foo1.mp4

Output #0, mp4, to './foo1.mp4':
Output file #0 does not contain any stream

吃完饭我看下

@imxood
Copy link
Contributor Author

imxood commented Jul 5, 2021

代码 我提了一个pr: #48

AVFormatContextOutput::create(filename: &CStr, io_context: Option)

我发现这个函数已经可以实现 custom io 了, 需要两个参数, 给第一个参数可以确定 输出的 mux format (不是写文件, 只用来确定format), 给定第二个参数 可以 自定义package的输出

但是这个自定义输出 写数据到文件中, 这个文件 无法播放.

#48 这个 test 是在 tests/transcoding.rs 文件的基础上, 添加了一个 io_context, 其它代码几乎不变.

    let mut buffer = File::create(filename.to_str()?)?;
    // Custom IO Context
    let io_context = AVIOContextCustom::alloc_context(
        AVMem::new(4096),
        true,
        vec![],
        None,
        Some(Box::new(move |_: &mut Vec<u8>, buf: &[u8]| {
            println!("write package, size: {:?}", buf.len());
            buffer.write_all(buf).unwrap();
            buf.len() as _
        })),
        Some(Box::new(|_: &mut Vec<u8>, _: i64, _: i32| 0)),
    );

    let mut output_format_context =
        AVFormatContextOutput::create(filename, Some(AVIOContextContainer::Custom(io_context)))?;
    let mut stream_contexts = vec![];

@ldm0
Copy link
Member

ldm0 commented Jul 7, 2021

代码 我提了一个pr: #48

AVFormatContextOutput::create(filename: &CStr, io_context: Option)

我发现这个函数已经可以实现 custom io 了, 需要两个参数, 给第一个参数可以确定 输出的 mux format (不是写文件, 只用来确定format), 给定第二个参数 可以 自定义package的输出

但是这个自定义输出 写数据到文件中, 这个文件 无法播放.

#48 这个 test 是在 tests/transcoding.rs 文件的基础上, 添加了一个 io_context, 其它代码几乎不变.

    let mut buffer = File::create(filename.to_str()?)?;
    // Custom IO Context
    let io_context = AVIOContextCustom::alloc_context(
        AVMem::new(4096),
        true,
        vec![],
        None,
        Some(Box::new(move |_: &mut Vec<u8>, buf: &[u8]| {
            println!("write package, size: {:?}", buf.len());
            buffer.write_all(buf).unwrap();
            buf.len() as _
        })),
        Some(Box::new(|_: &mut Vec<u8>, _: i64, _: i32| 0)),
    );

    let mut output_format_context =
        AVFormatContextOutput::create(filename, Some(AVIOContextContainer::Custom(io_context)))?;
    let mut stream_contexts = vec![];

抱歉耽搁了比较久,最近摸了。我找了一下你的rs-ffmpeg-player,尝试修了一下,发现第一个主要的问题在于你没有在结束时flush encoder(可以查看transcoding.rs那个test里是怎么flush encoder的)。这个问题修复后在不使用custom io context的情况下工作就正常了。至于CustomIOContext的话我明天确认一下现有的实现是不是有BUG。

@ldm0
Copy link
Member

ldm0 commented Jul 7, 2021

至于CustomIOContext的话我明天确认一下现有的实现是不是有BUG。

没有BUG,#48 (comment)

@imxood
Copy link
Contributor Author

imxood commented Jul 8, 2021

至于CustomIOContext的话我明天确认一下现有的实现是不是有BUG。

没有BUG,#48 (comment)

我本来的想法是: 在后端用 ffmpeg 解封装 -> 解码 -> 编码 -> 封装 这样的形式, 把封装后的数据直接发送到浏览器, 浏览器上用 ffmpeg wasm去解码渲染yuv数据.

但是最后发现这种方式是不可行的, 因为 moov atom not found, 这个问题, 这是由于 转mp4封装的时候 会seek, 在解码结束的时候 会写 moov (atom?)相关的信息, 这导致了 我那种方案, 写入网络的数据无法做到类似seek这样的操作, 如果是本地的file description, 实现fd的seek, 就没问题, 但是到网络中, 我不知道能不能实现类似seek的操作.

为难了我一周了><

现在我的想法是 后端 用 ffmpeg 解封装 -> 解码 -> 编码, 得到了 比如 h265的data和data_size, 把这些video data写入文件后, 是可以直接用ffplay播放这个文件的, 可以把这些编码后的video data直接发送给web, 再wasm decode, webgl渲染, 就可以. 这个方案应该是可行的. 然后会考虑 video 和 audio的同步问题, 又是一个难点...

@imxood
Copy link
Contributor Author

imxood commented Jul 8, 2021

代码 我提了一个pr: #48
AVFormatContextOutput::create(filename: &CStr, io_context: Option)
我发现这个函数已经可以实现 custom io 了, 需要两个参数, 给第一个参数可以确定 输出的 mux format (不是写文件, 只用来确定format), 给定第二个参数 可以 自定义package的输出
但是这个自定义输出 写数据到文件中, 这个文件 无法播放.
#48 这个 test 是在 tests/transcoding.rs 文件的基础上, 添加了一个 io_context, 其它代码几乎不变.

    let mut buffer = File::create(filename.to_str()?)?;
    // Custom IO Context
    let io_context = AVIOContextCustom::alloc_context(
        AVMem::new(4096),
        true,
        vec![],
        None,
        Some(Box::new(move |_: &mut Vec<u8>, buf: &[u8]| {
            println!("write package, size: {:?}", buf.len());
            buffer.write_all(buf).unwrap();
            buf.len() as _
        })),
        Some(Box::new(|_: &mut Vec<u8>, _: i64, _: i32| 0)),
    );

    let mut output_format_context =
        AVFormatContextOutput::create(filename, Some(AVIOContextContainer::Custom(io_context)))?;
    let mut stream_contexts = vec![];

抱歉耽搁了比较久,最近摸了。我找了一下你的rs-ffmpeg-player,尝试修了一下,发现第一个主要的问题在于你没有在结束时flush encoder(可以查看transcoding.rs那个test里是怎么flush encoder的)。这个问题修复后在不使用custom io context的情况下工作就正常了。至于CustomIOContext的话我明天确认一下现有的实现是不是有BUG。

flush的操作我也明白了, 在最后需要flush结束操作吧, 在rust里面目前说实话 很难受, 我这几天在 ffmpeg的doc/examples中写测试代码 调试 验证, 确保没问题, 才敢在rust实现, 不然在rust上要花更多的时间.

@imxood
Copy link
Contributor Author

imxood commented Jul 8, 2021

非常感谢你花时间看我的问题~

@ldm0
Copy link
Member

ldm0 commented Jul 8, 2021

至于CustomIOContext的话我明天确认一下现有的实现是不是有BUG。

没有BUG,#48 (comment)

我本来的想法是: 在后端用 ffmpeg 解封装 -> 解码 -> 编码 -> 封装 这样的形式, 把封装后的数据直接发送到浏览器, 浏览器上用 ffmpeg wasm去解码渲染yuv数据.

但是最后发现这种方式是不可行的, 因为 moov atom not found, 这个问题, 这是由于 转mp4封装的时候 会seek, 在解码结束的时候 会写 moov (atom?)相关的信息, 这导致了 我那种方案, 写入网络的数据无法做到类似seek这样的操作, 如果是本地的file description, 实现fd的seek, 就没问题, 但是到网络中, 我不知道能不能实现类似seek的操作.

为难了我一周了><

现在我的想法是 后端 用 ffmpeg 解封装 -> 解码 -> 编码, 得到了 比如 h265的data和data_size, 把这些video data写入文件后, 是可以直接用ffplay播放这个文件的, 可以把这些编码后的video data直接发送给web, 再wasm decode, webgl渲染, 就可以. 这个方案应该是可行的. 然后会考虑 video 和 audio的同步问题, 又是一个难点...

mp4 格式本身就不是为边下边播设计的,编码到最后阶段才有足够的知识去写文件头,你想要的可能是fragmented mp4或者是一些别的流媒体格式.

flush的操作我也明白了, 在最后需要flush结束操作吧, 在rust里面目前说实话 很难受, 我这几天在 ffmpeg的doc/examples中写测试代码 调试 验证, 确保没问题, 才敢在rust实现, 不然在rust上要花更多的时间.

啊这,我了解你的感受。只能说rsmpeg还是很年轻,没有很好的泛化,让每一种用法都很容易(其实也可以指责一下FFmpeg的文档又少又烂,使得目前rsmpeg还处于一种“懂得都懂,不懂的也用不会”的状态)。

那么我就先close这个issue了。

@ldm0 ldm0 closed this as completed Jul 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants