Skip to content
Merged
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
32 changes: 32 additions & 0 deletions webrtc-media/src/main/cpp/include/JNI_AllocationFault.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,38 @@ extern "C" {
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failNextStringCopy
(JNIEnv *, jclass);

/*
* Class: dev_onvoid_webrtc_media_player_AllocationFault
* Method: failNextAllocation
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failNextAllocation
(JNIEnv *, jclass);

/*
* Class: dev_onvoid_webrtc_media_player_AllocationFault
* Method: failNextThreadStart
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failNextThreadStart
(JNIEnv *, jclass);

/*
* Class: dev_onvoid_webrtc_media_player_AllocationFault
* Method: isArmed
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_isArmed
(JNIEnv *, jclass);

/*
* Class: dev_onvoid_webrtc_media_player_AllocationFault
* Method: throwInNative
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_throwInNative
(JNIEnv *, jclass, jint, jstring);

/*
* Class: dev_onvoid_webrtc_media_player_AllocationFault
* Method: disarm
Expand Down
11 changes: 11 additions & 0 deletions webrtc-media/src/main/cpp/include/media/AllocationFault.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@ namespace ffmpeg
public:
// Fails the next copy of a Java string, once.
static void FailNextStringCopy();
// Fails the next other allocation that is checked, once.
static void FailNextAllocation();
// Fails the next thread start that is checked, once, as one does
// that the system has no memory for.
static void FailNextThreadStart();
// Forgets a fault that has not fired.
static void Disarm();
// Whether a fault is armed that has not fired.
static bool IsArmed();
// Throws std::bad_alloc if a fault is armed, and disarms it.
static void CheckStringCopy();
// The same for the other allocations.
static void CheckAllocation();
// Throws std::system_error if a fault is armed, and disarms it.
static void CheckThreadStart();
};
}

Expand Down
7 changes: 6 additions & 1 deletion webrtc-media/src/main/cpp/include/media/MediaRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ namespace ffmpeg
// Start().
int AddTrack(void * frames);

// Attaches to every track and starts writing.
// Attaches to every track and starts writing. A writer thread that
// cannot be started throws, and leaves the recorder unstarted.
void Start();

// Detaches from every track, writes out what is queued, finishes
Expand Down Expand Up @@ -113,6 +114,10 @@ namespace ffmpeg
static void OnEncodedFrame(void * opaque, const wj_encoded_frame * frame);

void Enqueue(Input * input, const wj_encoded_frame * frame);

// Makes up for a frame that could not be queued: the video after
// it is useless until the next key frame.
void Dropped(Input * input, bool video);
void WantKeyFrameLocked(Input * input);

// Makes a video track wait for its next key frame, and asks for
Expand Down
48 changes: 48 additions & 0 deletions webrtc-media/src/main/cpp/src/JNI_AllocationFault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "media/AllocationFault.h"
#include "media/JniBoundary.h"

#include <new>
#include <stdexcept>

// The Java class this belongs to exists in the tests only, so that nothing but
// them can make an allocation fail.

Expand All @@ -27,6 +30,51 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failN
ffmpeg::AllocationFault::FailNextStringCopy();
}

JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failNextAllocation
(JNIEnv * env, jclass caller)
{
ffmpeg::AllocationFault::FailNextAllocation();
}

JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_failNextThreadStart
(JNIEnv * env, jclass caller)
{
ffmpeg::AllocationFault::FailNextThreadStart();
}

JNIEXPORT jboolean JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_isArmed
(JNIEnv * env, jclass caller)
{
return ffmpeg::AllocationFault::IsArmed() ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_throwInNative
(JNIEnv * env, jclass caller, jint kind, jstring message)
{
ffmpeg::CallFromJava(env, [&] {
// Held while throwing, as an entry point holds its arguments.
ffmpeg::JavaStringChars text(env, message);

if (text.get() == nullptr) {
return;
}

switch (kind) {
case 0:
throw std::bad_alloc();
case 1:
throw std::runtime_error(text.get());
case 2:
// A JNI call that failed, and a C++ exception after it.
env->FindClass("dev/onvoid/webrtc/media/player/NoSuchClass");

throw std::runtime_error(text.get());
default:
throw kind;
}
});
}

JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_player_AllocationFault_disarm
(JNIEnv * env, jclass caller)
{
Expand Down
15 changes: 10 additions & 5 deletions webrtc-media/src/main/cpp/src/JNI_FFmpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include "JNI_FFmpeg.h"
#include "media/JniBoundary.h"

extern "C" {
#include <libavformat/avformat.h>
Expand All @@ -24,14 +25,18 @@ extern "C" {
JNIEXPORT jstring JNICALL Java_dev_onvoid_webrtc_media_player_FFmpeg_version
(JNIEnv * env, jclass caller)
{
// The version of the libraries that were actually loaded, which is not
// necessarily the one this module was built against: the LGPL lets an
// application replace them.
return env->NewStringUTF(av_version_info());
return ffmpeg::CallFromJava<jstring>(env, nullptr, [&]() -> jstring {
// The version of the libraries that were actually loaded, which is not
// necessarily the one this module was built against: the LGPL lets an
// application replace them.
return env->NewStringUTF(av_version_info());
});
}

JNIEXPORT jstring JNICALL Java_dev_onvoid_webrtc_media_player_FFmpeg_license
(JNIEnv * env, jclass caller)
{
return env->NewStringUTF(avformat_license());
return ffmpeg::CallFromJava<jstring>(env, nullptr, [&]() -> jstring {
return env->NewStringUTF(avformat_license());
});
}
28 changes: 20 additions & 8 deletions webrtc-media/src/main/cpp/src/JNI_MediaPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ namespace
{
return reinterpret_cast<ffmpeg::MediaPlayer *>(handle);
}

// Releases a player that never made it to Java. Its observer goes first:
// closing reports the closed state, which would be a call into Java for a
// player nobody listens to yet, and with an exception pending such a call
// is not allowed, and would also clear it.
struct DiscardPlayer
{
void operator()(ffmpeg::MediaPlayer * player) const
{
player->SetObserver(nullptr);

delete player;
}
};
}

JNIEXPORT jlong JNICALL Java_dev_onvoid_webrtc_media_player_MediaPlayer_create
Expand Down Expand Up @@ -66,21 +80,19 @@ JNIEXPORT jlong JNICALL Java_dev_onvoid_webrtc_media_player_MediaPlayer_create
return 0;
}

auto player = std::make_unique<ffmpeg::MediaPlayer>(std::move(reader), api,
reinterpret_cast<void *>(videoSourceHandle),
reinterpret_cast<void *>(audioSourceHandle));
// Also released that way when a C++ exception unwinds through here.
std::unique_ptr<ffmpeg::MediaPlayer, DiscardPlayer> player(
new ffmpeg::MediaPlayer(std::move(reader), api,
reinterpret_cast<void *>(videoSourceHandle),
reinterpret_cast<void *>(audioSourceHandle)));

player->SetObserver(std::make_unique<ffmpeg::JavaPlayerObserver>(env, caller));
player->SetHardwareDecoding(hardwareDecoding == JNI_TRUE);

int result = player->Initialize();

if (result < 0) {
// Released before throwing: closing reports the closed state to the
// observer, which calls into Java, and a call into Java with an
// exception pending is not allowed, and would also clear it. Nothing
// is listening yet anyway, so the observer goes first.
player->SetObserver(nullptr);
// Released before throwing, see DiscardPlayer.
player.reset();

ffmpeg::ThrowIOException(env, "Opening the decoders failed: " + ffmpeg::ErrorText(result));
Expand Down
35 changes: 35 additions & 0 deletions webrtc-media/src/main/cpp/src/media/AllocationFault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@

#include <atomic>
#include <new>
#include <system_error>

namespace
{
std::atomic<bool> fail_string_copy{ false };
std::atomic<bool> fail_allocation{ false };
std::atomic<bool> fail_thread_start{ false };
}

namespace ffmpeg
Expand All @@ -31,9 +34,26 @@ namespace ffmpeg
fail_string_copy.store(true);
}

void AllocationFault::FailNextAllocation()
{
fail_allocation.store(true);
}

void AllocationFault::FailNextThreadStart()
{
fail_thread_start.store(true);
}

void AllocationFault::Disarm()
{
fail_string_copy.store(false);
fail_allocation.store(false);
fail_thread_start.store(false);
}

bool AllocationFault::IsArmed()
{
return fail_string_copy.load() || fail_allocation.load() || fail_thread_start.load();
}

void AllocationFault::CheckStringCopy()
Expand All @@ -42,4 +62,19 @@ namespace ffmpeg
throw std::bad_alloc();
}
}

void AllocationFault::CheckAllocation()
{
if (fail_allocation.load() && fail_allocation.exchange(false)) {
throw std::bad_alloc();
}
}

void AllocationFault::CheckThreadStart()
{
if (fail_thread_start.load() && fail_thread_start.exchange(false)) {
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again),
"Starting a thread failed");
}
}
}
25 changes: 24 additions & 1 deletion webrtc-media/src/main/cpp/src/media/JniBoundary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,31 @@
#include "media/AllocationFault.h"

#include <atomic>
#include <cstddef>
#include <exception>
#include <new>

namespace
{
std::atomic<int> held_strings{ 0 };

// The text of a standard exception is in no defined encoding, and may be
// in the locale's, while ThrowNew reads modified UTF-8. Printable ASCII is
// both, so everything else is replaced. It goes into a buffer of fixed
// size, because this may run when memory has run out.
void CopyAscii(const char * text, char * buffer, size_t size) noexcept
{
size_t length = 0;

while (text != nullptr && text[length] != '\0' && length + 1 < size) {
const unsigned char c = static_cast<unsigned char>(text[length]);

buffer[length] = (c >= 0x20 && c < 0x7F) ? static_cast<char>(c) : '?';
length++;
}

buffer[length] = '\0';
}
}

namespace ffmpeg
Expand Down Expand Up @@ -57,7 +76,11 @@ namespace ffmpeg
ThrowJavaException(env, "java/lang/OutOfMemoryError", "Native memory allocation failed");
}
catch (const std::exception & e) {
ThrowJavaException(env, "java/lang/RuntimeException", e.what());
char message[256];

CopyAscii(e.what(), message, sizeof(message));

ThrowJavaException(env, "java/lang/RuntimeException", message);
}
catch (...) {
ThrowJavaException(env, "java/lang/RuntimeException", "Unknown native exception");
Expand Down
3 changes: 3 additions & 0 deletions webrtc-media/src/main/cpp/src/media/MediaPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include "media/MediaPlayer.h"
#include "media/AllocationFault.h"
#include "media/ErrorText.h"

#include <chrono>
Expand Down Expand Up @@ -91,6 +92,8 @@ namespace ffmpeg
{
std::lock_guard<std::mutex> lock(mutex_);

AllocationFault::CheckThreadStart();

running_ = true;
thread_ = std::thread(&MediaPlayer::Run, this);
}
Expand Down
Loading
Loading