小编bcl*_*mer的帖子

使用FFmpeg在iOS上编写时将fMP4转码为HLS

TL; DR

我想将fMP4片段转换为TS段(用于HLS),因为片段是在iOS设备上使用FFmpeg编写的.

为什么?

我正在尝试在iOS上实现实时上传,同时在本地保持无缝的高清副本.

我试过的

  1. 滚动AVAssetWriters,每次写入8秒,然后通过FFmpeg连接MP4.

    出了什么问题 - 音频和视频有时会出现问题.我已经确定了3个原因.

    1)为AAC编码器写入的音频启动帧,从而产生间隙.

    2)由于视频帧长33.33ms,音频帧长0.022ms,因此它们可能不在文件末尾排队.

    3)Mac OS上缺少帧精确编码,但iOS 详细信息不适用于此

  2. FFmpeg将具有原始音频的大视频MP4文件复用到TS段中.这项工作基于Kickflip SDK

    什么是错的 - 每隔一段时间,只有一个音频文件会上传,没有任何视频.永远不能在内部重现它,但当他们没有记录他们认为他们做的事情时,它对我们的用户来说非常令人沮丧.在最后一段上也存在准确搜索的问题,几乎就像TS段的时间戳错误一样.

我现在在想什么

苹果公司今年(2016年)正在WWDC推动fMP4,在此之前我还没有对它进行太多调查.由于fMP4文件可以在写入读取和播放,我认为FFmpeg也可以在写入时对文件进行转码,只要我们阻止将字节发送到FFmpeg直到每个片段内文件完成了.

但是,我对FFmpeg C API并不熟悉,我只是在尝试#2中简单地使用它.

我需要你什么

  1. 这是可行的解决方案吗?有人对fMP4足够熟悉,知道我是否真的可以做到这一点?
  2. 我怎么知道AVFoundation已经完成在文件中写一个片段,以便我可以将它管道输入FFmpeg?
  3. 如何从磁盘上的文件中获取数据,一次块,将其传递给FFmpeg并让它吐出TS段?

ffmpeg avfoundation http-live-streaming ios fmp4

57
推荐指数
1
解决办法
1774
查看次数

Kotlin RxJava Nullable Bug

我在使用Kotlin和RxJava的Android应用中遇到了一个问题.它如下所示.

import rx.Observable

data class TestUser(val name: String)

fun getTestUser(): Observable<TestUser> {
    return Observable.just(TestUser("Brian")).flatMap { getUser() } // this compiles
}

fun getTestUser2(): Observable<TestUser> {
    val observable = Observable.just(TestUser("Brian")).flatMap { getUser() }
    return observable // this does not compile
}

fun getUser(): Observable<TestUser?> {
    return Observable.just(null)
}
Run Code Online (Sandbox Code Playgroud)

getTestUser2,编译器推断最终返回类型为Observable<TestUser?>和不编译.然而,getTestUser在代码中进行编译,并且当它运行时,该可观察对象的任何订阅者可能会在TestUser回来时感到意外null.

我猜这与在Kotlin和Java之间来回走动有关.但是,编译器可以看到差异的事实getTestUser2使我认为这可以解决.

编辑

这是在昨天(2016年2月15日)发布的最终版Kotlin 1.0上.

kotlin rx-java

11
推荐指数
1
解决办法
681
查看次数

"表面已被释放"内部"surfaceCreated"

我知道这是一个常见问题,但是这个堆栈跟踪显示其他错误.你可以看到即使setDisplay(holder)被称为内部surfaceCreated仍然会抛出IllegalArgumentException.这也不是一个罕见的例外,昨天在~3,000,000个剪辑视图中发生了~125,000次.我可以向你保证,mCurrentPlayer也可以正确初始化.

surfaceCreated:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mIsSurfaceCreated = true;
    mCurrentPlayer.setDisplay(holder);
}
Run Code Online (Sandbox Code Playgroud)

surfaceDestroy:

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mIsSurfaceCreated = false;

    // Could be called after player was released in onDestroy.
    if (mCurrentPlayer != null) {
        mCurrentPlayer.setDisplay(null);
    }
}
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪:

java.lang.IllegalArgumentException: The surface has been released
    at android.media.MediaPlayer._setVideoSurface(Native Method)
    at android.media.MediaPlayer.setDisplay(MediaPlayer.java:660)
    at com.xxx.xxx.view.VideoPlayerView.surfaceCreated(VideoPlayerView.java:464)
    at android.view.SurfaceView.updateWindow(SurfaceView.java:543)
    at android.view.SurfaceView.access$000(SurfaceView.java:81)
    at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:169)
    at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:590)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1644)
    at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2505)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:154)
    at …
Run Code Online (Sandbox Code Playgroud)

android surfaceholder android-mediaplayer

10
推荐指数
1
解决办法
1万
查看次数

GCD优先级的意外结果

我最近一直在试验GCD优先事项.这是我一直在使用的代码片段.

for _ in 1...1000 {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        for _ in 1...10000000 {
            let _ = sin(0.64739812)
        }
        print("Finished a default")
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0))     {
            for _ in 1...10000 {
                let _ = sin(0.64739812)
            }
            print("Finished a high")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望它能打印出来

Finished a default
Finished a high
// Repeat default and high alternating back and forth 1000 times (because there are 1000 loops)
Run Code Online (Sandbox Code Playgroud)

但实际发生的是打印的日志

Finished a default
Finished a high
Finished a default x 21
Finished a high
Finished …
Run Code Online (Sandbox Code Playgroud)

multithreading grand-central-dispatch ios swift

6
推荐指数
1
解决办法
151
查看次数

UITableView动画期间的用户交互

我有一个UITableView,我的单元格处理所有的触摸而不是tableview本身.细胞需要瞬间响应单击和双击.(是的,这确实意味着单击被调用然后双击,我希望它们都被调用).

单击动作是放入敲击单元格下方的另一个单元格(该单元格是上述单元格的菜单).我为此使用insertRowsAtIndexPaths.然而,在该动画期间,用户交互似乎完全失败,因此我无法在第二次点击时启动该操作.

使用2个UITapGestureRecognizers(单击和双击)并将单个设置为等待双击失败,但会导致单击操作中出现明显的延迟.

我知道在UIView动画中你可以标记它UIViewAnimationOptionAllowUserInteraction并且它会正常工作,但是这不是UITableView中的一个选项.

有关如何在tablview动画期间继续接听触摸事件的任何想法?

我尝试过使用UITapGestureRecognizers,使用touchesEnded和touchesBegan,没有人在动画期间注册过触摸.

animation objective-c touch uitableview ios

5
推荐指数
1
解决办法
395
查看次数

从断开连接的蓝牙设备捕获输入时,AVAudioSession 服务会重置

TL;DR -AVAudioSessionAVAudioSessionMediaServicesWereLostNotification在指定蓝牙端口时触发,AVAudioSession.setPreferredInput并且该设备在使用AVCaptureSession或主动读取输入时断开连接AVAudioEngine。示例项目在这里

https://openradar.appspot.com/FB8921592

问题

通过我认为的“快乐路径”,或者至少应该得到 Apple 支持的路径,我可以始终如一AVAudioSession地触发它AVAudioSessionMediaServicesWereLostNotificationAVAudioSessionMediaServicesWereResetNotification通知。

在这两个通知之间,您的应用无法执行任何音频或视频 I/O 或处理(编码或解码)。这是一个总的媒体服务关闭 1 到 2 秒。

触发

这仅在使用时发生AVAudioSession.setPreferredInput,其中您设置的端口是蓝牙设备。从该端口主动录制时,您关闭蓝牙设备。

发生这种情况时,在服务丢失之前不会触发来自AVAudioSessionAVAudioEngine或 的其他通知AVCaptureSession,因此无法抢先处理这种情况。用户可以随时关闭耳机,导致服务全面失败。

一些代码

首先我设置了音频会话

try! AVAudioSession.sharedInstance().setCategory(.record, mode: .videoRecording, options: .allowBluetooth)
try! AVAudioSession.sharedInstance().setActive(true)
Run Code Online (Sandbox Code Playgroud)

然后指定正确的端口

let bluetoothPort = AVAudioSession.sharedInstance().availableInputs?
    .first(where: { $0.portType == .bluetoothHFP })
guard let validPort = newPort else {
    print("Couldn't find a valid bluetooth port")
    return
} …
Run Code Online (Sandbox Code Playgroud)

core-audio ios avcapturesession avaudiosession avaudioengine

5
推荐指数
0
解决办法
400
查看次数